mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cloudstack
This commit is contained in:
commit
b80c3dc9c6
@ -8952,6 +8952,204 @@ div.panel.ui-dialog div.list-view div.fixed-header {
|
||||
background: #DFE1E3;
|
||||
}
|
||||
|
||||
/*Tagger*/
|
||||
.tagger {
|
||||
width: 94%;
|
||||
margin: auto;
|
||||
padding-bottom: 12px;
|
||||
background: #F2F0F0;
|
||||
border: 1px solid #CFC9C9;
|
||||
/*+placement:shift -4px 0px;*/
|
||||
position: relative;
|
||||
left: -4px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.tagger .field {
|
||||
width: 179px;
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tagger .tag-info {
|
||||
font-size: 11px;
|
||||
color: #757575;
|
||||
margin-top: 12px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.tagger .tag-info.title {
|
||||
font-size: 11px;
|
||||
color: #6F9BF0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tagger form {
|
||||
margin: 12px 9px 0px;
|
||||
}
|
||||
|
||||
.tagger.readonly form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagger form label {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 28px;
|
||||
text-align: right;
|
||||
font-size: 10px;
|
||||
color: #394552;
|
||||
margin-right: 9px;
|
||||
/*+placement:shift 5px 8px;*/
|
||||
position: relative;
|
||||
left: 5px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.tagger form label.error {
|
||||
position: absolute;
|
||||
color: #FF0000;
|
||||
left: 42px;
|
||||
top: 29px;
|
||||
/*[empty]background-color:;*/
|
||||
}
|
||||
|
||||
.tagger form input {
|
||||
padding: 4px;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #808080;
|
||||
/*+border-radius:4px;*/
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tagger form input[type=submit] {
|
||||
background: url(../images/bg-gradients.png) repeat-x 0px -220px;
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
/*+text-shadow:0px -1px 2px #000000;*/
|
||||
-moz-text-shadow: 0px -1px 2px #000000;
|
||||
-webkit-text-shadow: 0px -1px 2px #000000;
|
||||
-o-text-shadow: 0px -1px 2px #000000;
|
||||
text-shadow: 0px -1px 2px #000000;
|
||||
border: none;
|
||||
/*+border-radius:4px;*/
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
padding: 7px 25px 7px 26px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.tagger form input[type=submit]:hover {
|
||||
background-position: 0px -946px;
|
||||
}
|
||||
|
||||
.tagger ul {
|
||||
display: block;
|
||||
width: 96%;
|
||||
margin: 16px auto auto;
|
||||
/*+border-radius:2px;*/
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
-khtml-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
overflow: auto;
|
||||
padding-bottom: 10px;
|
||||
border: 1px solid #D2D2D2;
|
||||
background: #FFFFFF;
|
||||
/*+box-shadow:inset 0px 0px 10px #DCDCDC;*/
|
||||
-moz-box-shadow: inset 0px 0px 10px #DCDCDC;
|
||||
-webkit-box-shadow: inset 0px 0px 10px #DCDCDC;
|
||||
-o-box-shadow: inset 0px 0px 10px #DCDCDC;
|
||||
box-shadow: inset 0px 0px 10px #DCDCDC;
|
||||
}
|
||||
|
||||
.tagger.readonly ul {
|
||||
}
|
||||
|
||||
.tagger ul li {
|
||||
background: #DFDFDF 0px 4px;
|
||||
height: 15px;
|
||||
padding: 0px 18px 0 7px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-left: 7px;
|
||||
margin-right: 2px;
|
||||
margin-top: 5px;
|
||||
/*+border-radius:4px;*/
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
/*+placement:shift 0px 2px;*/
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.tagger ul li span {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.tagger ul li span.label {
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
left: 15px;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.tagger.readonly ul li span.label {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.tagger ul li span.remove {
|
||||
width: 15px !important;
|
||||
overflow: hidden !important;
|
||||
height: 11px !important;
|
||||
background: #DFDFDF url(../images/sprites.png) no-repeat -596px -1183px;
|
||||
display: block;
|
||||
top: 0px !important;
|
||||
left: -3px !important;
|
||||
text-indent: 4px;
|
||||
padding: 4px 0px 0px 8px;
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
position: absolute !important;
|
||||
color: #5B5B5B;
|
||||
}
|
||||
|
||||
.tagger.readonly ul li span.remove {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagger ul li span.remove:hover {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/** Dialog tagger*/
|
||||
.ui-dialog .tagger {
|
||||
}
|
||||
|
||||
.ui-dialog .tagger .field {
|
||||
width: 119px !important;
|
||||
}
|
||||
|
||||
.ui-dialog .tagger input.key,
|
||||
.ui-dialog .tagger input.value {
|
||||
width: 66px !important;
|
||||
height: 15px;
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
.ui-dialog .tagger input[type=submit] {
|
||||
padding: 6px 15px;
|
||||
}
|
||||
|
||||
/*VPC / vApps*/
|
||||
.vpc-chart {
|
||||
width: 100%;
|
||||
|
||||
@ -1614,7 +1614,8 @@
|
||||
<script type="text/javascript" src="scripts/ui/widgets/listView.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/ui/widgets/detailView.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/ui/widgets/treeView.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/ui/widgets/notifications.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/ui/widgets/notifications.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/ui/widgets/tagger.js?t=<%=now%>"></script>
|
||||
|
||||
<script type="text/javascript" src="scripts/cloud.core.callbacks.js?t=<%=now%>"></script>
|
||||
<script type="text/javascript" src="scripts/sharedFunctions.js?t=<%=now%>"></script>
|
||||
|
||||
@ -617,5 +617,83 @@ cloudStack.api = {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tags: function(args) {
|
||||
var resourceType = args.resourceType;
|
||||
var contextId = args.contextId;
|
||||
|
||||
return {
|
||||
actions: {
|
||||
add: function(args) {
|
||||
var data = args.data;
|
||||
var resourceId = args.context[contextId][0].id;
|
||||
|
||||
$.ajax({
|
||||
url: createURL(
|
||||
'createTags&tags[0].key=' + data.key + '&tags[0].value=' + data.value
|
||||
),
|
||||
data: {
|
||||
resourceIds: resourceId,
|
||||
resourceType: resourceType
|
||||
},
|
||||
success: function(json) {
|
||||
args.response.success({
|
||||
_custom: { jobId: json.createtagsresponse.jobid },
|
||||
notification: {
|
||||
desc: 'Add tag for instance',
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
remove: function(args) {
|
||||
var data = args.context.tagItems[0];
|
||||
var resourceId = args.context[contextId][0].id;
|
||||
|
||||
$.ajax({
|
||||
url: createURL(
|
||||
'deleteTags&tags[0].key=' + data.key + '&tags[0].value=' + data.value
|
||||
),
|
||||
data: {
|
||||
resourceIds: resourceId,
|
||||
resourceType: resourceType
|
||||
},
|
||||
success: function(json) {
|
||||
args.response.success({
|
||||
_custom: { jobId: json.deletetagsresponse.jobid },
|
||||
notification: {
|
||||
desc: 'Remove tag for instance',
|
||||
poll: pollAsyncJobResult
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
dataProvider: function(args) {
|
||||
var resourceId = args.context[contextId][0].id;
|
||||
|
||||
$.ajax({
|
||||
url: createURL('listTags'),
|
||||
data: {
|
||||
listAll: true,
|
||||
resourceId: resourceId,
|
||||
resourceType: resourceType
|
||||
},
|
||||
success: function(json) {
|
||||
args.response.success({
|
||||
data: json.listtagsresponse ?
|
||||
json.listtagsresponse.tag : []
|
||||
});
|
||||
},
|
||||
error: function(json) {
|
||||
args.response.error(parseXMLHttpResponse(json));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -287,10 +287,14 @@
|
||||
* @param callback
|
||||
*/
|
||||
edit: function($detailView, args) {
|
||||
$detailView.addClass('edit-mode');
|
||||
|
||||
if ($detailView.find('.button.done').size()) return false;
|
||||
|
||||
// Convert value TDs
|
||||
var $inputs = $detailView.find('input, select');
|
||||
var $inputs = $detailView.find('input, select').filter(function() {
|
||||
return !$(this).closest('.tagger').size() && !$(this).attr('type') == 'submit';
|
||||
});
|
||||
var action = args.actions[args.actionName];
|
||||
var id = $detailView.data('view-args').id;
|
||||
var $editButton = $('<div>').addClass('button done').html(_l('label.apply')).hide();
|
||||
@ -302,9 +306,14 @@
|
||||
$detailView.find('.ui-tabs-panel .detail-group.actions')
|
||||
).fadeIn();
|
||||
|
||||
$detailView.find('.tagger').removeClass('readonly');
|
||||
$detailView.find('.tagger').find('input[type=text]').val('');
|
||||
|
||||
var convertInputs = function($inputs) {
|
||||
// Save and turn back into labels
|
||||
$inputs.each(function() {
|
||||
if ($(this).closest('.tagger').size()) return true;
|
||||
|
||||
var $input = $(this);
|
||||
var $value = $input.closest('td.value span');
|
||||
|
||||
@ -328,8 +337,12 @@
|
||||
};
|
||||
|
||||
var removeEditForm = function() {
|
||||
$detailView.removeClass('edit-mode');
|
||||
|
||||
// Remove Edit form
|
||||
var $form = $detailView.find('form');
|
||||
var $form = $detailView.find('form').filter(function() {
|
||||
return !$(this).closest('.tagger').size();
|
||||
});
|
||||
if ($form.size()) {
|
||||
var $mainGroups = $form.find('div.main-groups').detach();
|
||||
$form.parent('div').append($mainGroups);
|
||||
@ -337,11 +350,15 @@
|
||||
}
|
||||
//Remove required labels
|
||||
$detailView.find('span.field-required').remove();
|
||||
}
|
||||
$detailView.find('.tagger').addClass('readonly');
|
||||
|
||||
};
|
||||
|
||||
// Put in original values
|
||||
var cancelEdits = function($inputs, $editButton) {
|
||||
$inputs.each(function() {
|
||||
if ($(this).closest('.tagger').size()) return true;
|
||||
|
||||
var $input = $(this);
|
||||
var $value = $input.closest('td.value span');
|
||||
var originalValue = $input.data('original-value');
|
||||
@ -424,8 +441,12 @@
|
||||
};
|
||||
|
||||
$editButton.click(function() {
|
||||
var $inputs = $detailView.find('input, select'),
|
||||
$form = $detailView.find('form');
|
||||
var $inputs = $detailView.find('input, select').filter(function() {
|
||||
return !$(this).closest('.tagger').size();
|
||||
});
|
||||
var $form = $detailView.find('form').filter(function() {
|
||||
return !$(this).closest('.tagger').size();
|
||||
});
|
||||
|
||||
if ($(this).hasClass('done')) {
|
||||
if (!$form.valid()) {
|
||||
@ -438,6 +459,8 @@
|
||||
} else { // Cancel
|
||||
cancelEdits($inputs, $editButton);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$detailView.find('td.value span').each(function() {
|
||||
@ -511,8 +534,11 @@
|
||||
}
|
||||
|
||||
// Setup form validation
|
||||
$detailView.find('form').validate();
|
||||
$detailView.find('form').find('input, select').each(function() {
|
||||
var $form = $detailView.find('form').filter(function() {
|
||||
return !$(this).closest('.tagger').size();
|
||||
});
|
||||
$form.validate();
|
||||
$form.find('input, select').each(function() {
|
||||
var data = $(this).parent('span').data('validation-rules');
|
||||
if (data) {
|
||||
$(this).rules('add', data);
|
||||
@ -934,6 +960,14 @@
|
||||
actionFilter: actionFilter
|
||||
}).appendTo($tabContent);
|
||||
|
||||
if (tabs.tags) {
|
||||
$('<div>').tagger(
|
||||
$.extend(true, {}, tabs.tags, {
|
||||
context: $detailView.data('view-args').context
|
||||
})
|
||||
).appendTo($detailView.find('.main-groups')).addClass('readonly');
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
error: function() {
|
||||
|
||||
@ -332,10 +332,16 @@
|
||||
after: function(args) {
|
||||
var $loading = $('<div>').addClass('loading-overlay').prependTo($dataItem);
|
||||
performAction({ data: args.data, complete: function() {
|
||||
$multi.multiEdit('refresh');
|
||||
$multi.trigger('refresh');
|
||||
} });
|
||||
}
|
||||
});
|
||||
|
||||
if (options.tags) {
|
||||
$(':ui-dialog').append(
|
||||
$('<div>').addClass('multi-edit-tags').tagger(options.tags)
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
@ -652,6 +658,7 @@
|
||||
$.fn.multiEdit = function(args) {
|
||||
var dataProvider = args.dataProvider;
|
||||
var multipleAdd = args.multipleAdd;
|
||||
var tags = args.tags;
|
||||
var $multi = $('<div>').addClass('multi-edit').appendTo(this);
|
||||
var $multiForm = $('<form>').appendTo($multi);
|
||||
var $inputTable = $('<table>').addClass('multi-edit').appendTo($multiForm);
|
||||
@ -918,7 +925,8 @@
|
||||
context: $.extend(true, {}, context, this._context),
|
||||
ignoreEmptyFields: ignoreEmptyFields,
|
||||
preFilter: actionPreFilter,
|
||||
listView: listView
|
||||
listView: listView,
|
||||
tags: tags
|
||||
}
|
||||
).appendTo($dataBody);
|
||||
});
|
||||
|
||||
201
ui/scripts/ui/widgets/tagger.js
Normal file
201
ui/scripts/ui/widgets/tagger.js
Normal file
@ -0,0 +1,201 @@
|
||||
(function($, cloudStack) {
|
||||
var elems = {
|
||||
inputArea: function(args) {
|
||||
var $form = $('<form>').addClass('tag-input');
|
||||
var $keyField = $('<div>').addClass('field key');
|
||||
var $keyLabel = $('<label>').attr('for', 'key').html('Key:');
|
||||
var $key = $('<input>').addClass('key required').attr('name', 'key');
|
||||
var $valueField = $('<div>').addClass('field value');
|
||||
var $valueLabel = $('<label>').attr('for', 'value').html('Value:');
|
||||
var $value = $('<input>').addClass('value required').attr('name', 'value');
|
||||
var $submit = $('<input>').attr('type', 'submit').val('Add');
|
||||
|
||||
$keyField.append($keyLabel, $key);
|
||||
$valueField.append($valueLabel, $value);
|
||||
$form.append(
|
||||
$keyField, $valueField,
|
||||
$submit
|
||||
);
|
||||
|
||||
$form.validate({ onfocusout: false });
|
||||
|
||||
$form.submit(
|
||||
args.onSubmit ?
|
||||
function() {
|
||||
if (!$form.valid()) return false;
|
||||
|
||||
args.onSubmit({
|
||||
data: cloudStack.serializeForm($form),
|
||||
response: {
|
||||
success: function() {
|
||||
// Restore editing of input
|
||||
$key.attr('disabled', false);
|
||||
$value.attr('disabled', false);
|
||||
|
||||
// Clear out old data
|
||||
$key.val(''); $value.val('');
|
||||
$key.focus();
|
||||
},
|
||||
error: function() {
|
||||
// Restore editing of input
|
||||
$key.attr('disabled', false);
|
||||
$value.attr('disabled', false);
|
||||
$key.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent input during submission
|
||||
$key.attr('disabled', 'disabled');
|
||||
$value.attr('disabled', 'disabled');
|
||||
|
||||
return false;
|
||||
} :
|
||||
function() { return false; }
|
||||
);
|
||||
|
||||
return $form;
|
||||
},
|
||||
tagItem: function(title, onRemove, data) {
|
||||
var $li = $('<li>');
|
||||
var $label = $('<span>').addClass('label').html(title);
|
||||
var $remove = $('<span>').addClass('remove').html('X');
|
||||
|
||||
$remove.click(function() {
|
||||
if (onRemove) onRemove($li, data);
|
||||
});
|
||||
|
||||
$li.append($remove, $label);
|
||||
|
||||
return $li;
|
||||
},
|
||||
|
||||
info: function(text) {
|
||||
var $info = $('<div>').addClass('tag-info');
|
||||
var $text = $('<span>').html(text);
|
||||
|
||||
$text.appendTo($info);
|
||||
|
||||
return $info;
|
||||
}
|
||||
};
|
||||
|
||||
$.widget('cloudStack.tagger', {
|
||||
_init: function(args) {
|
||||
var context = this.options.context;
|
||||
var dataProvider = this.options.dataProvider;
|
||||
var actions = this.options.actions;
|
||||
var $container = this.element.addClass('tagger');
|
||||
var $tagArea = $('<ul>').addClass('tags');
|
||||
var $title = elems.info('Tags').addClass('title');
|
||||
var $loading = $('<div>').addClass('loading-overlay');
|
||||
|
||||
var onRemoveItem = function($item, data) {
|
||||
$loading.appendTo($container);
|
||||
actions.remove({
|
||||
context: $.extend(true, {}, context, {
|
||||
tagItems: [data]
|
||||
}),
|
||||
response: {
|
||||
success: function(args) {
|
||||
var notification = $.extend(true, {} , args.notification, {
|
||||
interval: 500,
|
||||
_custom: args._custom
|
||||
});
|
||||
|
||||
cloudStack.ui.notifications.add(
|
||||
notification,
|
||||
|
||||
// Success
|
||||
function() {
|
||||
$loading.remove();
|
||||
$item.remove();
|
||||
}, {},
|
||||
|
||||
// Error
|
||||
function() {
|
||||
$loading.remove();
|
||||
}, {}
|
||||
);
|
||||
},
|
||||
error: function(message) {
|
||||
$loading.remove();
|
||||
cloudStack.dialog.notice({ message: message });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var $inputArea = elems.inputArea({
|
||||
onSubmit: function(args) {
|
||||
var data = args.data;
|
||||
var success = args.response.success;
|
||||
var error = args.response.error;
|
||||
var title = data.key + ' = ' + data.value;
|
||||
|
||||
$loading.appendTo($container);
|
||||
actions.add({
|
||||
data: data,
|
||||
context: context,
|
||||
response: {
|
||||
success: function(args) {
|
||||
var notification = $.extend(true, {} , args.notification, {
|
||||
interval: 500,
|
||||
_custom: args._custom
|
||||
});
|
||||
|
||||
cloudStack.ui.notifications.add(
|
||||
notification,
|
||||
|
||||
// Success
|
||||
function() {
|
||||
$loading.remove();
|
||||
elems.tagItem(title, onRemoveItem, data).appendTo($tagArea);
|
||||
success();
|
||||
}, {},
|
||||
|
||||
// Error
|
||||
function() {
|
||||
$loading.remove();
|
||||
error();
|
||||
}, {}
|
||||
);
|
||||
},
|
||||
error: function(message) {
|
||||
$loading.remove();
|
||||
error();
|
||||
cloudStack.dialog.notice({ message: message });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$container.append($title, $inputArea, $tagArea);
|
||||
|
||||
// Get data
|
||||
$loading.appendTo($container);
|
||||
dataProvider({
|
||||
context: context,
|
||||
response: {
|
||||
success: function(args) {
|
||||
var data = args.data;
|
||||
|
||||
$loading.remove();
|
||||
$(data).map(function(index, item) {
|
||||
var key = item.key;
|
||||
var value = item.value;
|
||||
var data = { key: key, value: value };
|
||||
|
||||
elems.tagItem(key + ' = ' + value, onRemoveItem, data).appendTo($tagArea);
|
||||
});
|
||||
},
|
||||
error: function(message) {
|
||||
$loading.remove();
|
||||
$container.find('ul').html(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}(jQuery, cloudStack));
|
||||
Loading…
x
Reference in New Issue
Block a user