http://git-wip-us.apache.org/repos/asf/ambari/blob/529ef7f7/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/backbone-forms.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/backbone-forms.js b/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/backbone-forms.js deleted file mode 100644 index 9e02ebc..0000000 --- a/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/backbone-forms.js +++ /dev/null @@ -1,2446 +0,0 @@ -/** - * Backbone Forms v0.13.0 - * - * Copyright (c) 2013 Charles Davison, Pow Media Ltd - * - * License and more information at: - * http://github.com/powmedia/backbone-forms - */ -;(function(root) { - - //DEPENDENCIES - //CommonJS - if (typeof exports !== 'undefined' && typeof require !== 'undefined') { - var $ = root.jQuery || root.Zepto || root.ender || require('jquery'), - _ = root._ || require('underscore'), - Backbone = root.Backbone || require('backbone'); - } - - //Browser - else { - var $ = root.jQuery, - _ = root._, - Backbone = root.Backbone; - } - - - //SOURCE - //================================================================================================== -//FORM -//================================================================================================== - -var Form = Backbone.View.extend({ - - /** - * Constructor - * - * @param {Object} [options.schema] - * @param {Backbone.Model} [options.model] - * @param {Object} [options.data] - * @param {String[]|Object[]} [options.fieldsets] - * @param {String[]} [options.fields] - * @param {String} [options.idPrefix] - * @param {Form.Field} [options.Field] - * @param {Form.Fieldset} [options.Fieldset] - * @param {Function} [options.template] - */ - initialize: function(options) { - var self = this; - - options = options || {}; - - //Find the schema to use - var schema = this.schema = (function() { - //Prefer schema from options - if (options.schema) return _.result(options, 'schema'); - - //Then schema on model - var model = options.model; - if (model && model.schema) { - return (_.isFunction(model.schema)) ? model.schema() : model.schema; - } - - //Then built-in schema - if (self.schema) { - return (_.isFunction(self.schema)) ? self.schema() : self.schema; - } - - //Fallback to empty schema - return {}; - })(); - - //Store important data - _.extend(this, _.pick(options, 'model', 'data', 'idPrefix', 'templateData')); - - //Override defaults - var constructor = this.constructor; - this.template = options.template || this.template || constructor.template; - this.Fieldset = options.Fieldset || this.Fieldset || constructor.Fieldset; - this.Field = options.Field || this.Field || constructor.Field; - this.NestedField = options.NestedField || this.NestedField || constructor.NestedField; - - //Check which fields will be included (defaults to all) - var selectedFields = this.selectedFields = options.fields || _.keys(schema); - - //Create fields - var fields = this.fields = {}; - - _.each(selectedFields, function(key) { - var fieldSchema = schema[key]; - fields[key] = this.createField(key, fieldSchema); - }, this); - - //Create fieldsets - var fieldsetSchema = options.fieldsets || [selectedFields], - fieldsets = this.fieldsets = []; - - _.each(fieldsetSchema, function(itemSchema) { - this.fieldsets.push(this.createFieldset(itemSchema)); - }, this); - }, - - /** - * Creates a Fieldset instance - * - * @param {String[]|Object[]} schema Fieldset schema - * - * @return {Form.Fieldset} - */ - createFieldset: function(schema) { - var options = { - schema: schema, - fields: this.fields - }; - - return new this.Fieldset(options); - }, - - /** - * Creates a Field instance - * - * @param {String} key - * @param {Object} schema Field schema - * - * @return {Form.Field} - */ - createField: function(key, schema) { - var options = { - form: this, - key: key, - schema: schema, - idPrefix: this.idPrefix - }; - - if (this.model) { - options.model = this.model; - } else if (this.data) { - options.value = this.data[key]; - } else { - options.value = null; - } - - var field = new this.Field(options); - - this.listenTo(field.editor, 'all', this.handleEditorEvent); - - return field; - }, - - /** - * Callback for when an editor event is fired. - * Re-triggers events on the form as key:event and triggers additional form-level events - * - * @param {String} event - * @param {Editor} editor - */ - handleEditorEvent: function(event, editor) { - //Re-trigger editor events on the form - var formEvent = editor.key+':'+event; - - this.trigger.call(this, formEvent, this, editor, Array.prototype.slice.call(arguments, 2)); - - //Trigger additional events - switch (event) { - case 'change': - this.trigger('change', this); - break; - - case 'focus': - if (!this.hasFocus) this.trigger('focus', this); - break; - - case 'blur': - if (this.hasFocus) { - //TODO: Is the timeout etc needed? - var self = this; - setTimeout(function() { - var focusedField = _.find(self.fields, function(field) { - return field.editor.hasFocus; - }); - - if (!focusedField) self.trigger('blur', self); - }, 0); - } - break; - } - }, - - render: function() { - var self = this, - fields = this.fields; - - //Render form - var $form = $($.trim(this.template(_.result(this, 'templateData')))); - - //Render standalone editors - $form.find('[data-editors]').add($form).each(function(i, el) { - var $container = $(el), - selection = $container.attr('data-editors'); - - if (_.isUndefined(selection)) return; - - //Work out which fields to include - var keys = (selection == '*') - ? self.selectedFields || _.keys(fields) - : selection.split(','); - - //Add them - _.each(keys, function(key) { - var field = fields[key]; - - $container.append(field.editor.render().el); - }); - }); - - //Render standalone fields - $form.find('[data-fields]').add($form).each(function(i, el) { - var $container = $(el), - selection = $container.attr('data-fields'); - - if (_.isUndefined(selection)) return; - - //Work out which fields to include - var keys = (selection == '*') - ? self.selectedFields || _.keys(fields) - : selection.split(','); - - //Add them - _.each(keys, function(key) { - var field = fields[key]; - - $container.append(field.render().el); - }); - }); - - //Render fieldsets - $form.find('[data-fieldsets]').add($form).each(function(i, el) { - var $container = $(el), - selection = $container.attr('data-fieldsets'); - - if (_.isUndefined(selection)) return; - - _.each(self.fieldsets, function(fieldset) { - $container.append(fieldset.render().el); - }); - }); - - //Set the main element - this.setElement($form); - - //Set class - $form.addClass(this.className); - - return this; - }, - - /** - * Validate the data - * - * @return {Object} Validation errors - */ - validate: function(options) { - var self = this, - fields = this.fields, - model = this.model, - errors = {}; - - options = options || {}; - - //Collect errors from schema validation - _.each(fields, function(field) { - var error = field.validate(); - if (error) { - errors[field.key] = error; - } - }); - - //Get errors from default Backbone model validator - if (!options.skipModelValidate && model && model.validate) { - var modelErrors = model.validate(this.getValue()); - - if (modelErrors) { - var isDictionary = _.isObject(modelErrors) && !_.isArray(modelErrors); - - //If errors are not in object form then just store on the error object - if (!isDictionary) { - errors._others = errors._others || []; - errors._others.push(modelErrors); - } - - //Merge programmatic errors (requires model.validate() to return an object e.g. { fieldKey: 'error' }) - if (isDictionary) { - _.each(modelErrors, function(val, key) { - //Set error on field if there isn't one already - if (fields[key] && !errors[key]) { - fields[key].setError(val); - errors[key] = val; - } - - else { - //Otherwise add to '_others' key - errors._others = errors._others || []; - var tmpErr = {}; - tmpErr[key] = val; - errors._others.push(tmpErr); - } - }); - } - } - } - - return _.isEmpty(errors) ? null : errors; - }, - - /** - * Update the model with all latest values. - * - * @param {Object} [options] Options to pass to Model#set (e.g. { silent: true }) - * - * @return {Object} Validation errors - */ - commit: function(options) { - //Validate - options = options || {}; - - var validateOptions = { - skipModelValidate: !options.validate - }; - - var errors = this.validate(validateOptions); - if (errors) return errors; - - //Commit - var modelError; - - var setOptions = _.extend({ - error: function(model, e) { - modelError = e; - } - }, options); - - this.model.set(this.getValue(), setOptions); - - if (modelError) return modelError; - }, - - /** - * Get all the field values as an object. - * Use this method when passing data instead of objects - * - * @param {String} [key] Specific field value to get - */ - getValue: function(key) { - //Return only given key if specified - if (key) return this.fields[key].getValue(); - - //Otherwise return entire form - var values = {}; - _.each(this.fields, function(field) { - values[field.key] = field.getValue(); - }); - - return values; - }, - - /** - * Update field values, referenced by key - * - * @param {Object|String} key New values to set, or property to set - * @param val Value to set - */ - setValue: function(prop, val) { - var data = {}; - if (typeof prop === 'string') { - data[prop] = val; - } else { - data = prop; - } - - var key; - for (key in this.schema) { - if (data[key] !== undefined) { - this.fields[key].setValue(data[key]); - } - } - }, - - /** - * Returns the editor for a given field key - * - * @param {String} key - * - * @return {Editor} - */ - getEditor: function(key) { - var field = this.fields[key]; - if (!field) throw new Error('Field not found: '+key); - - return field.editor; - }, - - /** - * Gives the first editor in the form focus - */ - focus: function() { - if (this.hasFocus) return; - - //Get the first field - var fieldset = this.fieldsets[0], - field = fieldset.getFieldAt(0); - - if (!field) return; - - //Set focus - field.editor.focus(); - }, - - /** - * Removes focus from the currently focused editor - */ - blur: function() { - if (!this.hasFocus) return; - - var focusedField = _.find(this.fields, function(field) { - return field.editor.hasFocus; - }); - - if (focusedField) focusedField.editor.blur(); - }, - - /** - * Manages the hasFocus property - * - * @param {String} event - */ - trigger: function(event) { - if (event === 'focus') { - this.hasFocus = true; - } - else if (event === 'blur') { - this.hasFocus = false; - } - - return Backbone.View.prototype.trigger.apply(this, arguments); - }, - - /** - * Override default remove function in order to remove embedded views - * - * TODO: If editors are included directly with data-editors="x", they need to be removed - * May be best to use XView to manage adding/removing views - */ - remove: function() { - _.each(this.fieldsets, function(fieldset) { - fieldset.remove(); - }); - - _.each(this.fields, function(field) { - field.remove(); - }); - - return Backbone.View.prototype.remove.apply(this, arguments); - } - -}, { - - //STATICS - template: _.template('\ - <form data-fieldsets></form>\ - ', null, this.templateSettings), - - templateSettings: { - evaluate: /<%([\s\S]+?)%>/g, - interpolate: /<%=([\s\S]+?)%>/g, - escape: /<%-([\s\S]+?)%>/g - }, - - editors: {} - -}); - - -//================================================================================================== -//VALIDATORS -//================================================================================================== - -Form.validators = (function() { - - var validators = {}; - - validators.errMessages = { - required: 'This field is required', - regexp: 'Invalid', - email: 'Invalid email address', - url: 'Invalid URL', - match: _.template('Must match field "<%= field %>"', null, Form.templateSettings) - }; - - validators.required = function(options) { - options = _.extend({ - type: 'required', - message: this.errMessages.required - }, options); - - return function required(value) { - options.value = value; - - var err = { - type: options.type, - message: _.isFunction(options.message) ? options.message(options) : options.message - }; - - if (value === null || value === undefined || value === false || value === '') return err; - }; - }; - - validators.regexp = function(options) { - if (!options.regexp) throw new Error('Missing required "regexp" option for "regexp" validator'); - - options = _.extend({ - type: 'regexp', - message: this.errMessages.regexp - }, options); - - return function regexp(value) { - options.value = value; - - var err = { - type: options.type, - message: _.isFunction(options.message) ? options.message(options) : options.message - }; - - //Don't check empty values (add a 'required' validator for this) - if (value === null || value === undefined || value === '') return; - - if (!options.regexp.test(value)) return err; - }; - }; - - validators.email = function(options) { - options = _.extend({ - type: 'email', - message: this.errMessages.email, - regexp: /^[\w\-]{1,}([\w\-\+.]{1,1}[\w\-]{1,}){0,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/ - }, options); - - return validators.regexp(options); - }; - - validators.url = function(options) { - options = _.extend({ - type: 'url', - message: this.errMessages.url, - regexp: /^(http|https):\/\/(([A-Z0-9][A-Z0-9_\-]*)(\.[A-Z0-9][A-Z0-9_\-]*)+)(:(\d+))?\/?/i - }, options); - - return validators.regexp(options); - }; - - validators.match = function(options) { - if (!options.field) throw new Error('Missing required "field" options for "match" validator'); - - options = _.extend({ - type: 'match', - message: this.errMessages.match - }, options); - - return function match(value, attrs) { - options.value = value; - - var err = { - type: options.type, - message: _.isFunction(options.message) ? options.message(options) : options.message - }; - - //Don't check empty values (add a 'required' validator for this) - if (value === null || value === undefined || value === '') return; - - if (value !== attrs[options.field]) return err; - }; - }; - - - return validators; - -})(); - - -//================================================================================================== -//FIELDSET -//================================================================================================== - -Form.Fieldset = Backbone.View.extend({ - - /** - * Constructor - * - * Valid fieldset schemas: - * ['field1', 'field2'] - * { legend: 'Some Fieldset', fields: ['field1', 'field2'] } - * - * @param {String[]|Object[]} options.schema Fieldset schema - * @param {Object} options.fields Form fields - */ - initialize: function(options) { - options = options || {}; - - //Create the full fieldset schema, merging defaults etc. - var schema = this.schema = this.createSchema(options.schema); - - //Store the fields for this fieldset - this.fields = _.pick(options.fields, schema.fields); - - //Override defaults - this.template = options.template || this.constructor.template; - }, - - /** - * Creates the full fieldset schema, normalising, merging defaults etc. - * - * @param {String[]|Object[]} schema - * - * @return {Object} - */ - createSchema: function(schema) { - //Normalise to object - if (_.isArray(schema)) { - schema = { fields: schema }; - } - - //Add null legend to prevent template error - schema.legend = schema.legend || null; - - return schema; - }, - - /** - * Returns the field for a given index - * - * @param {Number} index - * - * @return {Field} - */ - getFieldAt: function(index) { - var key = this.schema.fields[index]; - - return this.fields[key]; - }, - - /** - * Returns data to pass to template - * - * @return {Object} - */ - templateData: function() { - return this.schema; - }, - - /** - * Renders the fieldset and fields - * - * @return {Fieldset} this - */ - render: function() { - var schema = this.schema, - fields = this.fields; - - //Render fieldset - var $fieldset = $($.trim(this.template(_.result(this, 'templateData')))); - - //Render fields - $fieldset.find('[data-fields]').add($fieldset).each(function(i, el) { - var $container = $(el), - selection = $container.attr('data-fields'); - - if (_.isUndefined(selection)) return; - - _.each(fields, function(field) { - $container.append(field.render().el); - }); - }); - - this.setElement($fieldset); - - return this; - }, - - /** - * Remove embedded views then self - */ - remove: function() { - _.each(this.fields, function(field) { - field.remove(); - }); - - Backbone.View.prototype.remove.call(this); - } - -}, { - //STATICS - - template: _.template('\ - <fieldset class="form-horizontal" data-fields>\ - <% if (legend) { %>\ - <legend><%= legend %></legend>\ - <% } %>\ - </fieldset>\ - ', null, Form.templateSettings) - -}); - - -//================================================================================================== -//FIELD -//================================================================================================== - -Form.Field = Backbone.View.extend({ - - /** - * Constructor - * - * @param {Object} options.key - * @param {Object} options.form - * @param {Object} [options.schema] - * @param {Function} [options.schema.template] - * @param {Backbone.Model} [options.model] - * @param {Object} [options.value] - * @param {String} [options.idPrefix] - * @param {Function} [options.template] - * @param {Function} [options.errorClassName] - */ - initialize: function(options) { - options = options || {}; - - //Store important data - _.extend(this, _.pick(options, 'form', 'key', 'model', 'value', 'idPrefix')); - - //Create the full field schema, merging defaults etc. - var schema = this.schema = this.createSchema(options.schema); - - //Override defaults - this.template = options.template || schema.template || this.constructor.template; - this.errorClassName = options.errorClassName || this.constructor.errorClassName; - - //Create editor - this.editor = this.createEditor(); - }, - - /** - * Creates the full field schema, merging defaults etc. - * - * @param {Object|String} schema - * - * @return {Object} - */ - createSchema: function(schema) { - if (_.isString(schema)) schema = { type: schema }; - - //Set defaults - schema = _.extend({ - type: 'Text', - title: this.createTitle() - }, schema); - - //Get the real constructor function i.e. if type is a string such as 'Text' - schema.type = (_.isString(schema.type)) ? Form.editors[schema.type] : schema.type; - - return schema; - }, - - /** - * Creates the editor specified in the schema; either an editor string name or - * a constructor function - * - * @return {View} - */ - createEditor: function() { - var options = _.extend( - _.pick(this, 'schema', 'form', 'key', 'model', 'value'), - { id: this.createEditorId() } - ); - - var constructorFn = this.schema.type; - - return new constructorFn(options); - }, - - /** - * Creates the ID that will be assigned to the editor - * - * @return {String} - */ - createEditorId: function() { - var prefix = this.idPrefix, - id = this.key; - - //Replace periods with underscores (e.g. for when using paths) - id = id.replace(/\./g, '_'); - - //If a specific ID prefix is set, use it - if (_.isString(prefix) || _.isNumber(prefix)) return prefix + id; - if (_.isNull(prefix)) return id; - - //Otherwise, if there is a model use it's CID to avoid conflicts when multiple forms are on the page - if (this.model) return this.model.cid + '_' + id; - - return id; - }, - - /** - * Create the default field title (label text) from the key name. - * (Converts 'camelCase' to 'Camel Case') - * - * @return {String} - */ - createTitle: function() { - var str = this.key; - - //Add spaces - str = str.replace(/([A-Z])/g, ' $1'); - - //Uppercase first character - str = str.replace(/^./, function(str) { return str.toUpperCase(); }); - - return str; - }, - - /** - * Returns the data to be passed to the template - * - * @return {Object} - */ - templateData: function() { - var schema = this.schema; - - return { - help: schema.help || '', - title: schema.title, - fieldAttrs: schema.fieldAttrs, - editorAttrs: schema.editorAttrs, - key: this.key, - editorId: this.editor.id - }; - }, - - /** - * Render the field and editor - * - * @return {Field} self - */ - render: function() { - var schema = this.schema, - editor = this.editor; - - //Only render the editor if Hidden - if (schema.type == Form.editors.Hidden) { - return this.setElement(editor.render().el); - } - - //Render field - var $field = $($.trim(this.template(_.result(this, 'templateData')))); - - if (schema.fieldClass) $field.addClass(schema.fieldClass); - if (schema.fieldAttrs) $field.attr(schema.fieldAttrs); - - //Render editor - $field.find('[data-editor]').add($field).each(function(i, el) { - var $container = $(el), - selection = $container.attr('data-editor'); - - if (_.isUndefined(selection)) return; - - $container.append(editor.render().el); - }); - - this.setElement($field); - - return this; - }, - - /** - * Check the validity of the field - * - * @return {String} - */ - validate: function() { - var error = this.editor.validate(); - - if (error) { - this.setError(error.message); - } else { - this.clearError(); - } - - return error; - }, - - /** - * Set the field into an error state, adding the error class and setting the error message - * - * @param {String} msg Error message - */ - setError: function(msg) { - //Nested form editors (e.g. Object) set their errors internally - if (this.editor.hasNestedForm) return; - - //Add error CSS class - this.$el.addClass(this.errorClassName); - - //Set error message - this.$('[data-error]').html(msg); - }, - - /** - * Clear the error state and reset the help message - */ - clearError: function() { - //Remove error CSS class - this.$el.removeClass(this.errorClassName); - - //Clear error message - this.$('[data-error]').empty(); - }, - - /** - * Update the model with the new value from the editor - * - * @return {Mixed} - */ - commit: function() { - return this.editor.commit(); - }, - - /** - * Get the value from the editor - * - * @return {Mixed} - */ - getValue: function() { - return this.editor.getValue(); - }, - - /** - * Set/change the value of the editor - * - * @param {Mixed} value - */ - setValue: function(value) { - this.editor.setValue(value); - }, - - /** - * Give the editor focus - */ - focus: function() { - this.editor.focus(); - }, - - /** - * Remove focus from the editor - */ - blur: function() { - this.editor.blur(); - }, - - /** - * Remove the field and editor views - */ - remove: function() { - this.editor.remove(); - - Backbone.View.prototype.remove.call(this); - } - -}, { - //STATICS - - template: _.template('\ - <div class="form-group">\ - <label class="col-sm-3 control-label" align="right" for="<%= editorId %>"><%= title %></label>\ - <div class="col-sm-9">\ - <span data-editor></span>\ - <div data-error></div>\ - <div><%= help %></div>\ - </div>\ - </div>\ - ', null, Form.templateSettings), - - /** - * CSS class name added to the field when there is a validation error - */ - errorClassName: 'error' - -}); - - -//================================================================================================== -//NESTEDFIELD -//================================================================================================== - -Form.NestedField = Form.Field.extend({ - - template: _.template($.trim('\ - <div>\ - <span data-editor></span>\ - <% if (help) { %>\ - <div><%= help %></div>\ - <% } %>\ - <div data-error></div>\ - </div>\ - '), null, Form.templateSettings) - -}); - -/** - * Base editor (interface). To be extended, not used directly - * - * @param {Object} options - * @param {String} [options.id] Editor ID - * @param {Model} [options.model] Use instead of value, and use commit() - * @param {String} [options.key] The model attribute key. Required when using 'model' - * @param {Mixed} [options.value] When not using a model. If neither provided, defaultValue will be used - * @param {Object} [options.schema] Field schema; may be required by some editors - * @param {Object} [options.validators] Validators; falls back to those stored on schema - * @param {Object} [options.form] The form - */ -Form.Editor = Form.editors.Base = Backbone.View.extend({ - - defaultValue: null, - - hasFocus: false, - - initialize: function(options) { - var options = options || {}; - - //Set initial value - if (options.model) { - if (!options.key) throw new Error("Missing option: 'key'"); - - this.model = options.model; - - this.value = this.model.get(options.key); - } - else if (options.value !== undefined) { - this.value = options.value; - } - - if (this.value === undefined) this.value = this.defaultValue; - - //Store important data - _.extend(this, _.pick(options, 'key', 'form')); - - var schema = this.schema = options.schema || {}; - - this.validators = options.validators || schema.validators; - - //Main attributes - this.$el.attr('id', this.id); - this.$el.attr('name', this.getName()); - if (schema.editorClass) this.$el.addClass(schema.editorClass); - if (schema.editorAttrs) this.$el.attr(schema.editorAttrs); - }, - - /** - * Get the value for the form input 'name' attribute - * - * @return {String} - * - * @api private - */ - getName: function() { - var key = this.key || ''; - - //Replace periods with underscores (e.g. for when using paths) - return key.replace(/\./g, '_'); - }, - - /** - * Get editor value - * Extend and override this method to reflect changes in the DOM - * - * @return {Mixed} - */ - getValue: function() { - return this.value; - }, - - /** - * Set editor value - * Extend and override this method to reflect changes in the DOM - * - * @param {Mixed} value - */ - setValue: function(value) { - this.value = value; - }, - - /** - * Give the editor focus - * Extend and override this method - */ - focus: function() { - throw new Error('Not implemented'); - }, - - /** - * Remove focus from the editor - * Extend and override this method - */ - blur: function() { - throw new Error('Not implemented'); - }, - - /** - * Update the model with the current value - * - * @param {Object} [options] Options to pass to model.set() - * @param {Boolean} [options.validate] Set to true to trigger built-in model validation - * - * @return {Mixed} error - */ - commit: function(options) { - var error = this.validate(); - if (error) return error; - - this.listenTo(this.model, 'invalid', function(model, e) { - error = e; - }); - this.model.set(this.key, this.getValue(), options); - - if (error) return error; - }, - - /** - * Check validity - * - * @return {Object|Undefined} - */ - validate: function() { - var $el = this.$el, - error = null, - value = this.getValue(), - formValues = this.form ? this.form.getValue() : {}, - validators = this.validators, - getValidator = this.getValidator; - - if (validators) { - //Run through validators until an error is found - _.every(validators, function(validator) { - error = getValidator(validator)(value, formValues); - - return error ? false : true; - }); - } - - return error; - }, - - /** - * Set this.hasFocus, or call parent trigger() - * - * @param {String} event - */ - trigger: function(event) { - if (event === 'focus') { - this.hasFocus = true; - } - else if (event === 'blur') { - this.hasFocus = false; - } - - return Backbone.View.prototype.trigger.apply(this, arguments); - }, - - /** - * Returns a validation function based on the type defined in the schema - * - * @param {RegExp|String|Function} validator - * @return {Function} - */ - getValidator: function(validator) { - var validators = Form.validators; - - //Convert regular expressions to validators - if (_.isRegExp(validator)) { - return validators.regexp({ regexp: validator }); - } - - //Use a built-in validator if given a string - if (_.isString(validator)) { - if (!validators[validator]) throw new Error('Validator "'+validator+'" not found'); - - return validators[validator](); - } - - //Functions can be used directly - if (_.isFunction(validator)) return validator; - - //Use a customised built-in validator if given an object - if (_.isObject(validator) && validator.type) { - var config = validator; - - return validators[config.type](config); - } - - //Unkown validator type - throw new Error('Invalid validator: ' + validator); - } -}); - -/** - * Text - * - * Text input with focus, blur and change events - */ -Form.editors.Text = Form.Editor.extend({ - - tagName: 'input', - - defaultValue: '', - - previousValue: '', - - events: { - 'keyup': 'determineChange', - 'keypress': function(event) { - var self = this; - setTimeout(function() { - self.determineChange(); - }, 0); - }, - 'select': function(event) { - this.trigger('select', this); - }, - 'focus': function(event) { - this.trigger('focus', this); - }, - 'blur': function(event) { - this.trigger('blur', this); - } - }, - - initialize: function(options) { - Form.editors.Base.prototype.initialize.call(this, options); - - var schema = this.schema; - - //Allow customising text type (email, phone etc.) for HTML5 browsers - var type = 'text'; - - if (schema && schema.editorAttrs && schema.editorAttrs.type) type = schema.editorAttrs.type; - if (schema && schema.dataType) type = schema.dataType; - - this.$el.attr('type', type); - }, - - /** - * Adds the editor to the DOM - */ - render: function() { - this.setValue(this.value); - - return this; - }, - - determineChange: function(event) { - var currentValue = this.$el.val(); - var changed = (currentValue !== this.previousValue); - - if (changed) { - this.previousValue = currentValue; - - this.trigger('change', this); - } - }, - - /** - * Returns the current editor value - * @return {String} - */ - getValue: function() { - return this.$el.val(); - }, - - /** - * Sets the value of the form element - * @param {String} - */ - setValue: function(value) { - this.$el.val(value); - }, - - focus: function() { - if (this.hasFocus) return; - - this.$el.focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$el.blur(); - }, - - select: function() { - this.$el.select(); - } - -}); - -/** - * TextArea editor - */ -Form.editors.TextArea = Form.editors.Text.extend({ - - tagName: 'textarea', - - /** - * Override Text constructor so type property isn't set (issue #261) - */ - initialize: function(options) { - Form.editors.Base.prototype.initialize.call(this, options); - } - -}); - -/** - * Password editor - */ -Form.editors.Password = Form.editors.Text.extend({ - - initialize: function(options) { - Form.editors.Text.prototype.initialize.call(this, options); - - this.$el.attr('type', 'password'); - } - -}); - -/** - * NUMBER - * - * Normal text input that only allows a number. Letters etc. are not entered. - */ -Form.editors.Number = Form.editors.Text.extend({ - - defaultValue: 0, - - events: _.extend({}, Form.editors.Text.prototype.events, { - 'keypress': 'onKeyPress', - 'change': 'onKeyPress' - }), - - initialize: function(options) { - Form.editors.Text.prototype.initialize.call(this, options); - - var schema = this.schema; - - this.$el.attr('type', 'number'); - - if (!schema || !schema.editorAttrs || !schema.editorAttrs.step) { - // provide a default for `step` attr, - // but don't overwrite if already specified - this.$el.attr('step', 'any'); - } - this.$el.attr('class','form-control'); - this.$el.attr('min','0'); - }, - - /** - * Check value is numeric - */ - onKeyPress: function(event) { - var self = this, - delayedDetermineChange = function() { - setTimeout(function() { - self.determineChange(); - }, 0); - }; - - //Allow backspace - if (event.charCode === 0) { - delayedDetermineChange(); - return; - } - - //Get the whole new value so that we can prevent things like double decimals points etc. - var newVal = this.$el.val() - if( event.charCode != undefined ) { - newVal = newVal + String.fromCharCode(event.charCode); - } - - var numeric = /^[0-9]*\.?[0-9]*?$/.test(newVal); - - if (numeric) { - delayedDetermineChange(); - } - else { - event.preventDefault(); - } - }, - - getValue: function() { - var value = this.$el.val(); - - return value === "" ? null : parseFloat(value, 10); - }, - - setValue: function(value) { - value = (function() { - if (_.isNumber(value)) return value; - - if (_.isString(value) && value !== '') return parseFloat(value, 10); - - return null; - })(); - - if (_.isNaN(value)) value = null; - - Form.editors.Text.prototype.setValue.call(this, value); - } - -}); - -/** - * Hidden editor - */ -Form.editors.Hidden = Form.editors.Text.extend({ - - defaultValue: '', - - initialize: function(options) { - Form.editors.Text.prototype.initialize.call(this, options); - - this.$el.attr('type', 'hidden'); - }, - - focus: function() { - - }, - - blur: function() { - - } - -}); - -/** - * Checkbox editor - * - * Creates a single checkbox, i.e. boolean value - */ -Form.editors.Checkbox = Form.editors.Base.extend({ - - defaultValue: false, - - tagName: 'input', - - events: { - 'click': function(event) { - this.trigger('change', this); - }, - 'focus': function(event) { - this.trigger('focus', this); - }, - 'blur': function(event) { - this.trigger('blur', this); - } - }, - - initialize: function(options) { - Form.editors.Base.prototype.initialize.call(this, options); - - this.$el.attr('type', 'checkbox'); - }, - - /** - * Adds the editor to the DOM - */ - render: function() { - this.setValue(this.value); - - return this; - }, - - getValue: function() { - return this.$el.prop('checked'); - }, - - setValue: function(value) { - if (value) { - this.$el.prop('checked', true); - }else{ - this.$el.prop('checked', false); - } - }, - - focus: function() { - if (this.hasFocus) return; - - this.$el.focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$el.blur(); - } - -}); - -/** - * Select editor - * - * Renders a <select> with given options - * - * Requires an 'options' value on the schema. - * Can be an array of options, a function that calls back with the array of options, a string of HTML - * or a Backbone collection. If a collection, the models must implement a toString() method - */ -Form.editors.Select = Form.editors.Base.extend({ - - tagName: 'select', - - events: { - 'change': function(event) { - this.trigger('change', this); - }, - 'focus': function(event) { - this.trigger('focus', this); - }, - 'blur': function(event) { - this.trigger('blur', this); - } - }, - - initialize: function(options) { - Form.editors.Base.prototype.initialize.call(this, options); - - if (!this.schema || !this.schema.options) throw new Error("Missing required 'schema.options'"); - }, - - render: function() { - this.setOptions(this.schema.options); - - return this; - }, - - /** - * Sets the options that populate the <select> - * - * @param {Mixed} options - */ - setOptions: function(options) { - var self = this; - - //If a collection was passed, check if it needs fetching - if (options instanceof Backbone.Collection) { - var collection = options; - - //Don't do the fetch if it's already populated - if (collection.length > 0) { - this.renderOptions(options); - } else { - collection.fetch({ - success: function(collection) { - self.renderOptions(options); - } - }); - } - } - - //If a function was passed, run it to get the options - else if (_.isFunction(options)) { - options(function(result) { - self.renderOptions(result); - }, self); - } - - //Otherwise, ready to go straight to renderOptions - else { - this.renderOptions(options); - } - }, - - /** - * Adds the <option> html to the DOM - * @param {Mixed} Options as a simple array e.g. ['option1', 'option2'] - * or as an array of objects e.g. [{val: 543, label: 'Title for object 543'}] - * or as a string of <option> HTML to insert into the <select> - * or any object - */ - renderOptions: function(options) { - var $select = this.$el, - html; - - html = this._getOptionsHtml(options); - - //Insert options - $select.html(html); - - //Select correct option - this.setValue(this.value); - }, - - _getOptionsHtml: function(options) { - var html; - //Accept string of HTML - if (_.isString(options)) { - html = options; - } - - //Or array - else if (_.isArray(options)) { - html = this._arrayToHtml(options); - } - - //Or Backbone collection - else if (options instanceof Backbone.Collection) { - html = this._collectionToHtml(options); - } - - else if (_.isFunction(options)) { - var newOptions; - - options(function(opts) { - newOptions = opts; - }, this); - - html = this._getOptionsHtml(newOptions); - //Or any object - }else{ - html=this._objectToHtml(options); - } - - return html; - }, - - - getValue: function() { - return this.$el.val(); - }, - - setValue: function(value) { - this.$el.val(value); - }, - - focus: function() { - if (this.hasFocus) return; - - this.$el.focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$el.blur(); - }, - - /** - * Transforms a collection into HTML ready to use in the renderOptions method - * @param {Backbone.Collection} - * @return {String} - */ - _collectionToHtml: function(collection) { - //Convert collection to array first - var array = []; - collection.each(function(model) { - array.push({ val: model.id, label: model.toString() }); - }); - - //Now convert to HTML - var html = this._arrayToHtml(array); - - return html; - }, - /** - * Transforms an object into HTML ready to use in the renderOptions method - * @param {Object} - * @return {String} - */ - _objectToHtml: function(obj) { - //Convert object to array first - var array = []; - for(var key in obj){ - if( obj.hasOwnProperty( key ) ) { - array.push({ val: key, label: obj[key] }); - } - } - - //Now convert to HTML - var html = this._arrayToHtml(array); - - return html; - }, - - - - /** - * Create the <option> HTML - * @param {Array} Options as a simple array e.g. ['option1', 'option2'] - * or as an array of objects e.g. [{val: 543, label: 'Title for object 543'}] - * @return {String} HTML - */ - _arrayToHtml: function(array) { - var html = []; - - //Generate HTML - _.each(array, function(option) { - if (_.isObject(option)) { - if (option.group) { - html.push('<optgroup label="'+option.group+'">'); - html.push(this._getOptionsHtml(option.options)) - html.push('</optgroup>'); - } else { - var val = (option.val || option.val === 0) ? option.val : ''; - html.push('<option value="'+val+'">'+option.label+'</option>'); - } - } - else { - html.push('<option>'+option+'</option>'); - } - }, this); - - return html.join(''); - } - -}); - -/** - * Radio editor - * - * Renders a <ul> with given options represented as <li> objects containing radio buttons - * - * Requires an 'options' value on the schema. - * Can be an array of options, a function that calls back with the array of options, a string of HTML - * or a Backbone collection. If a collection, the models must implement a toString() method - */ -Form.editors.Radio = Form.editors.Select.extend({ - - tagName: 'ul', - - events: { - 'change input[type=radio]': function() { - this.trigger('change', this); - }, - 'focus input[type=radio]': function() { - if (this.hasFocus) return; - this.trigger('focus', this); - }, - 'blur input[type=radio]': function() { - if (!this.hasFocus) return; - var self = this; - setTimeout(function() { - if (self.$('input[type=radio]:focus')[0]) return; - self.trigger('blur', self); - }, 0); - } - }, - - getValue: function() { - return this.$('input[type=radio]:checked').val(); - }, - - setValue: function(value) { - this.$('input[type=radio]').val([value]); - }, - - focus: function() { - if (this.hasFocus) return; - - var checked = this.$('input[type=radio]:checked'); - if (checked[0]) { - checked.focus(); - return; - } - - this.$('input[type=radio]').first().focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$('input[type=radio]:focus').blur(); - }, - - /** - * Create the radio list HTML - * @param {Array} Options as a simple array e.g. ['option1', 'option2'] - * or as an array of objects e.g. [{val: 543, label: 'Title for object 543'}] - * @return {String} HTML - */ - _arrayToHtml: function (array) { - var html = []; - var self = this; - - _.each(array, function(option, index) { - var itemHtml = '<li>'; - if (_.isObject(option)) { - var val = (option.val || option.val === 0) ? option.val : ''; - itemHtml += ('<input type="radio" name="'+self.getName()+'" value="'+val+'" id="'+self.id+'-'+index+'" />'); - itemHtml += ('<label for="'+self.id+'-'+index+'">'+option.label+'</label>'); - } - else { - itemHtml += ('<input type="radio" name="'+self.getName()+'" value="'+option+'" id="'+self.id+'-'+index+'" />'); - itemHtml += ('<label for="'+self.id+'-'+index+'">'+option+'</label>'); - } - itemHtml += '</li>'; - html.push(itemHtml); - }); - - return html.join(''); - } - -}); - -/** - * Checkboxes editor - * - * Renders a <ul> with given options represented as <li> objects containing checkboxes - * - * Requires an 'options' value on the schema. - * Can be an array of options, a function that calls back with the array of options, a string of HTML - * or a Backbone collection. If a collection, the models must implement a toString() method - */ -Form.editors.Checkboxes = Form.editors.Select.extend({ - - tagName: 'ul', - - groupNumber: 0, - - events: { - 'click input[type=checkbox]': function() { - this.trigger('change', this); - }, - 'focus input[type=checkbox]': function() { - if (this.hasFocus) return; - this.trigger('focus', this); - }, - 'blur input[type=checkbox]': function() { - if (!this.hasFocus) return; - var self = this; - setTimeout(function() { - if (self.$('input[type=checkbox]:focus')[0]) return; - self.trigger('blur', self); - }, 0); - } - }, - - getValue: function() { - var values = []; - this.$('input[type=checkbox]:checked').each(function() { - values.push($(this).val()); - }); - return values; - }, - - setValue: function(values) { - if (!_.isArray(values)) values = [values]; - this.$('input[type=checkbox]').val(values); - }, - - focus: function() { - if (this.hasFocus) return; - - this.$('input[type=checkbox]').first().focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$('input[type=checkbox]:focus').blur(); - }, - - /** - * Create the checkbox list HTML - * @param {Array} Options as a simple array e.g. ['option1', 'option2'] - * or as an array of objects e.g. [{val: 543, label: 'Title for object 543'}] - * @return {String} HTML - */ - _arrayToHtml: function (array) { - var html = []; - var self = this; - - _.each(array, function(option, index) { - var itemHtml = '<li>'; - var close = true; - if (_.isObject(option)) { - if (option.group) { - var originalId = self.id; - self.id += "-" + self.groupNumber++; - itemHtml = ('<fieldset class="group"> <legend>'+option.group+'</legend>'); - itemHtml += (self._arrayToHtml(option.options)); - itemHtml += ('</fieldset>'); - self.id = originalId; - close = false; - }else{ - var val = (option.val || option.val === 0) ? option.val : ''; - itemHtml += ('<input type="checkbox" name="'+self.getName()+'" value="'+val+'" id="'+self.id+'-'+index+'" />'); - itemHtml += ('<label for="'+self.id+'-'+index+'">'+option.label+'</label>'); - } - } - else { - itemHtml += ('<input type="checkbox" name="'+self.getName()+'" value="'+option+'" id="'+self.id+'-'+index+'" />'); - itemHtml += ('<label for="'+self.id+'-'+index+'">'+option+'</label>'); - } - if(close){ - itemHtml += '</li>'; - } - html.push(itemHtml); - }); - - return html.join(''); - } - -}); - -/** - * Object editor - * - * Creates a child form. For editing Javascript objects - * - * @param {Object} options - * @param {Form} options.form The form this editor belongs to; used to determine the constructor for the nested form - * @param {Object} options.schema The schema for the object - * @param {Object} options.schema.subSchema The schema for the nested form - */ -Form.editors.Object = Form.editors.Base.extend({ - //Prevent error classes being set on the main control; they are internally on the individual fields - hasNestedForm: true, - - initialize: function(options) { - //Set default value for the instance so it's not a shared object - this.value = {}; - - //Init - Form.editors.Base.prototype.initialize.call(this, options); - - //Check required options - if (!this.form) throw new Error('Missing required option "form"'); - if (!this.schema.subSchema) throw new Error("Missing required 'schema.subSchema' option for Object editor"); - }, - - render: function() { - //Get the constructor for creating the nested form; i.e. the same constructor as used by the parent form - var NestedForm = this.form.constructor; - - //Create the nested form - this.nestedForm = new NestedForm({ - schema: this.schema.subSchema, - data: this.value, - idPrefix: this.id + '_', - Field: NestedForm.NestedField - }); - - this._observeFormEvents(); - - this.$el.html(this.nestedForm.render().el); - - if (this.hasFocus) this.trigger('blur', this); - - return this; - }, - - getValue: function() { - if (this.nestedForm) return this.nestedForm.getValue(); - - return this.value; - }, - - setValue: function(value) { - this.value = value; - - this.render(); - }, - - focus: function() { - if (this.hasFocus) return; - - this.nestedForm.focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.nestedForm.blur(); - }, - - remove: function() { - this.nestedForm.remove(); - - Backbone.View.prototype.remove.call(this); - }, - - validate: function() { - return this.nestedForm.validate(); - }, - - _observeFormEvents: function() { - if (!this.nestedForm) return; - - this.nestedForm.on('all', function() { - // args = ["key:change", form, fieldEditor] - var args = _.toArray(arguments); - args[1] = this; - // args = ["key:change", this=objectEditor, fieldEditor] - - this.trigger.apply(this, args); - }, this); - } - -}); - -/** - * NestedModel editor - * - * Creates a child form. For editing nested Backbone models - * - * Special options: - * schema.model: Embedded model constructor - */ -Form.editors.NestedModel = Form.editors.Object.extend({ - initialize: function(options) { - Form.editors.Base.prototype.initialize.call(this, options); - - if (!this.form) throw new Error('Missing required option "form"'); - if (!options.schema.model) throw new Error('Missing required "schema.model" option for NestedModel editor'); - }, - - render: function() { - //Get the constructor for creating the nested form; i.e. the same constructor as used by the parent form - var NestedForm = this.form.constructor; - - var data = this.value || {}, - key = this.key, - nestedModel = this.schema.model; - - //Wrap the data in a model if it isn't already a model instance - var modelInstance = (data.constructor === nestedModel) ? data : new nestedModel(data); - - this.nestedForm = new NestedForm({ - model: modelInstance, - idPrefix: this.id + '_', - fieldTemplate: 'nestedField' - }); - - this._observeFormEvents(); - - //Render form - this.$el.html(this.nestedForm.render().el); - - if (this.hasFocus) this.trigger('blur', this); - - return this; - }, - - /** - * Update the embedded model, checking for nested validation errors and pass them up - * Then update the main model if all OK - * - * @return {Error|null} Validation error or null - */ - commit: function() { - var error = this.nestedForm.commit(); - if (error) { - this.$el.addClass('error'); - return error; - } - - return Form.editors.Object.prototype.commit.call(this); - } - -}); - -/** - * Date editor - * - * Schema options - * @param {Number|String} [options.schema.yearStart] First year in list. Default: 100 years ago - * @param {Number|String} [options.schema.yearEnd] Last year in list. Default: current year - * - * Config options (if not set, defaults to options stored on the main Date class) - * @param {Boolean} [options.showMonthNames] Use month names instead of numbers. Default: true - * @param {String[]} [options.monthNames] Month names. Default: Full English names - */ -Form.editors.Date = Form.editors.Base.extend({ - - events: { - 'change select': function() { - this.updateHidden(); - this.trigger('change', this); - }, - 'focus select': function() { - if (this.hasFocus) return; - this.trigger('focus', this); - }, - 'blur select': function() { - if (!this.hasFocus) return; - var self = this; - setTimeout(function() { - if (self.$('select:focus')[0]) return; - self.trigger('blur', self); - }, 0); - } - }, - - initialize: function(options) { - options = options || {}; - - Form.editors.Base.prototype.initialize.call(this, options); - - var Self = Form.editors.Date, - today = new Date(); - - //Option defaults - this.options = _.extend({ - monthNames: Self.monthNames, - showMonthNames: Self.showMonthNames - }, options); - - //Schema defaults - this.schema = _.extend({ - yearStart: today.getFullYear() - 100, - yearEnd: today.getFullYear() - }, options.schema || {}); - - //Cast to Date - if (this.value && !_.isDate(this.value)) { - this.value = new Date(this.value); - } - - //Set default date - if (!this.value) { - var date = new Date(); - date.setSeconds(0); - date.setMilliseconds(0); - - this.value = date; - } - - //Template - this.template = options.template || this.constructor.template; - }, - - render: function() { - var options = this.options, - schema = this.schema; - - var datesOptions = _.map(_.range(1, 32), function(date) { - return '<option value="'+date+'">' + date + '</option>'; - }); - - var monthsOptions = _.map(_.range(0, 12), function(month) { - var value = (options.showMonthNames) - ? options.monthNames[month] - : (month + 1); - - return '<option value="'+month+'">' + value + '</option>'; - }); - - var yearRange = (schema.yearStart < schema.yearEnd) - ? _.range(schema.yearStart, schema.yearEnd + 1) - : _.range(schema.yearStart, schema.yearEnd - 1, -1); - - var yearsOptions = _.map(yearRange, function(year) { - return '<option value="'+year+'">' + year + '</option>'; - }); - - //Render the selects - var $el = $($.trim(this.template({ - dates: datesOptions.join(''), - months: monthsOptions.join(''), - years: yearsOptions.join('') - }))); - - //Store references to selects - this.$date = $el.find('[data-type="date"]'); - this.$month = $el.find('[data-type="month"]'); - this.$year = $el.find('[data-type="year"]'); - - //Create the hidden field to store values in case POSTed to server - this.$hidden = $('<input type="hidden" name="'+this.key+'" />'); - $el.append(this.$hidden); - - //Set value on this and hidden field - this.setValue(this.value); - - //Remove the wrapper tag - this.setElement($el); - this.$el.attr('id', this.id); - this.$el.attr('name', this.getName()); - - if (this.hasFocus) this.trigger('blur', this); - - return this; - }, - - /** - * @return {Date} Selected date - */ - getValue: function() { - var year = this.$year.val(), - month = this.$month.val(), - date = this.$date.val(); - - if (!year || !month || !date) return null; - - return new Date(year, month, date); - }, - - /** - * @param {Date} date - */ - setValue: function(date) { - this.$date.val(date.getDate()); - this.$month.val(date.getMonth()); - this.$year.val(date.getFullYear()); - - this.updateHidden(); - }, - - focus: function() { - if (this.hasFocus) return; - - this.$('select').first().focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$('select:focus').blur(); - }, - - /** - * Update the hidden input which is maintained for when submitting a form - * via a normal browser POST - */ - updateHidden: function() { - var val = this.getValue(); - - if (_.isDate(val)) val = val.toISOString(); - - this.$hidden.val(val); - } - -}, { - //STATICS - template: _.template('\ - <div>\ - <select data-type="date"><%= dates %></select>\ - <select data-type="month"><%= months %></select>\ - <select data-type="year"><%= years %></select>\ - </div>\ - ', null, Form.templateSettings), - - //Whether to show month names instead of numbers - showMonthNames: true, - - //Month names to use if showMonthNames is true - //Replace for localisation, e.g. Form.editors.Date.monthNames = ['Janvier', 'Fevrier'...] - monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] -}); - -/** - * DateTime editor - * - * @param {Editor} [options.DateEditor] Date editor view to use (not definition) - * @param {Number} [options.schema.minsInterval] Interval between minutes. Default: 15 - */ -Form.editors.DateTime = Form.editors.Base.extend({ - - events: { - 'change select': function() { - this.updateHidden(); - this.trigger('change', this); - }, - 'focus select': function() { - if (this.hasFocus) return; - this.trigger('focus', this); - }, - 'blur select': function() { - if (!this.hasFocus) return; - var self = this; - setTimeout(function() { - if (self.$('select:focus')[0]) return; - self.trigger('blur', self); - }, 0); - } - }, - - initialize: function(options) { - options = options || {}; - - Form.editors.Base.prototype.initialize.call(this, options); - - //Option defaults - this.options = _.extend({ - DateEditor: Form.editors.DateTime.DateEditor - }, options); - - //Schema defaults - this.schema = _.extend({ - minsInterval: 15 - }, options.schema || {}); - - //Create embedded date editor - this.dateEditor = new this.options.DateEditor(options); - - this.value = this.dateEditor.value; - - //Template - this.template = options.template || this.constructor.template; - }, - - render: function() { - function pad(n) { - return n < 10 ? '0' + n : n; - } - - var schema = this.schema; - - //Create options - var hoursOptions = _.map(_.range(0, 24), function(hour) { - return '<option value="'+hour+'">' + pad(hour) + '</option>'; - }); - - var minsOptions = _.map(_.range(0, 60, schema.minsInterval), function(min) { - return '<option value="'+min+'">' + pad(min) + '</option>'; - }); - - //Render time selects - var $el = $($.trim(this.template({ - hours: hoursOptions.join(), - mins: minsOptions.join() - }))); - - //Include the date editor - $el.find('[data-date]').append(this.dateEditor.render().el); - - //Store references to selects - this.$hour = $el.find('select[data-type="hour"]'); - this.$min = $el.find('select[data-type="min"]'); - - //Get the hidden date field to store values in case POSTed to server - this.$hidden = $el.find('input[type="hidden"]'); - - //Set time - this.setValue(this.value); - - this.setElement($el); - this.$el.attr('id', this.id); - this.$el.attr('name', this.getName()); - - if (this.hasFocus) this.trigger('blur', this); - - return this; - }, - - /** - * @return {Date} Selected datetime - */ - getValue: function() { - var date = this.dateEditor.getValue(); - - var hour = this.$hour.val(), - min = this.$min.val(); - - if (!date || !hour || !min) return null; - - date.setHours(hour); - date.setMinutes(min); - - return date; - }, - - /** - * @param {Date} - */ - setValue: function(date) { - if (!_.isDate(date)) date = new Date(date); - - this.dateEditor.setValue(date); - - this.$hour.val(date.getHours()); - this.$min.val(date.getMinutes()); - - this.updateHidden(); - }, - - focus: function() { - if (this.hasFocus) return; - - this.$('select').first().focus(); - }, - - blur: function() { - if (!this.hasFocus) return; - - this.$('select:focus').blur(); - }, - - /** - * Update the hidden input which is maintained for when submitting a form - * via a normal browser POST - */ - updateHidden: function() { - var val = this.getValue(); - if (_.isDate(val)) val = val.toISOString(); - - this.$hidden.val(val); - }, - - /** - * Remove the Date editor before removing self - */ - remove: function() { - this.dateEditor.remove(); - - Form.editors.Base.prototype.remove.call(this); - } - -}, { - //STATICS - template: _.template('\ - <div class="bbf-datetime">\ - <div class="bbf-date-container" data-date></div>\ - <select data-type="hour"><%= hours %></select>\ - :\ - <select data-type="min"><%= mins %></select>\ - </div>\ - ', null, Form.templateSettings), - - //The date editor to use (constructor function, not instance) - DateEditor: Form.editors.Date -}); - - - - //Metadata - Form.VERSION = '0.13.0'; - - - //Exports - Backbone.Form = Form; - if (typeof exports !== 'undefined') exports = Form; - -})(window || global || this);
http://git-wip-us.apache.org/repos/asf/ambari/blob/529ef7f7/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 deleted file mode 100644 index e21d748..0000000 --- a/contrib/views/storm/src/main/resources/libs/bower/backbone-forms/js/list.js +++ /dev/null @@ -1,650 +0,0 @@ -;(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/529ef7f7/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 deleted file mode 100644 index 58ea664..0000000 --- a/contrib/views/storm/src/main/resources/libs/bower/backbone.babysitter/js/backbone.babysitter.js +++ /dev/null @@ -1,190 +0,0 @@ -// 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; - -}));