http://git-wip-us.apache.org/repos/asf/ambari/blob/cd6398a1/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/list.js
----------------------------------------------------------------------
diff --git 
a/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/list.js 
b/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/list.js
new file mode 100644
index 0000000..e21d748
--- /dev/null
+++ 
b/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/list.js
@@ -0,0 +1,650 @@
+;(function(Form) {
+
+  /**
+   * List editor
+   *
+   * An array editor. Creates a list of other editor items.
+   *
+   * Special options:
+   * @param {String} [options.schema.itemType]          The editor type for 
each item in the list. Default: 'Text'
+   * @param {String} [options.schema.confirmDelete]     Text to display in a 
delete confirmation dialog. If falsey, will not ask for confirmation.
+   */
+  Form.editors.List = Form.editors.Base.extend({
+
+    events: {
+      'click [data-action="add"]': function(event) {
+        event.preventDefault();
+        this.addItem(null, true);
+      }
+    },
+
+    initialize: function(options) {
+      options = options || {};
+
+      var editors = Form.editors;
+
+      editors.Base.prototype.initialize.call(this, options);
+
+      var schema = this.schema;
+      if (!schema) throw new Error("Missing required option 'schema'");
+
+      this.template = options.template || this.constructor.template;
+
+      //Determine the editor to use
+      this.Editor = (function() {
+        var type = schema.itemType;
+
+        //Default to Text
+        if (!type) return editors.Text;
+
+        //Use List-specific version if available
+        if (editors.List[type]) return editors.List[type];
+
+        //Or whichever was passed
+        return editors[type];
+      })();
+
+      this.items = [];
+    },
+
+    render: function() {
+      var self = this,
+          value = this.value || [];
+
+      //Create main element
+      var $el = $($.trim(this.template()));
+
+      //Store a reference to the list (item container)
+      this.$list = $el.is('[data-items]') ? $el : $el.find('[data-items]');
+
+      //Add existing items
+      if (value.length) {
+        _.each(value, function(itemValue) {
+          self.addItem(itemValue);
+        });
+      }
+
+      //If no existing items create an empty one, unless the editor specifies 
otherwise
+      else {
+        if (!this.Editor.isAsync) this.addItem();
+      }
+
+      this.setElement($el);
+      this.$el.attr('id', this.id);
+      this.$el.attr('name', this.key);
+
+      if (this.hasFocus) this.trigger('blur', this);
+
+      return this;
+    },
+
+    /**
+     * Add a new item to the list
+     * @param {Mixed} [value]           Value for the new item editor
+     * @param {Boolean} [userInitiated] If the item was added by the user 
clicking 'add'
+     */
+    addItem: function(value, userInitiated) {
+      var self = this,
+          editors = Form.editors;
+
+      //Create the item
+      var item = new editors.List.Item({
+        list: this,
+        form: this.form,
+        schema: this.schema,
+        value: value,
+        Editor: this.Editor,
+        key: this.key
+      }).render();
+
+      var _addItem = function() {
+        self.items.push(item);
+        self.$list.append(item.el);
+
+        item.editor.on('all', function(event) {
+          if (event === 'change') return;
+
+          // args = ["key:change", itemEditor, fieldEditor]
+          var args = _.toArray(arguments);
+          args[0] = 'item:' + event;
+          args.splice(1, 0, self);
+          // args = ["item:key:change", this=listEditor, itemEditor, 
fieldEditor]
+
+          editors.List.prototype.trigger.apply(this, args);
+        }, self);
+
+        item.editor.on('change', function() {
+          if (!item.addEventTriggered) {
+            item.addEventTriggered = true;
+            this.trigger('add', this, item.editor);
+          }
+          this.trigger('item:change', this, item.editor);
+          this.trigger('change', this);
+        }, self);
+
+        item.editor.on('focus', function() {
+          if (this.hasFocus) return;
+          this.trigger('focus', this);
+        }, self);
+        item.editor.on('blur', function() {
+          if (!this.hasFocus) return;
+          var self = this;
+          setTimeout(function() {
+            if (_.find(self.items, function(item) { return 
item.editor.hasFocus; })) return;
+            self.trigger('blur', self);
+          }, 0);
+        }, self);
+
+        if (userInitiated || value) {
+          item.addEventTriggered = true;
+        }
+
+        if (userInitiated) {
+          self.trigger('add', self, item.editor);
+          self.trigger('change', self);
+        }
+      };
+
+      //Check if we need to wait for the item to complete before adding to the 
list
+      if (this.Editor.isAsync) {
+        item.editor.on('readyToAdd', _addItem, this);
+      }
+
+      //Most editors can be added automatically
+      else {
+        _addItem();
+        item.editor.focus();
+      }
+
+      return item;
+    },
+
+    /**
+     * Remove an item from the list
+     * @param {List.Item} item
+     */
+    removeItem: function(item) {
+      //Confirm delete
+      var confirmMsg = this.schema.confirmDelete;
+      if (confirmMsg && !confirm(confirmMsg)) return;
+
+      var index = _.indexOf(this.items, item);
+
+      this.items[index].remove();
+      this.items.splice(index, 1);
+
+      if (item.addEventTriggered) {
+        this.trigger('remove', this, item.editor);
+        this.trigger('change', this);
+      }
+
+      if (!this.items.length && !this.Editor.isAsync) this.addItem();
+    },
+
+    getValue: function() {
+      var values = _.map(this.items, function(item) {
+        return item.getValue();
+      });
+
+      //Filter empty items
+      return _.without(values, undefined, '');
+    },
+
+    setValue: function(value) {
+      this.value = value;
+      this.render();
+    },
+
+    focus: function() {
+      if (this.hasFocus) return;
+
+      if (this.items[0]) this.items[0].editor.focus();
+    },
+
+    blur: function() {
+      if (!this.hasFocus) return;
+
+      var focusedItem = _.find(this.items, function(item) { return 
item.editor.hasFocus; });
+
+      if (focusedItem) focusedItem.editor.blur();
+    },
+
+    /**
+     * Override default remove function in order to remove item views
+     */
+    remove: function() {
+      _.invoke(this.items, 'remove');
+
+      Form.editors.Base.prototype.remove.call(this);
+    },
+
+    /**
+     * Run validation
+     *
+     * @return {Object|Null}
+     */
+    validate: function() {
+      if (!this.validators) return null;
+
+      //Collect errors
+      var errors = _.map(this.items, function(item) {
+        return item.validate();
+      });
+
+      //Check if any item has errors
+      var hasErrors = _.compact(errors).length ? true : false;
+      if (!hasErrors) return null;
+
+      //If so create a shared error
+      var fieldError = {
+        type: 'list',
+        message: 'Some of the items in the list failed validation',
+        errors: errors
+      };
+
+      return fieldError;
+    }
+  }, {
+
+    //STATICS
+    template: _.template('\
+      <div>\
+        <div data-items></div>\
+        <button type="button" data-action="add">Add</button>\
+      </div>\
+    ', null, Form.templateSettings)
+
+  });
+
+
+  /**
+   * A single item in the list
+   *
+   * @param {editors.List} options.list The List editor instance this item 
belongs to
+   * @param {Function} options.Editor   Editor constructor function
+   * @param {String} options.key        Model key
+   * @param {Mixed} options.value       Value
+   * @param {Object} options.schema     Field schema
+   */
+  Form.editors.List.Item = Form.editors.Base.extend({
+
+    events: {
+      'click [data-action="remove"]': function(event) {
+        event.preventDefault();
+        this.list.removeItem(this);
+      },
+      'keydown input[type=text]': function(event) {
+        if(event.keyCode !== 13) return;
+        event.preventDefault();
+        this.list.addItem();
+        this.list.$list.find("> li:last input").focus();
+      }
+    },
+
+    initialize: function(options) {
+      this.list = options.list;
+      this.schema = options.schema || this.list.schema;
+      this.value = options.value;
+      this.Editor = options.Editor || Form.editors.Text;
+      this.key = options.key;
+      this.template = options.template || this.schema.itemTemplate || 
this.constructor.template;
+      this.errorClassName = options.errorClassName || 
this.constructor.errorClassName;
+      this.form = options.form;
+    },
+
+    render: function() {
+      //Create editor
+      this.editor = new this.Editor({
+        key: this.key,
+        schema: this.schema,
+        value: this.value,
+        list: this.list,
+        item: this,
+        form: this.form
+      }).render();
+
+      //Create main element
+      var $el = $($.trim(this.template()));
+
+      $el.find('[data-editor]').append(this.editor.el);
+
+      //Replace the entire element so there isn't a wrapper tag
+      this.setElement($el);
+
+      return this;
+    },
+
+    getValue: function() {
+      return this.editor.getValue();
+    },
+
+    setValue: function(value) {
+      this.editor.setValue(value);
+    },
+
+    focus: function() {
+      this.editor.focus();
+    },
+
+    blur: function() {
+      this.editor.blur();
+    },
+
+    remove: function() {
+      this.editor.remove();
+
+      Backbone.View.prototype.remove.call(this);
+    },
+
+    validate: function() {
+      var value = this.getValue(),
+          formValues = this.list.form ? this.list.form.getValue() : {},
+          validators = this.schema.validators,
+          getValidator = this.getValidator;
+
+      if (!validators) return null;
+
+      //Run through validators until an error is found
+      var error = null;
+      _.every(validators, function(validator) {
+        error = getValidator(validator)(value, formValues);
+
+        return error ? false : true;
+      });
+
+      //Show/hide error
+      if (error){
+        this.setError(error);
+      } else {
+        this.clearError();
+      }
+
+      //Return error to be aggregated by list
+      return error ? error : null;
+    },
+
+    /**
+     * Show a validation error
+     */
+    setError: function(err) {
+      this.$el.addClass(this.errorClassName);
+      this.$el.attr('title', err.message);
+    },
+
+    /**
+     * Hide validation errors
+     */
+    clearError: function() {
+      this.$el.removeClass(this.errorClassName);
+      this.$el.attr('title', null);
+    }
+  }, {
+
+    //STATICS
+    template: _.template('\
+      <div>\
+        <span data-editor></span>\
+        <button type="button" data-action="remove">&times;</button>\
+      </div>\
+    ', null, Form.templateSettings),
+
+    errorClassName: 'error'
+
+  });
+
+
+  /**
+   * Base modal object editor for use with the List editor; used by Object
+   * and NestedModal list types
+   */
+  Form.editors.List.Modal = Form.editors.Base.extend({
+
+    events: {
+      'click': 'openEditor'
+    },
+
+    /**
+     * @param {Object} options
+     * @param {Form} options.form                       The main form
+     * @param {Function} [options.schema.itemToString]  Function to transform 
the value for display in the list.
+     * @param {String} [options.schema.itemType]        Editor type e.g. 
'Text', 'Object'.
+     * @param {Object} [options.schema.subSchema]       Schema for nested 
form,. Required when itemType is 'Object'
+     * @param {Function} [options.schema.model]         Model constructor 
function. Required when itemType is 'NestedModel'
+     */
+    initialize: function(options) {
+      options = options || {};
+
+      Form.editors.Base.prototype.initialize.call(this, options);
+
+      //Dependencies
+      if (!Form.editors.List.Modal.ModalAdapter) throw new Error('A 
ModalAdapter is required');
+
+      this.form = options.form;
+      if (!options.form) throw new Error('Missing required option: "form"');
+
+      //Template
+      this.template = options.template || this.constructor.template;
+    },
+
+    /**
+     * Render the list item representation
+     */
+    render: function() {
+      var self = this;
+
+      //New items in the list are only rendered when the editor has been OK'd
+      if (_.isEmpty(this.value)) {
+        this.openEditor();
+      }
+
+      //But items with values are added automatically
+      else {
+        this.renderSummary();
+
+        setTimeout(function() {
+          self.trigger('readyToAdd');
+        }, 0);
+      }
+
+      if (this.hasFocus) this.trigger('blur', this);
+
+      return this;
+    },
+
+    /**
+     * Renders the list item representation
+     */
+    renderSummary: function() {
+      this.$el.html($.trim(this.template({
+        summary: this.getStringValue()
+      })));
+    },
+
+    /**
+     * Function which returns a generic string representation of an object
+     *
+     * @param {Object} value
+     *
+     * @return {String}
+     */
+    itemToString: function(value) {
+      var createTitle = function(key) {
+        var context = { key: key };
+
+        return Form.Field.prototype.createTitle.call(context);
+      };
+
+      value = value || {};
+
+      //Pretty print the object keys and values
+      var parts = [];
+      _.each(this.nestedSchema, function(schema, key) {
+        var desc = schema.title ? schema.title : createTitle(key),
+            val = value[key];
+
+        if (_.isUndefined(val) || _.isNull(val)) val = '';
+
+        parts.push(desc + ': ' + val);
+      });
+
+      return parts.join('<br />');
+    },
+
+    /**
+     * Returns the string representation of the object value
+     */
+    getStringValue: function() {
+      var schema = this.schema,
+          value = this.getValue();
+
+      if (_.isEmpty(value)) return '[Empty]';
+
+      //If there's a specified toString use that
+      if (schema.itemToString) return schema.itemToString(value);
+
+      //Otherwise use the generic method or custom overridden method
+      return this.itemToString(value);
+    },
+
+    openEditor: function() {
+      var self = this,
+          ModalForm = this.form.constructor;
+
+      var form = this.modalForm = new ModalForm({
+        schema: this.nestedSchema,
+        data: this.value
+      });
+
+      var modal = this.modal = new Form.editors.List.Modal.ModalAdapter({
+        content: form,
+        animate: true
+      });
+
+      modal.open();
+
+      this.trigger('open', this);
+      this.trigger('focus', this);
+
+      modal.on('cancel', this.onModalClosed, this);
+
+      modal.on('ok', _.bind(this.onModalSubmitted, this));
+    },
+
+    /**
+     * Called when the user clicks 'OK'.
+     * Runs validation and tells the list when ready to add the item
+     */
+    onModalSubmitted: function() {
+      var modal = this.modal,
+          form = this.modalForm,
+          isNew = !this.value;
+
+      //Stop if there are validation errors
+      var error = form.validate();
+      if (error) return modal.preventClose();
+
+      //Store form value
+      this.value = form.getValue();
+
+      //Render item
+      this.renderSummary();
+
+      if (isNew) this.trigger('readyToAdd');
+
+      this.trigger('change', this);
+
+      this.onModalClosed();
+    },
+
+    /**
+     * Cleans up references, triggers events. To be called whenever the modal 
closes
+     */
+    onModalClosed: function() {
+      this.modal = null;
+      this.modalForm = null;
+
+      this.trigger('close', this);
+      this.trigger('blur', this);
+    },
+
+    getValue: function() {
+      return this.value;
+    },
+
+    setValue: function(value) {
+      this.value = value;
+    },
+
+    focus: function() {
+      if (this.hasFocus) return;
+
+      this.openEditor();
+    },
+
+    blur: function() {
+      if (!this.hasFocus) return;
+
+      if (this.modal) {
+        this.modal.trigger('cancel');
+      }
+    }
+  }, {
+    //STATICS
+    template: _.template('\
+      <div><%= summary %></div>\
+    ', null, Form.templateSettings),
+
+    //The modal adapter that creates and manages the modal dialog.
+    //Defaults to BootstrapModal 
(http://github.com/powmedia/backbone.bootstrap-modal)
+    //Can be replaced with another adapter that implements the same interface.
+    ModalAdapter: Backbone.BootstrapModal,
+
+    //Make the wait list for the 'ready' event before adding the item to the 
list
+    isAsync: true
+  });
+
+
+  Form.editors.List.Object = Form.editors.List.Modal.extend({
+    initialize: function () {
+      Form.editors.List.Modal.prototype.initialize.apply(this, arguments);
+
+      var schema = this.schema;
+
+      if (!schema.subSchema) throw new Error('Missing required option 
"schema.subSchema"');
+
+      this.nestedSchema = schema.subSchema;
+    }
+  });
+
+
+  Form.editors.List.NestedModel = Form.editors.List.Modal.extend({
+    initialize: function() {
+      Form.editors.List.Modal.prototype.initialize.apply(this, arguments);
+
+      var schema = this.schema;
+
+      if (!schema.model) throw new Error('Missing required option 
"schema.model"');
+
+      var nestedSchema = schema.model.prototype.schema;
+
+      this.nestedSchema = (_.isFunction(nestedSchema)) ? nestedSchema() : 
nestedSchema;
+    },
+
+    /**
+     * Returns the string representation of the object value
+     */
+    getStringValue: function() {
+      var schema = this.schema,
+          value = this.getValue();
+
+      if (_.isEmpty(value)) return null;
+
+      //If there's a specified toString use that
+      if (schema.itemToString) return schema.itemToString(value);
+
+      //Otherwise use the model
+      return new (schema.model)(value).toString();
+    }
+  });
+
+})(Backbone.Form);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd6398a1/contrib/views/storm/src/main/resources/libs/bower/backbone.babysitter/js/backbone.babysitter.js
----------------------------------------------------------------------
diff --git 
a/contrib/views/storm/src/main/resources/libs/bower/backbone.babysitter/js/backbone.babysitter.js
 
b/contrib/views/storm/src/main/resources/libs/bower/backbone.babysitter/js/backbone.babysitter.js
new file mode 100644
index 0000000..58ea664
--- /dev/null
+++ 
b/contrib/views/storm/src/main/resources/libs/bower/backbone.babysitter/js/backbone.babysitter.js
@@ -0,0 +1,190 @@
+// Backbone.BabySitter
+// -------------------
+// v0.1.6
+//
+// Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
+// Distributed under MIT license
+//
+// http://github.com/marionettejs/backbone.babysitter
+
+(function(root, factory) {
+
+  if (typeof define === 'function' && define.amd) {
+    define(['backbone', 'underscore'], function(Backbone, _) {
+      return factory(Backbone, _);
+    });
+  } else if (typeof exports !== 'undefined') {
+    var Backbone = require('backbone');
+    var _ = require('underscore');
+    module.exports = factory(Backbone, _);
+  } else {
+    factory(root.Backbone, root._);
+  }
+
+}(this, function(Backbone, _) {
+  'use strict';
+
+  var previousChildViewContainer = Backbone.ChildViewContainer;
+
+  // BabySitter.ChildViewContainer
+  // -----------------------------
+  //
+  // Provide a container to store, retrieve and
+  // shut down child views.
+
+  Backbone.ChildViewContainer = (function (Backbone, _) {
+
+    // Container Constructor
+    // ---------------------
+
+    var Container = function(views){
+      this._views = {};
+      this._indexByModel = {};
+      this._indexByCustom = {};
+      this._updateLength();
+
+      _.each(views, this.add, this);
+    };
+
+    // Container Methods
+    // -----------------
+
+    _.extend(Container.prototype, {
+
+      // Add a view to this container. Stores the view
+      // by `cid` and makes it searchable by the model
+      // cid (and model itself). Optionally specify
+      // a custom key to store an retrieve the view.
+      add: function(view, customIndex){
+        var viewCid = view.cid;
+
+        // store the view
+        this._views[viewCid] = view;
+
+        // index it by model
+        if (view.model){
+          this._indexByModel[view.model.cid] = viewCid;
+        }
+
+        // index by custom
+        if (customIndex){
+          this._indexByCustom[customIndex] = viewCid;
+        }
+
+        this._updateLength();
+        return this;
+      },
+
+      // Find a view by the model that was attached to
+      // it. Uses the model's `cid` to find it.
+      findByModel: function(model){
+        return this.findByModelCid(model.cid);
+      },
+
+      // Find a view by the `cid` of the model that was attached to
+      // it. Uses the model's `cid` to find the view `cid` and
+      // retrieve the view using it.
+      findByModelCid: function(modelCid){
+        var viewCid = this._indexByModel[modelCid];
+        return this.findByCid(viewCid);
+      },
+
+      // Find a view by a custom indexer.
+      findByCustom: function(index){
+        var viewCid = this._indexByCustom[index];
+        return this.findByCid(viewCid);
+      },
+
+      // Find by index. This is not guaranteed to be a
+      // stable index.
+      findByIndex: function(index){
+        return _.values(this._views)[index];
+      },
+
+      // retrieve a view by its `cid` directly
+      findByCid: function(cid){
+        return this._views[cid];
+      },
+
+      // Remove a view
+      remove: function(view){
+        var viewCid = view.cid;
+
+        // delete model index
+        if (view.model){
+          delete this._indexByModel[view.model.cid];
+        }
+
+        // delete custom index
+        _.any(this._indexByCustom, function(cid, key) {
+          if (cid === viewCid) {
+            delete this._indexByCustom[key];
+            return true;
+          }
+        }, this);
+
+        // remove the view from the container
+        delete this._views[viewCid];
+
+        // update the length
+        this._updateLength();
+        return this;
+      },
+
+      // Call a method on every view in the container,
+      // passing parameters to the call method one at a
+      // time, like `function.call`.
+      call: function(method){
+        this.apply(method, _.tail(arguments));
+      },
+
+      // Apply a method on every view in the container,
+      // passing parameters to the call method one at a
+      // time, like `function.apply`.
+      apply: function(method, args){
+        _.each(this._views, function(view){
+          if (_.isFunction(view[method])){
+            view[method].apply(view, args || []);
+          }
+        });
+      },
+
+      // Update the `.length` attribute on this container
+      _updateLength: function(){
+        this.length = _.size(this._views);
+      }
+    });
+
+    // Borrowing this code from Backbone.Collection:
+    // http://backbonejs.org/docs/backbone.html#section-106
+    //
+    // Mix in methods from Underscore, for iteration, and other
+    // collection related features.
+    var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
+      'select', 'reject', 'every', 'all', 'some', 'any', 'include',
+      'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
+      'last', 'without', 'isEmpty', 'pluck', 'reduce'];
+
+    _.each(methods, function(method) {
+      Container.prototype[method] = function() {
+        var views = _.values(this._views);
+        var args = [views].concat(_.toArray(arguments));
+        return _[method].apply(_, args);
+      };
+    });
+
+    // return the public API
+    return Container;
+  })(Backbone, _);
+
+
+  Backbone.ChildViewContainer.VERSION = '0.1.6';
+
+  Backbone.ChildViewContainer.noConflict = function () {
+    Backbone.ChildViewContainer = previousChildViewContainer;
+    return this;
+  };
+
+  return Backbone.ChildViewContainer;
+
+}));

Reply via email to