// 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 $listView2 = $instanceRow.closest('.list-view');
$listView2.find('input.multiSelectMasterCheckbox').prop('checked', false);
toggleMultiSelectActions($listView2, 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 = $('
').addClass('loading-overlay');
if ($listView) {
$listView.prepend($loading);
} else {
$instanceRow.closest('.list-view').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($('
').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($('
').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 = $('
');
} 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}
to put input field into
*/
var createEditField = function($td) {
$td.addClass('editable');
// Put
label into a span
var sanitizedValue = $td.html();
$('').html(sanitizedValue).appendTo($td.html(''));
var $editArea = $('').addClass('edit');
var $editField = $('').addClass('edit').attr({
type: 'text',
value: cloudStack.sanitizeReverse(sanitizedValue)
});
var $actionButton = $('').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 = $('
');
var $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 = $('