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">×</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; + +}));
