/** * Service which provides helper methods for performing some simple UI operations. */ angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$location', 'ApiService', function($timeout, $rootScope, $location, ApiService) { var CheckStateController = function(items, itemKey) { this.items = items; this.checked = []; this.allItems_ = items; this.allCheckedMap_ = {}; this.itemKey_ = itemKey; this.listeners_ = []; this.page_ = null; this.lastChanged_ = null; this.ApiService = ApiService }; CheckStateController.prototype.listen = function(callback) { this.listeners_.push(callback); }; CheckStateController.prototype.isChecked = function(item) { return !!this.allCheckedMap_[item[this.itemKey_]]; }; CheckStateController.prototype.toggleItem = function(item, opt_shift) { if (opt_shift && this.lastChanged_) { var itemIndex = $.inArray(item, this.items); var lastIndex = $.inArray(this.lastChanged_, this.items); if (itemIndex >= 0 && lastIndex >= 0) { var startIndex = Math.min(itemIndex, lastIndex); var endIndex = Math.max(itemIndex, lastIndex); var shouldCheck = this.isChecked(this.lastChanged_); for (var i = startIndex; i <= endIndex; ++i) { if (shouldCheck) { this.checkItem(this.items[i]); } else { this.uncheckItem(this.items[i]); } } return; } } if (this.isChecked(item)) { this.uncheckItem(item); } else { this.checkItem(item); } }; CheckStateController.prototype.toggleItems = function() { this.lastChanged_= null; this.updateMap_(this.checked, false); if (this.checked.length) { this.checked = []; } else { this.checked = this.items.slice(); } this.updateMap_(this.checked, true); this.callListeners_(); }; CheckStateController.prototype.setPage = function(page, pageSize) { this.items = this.allItems_.slice(page * pageSize, (page + 1) * pageSize); this.rebuildCheckedList_(); }; CheckStateController.prototype.setChecked = function(items) { this.allCheckedMap_ = {}; this.updateMap_(items, true); this.rebuildCheckedList_(); }; CheckStateController.prototype.rebuildCheckedList_ = function() { var that = this; this.checked = []; this.items.forEach(function(item) { if (that.allCheckedMap_[item[that.itemKey_]]) { that.checked.push(item); } }); this.callListeners_(); }; CheckStateController.prototype.updateMap_ = function(items, is_checked) { var that = this; items.forEach(function(item) { if (item == null) { return; } that.allCheckedMap_[item[that.itemKey_]] = is_checked; }); }; CheckStateController.prototype.checkByFilter = function(filter) { this.updateMap_(this.checked, false); this.checked = $.grep(this.items, filter); this.updateMap_(this.checked, true); this.callListeners_(); }; CheckStateController.prototype.checkItem = function(item) { if (this.isChecked(item)) { return; } this.lastChanged_ = item; this.checked.push(item); this.allCheckedMap_[item[this.itemKey_]] = true; this.callListeners_(); }; CheckStateController.prototype.uncheckItem = function(item) { if (!this.isChecked(item)) { return; } this.lastChanged_ = item; this.checked = $.grep(this.checked, function(cItem) { return cItem != item; }); this.allCheckedMap_[item[this.itemKey_]] = false; this.callListeners_(); }; CheckStateController.prototype.callListeners_ = function() { var that = this; var allCheckedMap = this.allCheckedMap_; var allChecked = []; this.allItems_.forEach(function(item) { var key = item[that.itemKey_]; if (!!allCheckedMap[key]) { allChecked.push(item); } }); this.listeners_.map(function(listener) { listener(allChecked, that.checked); }); }; ////////////////////////////////////////////////////////////////////////////////////// var uiService = {}; uiService.hidePopover = function(elem) { var popover = $(elem).data('bs.popover'); if (popover) { popover.hide(); } }; uiService.showPopover = function(elem, content, opt_placement) { var popover = $(elem).data('bs.popover'); if (!popover) { $(elem).popover({'content': '-', 'placement': opt_placement || 'left'}); } setTimeout(function() { var popover = $(elem).data('bs.popover'); popover.options.content = content; popover.show(); }, 500); }; uiService.showFormError = function(elem, result, opt_placement) { var message = ApiService.getErrorMessage(result, 'error'); if (message) { uiService.showPopover(elem, message, opt_placement || 'bottom'); } else { uiService.hidePopover(elem); } }; uiService.createCheckStateController = function(items, opt_checked) { return new CheckStateController(items, opt_checked); }; uiService.showPasswordDialog = function(message, callback, opt_canceledCallback) { var success = function() { var password = $('#passDialogBox').val(); $('#passDialogBox').val(''); callback(password); }; var canceled = function() { $('#passDialogBox').val(''); opt_canceledCallback && opt_canceledCallback(); }; var box = bootbox.dialog({ "message": message + '
', "title": 'Please Verify', "buttons": { "verify": { "label": "Verify", "className": "btn-success", "callback": success }, "close": { "label": "Cancel", "className": "btn-default", "callback": canceled } } }); box.bind('shown.bs.modal', function(){ box.find("input").focus(); box.find("form").submit(function() { if (!$('#passDialogBox').val()) { return; } box.modal('hide'); success(); }); }); }; uiService.clickElement = function(el) { // From: http://stackoverflow.com/questions/16802795/click-not-working-in-mocha-phantomjs-on-certain-elements var ev = document.createEvent("MouseEvent"); ev.initMouseEvent( "click", true /* bubble */, true /* cancelable */, window, null, 0, 0, 0, 0, /* coordinates */ false, false, false, false, /* modifier keys */ 0 /*left*/, null); el.dispatchEvent(ev); }; uiService.initializeTabs = function(scope, element, opt_clickCallback) { var locationListener = null; var disposed = false; var changeTab = function(activeTab) { if (disposed) { return; } $('a[data-toggle="tab"]').each(function(index) { var tabName = this.getAttribute('data-target').substr(1); if (tabName != activeTab) { return; } if ($(this).parent().hasClass('active')) { return; } if (this.clientWidth == 0) { setTimeout(function() { changeTab(activeTab); }, 100); return; } var elem = this; setTimeout(function() { uiService.clickElement(elem); }, 0); }); }; var resetDefaultTab = function() { if (disposed) { return; } $timeout(function() { element.find('a[data-toggle="tab"]').each(function(index) { if (index == 0) { var elem = this; setTimeout(function() { uiService.clickElement(elem); }, 0); } }); }); }; var checkTabs = function() { if (disposed) { return; } // Poll until we find the tabs. var tabs = element.find('a[data-toggle="tab"]'); if (tabs.length == 0) { $timeout(checkTabs, 50); return; } // Register listeners. registerListeners(tabs); // Set the active tab (if any). var activeTab = $location.search()['tab']; if (activeTab) { changeTab(activeTab); } }; var registerListeners = function(tabs) { // Listen for scope destruction. scope.$on('$destroy', function() { disposed = true; locationListener && locationListener(); }); // Listen for route changes and update the tabs accordingly. locationListener = $rootScope.$on('$routeUpdate', function(){ if ($location.search()['tab']) { changeTab($location.search()['tab']); } else { resetDefaultTab(); } }); // Listen for tab changes. tabs.on('shown.bs.tab', function (e) { // Invoke the callback, if any. opt_clickCallback && opt_clickCallback(); // Update the search location. var tabName = e.target.getAttribute('data-target').substr(1); $rootScope.$apply(function() { var isDefaultTab = tabs[0] == e.target; var newSearch = $.extend($location.search(), {}); if (isDefaultTab) { delete newSearch['tab']; } else { newSearch['tab'] = tabName; } $location.search(newSearch); }); e.preventDefault(); }); }; // Start the checkTabs timer. checkTabs(); }; return uiService; }]);