').appendTo($item.find('tbody'));
$item.data('json-obj', multiRule);
if (itemData) {
$tr.data('multi-edit-data', itemData);
}
// Add reorder actions
if (reorder) {
$('').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')
};
$('')
.addClass('action reorder')
.addClass(actionName)
.append(
$('
').addClass('icon').html(' ')
)
.attr({
title: _l(fnLabel[actionName])
})
.appendTo($td)
.click(function() {
if (actionName == 'moveDrag') return false;
rowActions[actionName]($tr);
$tr.closest('.data-body').find('.data-item').each(function() {
sort($(this), action);
});
return false;
});
});
});
}
// Setup columns
$.each(fields, function(fieldName, field) {
if (!field || (options.ignoreEmptyFields && !data[fieldName])) {
return true;
}
var isHidden = $multi.find('th.' + fieldName).hasClass('always-hide');
if (isHidden) {
return true;
}
var $td = $(' ').addClass(fieldName).appendTo($tr);
var $input, val;
var $addButton = $multi.find('form .button.add-vm:not(.custom-action)').clone();
var newItemRows = [];
var addItemAction = function(data) {
var $loading = $('').addClass('loading-overlay');
var complete = function(args) {
var $tbody = $item.find('.expandable-listing tbody');
$loading.remove();
$(data).each(function() {
var item = this;
var $itemRow = _medit.multiItem.itemRow(item, options.itemActions, multiRule, $tbody);
$itemRow.data('json-obj', item);
$itemRow.appendTo($tbody);
newItemRows.push($itemRow);
cloudStack.evenOdd($tbody, 'tr:visible', {
even: function($elem) {
$elem.removeClass('odd');
$elem.addClass('even');
},
odd: function($elem) {
$elem.removeClass('even');
$elem.addClass('odd');
}
});
});
};
var error = function() {
$(newItemRows).each(function() {
var $itemRow = this;
$itemRow.remove();
});
$loading.remove();
};
$loading.prependTo($item);
options.itemActions.add.action({
context: options.context,
data: data,
multiRule: multiRule,
response: {
success: function(args) {
var notificationError = function(args) {
error();
};
cloudStack.ui.notifications.add(args.notification,
complete, {},
notificationError, {});
},
error: error
}
});
};
if (!itemData) itemData = [{}];
if (!options.noSelect &&
$multi.find('th,td').filter(function() {
return $(this).attr('rel') == fieldName;
}).is(':hidden')) {
return true;
}
if (!field.isPassword) {
if (field.edit) {
// Edit fields append value of data
if (field.range) {
var start = _s(data[field.range[0]]);
var end = _s(data[field.range[1]]);
$td.append($('
').html(start + ' - ' + end));
} else {
var maxLengths = data['_maxLength'];
if (maxLengths &&
maxLengths[fieldName] &&
data[fieldName].length >= maxLengths[fieldName]) {
$td.append($('').html(_s(data[fieldName].toString().substr(0, maxLengths[fieldName] - 3).concat('...'))));
} else {
$td.append($('').html(_s(data[fieldName])));
}
$td.attr('title', data[fieldName]);
}
} else if (field.isBoolean) {
var $checkbox = $(' ');
$checkbox.attr({
disabled: true,
name: fieldName,
type: 'checkbox'
});
if (_s(data[fieldName])) {
$checkbox.attr({
checked: true
});
}
$checkbox.appendTo($td);
} else if (field.select) {
// Get matching option text
var $matchingSelect = $multi.find('select')
.filter(function() {
return $(this).attr('name') == fieldName;
});
var $matchingOption = $matchingSelect.find('option')
.filter(function() {
return $(this).val() == data[fieldName];
});
if (selectPermission) {
// Wrap div to get its html code
selectedOptionHtml = $matchingOption.clone().wrap('').parent().html();
// Get html code from not matching option
$matchingSelect.find('option').each(
function() {
if ($(this).val() != data[fieldName]){
selectedOptionHtml += $(this).clone().wrap('
').parent().html();
}
}
);
$select = $('
');
$select.html(selectedOptionHtml);
$select.change(function(event) {
selectPermission.action({
roleid: data['roleid'],
ruleid: data['id'],
permission: $(this).val()
});
});
$td.append($select);
}
else {
var matchingValue = $matchingOption.size() ?
$matchingOption.html() : data[fieldName];
$td.append($('').html(_s(matchingValue)));
}
} else if (field.addButton && !options.noSelect) {
if (options.multipleAdd) {
$addButton.click(function() {
var context = $.extend(true, {}, options.context);
if ($td.hasClass('disabled')) return false;
var $subItems = $td.closest('.data-item').find('.expandable-listing tr');
if ($subItems.size()) {
context.subItemData = $subItems.map(function() {
return $(this).data('json-obj');
});
}
_medit.vmList($multi,
options.listView,
context,
options.multipleAdd, _l('label.add.vms'),
addItemAction, {
multiRule: multiRule
});
return true;
});
$td.append($addButton);
} else {
// Show VM data
var itemName = data._itemName ? itemData[0][data._itemName] : itemData[0].name;
$td.html(options.multipleAdd ?
itemData.length + ' VMs' : itemName);
$td.click(function() {
var $browser = $(this).closest('.detail-view').data('view-args').$browser;
if (options.multipleAdd) {
_medit.multiItem.details(itemData, $browser);
} else {
_medit.details(itemData[0], $browser, {
context: options.context,
itemName: itemName
});
}
});
}
} else if (field.custom) {
var $button = $('').addClass('button add-vm custom-action');
$td.data('multi-custom-data', data[fieldName]);
$button.html(data && data[fieldName] && data[fieldName]['_buttonLabel'] ?
_l(data[fieldName]['_buttonLabel']) : _l(field.custom.buttonLabel));
$button.click(function() {
if ($td.hasClass('disabled')) return false;
var $button = $(this);
var context = $.extend(true, {},
options.context ?
options.context : cloudStack.context, {
multiRules: [data]
});
field.custom.action({
context: context,
data: $td.data('multi-custom-data'),
$item: $td,
response: {
success: function(args) {
if (args.data['_buttonLabel']) {
$button.html(_l(args.data['_buttonLabel']));
}
$td.data('multi-custom-data', args.data);
}
}
});
return true;
});
$button.appendTo($td);
}
}
// Add blank styling for empty fields
if ($td.html() == '') {
$td.addClass('blank');
}
if (data._hideFields &&
$.inArray(fieldName, data._hideFields) > -1) {
$td.addClass('disabled');
}
return true;
});
// Actions column
var $actions = $('
').addClass('multi-actions').appendTo($item.find('tr'));
// Align action column width
$actions.width($multi.find('th.multi-actions').width() + 4);
// Action filter
var allowedActions = options.preFilter ? options.preFilter({
actions: $.map(actions, function(value, key) {
return key;
}),
context: $.extend(true, {}, options.context, {
multiRule: [data],
actions: $.map(actions, function(value, key) {
return key;
})
})
}) : null;
// Append actions
$.each(actions, function(actionID, action) {
if (allowedActions && $.inArray(actionID, allowedActions) == -1) return true;
$actions.append(
$('').addClass('action')
.addClass(actionID)
.append($('
').addClass('icon'))
.attr({
title: _l(action.label)
})
.click(function() {
var performAction = function(actionOptions) {
if (!actionOptions) actionOptions = {};
action.action({
context: $.extend(true, {}, options.context, {
multiRule: [data]
}),
data: actionOptions.data,
response: {
success: function(args) {
var notification = args ? args.notification : null;
var _custom = args ? args._custom : null;
if (notification) {
$('.notifications').notifications('add', {
section: 'network',
desc: notification.label,
interval: 3000,
_custom: _custom,
poll: function(args) {
var complete = args.complete;
var error = args.error;
notification.poll({
_custom: args._custom,
complete: function(args) {
if (isDestroy) {
$loading.remove();
$dataItem.remove();
} else {
$multi.trigger('refresh');
}
complete();
if (actionOptions.complete) actionOptions.complete();
},
error: function(args) {
error(args);
$multi.trigger('refresh');
return cloudStack.dialog.error;
}
});
}
});
} else {
$loading.remove();
if (isDestroy) {
$dataItem.remove();
}
}
},
error: function(message) {
cloudStack.dialog.notice({
message: message
});
$item.show();
$dataItem.find('.loading-overlay').remove();
}
}
});
};
var $target = $(this);
var $dataItem = $target.closest('.data-item');
var $expandable = $dataItem.find('.expandable-listing');
var isDestroy = $target.hasClass('destroy');
var isEdit = $target.hasClass('edit');
var createForm = action.createForm;
var reorder = options.reorder;
if (isDestroy) {
var $loading = _medit.loadingItem($multi, _l('label.removing') + '...');
if ($expandable.is(':visible')) {
$expandable.slideToggle(function() {
$dataItem.hide();
$dataItem.after($loading);
});
} else {
// Loading appearance
$dataItem.hide();
$dataItem.after($loading);
}
}
if (!isEdit) {
if (createForm) {
cloudStack.dialog.createForm({
form: createForm,
after: function(args) {
var $loading = $('').addClass('loading-overlay').prependTo($dataItem);
performAction({
data: args.data,
complete: function() {
$multi.trigger('refresh');
}
});
}
});
} else {
performAction();
}
} else {
// Get editable fields
var editableFields = {};
$.each(fields, function(key, field) {
field.isDisabled = false;
if (field && field.isEditable) editableFields[key] = $.extend(true, {}, field, {
defaultValue: data[key]
});
});
cloudStack.dialog.createForm({
form: {
title: 'label.edit.rule',
desc: '',
fields: editableFields
},
after: function(args) {
var $loading = $('
').addClass('loading-overlay').prependTo($dataItem);
performAction({
data: args.data,
complete: function() {
$multi.trigger('refresh');
}
});
}
});
}
})
);
});
// Add tagger action
if (options.tags) {
$actions.prepend(
$('
')
.addClass('action editTags')
.attr('title', _l('label.edit.tags'))
.append($('
').addClass('icon'))
.click(function() {
$('
')
.dialog({
dialogClass: 'editTags',
title: _l('label.edit.tags'),
width: 400,
buttons: [{
text: _l('label.done'),
'class': 'ok',
click: function() {
$(this).dialog('destroy');
$('div.overlay:last').remove();
return true;
}
}]
})
.append(
$('
').addClass('multi-edit-tags').tagger($.extend(true, {}, options.tags, {
context: $.extend(true, {}, options.context, {
multiRule: [multiRule]
})
}))
)
.closest('.ui-dialog').overlay();
return false;
})
)
}
// Add expandable listing, for multiple-item
if (options.multipleAdd) {
// Create expandable box
_medit.multiItem.expandable($item.find('tr').data('multi-edit-data'),
options.itemActions,
multiRule).appendTo($item);
// Expandable icon/action
$item.find('td:first').prepend(
$('
').addClass('expand').click(function() {
$item.closest('.data-item').find('.expandable-listing').slideToggle();
}));
}
return $item;
},
vmList: function($multi, listView, context, isMultipleAdd, label, complete, options) {
if (!options) options = {};
// Create a listing of instances, based on limited information
// from main instances list view
var $listView;
var instances = $.extend(true, {}, listView, {
context: $.extend(true, {}, context, {
multiData: getMultiData($multi),
multiRule: options.multiRule ? [options.multiRule] : null
}),
uiCustom: true
});
instances.listView.multiSelect = false;
instances.listView.actions = {
select: {
label: 'label.select.instance',
type: isMultipleAdd ? 'checkbox' : 'radio',
action: {
uiCustom: function(args) {
var $item = args.$item;
var $input = $item.find('td.actions input:visible');
if ($input.attr('type') == 'checkbox') {
if ($input.is(':checked'))
$item.addClass('multi-edit-selected');
else
$item.removeClass('multi-edit-selected');
} else {
$item.siblings().removeClass('multi-edit-selected');
$item.addClass('multi-edit-selected');
}
}
}
}
};
$listView = $('
').listView(instances);
// Change action label
$listView.find('th.actions').html(_l('Select'));
var $dataList = $listView.addClass('multi-edit-add-list').dialog({
dialogClass: 'multi-edit-add-list panel',
width: 900,
title: label,
buttons: [{
text: _l('label.apply'),
'class': 'ok',
click: function() {
if (!$listView.find('input[type=radio]:checked, input[type=checkbox]:checked').size()) {
cloudStack.dialog.notice({
message: _l('message.select.item')
});
return false;
}
$dataList.fadeOut(function() {
complete($.map(
$listView.find('tr.multi-edit-selected'),
// Attach VM data to row
function(elem) {
var itemData = $(elem).data('json-obj');
var $subselect = $(elem).find('.subselect select');
// Include subselect data
if ($subselect && $subselect.val()) {
return $.extend(itemData, {
_subselect: $subselect.val()
});
}
return itemData;
}
));
$dataList.remove();
});
$('div.overlay').fadeOut(function() {
$('div.overlay').remove();
});
return true;
}
}, {
text: _l('label.cancel'),
'class': 'cancel',
click: function() {
$dataList.fadeOut(function() {
$dataList.remove();
});
$('div.overlay').fadeOut(function() {
$('div.overlay').remove();
});
}
}]
}).parent('.ui-dialog').overlay();
},
/**
* Align width of each data row to main header
*/
refreshItemWidths: function($multi) {
$multi.find('.data-body').width(
$multi.find('form > table.multi-edit').width()
);
$multi.find('.data tr').filter(function() {
return !$(this).closest('.expandable-listing').size();
}).each(function() {
var $tr = $(this);
$tr.find('td').each(function() {
var $td = $(this);
$td.width($($multi.find('th:visible')[$td.index()]).width() + 5);
});
});
},
/**
* Create a fake 'loading' item box
*/
loadingItem: function($multi, label) {
var $loading = $('
').addClass('data-item loading');
// Align height with existing items
var $row = $multi.find('.data-item:first');
// Set label
if (label) {
$loading.append(
$('
').addClass('label').append(
$('
').html(_l(label))
)
);
}
return $loading;
},
details: function(data, $browser, options) {
if (!options) options = {};
var detailViewArgs, $detailView;
detailViewArgs = $.extend(true, {}, cloudStack.sections.instances.listView.detailView);
detailViewArgs.actions = null;
detailViewArgs.$browser = $browser;
detailViewArgs.id = data.id;
detailViewArgs.jsonObj = data;
detailViewArgs.context = options.context;
$browser.cloudBrowser('addPanel', {
title: options.itemName ? options.itemName : data.name,
maximizeIfSelected: true,
complete: function($newPanel) {
$newPanel.detailView(detailViewArgs);
}
});
},
multiItem: {
/**
* Show listing of load balanced VMs
*/
details: function(data, $browser) {
var listViewArgs, $listView;
// Setup list view
listViewArgs = $.extend(true, {}, cloudStack.sections.instances);
listViewArgs.listView.actions = null;
listViewArgs.listView.filters = null;
listViewArgs.$browser = $browser;
listViewArgs.listView.detailView.actions = null;
listViewArgs.listView.dataProvider = function(args) {
setTimeout(function() {
args.response.success({
data: data
});
}, 50);
};
$listView = $('').listView(listViewArgs);
// Show list view of selected VMs
$browser.cloudBrowser('addPanel', {
title: _l('label.item.listing'),
data: '',
noSelectPanel: true,
maximizeIfSelected: true,
complete: function($newPanel) {
return $newPanel.listView(listViewArgs);
}
});
},
itemRow: function(item, itemActions, multiRule, $tbody) {
var $tr = $('
');
var itemName = multiRule._itemName ? item[multiRule._itemName] : item.name;
var $itemName = $('').html(_s(itemName));
$tr.append($(' ').addClass('name').appendTo($tr).append($itemName));
$itemName.click(function() {
_medit.details(item, $('#browser .container'), {
itemName: itemName,
context: {
instances: [item]
}
});
});
var itemIp = multiRule._itemIp ? item[multiRule._itemIp] : null;
if (itemIp != null) {
var $itemIp = $('').html(_s(itemIp));
$tr.append($(' ').addClass('state').appendTo($tr).append($itemIp));
}
var itemState = item._itemState ? item._itemState : item.state;
$tr.append($(' ').addClass('state').appendTo($tr).append(
$('').text(
item._itemStateLabel ? _l(item._itemStateLabel) + ' - ' + itemState :
_l('label.state') + ' - ' + itemState
)
));
if (itemActions) {
var $itemActions = $(' ').addClass('actions item-actions');
$.each(itemActions, function(itemActionID, itemAction) {
if (itemActionID == 'add')
return true;
if (item._hideActions != null && $.inArray(itemActionID, item._hideActions) > -1)
return true;
var $itemAction = $('').addClass('action').addClass(itemActionID);
$itemAction.click(function() {
itemAction.action({
item: item,
multiRule: multiRule,
response: {
success: function(args) {
if (itemActionID == 'destroy') {
var notification = args.notification;
var success = function(args) {
$tr.remove();
};
var successArgs = {};
var error = function(args) {
$tr.show();
cloudStack.evenOdd($tbody, 'tr:visible', {
even: function($elem) {
$elem.removeClass('odd');
$elem.addClass('even');
},
odd: function($elem) {
$elem.removeClass('even');
$elem.addClass('odd');
}
});
};
var errorArgs = {};
$tr.hide();
cloudStack.evenOdd($tbody, 'tr:visible', {
even: function($elem) {
$elem.removeClass('odd');
$elem.addClass('even');
},
odd: function($elem) {
$elem.removeClass('even');
$elem.addClass('odd');
}
});
cloudStack.ui.notifications.add(notification,
success, successArgs,
error, errorArgs);
}
},
error: function(message) {
if (message) {
cloudStack.dialog.notice({
message: message
});
}
}
}
});
});
$itemAction.append($('
').addClass('icon'));
$itemAction.appendTo($itemActions);
return true;
});
$itemActions.appendTo($tr);
}
return $tr;
},
expandable: function(data, itemActions, multiRule) {
var $expandable = $('').addClass('expandable-listing');
var $tbody = $('
').appendTo($('').appendTo($expandable));
$(data).each(function() {
var field = this;
var $tr = _medit.multiItem.itemRow(field, itemActions, multiRule, $tbody).appendTo($tbody);
$tr.data('json-obj', field);
cloudStack.evenOdd($tbody, 'tr', {
even: function($elem) {
$elem.addClass('even');
},
odd: function($elem) {
$elem.addClass('odd');
}
});
});
return $expandable.hide();
}
}
};
$.fn.multiEdit = function(args) {
var dataProvider = args.dataProvider;
var multipleAdd = args.multipleAdd;
var tags = args.tags;
var $multi = $('').addClass('multi-edit').appendTo(this);
var $multiForm = $('