diff --git a/library/Icinga/Web/JavaScript.php b/library/Icinga/Web/JavaScript.php index 6a378559f..ed39c1b39 100644 --- a/library/Icinga/Web/JavaScript.php +++ b/library/Icinga/Web/JavaScript.php @@ -26,7 +26,8 @@ class JavaScript 'js/icinga/behavior/sparkline.js', 'js/icinga/behavior/tristate.js', 'js/icinga/behavior/navigation.js', - 'js/icinga/behavior/form.js' + 'js/icinga/behavior/form.js', + 'js/icinga/behavior/actiontable.js' ); protected static $vendorFiles = array( diff --git a/modules/monitoring/application/views/helpers/Link.php b/modules/monitoring/application/views/helpers/Link.php index 35c5a4cbc..e37375d14 100644 --- a/modules/monitoring/application/views/helpers/Link.php +++ b/modules/monitoring/application/views/helpers/Link.php @@ -45,10 +45,11 @@ class Zend_View_Helper_Link extends Zend_View_Helper_Abstract * @param string $serviceLinkText Text for the service link, e.g. the service's display name * @param string $host Hostname * @param string $hostLinkText Text for the host link, e.g. the host's display name + * @param string $class An optional class to use for this link * * @return string */ - public function service($service, $serviceLinkText, $host, $hostLinkText) + public function service($service, $serviceLinkText, $host, $hostLinkText, $class = null) { return sprintf( '%s: %s', @@ -57,11 +58,14 @@ class Zend_View_Helper_Link extends Zend_View_Helper_Abstract $serviceLinkText, 'monitoring/service/show', array('host' => $host, 'service' => $service), - array('title' => sprintf( - $this->view->translate('Show detailed information for service %s on host %s'), - $service, - $host - )) + array( + 'title' => sprintf( + $this->view->translate('Show detailed information for service %s on host %s'), + $service, + $host + ), + 'class' => $class + ) ) ); } diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml index 43b94be66..7c3863d17 100644 --- a/modules/monitoring/application/views/scripts/list/comments.phtml +++ b/modules/monitoring/application/views/scripts/list/comments.phtml @@ -43,11 +43,14 @@ if (count($comments) === 0) { ), 'monitoring/comment/show', array('comment_id' => $comment->id), - array('title' => sprintf( - $this->translate('Show detailed information for comment on %s for %s'), - $comment->service_display_name, - $comment->host_display_name - ))) ?> + array( + 'title' => sprintf( + $this->translate('Show detailed information for comment on %s for %s'), + $comment->service_display_name, + $comment->host_display_name + ), + 'class' => 'rowaction' + )) ?> icon('host', $this->translate('Host')); ?> diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index e0836aa26..0c587a03f 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -57,11 +57,14 @@ if (count($downtimes) === 0) { sprintf('%s: %s', $downtime->host_display_name, $downtime->service_display_name), 'monitoring/downtime/show', array('downtime_id' => $downtime->id), - array('title' => sprintf( - $this->translate('Show detailed information for downtime on %s for %s'), - $downtime->service_display_name, - $downtime->host_display_name - ))) ?> + array( + 'title' => sprintf( + $this->translate('Show detailed information for downtime on %s for %s'), + $downtime->service_display_name, + $downtime->host_display_name + ), + 'class' => 'rowaction' + )) ?>
icon('comment', $this->translate('Comment')); ?> [escape($downtime->author_name) ?>] escape($downtime->comment) ?>
diff --git a/modules/monitoring/application/views/scripts/list/eventhistory.phtml b/modules/monitoring/application/views/scripts/list/eventhistory.phtml index 538702a64..ab9cb2331 100644 --- a/modules/monitoring/application/views/scripts/list/eventhistory.phtml +++ b/modules/monitoring/application/views/scripts/list/eventhistory.phtml @@ -82,7 +82,7 @@ if (count($history) === 0) { link()->service( - $event->service_description, $event->service_display_name, $event->host_name, $event->host_display_name + $event->service_description, $event->service_display_name, $event->host_name, $event->host_display_name, 'rowaction' ) ?> link()->host($event->host_name, $event->host_display_name) ?> diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index 15dbb5a6f..0d033eaad 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -60,7 +60,8 @@ if (count($hosts) === 0) { $hostLink, null, array( - 'title' => sprintf($this->translate('Show detailed information for host %s'), $host->host_display_name) + 'title' => sprintf($this->translate('Show detailed information for host %s'), $host->host_display_name), + 'class' => 'rowaction' ) ); ?> host_unhandled_services) && $host->host_unhandled_services > 0): ?> diff --git a/public/js/icinga/behavior/actiontable.js b/public/js/icinga/behavior/actiontable.js new file mode 100644 index 000000000..d3df54614 --- /dev/null +++ b/public/js/icinga/behavior/actiontable.js @@ -0,0 +1,396 @@ +/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ + +/** + * Icinga.Behavior.ActionTable + * + * A multi selection that distincts between the table rows using the row action URL filter + */ +(function(Icinga, $) { + + "use strict"; + + /** + * Remove one leading and trailing bracket and all text outside those brackets + * + * @param str {String} + * @returns {string} + */ + var stripBrackets = function (str) { + return str.replace(/^[^\(]*\(/, '').replace(/\)[^\)]*$/, ''); + }; + + /** + * Parse the filter query contained in the given url filter string + * + * @param filterString {String} + * + * @returns {Array} An object containing each row filter + */ + var parseSelectionQuery = function(filterString) { + var selections = []; + $.each(stripBrackets(filterString).split('|'), function(i, row) { + var tuple = {}; + $.each(stripBrackets(row).split('&'), function(i, keyValue) { + var s = keyValue.split('='); + tuple[s[0]] = decodeURIComponent(s[1]); + }); + selections.push(tuple); + }); + return selections; + }; + + /** + * Handle the selection of an action table + * + * @param table {HTMLElement} The table + * @param {Icinga} + * + * @constructor + */ + var Selection = function(table, icinga) { + this.$el = $(table); + this.icinga = icinga; + + if (this.hasMultiselection()) { + if (! this.getMultiselectionKeys().length) { + icinga.logger.error('multiselect table has no data-icinga-multiselect-data'); + } + if (! this.getMultiselectionUrl()) { + icinga.logger.error('multiselect table has no data-icinga-multiselect-url'); + } + } + }; + + Selection.prototype = { + + /** + * Return all rows as jQuery selector + * + * @returns {jQuery} + */ + rows: function() { + return this.$el.find('tr'); + }, + + /** + * Return all row action links as jQuery selector + * + * @returns {jQuery} + */ + rowActions: function() { + return this.$el.find('tr a.rowaction'); + }, + + /** + * Return all selected rows as jQuery selector + * + * @returns {jQuery} + */ + selections: function() { + return this.$el.find('tr.active'); + }, + + /** + * If this selection allows selecting multiple rows + * + * @returns {Boolean} + */ + hasMultiselection: function() { + return this.$el.hasClass('multiselect'); + }, + + /** + * Return all filter keys that are significant when applying the selection + * + * @returns {Array} + */ + getMultiselectionKeys: function() { + var data = this.$el.data('icinga-multiselect-data'); + return (data && data.split(',')) || []; + }, + + /** + * Return the target URL that is used when multi selecting rows + * + * This URL may differ from the url that is used when applying single rows + * + * @returns {String} + */ + getMultiselectionUrl: function() { + return this.$el.data('icinga-multiselect-url'); + }, + + /** + * Read all filter data from the given row + * + * @param row {jQuery} The row element + * + * @returns {Object} An object containing all filter data in this row as key-value pairs + */ + getRowData: function(row) { + var params = this.icinga.utils.parseUrl(row.attr('href')).params; + var tuple = {}; + var keys = this.getMultiselectionKeys(); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (params[key]) { + tuple[key] = params[key]; + } + } + return tuple; + }, + + /** + * Deselect all selected rows + */ + clear: function() { + this.selections().removeClass('active'); + }, + + /** + * Add all rows that match the given filter to the selection + * + * @param filter {jQuery|Object} Either an object containing filter variables or the actual row to select + */ + select: function(filter) { + if (filter instanceof jQuery) { + filter.addClass('active'); + return; + } + var self = this; + var url = this.getMultiselectionUrl(); + this.rowActions() + .filter( + function (i, el) { + var params = self.getRowData($(el)); + if (self.icinga.utils.objectKeys(params).length !== self.icinga.utils.objectKeys(filter).length) { + return false; + } + var equal = true; + $.each(params, function(key, value) { + if (filter[key] !== value) { + equal = false; + } + }); + return equal; + } + ) + .closest('tr') + .addClass('active'); + }, + + /** + * Toggle the selection of the row between on and off + * + * @param row {jQuery} The row to toggle + */ + toggle: function(row) { + row.toggleClass('active'); + }, + + /** + * Add a new selection range to the closest table, using the selected row as + * range target. + * + * @param row {jQuery} The target of the selected range. + * + * @returns {boolean} If the selection was changed. + */ + range: function(row) { + var from, to; + var selected = row.first().get(0); + this.rows().each(function(i, el) { + if ($(el).hasClass('active') || el === selected) { + if (!from) { + from = el; + } + to = el; + } + }); + var inRange = false; + this.rows().each(function(i, el) { + if (el === from) { + inRange = true; + } + if (inRange) { + $(el).addClass('active'); + } + if (el === to) { + inRange = false; + } + }); + return false; + }, + + /** + * Select rows that target the given url + * + * @param url {String} The target url + */ + selectUrl: function(url) { + this.rows().filter('[href="' + url + '"]').addClass('active'); + }, + + /** + * Convert all currently selected rows into an url query string + * + * @returns {String} The filter string + */ + toQuery: function() { + var self = this; + var selections = this.selections(); + var queries = []; + if (selections.length === 1) { + return $(selections[0]).attr('href'); + } else if (selections.length > 1 && self.hasMultiselection()) { + selections.each(function (i, el) { + var parts = []; + $.each(self.getRowData($(el)), function(key, value) { + parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + queries.push('(' + parts.join('&') + ')'); + }); + return self.getMultiselectionUrl() + '?(' + queries.join('|') + ')'; + } else { + return ''; + } + }, + + /** + * Refresh the displayed active columns using the current page location + */ + refresh: function() { + this.clear(); + var hash = this.icinga.utils.parseUrl(window.location.href).hash; + if (this.hasMultiselection()) { + var query = parseSelectionQuery(hash); + if (query.length > 1 && this.getMultiselectionUrl() === this.icinga.utils.parseUrl(hash.substr(1)).path) { + // select all rows with matching filters + var self = this; + $.each(query, function(i, selection) { + self.select(selection); + }); + } + if (query.length > 1) { + return; + } + } + this.selectUrl(hash.substr(1)); + } + }; + + Icinga.Behaviors = Icinga.Behaviors || {}; + + var ActionTable = function (icinga) { + Icinga.EventListener.call(this, icinga); + + /** + * The hash that is currently being loaded + * + * @var String + */ + this.loadingHash = null; + + /** + * If currently loading + * + * @var Boolean + */ + this.loading = false; + + this.on('rendered', this.onRendered, this); + this.on('click', 'table.action tr[href]', this.onRowClicked, this); + }; + ActionTable.prototype = new Icinga.EventListener(); + + /** + * Return all active tables in this table, or in the context as jQuery selector + * + * @param context {HTMLElement} + * @returns {jQuery} + */ + ActionTable.prototype.tables = function(context) { + if (context) { + return $(context).find('table.action'); + } + return $('table.action'); + }; + + /** + * Handle clicks on table rows and update selection and history + */ + ActionTable.prototype.onRowClicked = function (event) { + var self = event.data.self; + var $tr = $(event.target).closest('tr'); + var table = new Selection($tr.closest('table.action')[0], self.icinga); + + // allow form actions in table rows to pass through + if ($(event.target).closest('form').length) { + return; + } + event.stopPropagation(); + event.preventDefault(); + + // update selection + if (table.hasMultiselection()) { + if (event.ctrlKey || event.metaKey) { + // add to selection + table.toggle($tr); + } else if (event.shiftKey) { + // range selection + table.range($tr); + } else { + // single selection + table.clear(); + table.select($tr); + } + } else { + table.clear(); + table.select($tr); + } + + // update history + var url = self.icinga.utils.parseUrl(window.location.href.split('#')[0]); + var count = table.selections().length; + var state = url.path + url.query; + if (count > 0) { + var query = table.toQuery(); + self.icinga.loader.loadUrl(query, self.icinga.events.getLinkTargetFor($tr)); + state += '#!' + query; + } else { + if (self.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') { + self.icinga.ui.layout1col(); + } + } + self.icinga.history.pushUrl(state); + + // re draw all table selections + self.tables().each(function () { + new Selection(this, self.icinga).refresh(); + }); + + // update selection info + $('.selection-info-count').text(table.selections().size()); + return false; + }; + + /** + * Ensure that + */ + ActionTable.prototype.onRendered = function(evt) { + var container = evt.target; + var self = evt.data.self; + + // draw all selections + self.tables().each(function(i, el) { + new Selection(el, self.icinga).refresh(); + }); + + // update displayed selection count + var table = new Selection(self.tables(container).first()); + $(container).find('.selection-info-count').text(table.selections().size()); + }; + + Icinga.Behaviors.ActionTable = ActionTable; + +}) (Icinga, jQuery); diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 4d4ae736c..00a0ff9f4 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -117,9 +117,6 @@ $(document).on('click', 'a', { self: this }, this.linkClicked); $(document).on('click', 'tr[href]', { self: this }, this.linkClicked); - // Select a table row - $(document).on('click', 'table.multiselect tr[href]', { self: this }, this.rowSelected); - // We catch all form submit events $(document).on('submit', 'form', { self: this }, this.submitForm); @@ -303,74 +300,6 @@ return false; }, - /** - * Handle table selection. - */ - rowSelected: function(event) { - var self = event.data.self; - var icinga = self.icinga; - var $tr = $(this); - var $table = $tr.closest('table.multiselect'); - var data = self.icinga.ui.getSelectionKeys($table); - var url = $table.data('icinga-multiselect-url'); - - if ($(event.target).closest('form').length) { - // allow form actions in table rows to pass through - return; - } - event.stopPropagation(); - event.preventDefault(); - - if (!data) { - icinga.logger.error('multiselect table has no data-icinga-multiselect-data'); - return; - } - if (!url) { - icinga.logger.error('multiselect table has no data-icinga-multiselect-url'); - return; - } - - // update selection - if (event.ctrlKey || event.metaKey) { - icinga.ui.toogleTableRowSelection($tr); - // multi selection - } else if (event.shiftKey) { - // range selection - icinga.ui.addTableRowRangeSelection($tr); - } else { - // single selection - icinga.ui.setTableRowSelection($tr); - } - // focus only the current table. - icinga.ui.focusTable($table[0]); - - var $target = self.getLinkTargetFor($tr); - - var $trs = $table.find('tr[href].active'); - if ($trs.length > 1) { - var selectionData = icinga.ui.getSelectionSetData($trs, data); - var query = icinga.ui.selectionDataToQuery(selectionData); - icinga.loader.loadUrl(url + '?' + query, $target); - icinga.ui.storeSelectionData(selectionData); - icinga.ui.provideSelectionCount(); - } else if ($trs.length === 1) { - // display a single row - $tr = $trs.first(); - icinga.loader.loadUrl($tr.attr('href'), $target); - icinga.ui.storeSelectionData($tr.attr('href')); - icinga.ui.provideSelectionCount(); - } else { - // display nothing - if ($target.attr('id') === 'col2') { - icinga.ui.layout1col(); - } - icinga.ui.storeSelectionData(null); - icinga.ui.provideSelectionCount(); - } - - return false; - }, - /** * Handle anchor, i.e. focus the element which is referenced by the anchor * @@ -406,16 +335,16 @@ // Special checks for link clicks in multiselect rows if (! $a.is('tr[href]') && $a.closest('tr[href]').length > 0 && $a.closest('table.multiselect').length > 0) { - // Forward clicks to ANY link with special key pressed to rowSelected + // ignoray clicks to ANY link with special key pressed if (event.ctrlKey || event.metaKey || event.shiftKey) { - return self.rowSelected.call($a.closest('tr[href]'), event); + return true; } - // Forward inner links matching the row URL to rowSelected + // ignore inner links matching the row URL if ($a.attr('href') === $a.closest('tr[href]').attr('href')) { - return self.rowSelected.call($a.closest('tr[href]'), event); + return true; } } @@ -477,8 +406,6 @@ icinga.ui.layout1col(); } $('table tr[href].active').removeClass('active'); - icinga.ui.storeSelectionData(null); - icinga.ui.loadSelectionData(); icinga.history.pushCurrentState(); } } @@ -574,8 +501,6 @@ $(window).off('beforeunload', this.onUnload); $(document).off('scroll', '.container', this.onContainerScroll); $(document).off('click', 'a', this.linkClicked); - $(document).off('click', 'table.action tr[href]', this.rowSelected); - $(document).off('click', 'table.action tr a', this.rowSelected); $(document).off('submit', 'form', this.submitForm); $(document).off('change', 'form select.autosubmit', this.submitForm); $(document).off('change', 'form input.autosubmit', this.submitForm); diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 082b41e87..5b345a308 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -504,42 +504,6 @@ this.icinga.ui.fixDebugVisibility().triggerWindowResize(); } self.cacheLoadedIcons(req.$target); - - if (active) { - var focusedUrl = this.icinga.ui.getFocusedContainerDataUrl(); - var oldSelectionData = this.icinga.ui.loadSelectionData(); - if (typeof oldSelectionData === 'string') { - $('[href="' + oldSelectionData + '"]', req.$target).addClass('active'); - - } else if (oldSelectionData !== null) { - var $container; - if (!focusedUrl) { - $container = $('document').first(); - } else { - $container = $('.container[data-icinga-url="' + focusedUrl + '"]'); - } - - var $table = $container.find('table.action').first(); - var keys = self.icinga.ui.getSelectionKeys($table); - - // build map of selected queries - var oldSelectionQueries = {}; - $.each(oldSelectionData, function(i, query){ - oldSelectionQueries[self.icinga.ui.selectionDataToQueryComp(query)] = true; - }); - - // set all new selections to active - $table.find('tr[href]').filter(function(){ - var $tr = $(this); - var rowData = self.icinga.ui.getSelectionData($tr, keys, self.icinga); - var newSelectionQuery = self.icinga.ui.selectionDataToQueryComp(rowData); - if (oldSelectionQueries[newSelectionQuery]) { - return true; - } - return false; - }).addClass('active'); - } - } }, /** diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index cd5290f32..d2f9ab138 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -9,13 +9,6 @@ 'use strict'; - // Stores the icinga-data-url of the last focused table. - var focusedTableDataUrl = null; - - // The stored selection data, useful for preserving selections over - // multiple reload-cycles. - var selectionData = null; - Icinga.UI = function (icinga) { this.icinga = icinga; @@ -38,8 +31,7 @@ this.fadeNotificationsAway(); }, - fadeNotificationsAway: function() - { + fadeNotificationsAway: function() { var icinga = this.icinga; $('#notifications li') .not('.fading-out') @@ -298,226 +290,6 @@ return $('#main > .container').length; }, - /** - * Add the given table-row to the selection of the closest - * table and deselect all other rows of the closest table. - * - * @param $tr {jQuery} The selected table row. - * @returns {boolean} If the selection was changed. - */ - setTableRowSelection: function ($tr) { - var $table = $tr.closest('table.multiselect'); - $table.find('tr[href].active').removeClass('active'); - $tr.addClass('active'); - return true; - }, - - /** - * Toggle the given table row to "on" when not selected, or to "off" when - * currently selected. - * - * @param $tr {jQuery} The table row. - * @returns {boolean} If the selection was changed. - */ - toogleTableRowSelection: function ($tr) { - // multi selection - if ($tr.hasClass('active')) { - $tr.removeClass('active'); - } else { - $tr.addClass('active'); - } - return true; - }, - - /** - * Add a new selection range to the closest table, using the selected row as - * range target. - * - * @param $tr {jQuery} The target of the selected range. - * @returns {boolean} If the selection was changed. - */ - addTableRowRangeSelection: function ($tr) { - var $table = $tr.closest('table.multiselect'); - var $rows = $table.find('tr[href]'), - from, to; - var selected = $tr.first().get(0); - $rows.each(function(i, el) { - if ($(el).hasClass('active') || el === selected) { - if (!from) { - from = el; - } - to = el; - } - }); - var inRange = false; - $rows.each(function(i, el){ - if (el === from) { - inRange = true; - } - if (inRange) { - $(el).addClass('active'); - } - if (el === to) { - inRange = false; - } - }); - return false; - }, - - - /** - * Read the data from a whole set of selections. - * - * @param $selections {jQuery} All selected rows in a jQuery-selector. - * @param keys {Array} An array containing all valid keys. - * @returns {Array} An array containing an object with the data for each selection. - */ - getSelectionSetData: function($selections, keys) { - var selections = []; - var icinga = this.icinga; - - // read all current selections - $selections.each(function(ind, selected) { - selections.push(icinga.ui.getSelectionData($(selected), keys, icinga)); - }); - return selections; - }, - - getSelectionKeys: function($selection) - { - var d = $selection.data('icinga-multiselect-data') && $selection.data('icinga-multiselect-data').split(','); - return d || []; - }, - - /** - * Read the data from the given selected object. - * - * @param $selection {jQuery} The selected object. - * @param keys {Array} An array containing all valid keys. - * @param icinga {Icinga} The main icinga object. - * @returns {Object} An object containing all key-value pairs associated with this selection. - */ - getSelectionData: function($selection, keys, icinga) - { - var url = $selection.attr('href'); - var params = this.icinga.utils.parseUrl(url).params; - var tuple = {}; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (params[key]) { - tuple[key] = params[key]; - } - } - return tuple; - }, - - /** - * Convert a set of selection data to a single query. - * - * @param selectionData {Array} The selection data generated from getSelectionData - * @returns {String} The formatted and uri-encoded query-string. - */ - selectionDataToQuery: function (selectionData) { - var queries = []; - - // create new url - if (selectionData.length < 2) { - this.icinga.logger.error('Something went wrong, we should never multiselect just one row'); - } else { - $.each(selectionData, function(i, el){ - var parts = [] - $.each(el, function(key, value) { - parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); - }); - queries.push('(' + parts.join('&') + ')'); - }); - } - return '(' + queries.join('|') + ')'; - }, - - /** - * Create a single query-argument (not compatible to selectionDataToQuery) - * - * @param data - * @returns {string} - */ - selectionDataToQueryComp: function(data) { - var queries = []; - $.each(data, function(key, value){ - queries.push(key + '=' + encodeURIComponent(value)); - }); - return queries.join('&'); - }, - - /** - * Store a set of selection-data to preserve it accross page-reloads - * - * @param data {Array|String|Null} The selection-data be an Array of Objects, - * containing the selection data (when multiple rows where selected), a - * String containing a single url (when only a single row was selected) or - * Null when nothing was selected. - */ - storeSelectionData: function(data) { - selectionData = data; - }, - - /** - * Load the last stored set of selection-data - * - * @returns {Array|String|Null} May be an Array of Objects, containing the selection data - * (when multiple rows where selected), a String containing a single url - * (when only a single row was selected) or Null when nothing was selected. - */ - loadSelectionData: function() { - this.provideSelectionCount(); - return selectionData; - }, - - /** - * Set the selections row count hint info - */ - provideSelectionCount: function() { - var $count = $('.selection-info-count'); - - if (typeof selectionData === 'undefined' || selectionData === null) { - $count.text(0); - return; - } - - if (typeof selectionData === 'string') { - $count.text(1); - } else if (selectionData.length > 1) { - $count.text(selectionData.length); - } else { - $count.text(0); - } - }, - - /** - * Focus the given table by deselecting all selections on all other tables. - * - * Focusing a table is important for environments with multiple tables like - * the dashboard. It should only be possible to select rows at one table at a time, - * when a user selects a row on a table all rows that are not child of the given table - * will be removed from the selection. - * - * @param table {htmlElement} The table to focus. - */ - focusTable: function (table) { - $('table').filter(function(){ return this !== table; }).find('tr[href]').removeClass('active'); - var n = $(table).closest('div.container').attr('data-icinga-url'); - focusedTableDataUrl = n; - }, - - /** - * Return the URL of the last focused table container. - * - * @returns {String} The data-icinga-url of the last focused table, which should be unique in each site. - */ - getFocusedContainerDataUrl: function() { - return focusedTableDataUrl; - }, - /** * Assign a unique ID to each .container without such * diff --git a/public/js/icinga/utils.js b/public/js/icinga/utils.js index 4b04b3d44..b447454e3 100644 --- a/public/js/icinga/utils.js +++ b/public/js/icinga/utils.js @@ -293,6 +293,14 @@ return $element[0]; }, + objectKeys: Object.keys || function (obj) { + var keys = []; + $.each(obj, function (key) { + keys.push(key); + }); + return keys; + }, + /** * Cleanup */