diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 79434abaf2b..c7d1db28948 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -8951,6 +8951,89 @@ div.panel.ui-dialog div.list-view div.fixed-header { background: #DFE1E3; } +/*Tagger*/ +.tagger { + overflow: hidden; + width: 95%; + background: #000000; +} + +.tagger input { + overflow: hidden; + position: absolute; + border: 1px solid #B2B2B2; + background: #FFFFFF; + height: 15px; + padding: 2px; + border-left: none; +} + +.tagger ul { + height: 19px; + border: 1px solid #B2B2B2; + border-right: none; + display: block; + position: absolute; + top: 2px; + left: 13px; + background: #FFFFFF; + overflow: hidden; +} + +.tagger ul li { + background: #DFDFDF 0px 4px; + height: 15px; + padding: 0px 18px 0 7px; + display: inline-block; + float: left; + margin-right: 2px; + /*+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 { + width: auto !important; + top: 2px !important; + left: 5px !important; + color: #000000; +} + +.tagger ul li span.label { + display: none; + /*+placement:shift 12px 2px;*/ + position: relative !important; + left: 12px !important; + top: 2px !important; +} + +.tagger ul li span.remove { + width: 15px !important; + overflow: hidden !important; + height: 11px !important; + background: #DFDFDF url(../images/sprites.png) no-repeat -595px -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 ul li span.remove:hover { + color: #000000; +} + /*VPC / vApps*/ .vpc-chart { width: 100%; diff --git a/ui/images/sprites.png b/ui/images/sprites.png index 71674d13b77..f1f5b8c5820 100644 Binary files a/ui/images/sprites.png and b/ui/images/sprites.png differ diff --git a/ui/index.jsp b/ui/index.jsp index f23fc38f7ea..cb9b4c6fd09 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1614,6 +1614,7 @@ + diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index 05341c21e87..e24f7f5e386 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -487,6 +487,10 @@ value: data }).data('original-value', data) ); + + if ($value.closest('tr').data('detail-view-is-tagged')) { + $value.find('input').tagger(); + } } if (rules && rules.required) { @@ -758,6 +762,10 @@ // Set up validation metadata $value.data('validation-rules', value.validation); + if (value.isTag) { + $detail.data('detail-view-is-tagged', true); + } + // Set up editable metadata if(typeof(value.isEditable) == 'function') $value.data('detail-view-is-editable', value.isEditable()); diff --git a/ui/scripts/ui/widgets/tagger.js b/ui/scripts/ui/widgets/tagger.js new file mode 100644 index 00000000000..059efcf7956 --- /dev/null +++ b/ui/scripts/ui/widgets/tagger.js @@ -0,0 +1,90 @@ +(function($) { + var elems = { + tagItem: function(title, onRemove) { + var $li = $('
  • '); + var $label = $('').addClass('label').html(title); + var $remove = $('').addClass('remove').html('X'); + + $remove.click(function() { + $li.remove(); + + if (onRemove) onRemove(); + }); + + $li.append($remove, $label); + + return $li; + } + }; + + $.widget('cloudStack.tagger', { + _init: function() { + var $container = $('
    ').addClass('tagger'); + var $tagArea = $('
      ').addClass('tags'); + var $originalInput = this.element; + var $input = $('').attr('type', 'text'); + + $originalInput.hide(); + $originalInput.after($container); + $container.append($tagArea, $input); + + // Reposition input to fit tag list + var relayout = function() { + $input.width( + $container.width() - $tagArea.width() - 25 + ); + $input.css({ + left: $tagArea.width(), + top: $tagArea.position().top + }); + }; + + var onRemove = function() { + syncInputs(true); + relayout(); + }; + + // sync original input box and tag list values + // + // flag == true: Sync tags->text + // flag == false: Sync text->tags + var syncInputs = function(flag) { + if (flag) { + $originalInput.val( + $tagArea.find('li').map(function(index, tag) { + return $(tag).find('span.label').html(); + }).toArray().join(',') + ); + } else if ($originalInput.val()) { + $($originalInput.val().split(',')).map(function(index, tag) { + elems.tagItem(tag, onRemove).appendTo($tagArea); + }); + + $tagArea.show(); + relayout(); + } + }; + + // Tag detection (comma-delimited) + $input.keypress(function(event) { + var tagCode = 44; // Symbol used to indicate a new tag + + if (event.which == tagCode) { + $tagArea.show(); + elems.tagItem($input.val(), onRemove).appendTo($tagArea); + $input.val(''); + relayout(); + syncInputs(true); + + return false; // Don't allow delineator to be added to input box + } + + return true; + }); + + $tagArea.hide(); + relayout(); + syncInputs(false); + } + }); +}(jQuery));