You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bf...@apache.org on 2012/07/24 00:18:41 UTC

[3/5] git commit: Implement tag UI widget

Implement tag UI widget

Create UI for handling new tag API. This currently supports the detail view and multi-edit

To enable tags UI, add a 'tags' object to each detailView/multiEdit configuration:

tabs: {
...
details: {
...

tags: {
  actions: {
    add: function(args) {
      setTimeout(function() {
        args.response.success({
          notification: {
            desc: 'Add tags for instance',
            poll: testData.notifications.testPoll
          }
        });
      }, 500);
    },

    remove: function(args) {
      args.response.success({
        notification: {
          desc: 'Remove tags for instance',
          poll: testData.notifications.testPoll
        }
      });
    }
  },
  dataProvider: function(args) {
    args.response.success({
      data: [
        {
          id: '1',
          key: 'user',
          value: 'brian'
        },
        {
          id: '2',
          key: 'region',
          value: 'usa'
        }
      ]
    });
  }
}

...

Conflicts:
	ui/css/cloudstack3.css
	ui/scripts/ui/widgets/tagger.js


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/5e94b0d1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/5e94b0d1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/5e94b0d1

Branch: refs/heads/master
Commit: 5e94b0d12e50c6ba3a7defd00aeb04c79286f14f
Parents: 88f7872
Author: bfederle <bf...@gmail.com>
Authored: Mon Jul 23 13:26:52 2012 -0700
Committer: Brian Federle <br...@citrix.com>
Committed: Mon Jul 23 15:18:36 2012 -0700

----------------------------------------------------------------------
 ui/css/cloudstack3.css              |  198 ++++++++++++++++++++++++++++++
 ui/scripts/ui/widgets/detailView.js |   48 ++++++-
 ui/scripts/ui/widgets/multiEdit.js  |   12 ++-
 ui/scripts/ui/widgets/tagger.js     |  197 +++++++++++++++++++++++++++++
 4 files changed, 446 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index ef81697..e405fbf 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -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%;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/detailView.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js
index b1e9a68..086f66d 100644
--- a/ui/scripts/ui/widgets/detailView.js
+++ b/ui/scripts/ui/widgets/detailView.js
@@ -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($tabContent).addClass('readonly');
+          }
+
           return true;
         },
         error: function() {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/multiEdit.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js
index 64d94f6..89d2603 100644
--- a/ui/scripts/ui/widgets/multiEdit.js
+++ b/ui/scripts/ui/widgets/multiEdit.js
@@ -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);
             });

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/tagger.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/tagger.js b/ui/scripts/ui/widgets/tagger.js
new file mode 100644
index 0000000..b6d2f0a
--- /dev/null
+++ b/ui/scripts/ui/widgets/tagger.js
@@ -0,0 +1,197 @@
+(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) {
+      var $li = $('<li>');
+      var $label = $('<span>').addClass('label').html(title);
+      var $remove = $('<span>').addClass('remove').html('X');
+
+      $remove.click(function() {
+        if (onRemove) onRemove($li);
+      });
+
+      $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) {
+        $loading.appendTo($container);
+        actions.remove({
+          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();
+                  $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({
+            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).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;
+
+              elems.tagItem(key + ' = ' + value, onRemoveItem).appendTo($tagArea);
+            });
+          },
+          error: function(message) {
+            $loading.remove();
+            $container.find('ul').html(message);
+          }
+        }
+      });
+    }
+  });
+}(jQuery, cloudStack));