Add support for multi-selection

Add classes to handle multi-row selection using the CTRL-Key and to
create the link for the selected query.

refs #3788
This commit is contained in:
Matthias Jentsch 2013-10-10 15:49:12 +02:00
parent 3166e1a3ff
commit a96331b4d6
3 changed files with 358 additions and 8 deletions

View File

@ -27,6 +27,17 @@
// {{{ICINGA_LICENSE_HEADER}}}
define(['components/app/container', 'jquery', 'logging', 'URIjs/URI', 'URIjs/URITemplate', 'icinga/util/url'],
function(Container, $, logger, URI, tpl, urlMgr) {
define(
[
'components/app/container',
'jquery',
'logging',
'icinga/selection/selectable',
'icinga/selection/multiSelection',
'URIjs/URI',
'URIjs/URITemplate'
],
function(Container, $, logger, Selectable, TableMultiSelection, URI) {
"use strict";
/**
@ -65,6 +76,13 @@ function(Container, $, logger, URI, tpl, urlMgr) {
*/
var controlForms;
/**
* Handles multi-selection
*
* @type {TableMultiSelection}
*/
var selection;
/**
* Detect and select control forms for this table and return them
*
@ -131,7 +149,7 @@ function(Container, $, logger, URI, tpl, urlMgr) {
if (a.length) {
// test if the URL is on the current server, if not open it directly
if (true || Container.isExternalLink(a.attr('href'))) {
if (Container.isExternalLink(a.attr('href'))) {
return true;
}
} else if ($.inArray('input', nodeNames) > -1 || $.inArray('button', nodeNames) > -1) {
@ -141,17 +159,35 @@ function(Container, $, logger, URI, tpl, urlMgr) {
}
}
urlMgr.setDetailUrl($('a', this).attr('href'));
if (!ev.ctrlKey && !ev.metaKey) {
$('tr', $(this).parent()).removeClass('active');
var selectable = new Selectable(this);
if (ev.ctrlKey || ev.metaKey) {
selection.toggle(selectable);
} else if (ev.shiftKey) {
// select range ?
selection.add(selectable);
} else {
selection.clear();
selection.add(selectable);
}
$(this).addClass('active');
// TODO: Detail target
var url = URI($('a', this).attr('href'));
var segments = url.segment();
if (selection.size() === 0) {
// don't open anything
url.search('?');
} else if (selection.size() > 1 && segments.length > 3) {
// open detail view for multiple objects
segments[2] = 'multi';
url.pathname(segments.join('/'));
url.search('?');
url.setSearch(selection.toQuery());
}
Container.getDetailContainer().replaceDomFromUrl(url);
return false;
});
};
/**
* Register submit handler for the form controls (sorting, filtering, etc). Reloading happens in the
* current container
@ -193,18 +229,22 @@ function(Container, $, logger, URI, tpl, urlMgr) {
});
};
/*
var getSelectedRows = function() {
return $('a[href="' + urlMgr.getDetailUrl() + '"]', determineContentTable()).
parentsUntil('table', 'tr');
};
*/
/**
* Synchronize the current selection with the url displayed in the detail box
*/
/*
this.syncSelectionWithDetail = function() {
$('tr', contentNode).removeClass('active');
getSelectedRows().addClass('active');
};
*/
/**
* Register listener for history changes in the detail box
@ -221,7 +261,10 @@ function(Container, $, logger, URI, tpl, urlMgr) {
this.container.removeDefaultLoadIndicator();
controlForms = determineControlForms();
contentNode = determineContentTable();
this.syncSelectionWithDetail();
selection = new TableMultiSelection(
contentNode,
Container.getDetailContainer().getContainerHref()
);
this.registerControls();
this.registerTableLinks();
this.registerHistoryChanges();

View File

@ -0,0 +1,224 @@
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
define(
['jquery', 'URIjs/URI', 'icinga/selection/selectable'],
function($, URI, Selectable) {
"use strict";
/**
* Handle the multi-selection of table rows and generate the query string
* that can be used to open the selected items.
*
* NOTE: After each site reload, the state (the current selection) of this object will be
* restored automatically. The selectable items are determined by finding all TR elements
* in the targeted table. The active selection is determined by checking the query elements
* of the given url.
*
* @param {HtmlElement} The table that contains the selectable rows.
*
* @param {Object} The query that contains the selected rows.
*/
return function MultiSelection(table, detailUrl) {
var self = this;
/**
* Contains all selected selectables
*
* @type {Object}
*/
var selection = {};
/**
* If the selectable was already added, remove it, otherwise add it.
*
* @param {Selectable} The selectable to use.
*/
this.toggle = function(selectable) {
if (selection[selectable.getId()]) {
self.remove(selectable);
} else {
self.add(selectable);
}
};
/**
* Add the selectable to the current selection.
*
* @param {Selectable} The selectable to use.
*/
this.add = function(selectable) {
selectable.setActive(true);
selection[selectable.getId()] = selectable;
};
/**
* Remove the selectable from the current selection.
*
* @param {Selectable} The selectable to use.
*/
this.remove = function(selectable) {
selectable.setActive(false);
delete selection[selectable.getId()];
};
/**
* Clear the current selection
*/
this.clear = function() {
$.each(selection, function(index, selectable){
selectable.setActive(false);
});
selection = {};
};
/**
* Convert the current selection to its query-representation.
*
* @returns {String} The query
*/
this.toQuery = function() {
var query = {};
var i = 0;
$.each(selection, function(id, selectable) {
$.each(selectable.getQuery(), function(key, value) {
query[key + '[' + i + ']'] = value;
});
i++;
});
return query;
};
this.size = function() {
var size = 0;
$.each(selection, function(){ size++; });
return size;
};
/**
* Fetch the selections from a query containing multiple selections
*/
var selectionFromMultiQuery = function(query) {
var i = 0;
var selections = [];
alert('query ' + JSON.stringify(query));
$.each(query, function(key, value) {
// Fetch the index from the key
var id = key.match(/\[([0-9]+)\]/);
alert(id);
if (id) {
alert('extracted id ' + id[1]);
// Remove the index from the key
key = key.replace(/\[[0-9]+\]/,'');
key = encodeURIComponent(key);
value = encodeURIComponent(value);
// Add it to the array representing this index
if (id[1] !== i) {
// begin a new index
selections[i] = [ key + '=' + value ];
i = id[1];
} else {
selections[i].push(key + '=' + value);
}
}
});
return selections;
};
/**
* Fetch the selections from a default query.
*/
var selectionFromQuery = function(query) {
var selection = [];
$.each(query, function(key, value){
key = encodeURIComponent(key);
value = encodeURIComponent(value);
selection.push(key + '=' + value);
});
return [ selection ];
};
/**
* Restore the selected ids from the given URL.
*
* @param {URI} The used URL
*
* @returns {Array} The selected ids
*/
var restoreSelectionStateUrl = function(url) {
if (!url) {
return [];
}
if (!url.query) {
url = new URI(url);
}
var segments = url.segment();
var parts;
if (segments.length > 2 && segments[1] === 'Multi') {
alert('from multiselection');
parts = selectionFromMultiQuery(url.query(true));
} else {
parts = selectionFromQuery(url.query(true));
}
return $.map(parts, function(part) {
part.sort(function(a, b){
a = a.toUpperCase();
b = b.toUpperCase();
return (a < b ? -1 : (a > b) ? 1 : 0);
});
return part.join('&');
});
};
/**
* Create the selectables from the given table-Html
*/
var createSelectables = function(table) {
var selectables = {};
$(table).find('tr').each(function(i, row) {
var selectable = new Selectable(row);
selectables[selectable.getId()] = selectable;
});
return selectables;
};
/**
* Restore the selectables from the given table and the given url
*/
var restoreSelection = function() {
var selectables = createSelectables(table);
var selected = restoreSelectionStateUrl(detailUrl);
var selection = {};
$.each(selected, function(i, selectionId) {
if (selectables[selectionId]) {
selection[selectionId] = selectables[selectionId];
}
});
return selection;
};
selection = restoreSelection();
};
});

View File

@ -0,0 +1,83 @@
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
define(['jquery', 'URIjs/URI'], function($, URI) {
"use strict";
/**
* Wrapper around a selectable table row. Searches the first href and to identify the associated
* query and use this query as an Identifier.
*
* @param {HtmlElement} The table row.
*/
return function Selectable(tableRow) {
/**
* The href that is called when this row clicked.
*
* @type {*}
*/
var href = URI($(tableRow).find('a').first().attr('href'));
/*
* Sort queries alphabetically to ensure non-ambiguous ids.
*/
var query = href.query();
var parts = query.split('&');
parts.sort(function(a, b){
a = a.toUpperCase();
b = b.toUpperCase();
return (a < b ? -1 : (a > b) ? 1 : 0);
});
href.query(parts.join('&'));
/**
* Return an ID for this selectable.
*
* @returns {String} The id.
*/
this.getId = function () {
return href.query();
};
/**
* Return the query object associated with this selectable.
*
* @returns {String} The id.
*/
this.getQuery = function() {
return href.query(true);
};
this.setActive = function(value) {
if (value) {
$(tableRow).addClass('active');
} else {
$(tableRow).removeClass('active');
}
};
};
});