mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
We want to support hiding table columns, specifically in metrics table, through config file so that users can make the relevant bits hidden as per their organization. Current work will support the metrics table but can be extended to any table with minimal work in future. Config file will take the key of the metrics column from metrics.js file for the sake of minimal changes and simplicity of development. Problem: The keyboard list in the UI is not consistent across views such as in the instance wizard and in the register template form. There is also no way to custom about url/text and doc title and help URL in the UI. Root Cause: The list is hardcoded in the UI allowing no centralised configuration. Solution: Introduce a new config.js file installed at the /usr/share/cloudstackmanagement/webapp/config.js location. The config.js allows configurable keyboard list, about url/text, doc title, and help URL. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2542 lines
102 KiB
JavaScript
2542 lines
102 KiB
JavaScript
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
/**
|
|
* Create dynamic list view based on data callbacks
|
|
*/
|
|
(function($, cloudStack, _l, _s) {
|
|
var uiActions = {
|
|
standard: function($instanceRow, args, additional) {
|
|
var isAddAction = args.action.isAdd;
|
|
|
|
var listViewArgs = $instanceRow.closest('div.list-view').data('view-args');
|
|
var notification = args.action.notification ? args.action.notification : {};
|
|
var messages = args.action ? args.action.messages : {};
|
|
var preAction = args.action ? args.action.preAction : {};
|
|
var action = args.action ? args.action.action : {};
|
|
var multiSelect = args.action.isMultiSelectAction;
|
|
var needsRefresh = args.action.needsRefresh;
|
|
var section;
|
|
var data;
|
|
var messageArgs;
|
|
if (multiSelect) {
|
|
data = {
|
|
id: $.map($instanceRow, function(elem) { return $(elem).data('list-view-item-id'); }),
|
|
jsonObj: $.map($instanceRow, function(elem) { return $(elem).data('jsonObj'); })
|
|
};
|
|
messageArgs = {
|
|
name: $.map($instanceRow, function(elem) { return $(elem).find('td.name span').html() })
|
|
};
|
|
} else {
|
|
data = {
|
|
id: $instanceRow.data('list-view-item-id'),
|
|
jsonObj: $instanceRow.data('jsonObj')
|
|
};
|
|
messageArgs = {
|
|
name: $instanceRow.find('td.name span').html()
|
|
};
|
|
}
|
|
var $listView = $instanceRow.closest('.list-view');
|
|
|
|
if (args.data) $.extend(true, data, args.data);
|
|
if (listViewArgs) section = listViewArgs.section;
|
|
|
|
notification.desc = messages ?
|
|
messages.notification(messageArgs) : null;
|
|
|
|
if (listViewArgs)
|
|
notification.section = listViewArgs.id;
|
|
|
|
// Handle pre-action (occurs before any other behavior happens)
|
|
if (preAction) {
|
|
var preActionContext = $.extend(true, {}, listViewArgs.context);
|
|
|
|
preActionContext[
|
|
listViewArgs.activeSection
|
|
] = (multiSelect ? data.jsonObj : [data.jsonObj]);
|
|
|
|
if (!preAction({
|
|
context: preActionContext
|
|
})) return false;
|
|
}
|
|
|
|
var performAction = function(data, options) {
|
|
if (!options) options = {};
|
|
|
|
var $form = options.$form;
|
|
var isHeader = options.isHeader;
|
|
|
|
$instanceRow = options.$item ? options.$item : $instanceRow;
|
|
var $item = options.$item;
|
|
var context = $.extend(true, {}, listViewArgs.context);
|
|
context[
|
|
listViewArgs.activeSection
|
|
] = (multiSelect ? $.map($instanceRow, function(elem) { return $(elem).data('jsonObj'); }) : [$instanceRow.data('jsonObj')]);
|
|
|
|
// Make sure the master checkbox is unselected
|
|
if (multiSelect) {
|
|
var $listView = $instanceRow.closest('.list-view');
|
|
$listView.find('input.multiSelectMasterCheckbox').prop('checked', false);
|
|
toggleMultiSelectActions($listView, false);
|
|
}
|
|
|
|
var externalLinkAction = action.externalLink;
|
|
if (externalLinkAction) {
|
|
// Show popup immediately, do not proceed through normal action process
|
|
window.open(
|
|
// URL
|
|
externalLinkAction.url({
|
|
context: context
|
|
}),
|
|
|
|
// Title
|
|
externalLinkAction.title({
|
|
context: context
|
|
}),
|
|
|
|
// Window options
|
|
'menubar=0,resizable=0,' + 'width=' + externalLinkAction.width + ',' + 'height=' + externalLinkAction.height
|
|
);
|
|
} else if (action.custom && !action.noAdd) {
|
|
action.custom({
|
|
data: data,
|
|
ref: options.ref,
|
|
context: context,
|
|
$instanceRow: $instanceRow,
|
|
complete: function(args) {
|
|
args = args ? args : {};
|
|
|
|
var $item = args.$item;
|
|
|
|
notification.desc = messages.notification(args.messageArgs);
|
|
notification._custom = args._custom;
|
|
|
|
cloudStack.ui.notifications.add(
|
|
notification,
|
|
function(args) {
|
|
if (listViewArgs.onActionComplete) {
|
|
listViewArgs.onActionComplete();
|
|
}
|
|
|
|
if ($item.is(':visible') && !isHeader) {
|
|
replaceItem(
|
|
$item,
|
|
args.data,
|
|
args.actionFilter ?
|
|
args.actionFilter : $instanceRow.next().data('list-view-action-filter')
|
|
);
|
|
}
|
|
},
|
|
|
|
{},
|
|
|
|
// Error
|
|
|
|
function(args) {
|
|
if (args && args.updatedData) {
|
|
if ($item.is(':visible') && !isHeader) {
|
|
replaceItem(
|
|
$item,
|
|
args.updatedData,
|
|
args.actionFilter
|
|
);
|
|
}
|
|
} else {
|
|
$item.remove();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
});
|
|
} else if (action.uiCustom) {
|
|
action.uiCustom({
|
|
$item: $instanceRow
|
|
});
|
|
} else {
|
|
if (needsRefresh) {
|
|
var $loading = $('<div>').addClass('loading-overlay');
|
|
|
|
$listView.prepend($loading);
|
|
}
|
|
|
|
var actionArgs = {
|
|
data: data,
|
|
ref: options.ref,
|
|
context: options.context,
|
|
$form: $form,
|
|
response: {
|
|
success: function(args) {
|
|
args = args ? args : {};
|
|
|
|
var $prevRow, $newRow;
|
|
|
|
// Make copy of previous row, in case data is needed
|
|
$prevRow = $instanceRow.clone();
|
|
if (multiSelect) {
|
|
$prevRow.find('.quick-view').addClass('loading-overlay');
|
|
$.each($prevRow, function(index, elem) {
|
|
$(elem).data($($instanceRow[index]).data());
|
|
});
|
|
} else {
|
|
$prevRow.data($instanceRow.data());
|
|
}
|
|
|
|
// Set loading appearance
|
|
if (args.data && (!isHeader || multiSelect)) {
|
|
if (multiSelect) {
|
|
$instanceRow = $.map($instanceRow, function(elem, index) {
|
|
return replaceItem(
|
|
$(elem),
|
|
$.extend($(elem).data('json-obj'), args.data[index]),
|
|
$(elem).data('list-view-action-filter')
|
|
)[0];
|
|
});
|
|
} else {
|
|
$instanceRow = replaceItem(
|
|
$instanceRow,
|
|
$.extend($instanceRow.data('json-obj'), args.data),
|
|
$instanceRow.data('list-view-action-filter')
|
|
);
|
|
}
|
|
}
|
|
|
|
if (multiSelect) {
|
|
$.each($instanceRow, function(index, elem) {
|
|
$(elem).find('td:last').children().remove();
|
|
$(elem).find('td:last').append($('<div>').addClass('loading'));
|
|
$(elem).addClass('loading');
|
|
|
|
if (options.$item) $(elem).data('list-view-new-item', true);
|
|
|
|
// Disable any clicking/actions for row
|
|
$(elem).bind('click', function() {
|
|
return false;
|
|
});
|
|
});
|
|
} else {
|
|
$instanceRow.find('td:last').children().remove();
|
|
$instanceRow.find('td:last').append($('<div>').addClass('loading'));
|
|
$instanceRow.addClass('loading');
|
|
|
|
if (options.$item) $instanceRow.data('list-view-new-item', true);
|
|
|
|
// Disable any clicking/actions for row
|
|
$instanceRow.bind('click', function() {
|
|
return false;
|
|
});
|
|
}
|
|
|
|
if(args.notification) notification = args.notification;
|
|
|
|
notification._custom = args._custom;
|
|
|
|
if (additional && additional.success) additional.success(args);
|
|
|
|
if (listViewArgs.onActionComplete == true) {
|
|
listViewArgs.onActionComplete();
|
|
}
|
|
|
|
cloudStack.ui.notifications.add(
|
|
notification,
|
|
|
|
// Success
|
|
|
|
function(args) {
|
|
if (!args) args = {};
|
|
|
|
var actionFilter = args.actionFilter ?
|
|
args.actionFilter : (multiSelect ?
|
|
$.map($instanceRow, function(elem) { $(elem).data('list-view-action-filter') }) :
|
|
$instanceRow.data('list-view-action-filter'));
|
|
|
|
if (!isHeader || multiSelect) {
|
|
var visible = (multiSelect ? $($instanceRow[0]).is(':visible') : $instanceRow.is(':visible'));
|
|
if (visible) {
|
|
if (args.data) {
|
|
if (multiSelect) {
|
|
$newRow = [];
|
|
$.each($instanceRow, function(index, elem) {
|
|
$newRow.push(
|
|
replaceItem($(elem),
|
|
args.data, //$.extend($(elem).data('json-obj'), args.data[index]),
|
|
actionFilter)
|
|
);
|
|
});
|
|
} else {
|
|
$newRow = replaceItem($instanceRow,
|
|
args.data,
|
|
actionFilter);
|
|
}
|
|
} else {
|
|
// Nothing new, so just put in existing data
|
|
if (multiSelect) {
|
|
$instanceRow = $.map($instanceRow, function(elem) {
|
|
replaceItem($(elem),
|
|
$(elem).data('json-obj'),
|
|
actionFilter)[0]
|
|
});
|
|
} else {
|
|
$newRow = replaceItem($instanceRow,
|
|
$instanceRow.data('json-obj'),
|
|
actionFilter);
|
|
}
|
|
}
|
|
|
|
if (needsRefresh) {
|
|
if ($listView.closest('.detail-view').length) {
|
|
$('.detail-view:last .button.refresh').click();
|
|
} else {
|
|
$loading.remove();
|
|
$listView.listView('refresh');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (additional && additional.complete)
|
|
additional.complete(args, $newRow);
|
|
}
|
|
|
|
if (messages.complete) {
|
|
cloudStack.dialog.notice({
|
|
message: messages.complete(args.data)
|
|
});
|
|
}
|
|
|
|
if (options.complete) {
|
|
options.complete(args);
|
|
}
|
|
|
|
if (listViewArgs.onActionComplete) {
|
|
listViewArgs.onActionComplete();
|
|
}
|
|
},
|
|
|
|
{},
|
|
|
|
// Error
|
|
|
|
function(errorArgs) {
|
|
if (!isHeader) {
|
|
if (isAddAction == true && $instanceRow.data('list-view-new-item')) {
|
|
// For create forms
|
|
$instanceRow.remove();
|
|
} else {
|
|
// For standard actions
|
|
if(!args.notification) {
|
|
if (multiSelect) {
|
|
$.each($instanceRow, function(index, elem) {
|
|
replaceItem(
|
|
$(elem),
|
|
$.extend($(elem).data('json-obj'), errorArgs.data),
|
|
errorArgs.actionFilter ?
|
|
errorArgs.actionFilter :
|
|
$(elem).data('list-view-action-filter')
|
|
);
|
|
});
|
|
} else {
|
|
replaceItem(
|
|
$instanceRow,
|
|
$.extend($instanceRow.data('json-obj'), errorArgs.data),
|
|
errorArgs.actionFilter ?
|
|
errorArgs.actionFilter :
|
|
$instanceRow.data('list-view-action-filter')
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (options.error) {
|
|
options.error(errorArgs);
|
|
}
|
|
}
|
|
);
|
|
},
|
|
error: function(message) {
|
|
$instanceRow.removeClass('loading');
|
|
$instanceRow.find('td.quick-view').removeClass('loading-overlay');
|
|
|
|
if (!isHeader) {
|
|
if (($.isPlainObject(args.action.createForm) && args.action.addRow != 'false') ||
|
|
(!args.action.createForm && args.action.addRow == 'true')) {
|
|
$instanceRow.remove();
|
|
}
|
|
}
|
|
|
|
if (needsRefresh) {
|
|
if (!$listView.closest('.detail-view').length) {
|
|
$loading.remove();
|
|
}
|
|
}
|
|
|
|
if (options.error) options.error(message);
|
|
|
|
if (message) cloudStack.dialog.notice({
|
|
message: message
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
if (action.custom && action.noAdd) {
|
|
action.custom({
|
|
data: data,
|
|
ref: options.ref,
|
|
context: context,
|
|
$instanceRow: $instanceRow,
|
|
complete: actionArgs.response.success
|
|
});
|
|
} else {
|
|
action(actionArgs);
|
|
}
|
|
}
|
|
};
|
|
|
|
var context = $.extend({}, listViewArgs.context);
|
|
|
|
context[
|
|
listViewArgs.activeSection
|
|
] = (multiSelect ? $.map($instanceRow, function(elem) { return $(elem).data('jsonObj'); }) : [$instanceRow.data('jsonObj')]);
|
|
|
|
messageArgs.context = context;
|
|
|
|
if (!args.action.action.externalLink && !args.action.createForm &&
|
|
args.action.addRow != 'true' && !action.custom && !action.uiCustom && !args.action.listView) {
|
|
cloudStack.dialog.confirm({
|
|
message: messages.confirm(messageArgs),
|
|
action: function() {
|
|
performAction(data, {
|
|
context: context,
|
|
isMultiSelectAction: multiSelect,
|
|
isHeader: args.action.isHeader
|
|
});
|
|
}
|
|
});
|
|
} else if (action.custom || action.uiCustom) {
|
|
performAction();
|
|
} else if (args.action.listView) {
|
|
cloudStack.dialog.listView({
|
|
context: context,
|
|
listView: args.action.listView,
|
|
after: function(args) {
|
|
performAction(null, {
|
|
context: args.context
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
var addRow = args.action.addRow == "false" ? false : true;
|
|
var isHeader = args.action.isHeader;
|
|
var createFormContext = $.extend({}, context);
|
|
|
|
var externalLinkAction = action.externalLink;
|
|
if (externalLinkAction) {
|
|
// Show popup immediately, do not proceed through normal action process
|
|
window.open(
|
|
// URL
|
|
externalLinkAction.url({
|
|
context: context
|
|
}),
|
|
|
|
// Title
|
|
externalLinkAction.title({
|
|
context: context
|
|
}),
|
|
|
|
// Window options
|
|
'menubar=0,resizable=0,' + 'width=' + externalLinkAction.width + ',' + 'height=' + externalLinkAction.height
|
|
);
|
|
} else if (args.action.createForm) {
|
|
cloudStack.dialog.createForm({
|
|
form: args.action.createForm,
|
|
after: function(args) {
|
|
var $newItem;
|
|
|
|
if (!isHeader) {
|
|
if (addRow != false) {
|
|
$newItem = $listView.listView('prependItem', {
|
|
data: [
|
|
$.extend(args.data, {
|
|
state: 'Creating',
|
|
status: 'state.Creating',
|
|
allocationstate: 'Creating'
|
|
})
|
|
]
|
|
});
|
|
} else {
|
|
$newItem = $instanceRow;
|
|
}
|
|
|
|
performAction(args.data, {
|
|
ref: args.ref,
|
|
context: createFormContext,
|
|
$item: $newItem,
|
|
$form: args.$form
|
|
});
|
|
} else {
|
|
var $loading = $('<div>').addClass('loading-overlay');
|
|
|
|
$loading.appendTo($listView);
|
|
performAction(args.data, {
|
|
ref: args.ref,
|
|
context: createFormContext,
|
|
$form: args.$form,
|
|
isHeader: isHeader,
|
|
complete: function(args) {
|
|
$loading.remove();
|
|
$listView.listView('refresh');
|
|
},
|
|
error: function(args) {
|
|
$loading.remove();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
ref: listViewArgs.ref,
|
|
context: createFormContext
|
|
});
|
|
} else {
|
|
cloudStack.dialog.confirm({
|
|
message: messages.confirm(messageArgs),
|
|
action: function() {
|
|
var $newItem;
|
|
if (addRow && !action.isHeader) {
|
|
$newItem = $listView.listView('prependItem', {
|
|
data: [
|
|
$.extend(args.data, {
|
|
state: 'Creating',
|
|
status: 'state.Creating',
|
|
allocationstate: 'Creating'
|
|
})
|
|
]
|
|
});
|
|
} else if (action.isHeader) {
|
|
$newItem = $('<div>');
|
|
} else {
|
|
$newItem = $instanceRow;
|
|
}
|
|
|
|
performAction(args.data, {
|
|
ref: args.ref,
|
|
context: createFormContext,
|
|
$item: $newItem,
|
|
$form: args.$form
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
remove: function($instanceRow, args) {
|
|
uiActions.standard($instanceRow, args, {
|
|
complete: function(args, $newRow) {
|
|
$newRow.remove();
|
|
}
|
|
});
|
|
},
|
|
|
|
edit: function($instanceRow, args) {
|
|
var $td = $instanceRow.find('td.editable');
|
|
var $edit = $td.find('div.edit');
|
|
var $editInput = $edit.find('input');
|
|
var $label = $td.find('span');
|
|
var $listView = $instanceRow.closest('.list-view');
|
|
var listViewArgs = $listView.data('view-args');
|
|
|
|
// Hide label, show edit field
|
|
var showEditField = function() {
|
|
$edit.css({
|
|
opacity: 1
|
|
});
|
|
$label.fadeOut('fast', function() {
|
|
$edit.fadeIn();
|
|
$editInput.focus();
|
|
$instanceRow.closest('div.data-table').dataTable('refresh');
|
|
});
|
|
};
|
|
|
|
// Hide edit field, validate and save changes
|
|
var showLabel = function(val, options) {
|
|
if (!options) options = {};
|
|
|
|
var oldVal = $label.html();
|
|
|
|
if (val != null)
|
|
$label.html(_s(val));
|
|
|
|
var data = {
|
|
id: $instanceRow.data('list-view-item-id'),
|
|
jsonObj: $instanceRow.data('jsonObj')
|
|
};
|
|
|
|
data[$td.data('list-view-item-field')] = $editInput.val();
|
|
|
|
var context = $.extend({}, listViewArgs.context);
|
|
context[
|
|
listViewArgs.activeSection
|
|
] = $instanceRow.data('jsonObj');
|
|
|
|
args.callback({
|
|
data: data,
|
|
context: context,
|
|
response: {
|
|
success: function(args) {
|
|
$edit.hide();
|
|
$label.fadeIn();
|
|
$instanceRow.closest('div.data-table').dataTable('refresh');
|
|
|
|
if (options.success) options.success(args);
|
|
},
|
|
error: function(message) {
|
|
if (message) {
|
|
cloudStack.dialog.notice({
|
|
message: message
|
|
});
|
|
$edit.hide(),
|
|
$label.html(_s(oldVal)).fadeIn();
|
|
$instanceRow.closest('div.data-table').dataTable('refresh');
|
|
|
|
if (options.error) options.error(args);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
if (args.cancel) { //click Cancel button
|
|
// showLabel();
|
|
var oldVal = $label.html();
|
|
$edit.hide();
|
|
$label.fadeIn();
|
|
$instanceRow.closest('div.data-table').dataTable('refresh');
|
|
$editInput.val(_s(oldVal));
|
|
return false;
|
|
}
|
|
|
|
if (!$editInput.is(':visible') || !(typeof(args.action) == 'undefined')) { //click Edit button
|
|
showEditField();
|
|
} else if ($editInput.val() != $label.html()) { //click Save button with changed value
|
|
if ($editInput.val().match(/<|>/)) {
|
|
cloudStack.dialog.notice({ message: 'message.validate.invalid.characters' });
|
|
return false;
|
|
}
|
|
|
|
$edit.animate({
|
|
opacity: 0.5
|
|
});
|
|
|
|
var originalName = $label.html();
|
|
var newName = $editInput.val();
|
|
showLabel(newName, {
|
|
success: function() {
|
|
cloudStack.ui.notifications.add({
|
|
section: $instanceRow.closest('div.view').data('view-args').id,
|
|
desc: newName ? _l('Set value of') + ' ' + $instanceRow.find('td.name span').html() + ' ' + _l('to') + ' ' + _s(newName) : _l('Unset value for') + ' ' + $instanceRow.find('td.name span').html()
|
|
},
|
|
function(args) {}, [{
|
|
name: newName
|
|
}]
|
|
);
|
|
}
|
|
});
|
|
} else { //click Save button with unchanged value
|
|
showLabel();
|
|
}
|
|
|
|
return $instanceRow;
|
|
}
|
|
};
|
|
|
|
var rowActions = {
|
|
_std: function($tr, action) {
|
|
action();
|
|
|
|
$tr.closest('.data-table').dataTable('refresh');
|
|
|
|
setTimeout(function() {
|
|
$tr.closest('.data-table').dataTable('selectRow', $tr.index());
|
|
}, 0);
|
|
},
|
|
|
|
moveTop: function($tr) {
|
|
rowActions._std($tr, function() {
|
|
$tr.closest('tbody').prepend($tr);
|
|
$tr.closest('.list-view').animate({
|
|
scrollTop: 0
|
|
});
|
|
});
|
|
},
|
|
|
|
moveBottom: function($tr) {
|
|
rowActions._std($tr, function() {
|
|
$tr.closest('tbody').append($tr);
|
|
$tr.closest('.list-view').animate({
|
|
scrollTop: 0
|
|
});
|
|
});
|
|
},
|
|
|
|
moveUp: function($tr) {
|
|
rowActions._std($tr, function() {
|
|
$tr.prev().before($tr);
|
|
});
|
|
},
|
|
|
|
moveDown: function($tr) {
|
|
rowActions._std($tr, function() {
|
|
$tr.next().after($tr);
|
|
});
|
|
},
|
|
|
|
moveTo: function($tr, index, after) {
|
|
rowActions._std($tr, function() {
|
|
var $target = $tr.closest('tbody').find('tr').filter(function() {
|
|
return $(this).index() == index;
|
|
});
|
|
|
|
// if ($target.index() > $tr.index()) $target.after($tr);
|
|
// else $target.before($tr);
|
|
|
|
$tr.closest('.list-view').scrollTop($tr.position().top - $tr.height() * 2);
|
|
|
|
if (after)
|
|
setTimeout(function() {
|
|
after();
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Edit field text
|
|
*
|
|
* @param $td {jQuery} <td> to put input field into
|
|
*/
|
|
var createEditField = function($td) {
|
|
$td.addClass('editable');
|
|
|
|
// Put <td> label into a span
|
|
var sanitizedValue = $td.html();
|
|
$('<span></span>').html(sanitizedValue).appendTo($td.html(''));
|
|
|
|
var $editArea = $('<div></div>').addClass('edit');
|
|
var $editField = $('<input />').addClass('edit').attr({
|
|
type: 'text',
|
|
value: cloudStack.sanitizeReverse(sanitizedValue)
|
|
});
|
|
var $actionButton = $('<div></div>').addClass('action');
|
|
var $saveButton = $actionButton.clone().addClass('save').attr({
|
|
'title': _l('Save')
|
|
});
|
|
var $cancelButton = $actionButton.clone().addClass('cancel').attr({
|
|
'title': _l('Cancel edit')
|
|
});
|
|
|
|
$([$editField, $saveButton, $cancelButton]).each(function() {
|
|
this.appendTo($editArea);
|
|
});
|
|
|
|
return $editArea.hide();
|
|
};
|
|
|
|
var renderActionCol = function(actions) {
|
|
return $.grep(
|
|
$.map(actions, function(value, key) {
|
|
return key;
|
|
}),
|
|
function(elem) {
|
|
return elem != 'add';
|
|
}
|
|
).length;
|
|
};
|
|
|
|
var createHeader = function(preFilter, fields, $table, actions, options) {
|
|
if (!options) options = {};
|
|
|
|
var $tr = $('<tr>');
|
|
var $thead = $('<thead>').prependTo($table).append($tr);
|
|
var reorder = options.reorder;
|
|
var detailView = options.detailView;
|
|
var multiSelect = options.multiSelect;
|
|
var groupableColumns = options.groupableColumns;
|
|
var viewArgs = $table.closest('.list-view').data('view-args');
|
|
var uiCustom = viewArgs.uiCustom;
|
|
var hiddenFields = [];
|
|
|
|
if (preFilter != null)
|
|
hiddenFields = preFilter();
|
|
|
|
var addColumnToTr = function($tr, key, colspan, label, needsCollapsibleColumn) {
|
|
var trText = _l(label);
|
|
var $th = $('<th>').addClass(key).attr('colspan', colspan).attr('title', trText).appendTo($tr);
|
|
if ($th.index()) $th.addClass('reduced-hide');
|
|
$th.css({'border-right': '1px solid #C6C3C3', 'border-left': '1px solid #C6C3C3'});
|
|
if (needsCollapsibleColumn) {
|
|
var karetLeft = $('<span>').css({'margin-right': '10px'});
|
|
karetLeft.attr('title', trText);
|
|
karetLeft.appendTo($th);
|
|
$('<span>').html('«').css({'font-size': '15px', 'float': 'right'}).appendTo(karetLeft);
|
|
$('<span>').html(trText).appendTo(karetLeft);
|
|
|
|
$th.click(function(event) {
|
|
event.stopPropagation();
|
|
var $th = $(this);
|
|
var startIndex = 0;
|
|
$th.prevAll('th').each(function() {
|
|
startIndex += parseInt($(this).attr('colspan'));
|
|
});
|
|
var endIndex = startIndex + parseInt($th.attr('colspan'));
|
|
// Hide Column group
|
|
$th.hide();
|
|
$th.closest('table').find('tbody td').filter(function() {
|
|
return $(this).index() >= startIndex && $(this).index() < endIndex;
|
|
}).hide();
|
|
$th.closest('table').find('thead tr:last th').filter(function() {
|
|
return $(this).index() >= startIndex && $(this).index() < endIndex;
|
|
}).hide();
|
|
// Show collapsible column with blank cells
|
|
$th.next('th').show();
|
|
$th.closest('table').find('tbody td').filter(function() {
|
|
return $(this).index() == endIndex;
|
|
}).show();
|
|
$th.closest('table').find('thead tr:last th').filter(function() {
|
|
return $(this).index() == endIndex;
|
|
}).show();
|
|
// Refresh list view
|
|
$tr.closest('.list-view').find('.no-split').dataTable('refresh');
|
|
});
|
|
|
|
var karetRight = addColumnToTr($tr, 'collapsible-column', 1, '');
|
|
$('<span>').html(trText.substring(0,3)).appendTo(karetRight);
|
|
$('<span>').css({'font-size': '15px'}).html(' »').appendTo(karetRight);
|
|
karetRight.attr('title', trText);
|
|
karetRight.css({'border-right': '1px solid #C6C3C3', 'border-left': '1px solid #C6C3C3', 'min-width': '10px', 'width': '10px', 'max-width': '45px', 'padding': '2px'});
|
|
karetRight.hide();
|
|
karetRight.click(function(event) {
|
|
event.stopPropagation();
|
|
var prevTh = $(this).prev('th');
|
|
var startIndex = 0;
|
|
prevTh.prevAll('th').each(function() {
|
|
startIndex += parseInt($(this).attr('colspan'));
|
|
});
|
|
var endIndex = startIndex + parseInt(prevTh.attr('colspan'));
|
|
|
|
prevTh.show();
|
|
prevTh.closest('table').find('tbody td').filter(function() {
|
|
return $(this).index() >= startIndex && $(this).index() < endIndex;
|
|
}).show();
|
|
prevTh.closest('table').find('thead tr:last th').filter(function() {
|
|
return $(this).index() >= startIndex && $(this).index() < endIndex;
|
|
}).show();
|
|
|
|
prevTh.next('th').hide();
|
|
prevTh.closest('table').find('tbody td').filter(function() {
|
|
return $(this).index() == endIndex;
|
|
}).hide();
|
|
prevTh.closest('table').find('thead tr:last th').filter(function() {
|
|
return $(this).index() == endIndex;
|
|
}).hide();
|
|
|
|
$tr.closest('.list-view').find('.no-split').dataTable('refresh');
|
|
});
|
|
} else {
|
|
$th.html(trText);
|
|
}
|
|
return $th;
|
|
};
|
|
|
|
if (groupableColumns) {
|
|
$tr.addClass('groupable-header-columns').addClass('groupable-header');
|
|
$.each(fields, function(key) {
|
|
if ($.inArray(key, hiddenFields) != -1)
|
|
return true;
|
|
var field = this;
|
|
if (field.columns) {
|
|
var colspan = Object.keys(field.columns).length;
|
|
addColumnToTr($tr, key, colspan, field.label, true);
|
|
} else {
|
|
var label = '';
|
|
if (key == 'name') {
|
|
label = 'label.resources';
|
|
}
|
|
addColumnToTr($tr, key, 1, label);
|
|
}
|
|
return true;
|
|
});
|
|
if (detailView && !$.isFunction(detailView) && !detailView.noCompact && !uiCustom) {
|
|
addColumnToTr($tr, 'quick-view', 1, '');
|
|
}
|
|
$tr = $('<tr>').appendTo($thead);
|
|
$tr.addClass('groupable-header');
|
|
}
|
|
|
|
if (multiSelect) {
|
|
var $th = $('<th>').addClass('multiselect').appendTo($tr);
|
|
var $multiSelectMaster = $('<input>')
|
|
.attr('type', 'checkbox')
|
|
.addClass('multiSelectMasterCheckbox');
|
|
$multiSelectMaster.appendTo($th);
|
|
|
|
$multiSelectMaster.click(function() {
|
|
var isMasterChecked = $(this).prop('checked');
|
|
$('.multiSelectCheckbox').prop('checked', isMasterChecked);
|
|
toggleMultiSelectActions($table.closest('.list-view'), isMasterChecked);
|
|
});
|
|
}
|
|
|
|
$.each(fields, function(key) {
|
|
if ($.inArray(key, hiddenFields) != -1)
|
|
return true;
|
|
var field = this;
|
|
if (field.columns) {
|
|
$.each(field.columns, function(idx) {
|
|
var subfield = this;
|
|
addColumnToTr($tr, key, 1, subfield.label);
|
|
return true;
|
|
});
|
|
var blankCell = addColumnToTr($tr, 'collapsible-column', 1, '');
|
|
blankCell.css({'min-width': '10px', 'width': '10px'});
|
|
blankCell.hide();
|
|
} else {
|
|
addColumnToTr($tr, key, 1, field.label);
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Re-order row buttons
|
|
if (reorder) {
|
|
$tr.append(
|
|
$('<th>').html(_l('label.order')).addClass('reorder-actions reduced-hide')
|
|
);
|
|
}
|
|
|
|
// Actions column
|
|
var actionsArray = actions ? $.map(actions, function(v, k) {
|
|
if (k == 'add' || k == 'rootAdminAddGuestNetwork') {
|
|
v.isAdd = true;
|
|
}
|
|
|
|
return v;
|
|
}) : [];
|
|
var headerActionsArray = $.grep(
|
|
actionsArray,
|
|
function(action) {
|
|
return action.isHeader || action.isAdd;
|
|
}
|
|
);
|
|
|
|
if (actions && !options.noActionCol && renderActionCol(actions) && actionsArray.length != headerActionsArray.length) {
|
|
$tr.append(
|
|
$('<th></th>')
|
|
.html(_l('label.actions'))
|
|
.addClass('actions reduced-hide')
|
|
);
|
|
}
|
|
|
|
// Quick view
|
|
if (detailView && !$.isFunction(detailView) && !detailView.noCompact && !uiCustom) {
|
|
$tr.append(
|
|
$('<th></th>')
|
|
.html(_l('label.quickview'))
|
|
.addClass('quick-view reduced-hide')
|
|
);
|
|
}
|
|
|
|
return $thead;
|
|
};
|
|
|
|
var createFilters = function($toolbar, filters) {
|
|
if (!filters) return false;
|
|
|
|
var $filters = $('<div></div>').addClass('filters reduced-hide');
|
|
$filters.append($('<label>').html(_l('label.filterBy')));
|
|
|
|
var $filterSelect = $('<select id="filterBy"></select>').appendTo($filters);
|
|
|
|
if (filters)
|
|
$.each(filters, function(key) {
|
|
if (this.preFilter != null && this.preFilter() == false) {
|
|
return true; //skip to next item in each loop
|
|
}
|
|
var $option = $('<option>').attr({
|
|
value: key
|
|
}).html(_l(this.label));
|
|
|
|
$option.appendTo($filterSelect);
|
|
|
|
return true;
|
|
});
|
|
|
|
return $filters.appendTo($toolbar);
|
|
};
|
|
|
|
var createSearchBar = function($toolbar, listViewData) {
|
|
var $search = $('<div></div>').addClass('text-search reduced-hide');
|
|
var $searchBar = $('<div></div>').addClass('search-bar reduced hide').appendTo($search);
|
|
$searchBar.append('<input type="text" />');
|
|
$search.append('<div id="basic_search" class="button search"></div>');
|
|
|
|
if (listViewData.advSearchFields != null) {
|
|
$search.append(
|
|
$('<div>').attr({
|
|
id: 'advanced_search'
|
|
})
|
|
.addClass('button search advanced-search')
|
|
.append($('<div>').addClass('icon'))
|
|
);
|
|
}
|
|
|
|
return $search.appendTo($toolbar);
|
|
};
|
|
|
|
/**
|
|
* Makes set of icons from data, in the for of a table cell
|
|
*/
|
|
var makeActionIcons = function($td, actions, options) {
|
|
options = options ? options : {};
|
|
var allowedActions = options.allowedActions;
|
|
var $tr = $td.closest('tr');
|
|
var data = $tr && $tr.data('json-obj') ? $tr.data('json-obj') : null;
|
|
|
|
$.each(actions, function(actionName, action) {
|
|
if (actionName == 'add' || action.isHeader)
|
|
return true;
|
|
|
|
if (action.type == 'radio') {
|
|
$td.closest('.list-view').addClass('list-view-select');
|
|
$td.append(
|
|
$('<div></div>')
|
|
.addClass('action')
|
|
.addClass(actionName)
|
|
.append(
|
|
$('<input>').attr({
|
|
type: 'radio',
|
|
name: actionName
|
|
})
|
|
)
|
|
.attr({
|
|
alt: _l(action.label),
|
|
title: _l(action.label)
|
|
})
|
|
.data('list-view-action-id', actionName)
|
|
);
|
|
|
|
return true;
|
|
} else if (action.type == 'checkbox') {
|
|
$td.closest('.list-view').addClass('list-view-select');
|
|
$td.append(
|
|
$('<div></div>')
|
|
.addClass('action')
|
|
.addClass(actionName)
|
|
.append(
|
|
$('<input>').attr({
|
|
type: 'checkbox',
|
|
name: actionName,
|
|
checked: data && data._isSelected ? 'checked' : false
|
|
})
|
|
)
|
|
.attr({
|
|
alt: _l(action.label),
|
|
title: _l(action.label)
|
|
})
|
|
.data('list-view-action-id', actionName)
|
|
);
|
|
|
|
if ($td.find('input[type=checkbox]').is(':checked')) {
|
|
$tr.addClass('multi-edit-selected');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
var $action = $('<div></div>')
|
|
.addClass('action')
|
|
.addClass(actionName)
|
|
.append($('<span>').addClass('icon').html(' '))
|
|
.attr({
|
|
alt: _l(action.label),
|
|
title: _l(action.label)
|
|
})
|
|
.data('list-view-action-id', actionName);
|
|
|
|
if (action.textLabel) {
|
|
$action
|
|
.addClass('text')
|
|
.prepend(
|
|
$('<span>').addClass('label').html(_l(action.textLabel))
|
|
);
|
|
}
|
|
|
|
// Disabled appearance/behavior for filtered actions
|
|
if (allowedActions && $.inArray(actionName, allowedActions) == -1) {
|
|
$action.addClass('disabled');
|
|
}
|
|
|
|
$td.append($action);
|
|
|
|
return true;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Initialize detail view for specific ID from list view
|
|
*/
|
|
var createDetailView = function(args, complete, $row, options) {
|
|
var $panel = args.$panel;
|
|
var title = args.title;
|
|
var id = args.id;
|
|
var data = $.extend(true, {}, args.data, {
|
|
$browser: $('#browser .container'),
|
|
id: id,
|
|
jsonObj: args.jsonObj,
|
|
section: args.section,
|
|
context: args.context,
|
|
$listViewRow: $row,
|
|
compact: options ? options.compact : false
|
|
});
|
|
|
|
var noPanel = options ? options.noPanel : false;
|
|
var $detailView, $detailsPanel;
|
|
var panelArgs = {
|
|
title: title,
|
|
parent: $panel,
|
|
maximizeIfSelected: data.isMaximized,
|
|
complete: function($newPanel) {
|
|
// Make detail view element
|
|
if (!args.pageGenerator && !data.isMaximized)
|
|
$detailView = $('<div>').addClass('detail-view').detailView(data).appendTo($newPanel);
|
|
else if (!args.pageGenerator && data.isMaximized)
|
|
$detailView = $newPanel.detailView(data);
|
|
else
|
|
$detailView = args.pageGenerator(data).appendTo($newPanel);
|
|
|
|
if (complete){
|
|
complete($detailView);
|
|
}
|
|
return $detailView;
|
|
}
|
|
};
|
|
|
|
if (noPanel) {
|
|
return $('<div>').detailView(data);
|
|
} else {
|
|
$detailsPanel = data.$browser.cloudBrowser('addPanel', panelArgs);
|
|
}
|
|
};
|
|
|
|
var addTableRows = function(preFilter, fields, data, $tbody, actions, options) {
|
|
if (!options) options = {};
|
|
var rows = [];
|
|
var reorder = options.reorder;
|
|
var detailView = options.detailView;
|
|
var multiSelect = options.multiSelect;
|
|
var $listView = $tbody.closest('.list-view');
|
|
var listViewArgs = $listView.data('view-args');
|
|
var uiCustom = listViewArgs.uiCustom;
|
|
var subselect = uiCustom ? listViewArgs.listView.subselect : null;
|
|
var hasCollapsibleColumn = false;
|
|
|
|
if (!(data && data.length)) {
|
|
$listView.data('end-of-table', true);
|
|
if (!$tbody.find('tr').length) {
|
|
return [
|
|
$('<tr>').addClass('empty last').append(
|
|
$('<td>').html(_l('label.no.data'))
|
|
).appendTo($tbody)
|
|
];
|
|
}
|
|
}
|
|
|
|
$tbody.find('tr.empty').remove();
|
|
|
|
$(data).each(function() {
|
|
var dataItem = this;
|
|
var id = dataItem.id;
|
|
var $quickView;
|
|
|
|
var $tr = $('<tr>');
|
|
rows.push($tr);
|
|
|
|
if (options.prepend) {
|
|
$tr.prependTo($tbody);
|
|
} else {
|
|
$tr.appendTo($tbody);
|
|
}
|
|
|
|
var hiddenFields = [];
|
|
if (preFilter != null)
|
|
hiddenFields = preFilter();
|
|
|
|
if (multiSelect) {
|
|
var $td = $('<td>')
|
|
.addClass('multiselect')
|
|
.appendTo($tr);
|
|
var content = $('<input>')
|
|
.attr('type', 'checkbox')
|
|
.addClass('multiSelectCheckbox')
|
|
.click(function() {
|
|
var checked = $(this).is(':checked');
|
|
var numRows = $(this).parents('tbody').find('input.multiSelectCheckbox').length;
|
|
var numRowsChecked = $(this).parents('tbody').find('input.multiSelectCheckbox:checked').length;
|
|
var enabled = checked || (numRowsChecked > 0);
|
|
|
|
toggleMultiSelectActions($td.closest('.list-view'), enabled);
|
|
|
|
$td.closest('.list-view').find('input.multiSelectMasterCheckbox').attr('checked', (numRows === numRowsChecked));
|
|
});
|
|
|
|
$td.append(
|
|
$('<span></span>').html(content)
|
|
);
|
|
}
|
|
|
|
var reducedFields = {};
|
|
var idx = 0;
|
|
$.each(fields, function(key) {
|
|
if ($.inArray(key, hiddenFields) != -1)
|
|
return true;
|
|
var field = this;
|
|
if (field.columns) {
|
|
$.each(field.columns, function(innerKey) {
|
|
reducedFields[innerKey] = this;
|
|
});
|
|
reducedFields['blank-cell-' + idx] = {blankCell: true};
|
|
idx += 1;
|
|
hasCollapsibleColumn = true;
|
|
} else {
|
|
reducedFields[key] = this;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Add field data
|
|
$.each(reducedFields, function(key) {
|
|
if ($.inArray(key, hiddenFields) != -1)
|
|
return true;
|
|
var field = this;
|
|
var $td = $('<td>')
|
|
.addClass(key)
|
|
.data('list-view-item-field', key)
|
|
.appendTo($tr);
|
|
var content = dataItem[key];
|
|
|
|
if (field.truncate) {
|
|
$td.addClass('truncated');
|
|
}
|
|
|
|
if (field.blankCell) {
|
|
$td.css({'min-width': '10px', 'width': '10px'});
|
|
$td.hide();
|
|
}
|
|
|
|
if (field.indicator) {
|
|
$td.addClass('state').addClass(field.indicator[content]);
|
|
|
|
// Disabling indicator for now per new design
|
|
//$tr.find('td:first').addClass('item-state-' + field.indicator[content]);
|
|
}
|
|
|
|
if (field.thresholdcolor && field.thresholds) {
|
|
if ((field.thresholds.disable in dataItem) && (field.thresholds.notification in dataItem)) {
|
|
var disableThreshold = dataItem[field.thresholds.disable];
|
|
var notificationThreshold = dataItem[field.thresholds.notification];
|
|
if (disableThreshold) {
|
|
$td.addClass('alert-disable-threshold');
|
|
} else if (notificationThreshold) {
|
|
$td.addClass('alert-notification-threshold');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (field.limitcolor && field.limits) {
|
|
if ((field.limits.lowerlimit in dataItem) && (field.limits.upperlimit in dataItem)) {
|
|
var upperlimit = parseFloat(dataItem[field.limits.upperlimit]);
|
|
var lowerlimit = parseFloat(dataItem[field.limits.lowerlimit ]);
|
|
var value = parseFloat(content);
|
|
if (value <= lowerlimit) {
|
|
$td.addClass('alert-disable-threshold');
|
|
} else if (value <= upperlimit) {
|
|
$td.addClass('alert-notification-threshold');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (field.id == true) id = field.id;
|
|
if ($td.index()) $td.addClass('reduced-hide');
|
|
if (field.action) {
|
|
$td.data('list-view-action', key);
|
|
}
|
|
if (field.converter) {
|
|
content = _l(field.converter(content, dataItem));
|
|
}
|
|
if (field.editable) {
|
|
$td.html(_s(content));
|
|
createEditField($td).appendTo($td);
|
|
} else {
|
|
$td.html('');
|
|
|
|
if ($.isArray(content)) {
|
|
var $ul = $('<ul>');
|
|
|
|
$(content).map(function (index, contentItem) {
|
|
var $li = $('<li>');
|
|
|
|
$li.append('<span>').text(contentItem.toString());
|
|
$li.appendTo($ul);
|
|
});
|
|
|
|
$ul.appendTo($td);
|
|
} else if (field.span == false) {
|
|
$td.append(
|
|
$('<pre>').html(_s(content))
|
|
);
|
|
} else {
|
|
var span = $('<span>').html(_s(content));
|
|
if (field.compact) {
|
|
span.addClass('compact');
|
|
span.html('');
|
|
}
|
|
$td.append(span);
|
|
}
|
|
}
|
|
|
|
$td.attr('title', _s(content));
|
|
});
|
|
|
|
var $first = $tr.find('td:first');
|
|
if (multiSelect)
|
|
$first = $first.next();
|
|
$first.addClass('first');
|
|
|
|
// Add reorder actions
|
|
if (reorder) {
|
|
var sort = function($tr, action) {
|
|
var $listView = $tr.closest('.list-view');
|
|
var viewArgs = $listView.data('view-args');
|
|
var context = $.extend(
|
|
true, {},
|
|
$tr.closest('.list-view').data('view-args').context
|
|
);
|
|
var rowIndex = $tr.closest('tbody').find('tr').length - ($tr.index());
|
|
|
|
context[viewArgs.activeSection] = $tr.data('json-obj');
|
|
|
|
action.action({
|
|
context: context,
|
|
index: rowIndex,
|
|
response: {
|
|
success: function(args) {},
|
|
error: function(args) {
|
|
// Move back to previous position
|
|
rowActions.moveTo($tr, rowIndex);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
$('<td>').addClass('actions reorder').appendTo($tr).append(function() {
|
|
var $td = $(this);
|
|
|
|
$.each(reorder, function(actionName, action) {
|
|
var fnLabel = {
|
|
moveTop: _l('label.move.to.top'),
|
|
moveBottom: _l('label.move.to.bottom'),
|
|
moveUp: _l('label.move.up.row'),
|
|
moveDown: _l('label.move.down.row'),
|
|
moveDrag: _l('label.drag.new.position')
|
|
};
|
|
|
|
$('<div>')
|
|
.addClass('action reorder')
|
|
.addClass(actionName)
|
|
.append(
|
|
$('<span>').addClass('icon').html(' ')
|
|
)
|
|
.attr({
|
|
title: _l(fnLabel[actionName])
|
|
})
|
|
.appendTo($td)
|
|
.click(function() {
|
|
if (actionName == 'moveDrag') return false;
|
|
|
|
rowActions[actionName]($tr);
|
|
var map1 = {};
|
|
$tr.closest('tbody').find('tr').each(function() {
|
|
/*
|
|
* fire only one sorting API call(updateXXXXXXX&sortKey=n&id=UUID) for items who have the same UUID.
|
|
* e.g. An Template/ISO of multiple zones have the same UUID.
|
|
*/
|
|
var objId = $(this).data('json-obj').id;
|
|
if(!(objId in map1)) {
|
|
sort($(this), action);
|
|
map1[objId] = 1;
|
|
}
|
|
});
|
|
$tr.closest('.data-table').dataTable('selectRow', $tr.index());
|
|
|
|
return false;
|
|
});
|
|
});
|
|
});
|
|
|
|
// Draggable action
|
|
var initDraggable = function($tr) {
|
|
var originalIndex;
|
|
|
|
return $tr.closest('tbody').sortable({
|
|
handle: '.action.moveDrag',
|
|
start: function(event, ui) {
|
|
originalIndex = ui.item.index();
|
|
},
|
|
stop: function(event, ui) {
|
|
rowActions._std($tr, function() {});
|
|
var map1 = {};
|
|
$tr.closest('tbody').find('tr').each(function() {
|
|
/*
|
|
* fire only one sorting API call(updateXXXXXXX&sortKey=n&id=UUID) for items who have the same UUID.
|
|
* e.g. An Template/ISO of multiple zones have the same UUID.
|
|
*/
|
|
var objId = $(this).data('json-obj').id;
|
|
if(!(objId in map1)) {
|
|
sort($(this), reorder.moveDrag);
|
|
map1[objId] = 1;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
if (reorder && reorder.moveDrag) {
|
|
initDraggable($tr);
|
|
}
|
|
}
|
|
|
|
// Add action data
|
|
$tr.data('list-view-item-id', id);
|
|
$tr.data('jsonObj', dataItem);
|
|
$tr.data('list-view-action-filter', options.actionFilter);
|
|
|
|
var actionsArray = actions ? $.map(actions, function(v, k) {
|
|
if (k == 'add') {
|
|
v.isAdd = true;
|
|
}
|
|
|
|
return v;
|
|
}) : [];
|
|
var headerActionsArray = $.grep(
|
|
actionsArray,
|
|
function(action) {
|
|
return action.isHeader || action.isAdd;
|
|
}
|
|
);
|
|
|
|
if (actions && !options.noActionCol && renderActionCol(actions) && actionsArray.length != headerActionsArray.length) {
|
|
var allowedActions = $.map(actions, function(value, key) {
|
|
return key;
|
|
});
|
|
|
|
var $listView = $tr.closest('.list-view');
|
|
var isUICustom = $listView.data('view-args') ?
|
|
$tr.closest('.list-view').data('view-args').uiCustom : false;
|
|
|
|
if ($.isFunction(options.actionFilter) && !isUICustom) {
|
|
allowedActions = options.actionFilter({
|
|
context: $.extend(true, {}, options.context, {
|
|
actions: allowedActions,
|
|
item: dataItem
|
|
})
|
|
});
|
|
}
|
|
|
|
makeActionIcons(
|
|
$('<td></td>').addClass('actions reduced-hide')
|
|
.appendTo($tr),
|
|
actions, {
|
|
allowedActions: allowedActions
|
|
}
|
|
);
|
|
}
|
|
|
|
$tr.closest('.list-view').trigger('cloudStack.listView.addRow', {
|
|
$tr: $tr
|
|
});
|
|
|
|
// Add sub-select
|
|
if (subselect) {
|
|
var $td = $tr.find('td.first');
|
|
var $select = $('<div></div>').addClass('subselect').append(
|
|
$('<span>').html(_l(subselect.label))
|
|
).hide();
|
|
var $selectionArea = $tr.find('td:last').find('input');
|
|
|
|
if (subselect.isMultiple) {
|
|
$select.append(
|
|
$('<select multiple>'),
|
|
$('<span>').addClass('info').html(_l('message.listView.subselect.multi'))
|
|
);
|
|
} else {
|
|
$select.append($('<select>'));
|
|
}
|
|
|
|
$td.append($select);
|
|
|
|
// Show and populate selection
|
|
$selectionArea.change(function() {
|
|
if ($(this).is(':checked')) {
|
|
// Populate data
|
|
subselect.dataProvider({
|
|
context: $.extend(true, {},
|
|
($listView && $listView.data('view-args') ?
|
|
$.extend(true, {}, $listView.data('view-args').context, options.context) :
|
|
options.context), {
|
|
instances: [$tr.data('json-obj')]
|
|
}),
|
|
response: {
|
|
success: function(args) {
|
|
var data = args.data;
|
|
|
|
if (data.length) {
|
|
$(data).map(function(index, item) {
|
|
var $option = $('<option>');
|
|
|
|
$option.attr('value', item.id);
|
|
$option.attr('title', item.description);
|
|
$option.append(item.description);
|
|
$option.appendTo($select.find('select'));
|
|
});
|
|
$select.show();
|
|
} else {
|
|
$select.hide();
|
|
}
|
|
|
|
$select.find('option:first').attr('selected', 'selected');
|
|
$listView.find('.data-table').dataTable('refresh');
|
|
}
|
|
}
|
|
});
|
|
|
|
if ($(this).is('input[type=radio]')) {
|
|
$(this).closest('tr').siblings().find('input[type=radio]').change();
|
|
}
|
|
} else {
|
|
$select.find('option').remove();
|
|
$select.hide();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Add quick view
|
|
if (detailView && !$.isFunction(detailView) && !detailView.noCompact && !uiCustom) {
|
|
$quickView = $('<td>').addClass('quick-view reduced-hide')
|
|
.append(
|
|
$('<span>').addClass('icon').html(' ')
|
|
)
|
|
.appendTo($tr);
|
|
$quickView.mouseover(
|
|
// Show quick view
|
|
function() {
|
|
var $quickView = $(this);
|
|
var $quickViewTooltip = $('<div>').addClass('quick-view-tooltip hovered-elem');
|
|
var $tr = $quickView.closest('tr');
|
|
var $listView = $tr.closest('.list-view');
|
|
var $title = $('<div>').addClass('title');
|
|
var $detailsContainer = $('<div>').addClass('container').appendTo($quickViewTooltip);
|
|
var context = $.extend(true, {}, $listView.data('view-args').context);
|
|
var activeSection = $listView.data('view-args').activeSection;
|
|
var itemID = $tr.data('list-view-item-id');
|
|
var jsonObj = $tr.data('json-obj');
|
|
var $loading = $('<div>').addClass('loading-overlay').appendTo($detailsContainer);
|
|
var listViewArgs = $listView.data('view-args');
|
|
var listViewActiveSection = activeSection;
|
|
var targetSection;
|
|
|
|
if ($tr.hasClass('loading')) return;
|
|
|
|
if (listViewActiveSection != '_zone') {
|
|
if (listViewArgs.sections &&
|
|
listViewArgs.sections[listViewActiveSection] &&
|
|
listViewArgs.sections[listViewActiveSection].id) {
|
|
targetSection = listViewArgs.sections[listViewActiveSection].id;
|
|
} else {
|
|
targetSection = listViewActiveSection;
|
|
}
|
|
} else {
|
|
targetSection = detailViewArgs.section;
|
|
}
|
|
|
|
// Title
|
|
$title.append(
|
|
$('<span>').html(_l('label.quickview') + ': '),
|
|
$('<span>').addClass('title').html(
|
|
cloudStack.concat(
|
|
$tr.find('td:first span').html(), 30
|
|
)
|
|
).attr({
|
|
title: $tr.find('td:first span').html()
|
|
}),
|
|
$('<span>').addClass('icon').html(' ')
|
|
);
|
|
$quickViewTooltip.append($title);
|
|
$('.quick-view-tooltip').remove();
|
|
// Setup positioning
|
|
$quickViewTooltip.hide().appendTo('#container').fadeIn(200, function() {
|
|
if (!$quickViewTooltip.is(':visible')) return;
|
|
|
|
// Init detail view
|
|
context[targetSection] = [jsonObj];
|
|
createDetailView({
|
|
data: $.extend(true, {}, detailView, {
|
|
onLoad: function($detailView) {
|
|
$loading.remove();
|
|
$detailView.slideToggle('fast');
|
|
},
|
|
onPerformAction: function() {
|
|
$tr.addClass('loading').find('td:last').prepend($('<div>').addClass('loading'));
|
|
$quickViewTooltip.detach();
|
|
},
|
|
onActionComplete: function() {
|
|
if (listViewArgs.onActionComplete) {
|
|
listViewArgs.onActionComplete();
|
|
}
|
|
|
|
$tr.removeClass('loading').find('td:last .loading').remove();
|
|
$quickViewTooltip.remove();
|
|
}
|
|
}),
|
|
id: itemID,
|
|
jsonObj: jsonObj,
|
|
section: activeSection,
|
|
context: context,
|
|
$listViewRow: $tr
|
|
},
|
|
function($detailView) { //complete(), callback funcion
|
|
$detailView.data('list-view', $listView);
|
|
}, $tr, {
|
|
compact: true,
|
|
noPanel: true
|
|
}
|
|
).appendTo($detailsContainer).hide();
|
|
});
|
|
$quickViewTooltip.css({
|
|
position: 'absolute',
|
|
left: $quickView.offset().left + $quickView.outerWidth() - $quickViewTooltip.width() - 2*(parseInt($quickView.css('border-left-width')) + parseInt($quickView.css('border-right-width'))),
|
|
top: $quickView.offset().top,
|
|
zIndex: $tr.closest('.panel').css("zIndex") + 1
|
|
});
|
|
|
|
$quickViewTooltip.mouseleave(function() {
|
|
if (!$('.overlay:visible').length) {
|
|
$quickViewTooltip.remove();
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
// Toggle collapsible column to fix alignment of hidden/shown cells
|
|
if (hasCollapsibleColumn) {
|
|
$tbody.closest('table').find('tr:first th.collapsible-column:visible').prev('th').click();
|
|
}
|
|
|
|
// Re-sort table if a column was previously sorted
|
|
$listView.find('thead tr:last th.sorted').click().click();
|
|
|
|
return rows;
|
|
};
|
|
|
|
var setLoading = function($table, completeFn) {
|
|
var $loading = $('<tr>')
|
|
.addClass('loading')
|
|
.appendTo($table.find('tbody'))
|
|
.append(
|
|
$('<td>')
|
|
.addClass('loading icon')
|
|
.attr({
|
|
'colspan': $table.find('th').length
|
|
})
|
|
);
|
|
|
|
$table.closest('div.list-view').scrollTop($table.height() + 100);
|
|
|
|
return completeFn({
|
|
loadingCompleted: function() {
|
|
$loading.remove();
|
|
}
|
|
});
|
|
};
|
|
|
|
var loadBody = function($table, dataProvider, preFilter, fields, append, loadArgs, actions, options) {
|
|
if (!options) options = {};
|
|
var context = options.context;
|
|
var reorder = options.reorder;
|
|
var multiSelect = options.multiSelect;
|
|
var $tbody = $table.find('tbody');
|
|
var $listView = $table.closest('.list-view');
|
|
|
|
if (!loadArgs) loadArgs = {
|
|
page: 1,
|
|
filterBy: {
|
|
search: {},
|
|
kind: 'all',
|
|
page: 1
|
|
}
|
|
};
|
|
|
|
if (options.clearEndTable) {
|
|
$listView.data('page', 1);
|
|
$table.closest('.list-view').data('end-of-table', false);
|
|
}
|
|
|
|
if (!append) {
|
|
if (!append) $table.find('tbody tr').remove();
|
|
}
|
|
|
|
var viewArgs = $listView.data('view-args');
|
|
var uiCustom = viewArgs.listView ? viewArgs.listView.uiCustom : false;
|
|
|
|
setLoading($table, function(setLoadingArgs) {
|
|
$table.dataTable();
|
|
$.extend(loadArgs, {
|
|
context: options.context,
|
|
response: {
|
|
success: function(args) {
|
|
setLoadingArgs.loadingCompleted();
|
|
|
|
addTableRows(preFilter, fields, args.data, $tbody, actions, {
|
|
actionFilter: args.actionFilter,
|
|
context: context,
|
|
reorder: reorder,
|
|
detailView: options.detailView,
|
|
'multiSelect': options.multiSelect,
|
|
noActionCol: options.noActionCol
|
|
});
|
|
$table.dataTable(null, {
|
|
noSelect: uiCustom
|
|
});
|
|
|
|
setTimeout(function() {
|
|
$table.dataTable('refresh');
|
|
});
|
|
},
|
|
error: function(args) {
|
|
setLoadingArgs.loadingCompleted();
|
|
addTableRows(preFilter, fields, [], $tbody, actions);
|
|
$table.find('td:first').html(_l('ERROR'));
|
|
$table.dataTable(null, {
|
|
noSelect: uiCustom
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
return dataProvider(loadArgs);
|
|
};
|
|
|
|
/**
|
|
* Make 'switcher' buttons for sections
|
|
*/
|
|
var createSectionSwitcher = function(args) {
|
|
var sections = args.sections;
|
|
var $switcher = $('<div>').addClass('section-switcher reduced-hide');
|
|
var $sectionSelect = $('<select></select>')
|
|
.appendTo(
|
|
$('<div></div>')
|
|
.addClass('section-select')
|
|
.appendTo($switcher)
|
|
);
|
|
var sectionPreFilter;
|
|
|
|
if (args.sectionSelect) {
|
|
$('<label>')
|
|
.prependTo($sectionSelect.parent())
|
|
.html(_l(args.sectionSelect.label) + ':');
|
|
|
|
sectionPreFilter = args.sectionSelect.preFilter ?
|
|
args.sectionSelect.preFilter({
|
|
context: $.extend({}, cloudStack.context, args.context)
|
|
}) : null;
|
|
} else {
|
|
$sectionSelect.hide();
|
|
}
|
|
|
|
// No need to display switcher if only one entry is present
|
|
if (sectionPreFilter && sectionPreFilter.length == 1) {
|
|
$switcher.find('select').hide();
|
|
$switcher.find('label').html(
|
|
_l('label.viewing') + ' ' + _l(sections[sectionPreFilter[0]].title)
|
|
);
|
|
}
|
|
|
|
$.each(sections, function(key) {
|
|
if (sectionPreFilter && $.inArray(key, sectionPreFilter) == -1) {
|
|
return true;
|
|
}
|
|
|
|
var $sectionButton;
|
|
|
|
if (!this.type || this.type == 'button') {
|
|
$sectionButton = $('<div>')
|
|
.addClass('section')
|
|
.append(
|
|
$('<a>')
|
|
.addClass(key)
|
|
.attr({
|
|
href: '#'
|
|
})
|
|
.data('list-view-section-id', key)
|
|
.html(_l(this.title))
|
|
);
|
|
|
|
$sectionButton.appendTo($switcher);
|
|
} else if (this.type == 'select') {
|
|
$sectionSelect.append(
|
|
$('<option></option>')
|
|
.attr('value', key)
|
|
.html(_l(this.title))
|
|
);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
$switcher.find('div.section:first').addClass('first');
|
|
$switcher.find('div.section:last').addClass('last');
|
|
|
|
return $switcher;
|
|
};
|
|
|
|
/**
|
|
* Generate/reset entire list view elements
|
|
*
|
|
* @param $container Container to place list view inside
|
|
* @param args List view setup data
|
|
* @param section If section, reset list view to specified section
|
|
*/
|
|
var makeListView = function($container, args, section) {
|
|
args.activeSection = section ? section : (
|
|
args.listView.id ? args.listView.id : args.id
|
|
);
|
|
|
|
// Clear out any existing list view
|
|
var $existingListView = $container.find('div.list-view');
|
|
if ($existingListView.length) {
|
|
$existingListView.remove();
|
|
}
|
|
|
|
var listViewData = args.listView;
|
|
|
|
if (section) {
|
|
listViewData = args.sections[section].listView;
|
|
|
|
var sectionTitle = _l(args.title);
|
|
var subsectionTitle = _l(args.sections[section].title);
|
|
|
|
// Show subsection in breadcrumb
|
|
if (args.$breadcrumb) {
|
|
if ((sectionTitle && subsectionTitle) &&
|
|
(sectionTitle != subsectionTitle)) {
|
|
args.$breadcrumb.find('span.subsection').html(' - ' + subsectionTitle);
|
|
args.$breadcrumb.attr('title', sectionTitle + ' - ' + subsectionTitle);
|
|
} else {
|
|
args.$breadcrumb.find('span.subsection').html('');
|
|
args.$breadcrumb.attr('title', sectionTitle);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create table and other elems
|
|
var $listView = $('<div></div>')
|
|
.addClass('view list-view')
|
|
.addClass(listViewData.section);
|
|
|
|
$listView.data('view-args', args);
|
|
|
|
var $toolbar = $('<div>').addClass('toolbar').appendTo($listView);
|
|
var $table = $('<table>').appendTo($listView);
|
|
var infScrollTimer;
|
|
var actions = listViewData.actions;
|
|
var reorder = listViewData.reorder;
|
|
var multiSelect = listViewData.multiSelect;
|
|
var tableHeight = $table.height();
|
|
|
|
$listView.data('end-of-table', false);
|
|
$listView.data('page', 1);
|
|
|
|
var $switcher;
|
|
if (args.sections) {
|
|
$switcher = createSectionSwitcher(args);
|
|
if (section) {
|
|
$switcher
|
|
.appendTo($toolbar)
|
|
.find('a.' + section).addClass('active');
|
|
$switcher.find('div.section-select select').val(section);
|
|
}
|
|
}
|
|
|
|
if ($switcher && $switcher.find('option').length == 1) {
|
|
listViewData = args.sections[
|
|
$switcher.find('select').val()
|
|
].listView;
|
|
|
|
args.activeSection = listViewData.id;
|
|
}
|
|
|
|
if (listViewData.hideToolbar) {
|
|
$toolbar.hide();
|
|
}
|
|
|
|
// Add panel controls
|
|
$('<div class="panel-controls">').append($('<div class="control expand">').attr({
|
|
'ui-id': 'toggle-expand-panel'
|
|
})).appendTo($toolbar);
|
|
|
|
if (listViewData.actions && listViewData.actions.add) {
|
|
var showAdd = listViewData.actions.add.preFilter ?
|
|
listViewData.actions.add.preFilter({
|
|
context: listViewData.context ? listViewData.context : args.context
|
|
}) : true;
|
|
|
|
if (showAdd) {
|
|
$toolbar
|
|
.append(
|
|
$('<div>')
|
|
.addClass('button action add reduced-hide')
|
|
.data('list-view-action-id', 'add')
|
|
.append(
|
|
$('<span>').html(_l(listViewData.actions.add.label))
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
// List view header actions
|
|
if (listViewData.actions) {
|
|
$.each(listViewData.actions, function(actionName, action) {
|
|
var preFilter = function(extendContext) {
|
|
var context = $.extend(true, {},
|
|
$listView.data('view-args').context ? $listView.data('view-args').context : cloudStack.context);
|
|
|
|
if (extendContext) {
|
|
$.extend(context, extendContext);
|
|
}
|
|
|
|
return action.preFilter ? action.preFilter({
|
|
id: listViewData.id,
|
|
context: context
|
|
}) : null;
|
|
}
|
|
|
|
if (!action.isHeader || (action.preFilter && !preFilter())) return true;
|
|
|
|
var $action = $('<div>')
|
|
.addClass('button action main-action reduced-hide').addClass(actionName)
|
|
.data('list-view-action-id', actionName)
|
|
.append($('<span>').addClass('icon'))
|
|
.append($('<span>').html(_l(action.label)));
|
|
|
|
if (action.isMultiSelectAction) {
|
|
$action.addClass('multiSelectAction');
|
|
$action.hide();
|
|
|
|
if (action.preFilter) {
|
|
$action.data('list-view-action-prefilter', preFilter);
|
|
}
|
|
}
|
|
|
|
$toolbar.append($action)
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
$('<tbody>').appendTo($table);
|
|
|
|
createHeader(listViewData.preFilter,
|
|
listViewData.fields,
|
|
$table,
|
|
listViewData.actions, {
|
|
reorder: reorder,
|
|
detailView: listViewData.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: listViewData.noActionCol,
|
|
groupableColumns: listViewData.groupableColumns
|
|
});
|
|
|
|
if (listViewData.noSplit) {
|
|
$table.addClass('no-split');
|
|
}
|
|
|
|
if (listViewData.horizontalOverflow) {
|
|
$table.addClass('horizontal-overflow');
|
|
$table.parent().css({'overflow-x': 'auto'});
|
|
}
|
|
|
|
createFilters($toolbar, listViewData.filters);
|
|
|
|
if (listViewData.hideSearchBar != true) {
|
|
createSearchBar($toolbar, listViewData);
|
|
}
|
|
|
|
loadBody(
|
|
$table,
|
|
listViewData.dataProvider,
|
|
listViewData.preFilter,
|
|
listViewData.fields,
|
|
false, {
|
|
page: $listView.data('page'),
|
|
filterBy: {
|
|
kind: $listView.find('select[id=filterBy]').val(),
|
|
search: {
|
|
value: $listView.find('input[type=text]').val(),
|
|
by: 'name'
|
|
}
|
|
},
|
|
ref: args.ref
|
|
},
|
|
listViewData.actions, {
|
|
context: args.context,
|
|
reorder: reorder,
|
|
detailView: listViewData.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: listViewData.noActionCol
|
|
}
|
|
);
|
|
|
|
// Keyboard events
|
|
$listView.bind('keypress', function(event) {
|
|
var code = (event.keyCode ? event.keyCode : event.which);
|
|
var $input = $listView.find('input:focus');
|
|
|
|
if ($input.length && $input.hasClass('edit') && code === 13) {
|
|
uiActions.edit($input.closest('tr'), {
|
|
callback: listViewData.actions.edit.action
|
|
});
|
|
}
|
|
});
|
|
|
|
// Setup item events
|
|
$listView.find('tbody').bind('click', function(event) {
|
|
var $target = $(event.target);
|
|
var listViewAction = $target.data('list-view-action');
|
|
|
|
if (!listViewAction) return true;
|
|
|
|
listViewData.fields[listViewAction].action();
|
|
|
|
return true;
|
|
});
|
|
|
|
//basic search
|
|
var basicSearch = function() {
|
|
$listView.removeData('advSearch');
|
|
advancedSearchData = {};
|
|
|
|
$listView.data('page', 1);
|
|
loadBody(
|
|
$table,
|
|
listViewData.dataProvider,
|
|
listViewData.preFilter,
|
|
listViewData.fields,
|
|
false, {
|
|
page: $listView.data('page'),
|
|
filterBy: {
|
|
kind: $listView.find('select[id=filterBy]').val(),
|
|
search: {
|
|
value: $listView.find('input[type=text]').val(),
|
|
by: 'name'
|
|
}
|
|
}
|
|
},
|
|
listViewData.actions, {
|
|
context: $listView.data('view-args').context,
|
|
reorder: listViewData.reorder,
|
|
detailView: listViewData.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: listViewData.noActionCol
|
|
}
|
|
);
|
|
};
|
|
|
|
$listView.find('.search-bar input[type=text]').keyup(function(event) {
|
|
if (event.keyCode == 13) //13 is keycode of Enter key
|
|
basicSearch();
|
|
return true;
|
|
});
|
|
$listView.find('.button.search#basic_search').bind('click', function(event) {
|
|
basicSearch();
|
|
return true;
|
|
});
|
|
$listView.find('select').bind('change', function(event) {
|
|
if ($(event.target).closest('.section-select').length) return true;
|
|
if ((event.type == 'click' ||
|
|
event.type == 'mouseup') &&
|
|
($(event.target).is('select') ||
|
|
$(event.target).is('option') ||
|
|
$(event.target).is('input')))
|
|
return true;
|
|
|
|
basicSearch();
|
|
|
|
return true;
|
|
});
|
|
|
|
//advanced search
|
|
var advancedSearch = function(args) {
|
|
$listView.data('advSearch', args.data);
|
|
$listView.data('page', 1);
|
|
loadBody(
|
|
$table,
|
|
listViewData.dataProvider,
|
|
listViewData.preFilter,
|
|
listViewData.fields,
|
|
false, {
|
|
page: $listView.data('page'),
|
|
filterBy: {
|
|
kind: $listView.find('select[id=filterBy]').val(),
|
|
advSearch: args.data
|
|
}
|
|
},
|
|
listViewData.actions, {
|
|
context: $listView.data('view-args').context,
|
|
reorder: listViewData.reorder,
|
|
detailView: listViewData.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: listViewData.noActionCol
|
|
}
|
|
);
|
|
};
|
|
|
|
var advancedSearchData = {};
|
|
|
|
var closeAdvancedSearch = function() {
|
|
$listView.find('.advanced-search .form-container:visible').remove();
|
|
};
|
|
|
|
$listView.find('.advanced-search .icon').bind('click', function(event) {
|
|
if ($listView.find('.advanced-search .form-container:visible').length) {
|
|
closeAdvancedSearch();
|
|
|
|
return false;
|
|
}
|
|
|
|
// Setup advanced search default values, when existing data is present
|
|
$.each(listViewData.advSearchFields, function(fieldID, field) {
|
|
field.defaultValue = advancedSearchData[fieldID];
|
|
});
|
|
|
|
var form = cloudStack.dialog.createForm({
|
|
noDialog: true,
|
|
form: {
|
|
title: 'label.advanced.search',
|
|
fields: listViewData.advSearchFields
|
|
},
|
|
after: function(args) {
|
|
advancedSearch(args);
|
|
advancedSearchData = args.data;
|
|
$listView.find('.button.search#basic_search').siblings('.search-bar').find('input').val(''); //clear basic search input field to avoid confusion of search result
|
|
closeAdvancedSearch();
|
|
}
|
|
});
|
|
var $formContainer = form.$formContainer;
|
|
var $form = $formContainer.find('form');
|
|
|
|
$formContainer.hide().appendTo($listView.find('.advanced-search')).show();
|
|
$form.find('.form-item:first input').focus();
|
|
$form.find('input[type=submit]')
|
|
.show()
|
|
.appendTo($form)
|
|
.val(_l('label.search'));
|
|
|
|
// Cancel button
|
|
$form.append(
|
|
$('<div>').addClass('button cancel').html(_l('label.cancel'))
|
|
.click(function() {
|
|
closeAdvancedSearch();
|
|
})
|
|
);
|
|
|
|
$form.submit(function() {
|
|
form.completeAction($formContainer);
|
|
});
|
|
|
|
return false;
|
|
});
|
|
|
|
|
|
// Infinite scrolling event
|
|
$listView.bind('scroll', function(event) {
|
|
var listView = args.listView;
|
|
if (!listView && args.sections && args.sections.hasOwnProperty(args.activeSection)) {
|
|
listView = args.sections[args.activeSection].listView;
|
|
}
|
|
if (listView && listView.disableInfiniteScrolling) return false;
|
|
if ($listView.find('tr.last, td.loading:visible').length) return false;
|
|
|
|
clearTimeout(infScrollTimer);
|
|
infScrollTimer = setTimeout(function() {
|
|
var loadMoreData = $listView.scrollTop() >= ($table.height() - $listView.height()) - $listView.height() / 4;
|
|
var context = $listView.data('view-args').context;
|
|
|
|
if (loadMoreData && !$listView.data('end-of-table')) {
|
|
$listView.data('page', $listView.data('page') + 1);
|
|
|
|
var filterBy = {
|
|
kind: $listView.find('select[id=filterBy]').length > 0 ? $listView.find('select[id=filterBy]').val() : 'all'
|
|
};
|
|
if ($listView.data('advSearch') == null) {
|
|
filterBy.search = {
|
|
value: $listView.find('input[type=text]').length > 0 ? $listView.find('input[type=text]').val() : '',
|
|
by: 'name'
|
|
};
|
|
} else {
|
|
filterBy.advSearch = $listView.data('advSearch');
|
|
}
|
|
|
|
loadBody(
|
|
$table,
|
|
listViewData.dataProvider,
|
|
listViewData.preFilter,
|
|
listViewData.fields, true, {
|
|
context: context,
|
|
page: $listView.data('page'),
|
|
filterBy: filterBy
|
|
}, actions, {
|
|
reorder: listViewData.reorder,
|
|
detailView: listViewData.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: listViewData.noActionCol
|
|
});
|
|
$table.height() == tableHeight ? endTable = true : tableHeight = $table.height();
|
|
}
|
|
}, 500);
|
|
|
|
return true;
|
|
});
|
|
|
|
// Action events
|
|
$(window).bind('cloudstack.view-item-action', function(event, data) {
|
|
var actionName = data.actionName;
|
|
var $tr = $listView.find('tr').filter(function() {
|
|
return $(this).data('list-view-item-id') == data.id;
|
|
});
|
|
|
|
if (actionName == 'destroy') {
|
|
$tr.animate({
|
|
opacity: 0.5
|
|
});
|
|
$tr.bind('click', function() {
|
|
return false;
|
|
});
|
|
}
|
|
});
|
|
|
|
$listView.bind('click change', function(event) {
|
|
var $target = $(event.target);
|
|
var id = $target.closest('tr').data('list-view-item-id');
|
|
var jsonObj = $target.closest('tr').data('jsonObj');
|
|
var detailViewArgs;
|
|
var detailViewPresent = ($target.closest('div.data-table tr td.first').length &&
|
|
listViewData.detailView && !$target.closest('div.edit').length) && !listViewData.detailView.noPanelView;
|
|
var uiCustom = args.uiCustom == true ? true : false;
|
|
|
|
// Click on first item will trigger detail view (if present)
|
|
if (detailViewPresent && !uiCustom && !$target.closest('.empty, .loading').length) {
|
|
var $loading = $('<div>').addClass('loading-overlay');
|
|
$target.closest('div.data-table').prepend($loading); //overlay the whole listView, so users can't click another row until click-handling for this row is done (e.g. API response is back)
|
|
|
|
listViewData.detailView.$browser = args.$browser;
|
|
detailViewArgs = {
|
|
$panel: $target.closest('div.panel'),
|
|
data: listViewData.detailView,
|
|
title: $target.closest('td').find('span').html(),
|
|
id: id,
|
|
jsonObj: jsonObj,
|
|
ref: jsonObj,
|
|
context: $.extend(true, {}, $listView.data('view-args').context)
|
|
};
|
|
|
|
// Populate context object w/ instance data
|
|
var listViewActiveSection = $listView.data('view-args').activeSection;
|
|
|
|
// Create custom-generated detail view
|
|
if (listViewData.detailView.pageGenerator) {
|
|
detailViewArgs.pageGenerator = listViewData.detailView.pageGenerator;
|
|
}
|
|
|
|
var listViewArgs = $listView.data('view-args');
|
|
|
|
var targetSection;
|
|
|
|
detailViewArgs.section = listViewArgs.activeSection ?
|
|
listViewArgs.activeSection : listViewArgs.id;
|
|
|
|
if (listViewActiveSection != '_zone') {
|
|
if (listViewArgs.sections &&
|
|
listViewArgs.sections[listViewActiveSection] &&
|
|
listViewArgs.sections[listViewActiveSection].id) {
|
|
targetSection = listViewArgs.sections[listViewActiveSection].id;
|
|
} else {
|
|
targetSection = listViewActiveSection;
|
|
}
|
|
} else {
|
|
targetSection = detailViewArgs.section;
|
|
}
|
|
|
|
detailViewArgs.context[targetSection] = [jsonObj];
|
|
|
|
if ($.isFunction(detailViewArgs.data)) {
|
|
detailViewArgs.data = detailViewArgs.data({
|
|
context: detailViewArgs.context
|
|
});
|
|
}
|
|
|
|
detailViewArgs.data.onActionComplete = listViewArgs.onActionComplete;
|
|
|
|
createDetailView(
|
|
detailViewArgs,
|
|
function($detailView) { //complete(), callback funcion
|
|
$detailView.data('list-view', $listView);
|
|
$loading.remove();
|
|
},
|
|
$target.closest('tr')
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Action icons
|
|
if (!$target.closest('td.actions').hasClass('reorder') &&
|
|
($target.closest('td.actions').length ||
|
|
$target.closest('.action.add').length ||
|
|
$target.closest('.action.main-action').length)) {
|
|
var actionID = $target.closest('.action').data('list-view-action-id');
|
|
var $tr;
|
|
|
|
if ($target.closest('.action').is('.disabled')) {
|
|
return false;
|
|
}
|
|
|
|
if ($target.closest('.action.add').length ||
|
|
$target.closest('.action.main-action:not(.multiSelectAction)').length) {
|
|
$tr = $target.closest('div.list-view').find('tr:first'); // Dummy row
|
|
} else {
|
|
if (listViewData.actions[actionID].isMultiSelectAction) {
|
|
$tr = $listView.find('input.multiSelectCheckbox:checked').parents('tr');
|
|
} else {
|
|
$tr = $target.closest('tr');
|
|
}
|
|
}
|
|
|
|
var uiCallback = uiActions[actionID];
|
|
|
|
if (!uiCallback)
|
|
uiCallback = uiActions['standard'];
|
|
|
|
uiCallback($tr, {
|
|
action: listViewData.actions[actionID]
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
// Edit field action icons
|
|
if ($target.hasClass('action') && $target.parent().is('div.edit')) {
|
|
uiActions.edit($target.closest('tr'), {
|
|
callback: listViewData.actions.edit.action,
|
|
cancel: $target.hasClass('cancel')
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// Section switcher
|
|
if ($target.is('a') && $target.closest('div.section-switcher').length) {
|
|
makeListView($container, args, $target.data('list-view-section-id'));
|
|
|
|
return false;
|
|
}
|
|
|
|
if ($target.is('div.section-switcher select') && event.type == 'change') {
|
|
makeListView($container, args, $target.val());
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return $listView.appendTo($container);
|
|
};
|
|
|
|
var prependItem = function(listView, data, actionFilter, options) {
|
|
if (!options) options = {};
|
|
|
|
var viewArgs = listView.data('view-args');
|
|
var listViewArgs = $.isPlainObject(viewArgs.listView) ? viewArgs.listView : viewArgs;
|
|
var targetArgs = listViewArgs.activeSection ? listViewArgs.sections[
|
|
listViewArgs.activeSection
|
|
].listView : listViewArgs;
|
|
var reorder = targetArgs.reorder;
|
|
var $tr = addTableRows(
|
|
targetArgs.preFilter,
|
|
targetArgs.fields,
|
|
data,
|
|
listView.find('table tbody'),
|
|
targetArgs.actions, {
|
|
prepend: true,
|
|
actionFilter: actionFilter,
|
|
reorder: reorder,
|
|
detailView: targetArgs.detailView,
|
|
'multiSelect': targetArgs.multiSelect,
|
|
noActionCol: targetArgs.noActionCol
|
|
}
|
|
)[0];
|
|
listView.find('table').dataTable('refresh');
|
|
|
|
$tr.addClass('loading').find('td:last').prepend($('<div>').addClass('loading'));
|
|
$tr.find('.action').remove();
|
|
|
|
return $tr;
|
|
};
|
|
|
|
var replaceItem = function($row, data, actionFilter, after) {
|
|
var $newRow;
|
|
var $listView = $row.closest('.list-view');
|
|
var viewArgs = $listView.data('view-args');
|
|
var listViewArgs = $.isPlainObject(viewArgs.listView) ? viewArgs.listView : viewArgs;
|
|
var targetArgs = listViewArgs.activeSection ? listViewArgs.sections[
|
|
listViewArgs.activeSection
|
|
].listView : listViewArgs;
|
|
var reorder = targetArgs.reorder;
|
|
var multiSelect = targetArgs.multiSelect;
|
|
var $table = $row.closest('table');
|
|
var defaultActionFilter = $row.data('list-view-action-filter');
|
|
|
|
$newRow = addTableRows(
|
|
targetArgs.preFilter,
|
|
targetArgs.fields,
|
|
data,
|
|
$listView.find('table tbody'),
|
|
targetArgs.actions, {
|
|
actionFilter: actionFilter ? actionFilter : defaultActionFilter,
|
|
reorder: reorder,
|
|
detailView: targetArgs.detailView,
|
|
'multiSelect': multiSelect,
|
|
noActionCol: targetArgs.noActionCol
|
|
}
|
|
)[0];
|
|
|
|
$newRow.data('json-obj', data);
|
|
$row.replaceWith($newRow);
|
|
$table.dataTable('refresh');
|
|
|
|
if (after) after($newRow);
|
|
|
|
return $newRow;
|
|
};
|
|
|
|
var toggleMultiSelectActions = function($listView, enabled) {
|
|
var $multiSelectActions = $listView.find('div.main-action.multiSelectAction');
|
|
|
|
$listView.find('div.action.add')[enabled ? 'hide' : 'show']();
|
|
$listView.find('div.main-action:not(.multiSelectAction)')[enabled ? 'hide' : 'show']();
|
|
$multiSelectActions.hide();
|
|
|
|
if (enabled) {
|
|
$multiSelectActions.filter(function() {
|
|
var preFilter = $(this).data('list-view-action-prefilter');
|
|
var $selectedVMs;
|
|
var context = {};
|
|
|
|
if (preFilter) {
|
|
$selectedVMs = $listView.find('tbody tr').filter(function() {
|
|
return $(this).find('td.multiselect input[type=checkbox]:checked').length
|
|
});
|
|
context[$listView.data('view-args').activeSection] = $selectedVMs.map(function(index, item) {
|
|
return $(item).data('json-obj');
|
|
});
|
|
|
|
return preFilter(context);
|
|
}
|
|
|
|
return true;
|
|
}).show();
|
|
}
|
|
}
|
|
|
|
$.fn.listView = function(args, options) {
|
|
if (!options) options = {};
|
|
if (args == 'prependItem') {
|
|
return prependItem(this, options.data, options.actionFilter);
|
|
} else if (args == 'replaceItem') {
|
|
replaceItem(options.$row, options.data, options.actionFilter, options.after);
|
|
} else if (args.sections) {
|
|
var targetSection;
|
|
$.each(args.sections, function(key) {
|
|
targetSection = key;
|
|
return false;
|
|
});
|
|
makeListView(this, $.extend(true, {}, args, {
|
|
context: options.context
|
|
}), targetSection);
|
|
} else if (args == 'refresh') {
|
|
var activeSection = this.data('view-args').activeSection;
|
|
var listViewArgs = this.data('view-args').sections ?
|
|
this.data('view-args').sections[activeSection].listView :
|
|
this.data('view-args').listView;
|
|
|
|
toggleMultiSelectActions(this, false);
|
|
|
|
loadBody(
|
|
this.find('table:last'),
|
|
listViewArgs.dataProvider,
|
|
listViewArgs.preFilter,
|
|
listViewArgs.fields,
|
|
false,
|
|
null,
|
|
listViewArgs.actions, {
|
|
clearEndTable: true,
|
|
multiSelect: listViewArgs.multiSelect,
|
|
context: this.data('view-args').context,
|
|
detailView: listViewArgs.detailView
|
|
}
|
|
);
|
|
} else {
|
|
makeListView(
|
|
this,
|
|
$.extend(true, {}, args, {
|
|
context: options.context ? options.context : cloudStack.context
|
|
}));
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
// List view refresh handler
|
|
$(window).bind('cloudStack.fullRefresh', function() {
|
|
var $listViews = $('.list-view');
|
|
|
|
$listViews.each(function() {
|
|
var $listView = $(this);
|
|
|
|
$listView.listView('refresh');
|
|
});
|
|
});
|
|
})(window.jQuery, window.cloudStack, window._l, window._s);
|