Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'fix-userinitiated' of https://github.com/elasticsales/b…

…ackbone-forms

Add addItem docs
  • Loading branch information...
commit 461c3c7db2e5e7aae00878f5fd0e48d16658f091 1 parent c6d56d4
@powmedia authored
View
4 distribution.amd/backbone-forms.js
@@ -332,7 +332,9 @@ var Form = (function() {
*/
setValue: function(data) {
for (var key in data) {
- this.fields[key].setValue(data[key]);
+ if (_.has(this.fields, key)) {
+ this.fields[key].setValue(data[key]);
+ }
}
},
View
2  distribution.amd/backbone-forms.min.js
@@ -1 +1 @@
-define(["jquery","underscore","backbone"],function($,_,Backbone){var Form=function(){return Backbone.View.extend({hasFocus:!1,initialize:function(options){if(!Form.templates.form)throw new Error("Templates not loaded");this.schema=function(){if(options.schema)return options.schema;var model=options.model;if(!model)throw new Error("Could not find schema");return _.isFunction(model.schema)?model.schema():model.schema}(),options=_.extend({template:"form",fieldsetTemplate:"fieldset",fieldTemplate:"field"},options);if(!options.fieldsets){var fields=options.fields||_.keys(this.schema);options.fieldsets=[{fields:fields}]}this.options=options,this.model=options.model,this.data=options.data,this.fields={}},render:function(){var self=this,options=this.options,template=Form.templates[options.template],$form=$(template({fieldsets:'<b class="bbf-tmp"></b>'})),$fieldsetContainer=$(".bbf-tmp",$form);return _.each(options.fieldsets,function(fieldset){$fieldsetContainer.append(self.renderFieldset(fieldset))}),$fieldsetContainer.children().unwrap(),this.setElement($form),this.hasFocus&&this.trigger("blur",this),this},renderFieldset:function(fieldset){var self=this,template=Form.templates[this.options.fieldsetTemplate],schema=this.schema,getNested=Form.helpers.getNested;_.isArray(fieldset)&&(fieldset={fields:fieldset});var $fieldset=$(template(_.extend({},fieldset,{legend:'<b class="bbf-tmp-legend"></b>',fields:'<b class="bbf-tmp-fields"></b>'})));fieldset.legend?$fieldset.find(".bbf-tmp-legend").replaceWith(fieldset.legend):$fieldset.find(".bbf-tmp-legend").parent().remove();var $fieldsContainer=$(".bbf-tmp-fields",$fieldset);return _.each(fieldset.fields,function(key){var itemSchema=function(){if(schema[key])return schema[key];var path=key.replace(/\./g,".subSchema.");return getNested(schema,path)}();if(!itemSchema)throw"Field '"+key+"' not found in schema";var field=self.fields[key]=self.createField(key,itemSchema),fieldEl=field.render().el;field.editor.on("all",function(event){args=_.toArray(arguments),args[0]=key+":"+event,args.splice(1,0,this),this.trigger.apply(this,args)},self),field.editor.on("change",function(){this.trigger("change",self)},self),field.editor.on("focus",function(){if(this.hasFocus)return;this.trigger("focus",this)},self),field.editor.on("blur",function(){if(!this.hasFocus)return;var self=this;setTimeout(function(){if(_.find(self.fields,function(field){return field.editor.hasFocus}))return;self.trigger("blur",self)},0)},self),itemSchema.type!="Hidden"&&$fieldsContainer.append(fieldEl)}),$fieldsContainer=$fieldsContainer.children().unwrap(),$fieldset},createField:function(key,schema){schema.template=schema.template||this.options.fieldTemplate;var options={form:this,key:key,schema:schema,idPrefix:this.options.idPrefix,template:this.options.fieldTemplate};return this.model?options.model=this.model:this.data?options.value=this.data[key]:options.value=null,new Form.Field(options)},validate:function(){var self=this,fields=this.fields,model=this.model,errors={};_.each(fields,function(field){var error=field.validate();error&&(errors[field.key]=error)});if(model&&model.validate){var modelErrors=model.validate(this.getValue());if(modelErrors){var isDictionary=_.isObject(modelErrors)&&!_.isArray(modelErrors);isDictionary||(errors._others=errors._others||[],errors._others.push(modelErrors)),isDictionary&&_.each(modelErrors,function(val,key){if(self.fields[key]&&!errors[key])self.fields[key].setError(val);else{errors._others=errors._others||[];var tmpErr={};tmpErr[key]=val,errors._others.push(tmpErr)}})}}return _.isEmpty(errors)?null:errors},commit:function(){var errors=this.validate();if(errors)return errors;var modelError;this.model.set(this.getValue(),{error:function(model,e){modelError=e}});if(modelError)return modelError},getValue:function(key){if(key)return this.fields[key].getValue();var values={};return _.each(this.fields,function(field){values[field.key]=field.getValue()}),values},setValue:function(data){for(var key in data)this.fields[key].setValue(data[key])},focus:function(){if(this.hasFocus)return;var fieldset=this.options.fieldsets[0];if(fieldset){var field;_.isArray(fieldset)?field=fieldset[0]:field=fieldset.fields[0],field&&this.fields[field].editor.focus()}},blur:function(){if(!this.hasFocus)return;focusedField=_.find(this.fields,function(field){return field.editor.hasFocus}),focusedField&&focusedField.editor.blur()},remove:function(){var fields=this.fields;for(var key in fields)fields[key].remove();Backbone.View.prototype.remove.call(this)},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}})}();Form.helpers=function(){var helpers={};return helpers.getNested=function(obj,path){var fields=path.split("."),result=obj;for(var i=0,n=fields.length;i<n;i++)result=result[fields[i]];return result},helpers.keyToTitle=function(str){return str=str.replace(/([A-Z])/g," $1"),str=str.replace(/^./,function(str){return str.toUpperCase()}),str},helpers.compileTemplate=function(str){var _interpolateBackup=_.templateSettings.interpolate;_.templateSettings.interpolate=/\{\{(.+?)\}\}/g;var template=_.template(str);return _.templateSettings.interpolate=_interpolateBackup,template},helpers.createTemplate=function(str,context){var template=helpers.compileTemplate(str);return context?template(context):template},helpers.setTemplateCompiler=function(compiler){helpers.compileTemplate=compiler},helpers.setTemplates=function(templates,classNames){var createTemplate=helpers.createTemplate;Form.templates=Form.templates||{},Form.classNames=Form.classNames||{},_.each(templates,function(template,key,index){_.isString(template)&&(template=createTemplate(template)),Form.templates[key]=template}),_.extend(Form.classNames,classNames)},helpers.createEditor=function(schemaType,options){var constructorFn;return _.isString(schemaType)?constructorFn=Form.editors[schemaType]:constructorFn=schemaType,new constructorFn(options)},helpers.triggerCancellableEvent=function(subject,event,args,callback){if(!subject._callbacks||!subject._callbacks[event])return callback();var next=subject._callbacks[event].next;if(!next)return callback();var fn=next.callback,context=next.context||this;args.push(callback),fn.apply(context,args)},helpers.getValidator=function(validator){var validators=Form.validators;if(_.isRegExp(validator))return validators.regexp({regexp:validator});if(_.isString(validator)){if(!validators[validator])throw new Error('Validator "'+validator+'" not found');return validators[validator]()}if(_.isFunction(validator))return validator;if(_.isObject(validator)&&validator.type){var config=validator;return validators[config.type](config)}throw new Error("Invalid validator: "+validator)},helpers}(),Form.validators=function(){var validators={};return validators.errMessages={required:"Required",regexp:"Invalid",email:"Invalid email address",url:"Invalid URL",match:'Must match field "{{field}}"'},validators.required=function(options){return options=_.extend({type:"required",message:this.errMessages.required},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return err}},validators.regexp=function(options){if(!options.regexp)throw new Error('Missing required "regexp" option for "regexp" validator');return options=_.extend({type:"regexp",message:this.errMessages.regexp},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(!options.regexp.test(value))return err}},validators.email=function(options){return options=_.extend({type:"email",message:this.errMessages.email,regexp:/^[\w\-]{1,}([\w\-\+.]{1,1}[\w\-]{1,}){0,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/},options),validators.regexp(options)},validators.url=function(options){return 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),validators.regexp(options)},validators.match=function(options){if(!options.field)throw new Error('Missing required "field" options for "match" validator');return options=_.extend({type:"match",message:this.errMessages.match},options),function(value,attrs){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(value!=attrs[options.field])return err}},validators}(),Form.Field=function(){var helpers=Form.helpers,templates=Form.templates;return Backbone.View.extend({initialize:function(options){options=options||{},this.form=options.form,this.key=options.key,this.value=options.value,this.model=options.model,_.isString(options.schema)&&(options.schema={type:options.schema}),this.schema=_.extend({type:"Text",title:helpers.keyToTitle(this.key),template:"field"},options.schema)},render:function(){var schema=this.schema,templates=Form.templates,options={form:this.form,key:this.key,schema:schema,idPrefix:this.options.idPrefix,id:this.getId()};this.model?options.model=this.model:options.value=this.value;var editor=this.editor=helpers.createEditor(schema.type,options),$field=$(templates[schema.template]({key:this.key,title:schema.title,id:editor.id,type:schema.type,editor:'<b class="bbf-tmp-editor"></b>',help:'<b class="bbf-tmp-help"></b>'}));return $field.find(".bbf-tmp-editor").replaceWith(editor.render().el),this.$help=$(".bbf-tmp-help",$field).parent(),this.$help.empty(),this.schema.help&&this.$help.html(this.schema.help),this.schema.fieldClass&&$field.addClass(this.schema.fieldClass),this.schema.fieldAttrs&&$field.attr(this.schema.fieldAttrs),this.setElement($field),this},getId:function(){var prefix=this.options.idPrefix,id=this.key;return id=id.replace(/\./g,"_"),_.isString(prefix)||_.isNumber(prefix)?prefix+id:_.isNull(prefix)?id:this.model?this.model.cid+"_"+id:id},validate:function(){var error=this.editor.validate();return error?this.setError(error.message):this.clearError(),error},setError:function(msg){if(this.editor.hasNestedForm)return;var errClass=Form.classNames.error;this.$el.addClass(errClass),this.$help&&this.$help.html(msg)},clearError:function(){var errClass=Form.classNames.error;this.$el.removeClass(errClass);if(this.$help){this.$help.empty();var helpMsg=this.schema.help;helpMsg&&this.$help.html(helpMsg)}},commit:function(){return this.editor.commit()},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)}})}(),Form.editors=function(){var helpers=Form.helpers,editors={};return editors.Base=Backbone.View.extend({defaultValue:null,hasFocus:!1,initialize:function(options){var options=options||{};if(options.model){if(!options.key)throw"Missing option: 'key'";this.model=options.model,this.value=this.model.get(options.key)}else options.value&&(this.value=options.value);this.value===undefined&&(this.value=this.defaultValue),this.key=options.key,this.form=options.form,this.schema=options.schema||{},this.validators=options.validators||this.schema.validators,this.$el.attr("name",this.getName()),this.schema.editorClass&&this.$el.addClass(this.schema.editorClass),this.schema.editorAttrs&&this.$el.attr(this.schema.editorAttrs)},getValue:function(){throw"Not implemented. Extend and override this method."},setValue:function(){throw"Not implemented. Extend and override this method."},focus:function(){throw"Not implemented. Extend and override this method."},blur:function(){throw"Not implemented. Extend and override this method."},getName:function(){var key=this.key||"";return key.replace(/\./g,"_")},commit:function(){var error=this.validate();if(error)return error;this.model.set(this.key,this.getValue(),{error:function(model,e){error=e}});if(error)return error},validate:function(){var $el=this.$el,error=null,value=this.getValue(),formValues=this.form?this.form.getValue():{},validators=this.validators,getValidator=Form.helpers.getValidator;return validators&&_.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}}),editors.Text=editors.Base.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){editors.Base.prototype.initialize.call(this,options);var schema=this.schema,type="text";schema&&schema.editorAttrs&&schema.editorAttrs.type&&(type=schema.editorAttrs.type),schema&&schema.dataType&&(type=schema.dataType),this.$el.attr("type",type)},render:function(){return this.setValue(this.value),this},determineChange:function(event){var currentValue=this.$el.val(),changed=currentValue!=this.previousValue;changed&&(this.previousValue=currentValue,this.trigger("change",this))},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()},select:function(){this.$el.select()}}),editors.Number=editors.Text.extend({defaultValue:0,events:_.extend({},editors.Text.prototype.events,{keypress:"onKeyPress"}),initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","number")},onKeyPress:function(event){var self=this,delayedDetermineChange=function(){setTimeout(function(){self.determineChange()},0)};if(event.charCode==0){delayedDetermineChange();return}var newVal=this.$el.val()+String.fromCharCode(event.charCode),numeric=/^[0-9]*\.?[0-9]*?$/.test(newVal);numeric?delayedDetermineChange():event.preventDefault()},getValue:function(){var value=this.$el.val();return value===""?null:parseFloat(value,10)},setValue:function(value){value=function(){return _.isNumber(value)?value:_.isString(value)&&value!==""?parseFloat(value,10):null}(),_.isNaN(value)&&(value=null),editors.Text.prototype.setValue.call(this,value)}}),editors.Password=editors.Text.extend({initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","password")}}),editors.TextArea=editors.Text.extend({tagName:"textarea"}),editors.Checkbox=editors.Base.extend({defaultValue:!1,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){editors.Base.prototype.initialize.call(this,options),this.$el.attr("type","checkbox")},render:function(){return this.setValue(this.value),this},getValue:function(){return this.$el.prop("checked")},setValue:function(value){value&&this.$el.prop("checked",!0)},focus:function(){if(this.hasFocus)return;this.$el.focus()},blur:function(){if(!this.hasFocus)return;this.$el.blur()}}),editors.Hidden=editors.Base.extend({defaultValue:"",initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","hidden")},getValue:function(){return this.value},setValue:function(value){this.value=value},focus:function(){},blur:function(){}}),editors.Select=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){editors.Base.prototype.initialize.call(this,options);if(!this.schema||!this.schema.options)throw"Missing required 'schema.options'"},render:function(){return this.setOptions(this.schema.options),this},setOptions:function(options){var self=this;if(options instanceof Backbone.Collection){var collection=options;collection.length>0?this.renderOptions(options):collection.fetch({success:function(collection){self.renderOptions(options)}})}else _.isFunction(options)?options(function(result){self.renderOptions(result)}):this.renderOptions(options)},renderOptions:function(options){var $select=this.$el,html;_.isString(options)?html=options:_.isArray(options)?html=this._arrayToHtml(options):options instanceof Backbone.Collection&&(html=this._collectionToHtml(options)),$select.html(html),this.setValue(this.value)},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()},_collectionToHtml:function(collection){var array=[];collection.each(function(model){array.push({val:model.id,label:model.toString()})});var html=this._arrayToHtml(array);return html},_arrayToHtml:function(array){var html=[];return _.each(array,function(option){if(_.isObject(option)){var val=option.val?option.val:"";html.push('<option value="'+val+'">'+option.label+"</option>")}else html.push("<option>"+option+"</option>")}),html.join("")}}),editors.Radio=editors.Select.extend({tagName:"ul",className:"bbf-radio",events:{"click input[type=radio]:not(:checked)":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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="radio" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="radio" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Checkboxes=editors.Select.extend({tagName:"ul",className:"bbf-checkboxes",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=[];return this.$("input[type=checkbox]:checked").each(function(){values.push($(this).val())}),values},setValue:function(values){_.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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Object=editors.Base.extend({hasNestedForm:!0,className:"bbf-object",initialize:function(options){this.value={},editors.Base.prototype.initialize.call(this,options);if(!this.schema.subSchema)throw new Error("Missing required 'schema.subSchema' option for Object editor")},render:function(){return this.form=new Form({schema:this.schema.subSchema,data:this.value,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){return this.form?this.form.getValue():this.value},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.form.focus()},blur:function(){if(!this.hasFocus)return;this.form.blur()},remove:function(){this.form.remove(),Backbone.View.prototype.remove.call(this)},validate:function(){return this.form.validate()},_observeFormEvents:function(){this.form.on("all",function(){args=_.toArray(arguments),args[1]=this,this.trigger.apply(this,args)},this)}}),editors.NestedModel=editors.Object.extend({initialize:function(options){editors.Base.prototype.initialize.call(this,options);if(!options.schema.model)throw'Missing required "schema.model" option for NestedModel editor'},render:function(){var data=this.value||{},key=this.key,nestedModel=this.schema.model,modelInstance=data.constructor==nestedModel?data:new nestedModel(data);return this.form=new Form({model:modelInstance,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},commit:function(){var error=this.form.commit();return error?(this.$el.addClass("error"),error):editors.Object.prototype.commit.call(this)}}),editors.Date=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||{},editors.Base.prototype.initialize.call(this,options);var Self=editors.Date,today=new Date;this.options=_.extend({monthNames:Self.monthNames,showMonthNames:Self.showMonthNames},options),this.schema=_.extend({yearStart:today.getFullYear()-100,yearEnd:today.getFullYear()},options.schema||{}),this.value&&!_.isDate(this.value)&&(this.value=new Date(this.value));if(!this.value){var date=new Date;date.setSeconds(0),date.setMilliseconds(0),this.value=date}},render:function(){var options=this.options,schema=this.schema,datesOptions=_.map(_.range(1,32),function(date){return'<option value="'+date+'">'+date+"</option>"}),monthsOptions=_.map(_.range(0,12),function(month){var value=options.showMonthNames?options.monthNames[month]:month+1;return'<option value="'+month+'">'+value+"</option>"}),yearsOptions=_.map(_.range(schema.yearStart,schema.yearEnd+1),function(year){return'<option value="'+year+'">'+year+"</option>"}),$el=$(Form.templates.date({dates:datesOptions.join(""),months:monthsOptions.join(""),years:yearsOptions.join("")}));return this.$date=$el.find('select[data-type="date"]'),this.$month=$el.find('select[data-type="month"]'),this.$year=$el.find('select[data-type="year"]'),this.$hidden=$('<input type="hidden" name="'+this.key+'" />'),$el.append(this.$hidden),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var year=this.$year.val(),month=this.$month.val(),date=this.$date.val();return!year||!month||!date?null:new Date(year,month,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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)}},{showMonthNames:!0,monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"]}),editors.DateTime=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||{},editors.Base.prototype.initialize.call(this,options),this.options=_.extend({DateEditor:editors.DateTime.DateEditor},options),this.schema=_.extend({minsInterval:15},options.schema||{}),this.dateEditor=new this.options.DateEditor(options),this.value=this.dateEditor.value},render:function(){function pad(n){return n<10?"0"+n:n}var schema=this.schema,hoursOptions=_.map(_.range(0,24),function(hour){return'<option value="'+hour+'">'+pad(hour)+"</option>"}),minsOptions=_.map(_.range(0,60,schema.minsInterval),function(min){return'<option value="'+min+'">'+pad(min)+"</option>"}),$el=$(Form.templates.dateTime({date:'<b class="bbf-tmp"></b>',hours:hoursOptions.join(),mins:minsOptions.join()}));return $el.find(".bbf-tmp").replaceWith(this.dateEditor.render().el),this.$hour=$el.find('select[data-type="hour"]'),this.$min=$el.find('select[data-type="min"]'),this.$hidden=$el.find('input[type="hidden"]'),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var date=this.dateEditor.getValue(),hour=this.$hour.val(),min=this.$min.val();return!date||!hour||!min?null:(date.setHours(hour),date.setMinutes(min),date)},setValue:function(date){_.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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)},remove:function(){this.dateEditor.remove(),editors.Base.prototype.remove.call(this)}},{DateEditor:editors.Date}),editors}(),Form.setTemplates=Form.helpers.setTemplates,Form.setTemplateCompiler=Form.helpers.setTemplateCompiler,Form.templates={},Form.setTemplates({form:' <form class="bbf-form">{{fieldsets}}</form> ',fieldset:" <fieldset> <legend>{{legend}}</legend> <ul>{{fields}}</ul> </fieldset> ",field:' <li class="bbf-field field-{{key}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',nestedField:' <li class="bbf-field bbf-nested-field field-{{key}}" title="{{title}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',list:' <div class="bbf-list"> <ul>{{items}}</ul> <div class="bbf-actions"><button type="button" data-action="add">Add</div> </div> ',listItem:' <li> <button type="button" data-action="remove" class="bbf-remove">&times;</button> <div class="bbf-editor-container">{{editor}}</div> </li> ',date:' <div class="bbf-date"> <select data-type="date" class="bbf-date">{{dates}}</select> <select data-type="month" class="bbf-month">{{months}}</select> <select data-type="year" class="bbf-year">{{years}}</select> </div> ',dateTime:' <div class="bbf-datetime"> <div class="bbf-date-container">{{date}}</div> <select data-type="hour">{{hours}}</select> : <select data-type="min">{{mins}}</select> </div> ',"list.Modal":' <div class="bbf-list-modal"> {{summary}} </div> '},{error:"bbf-error"}),Form.VERSION="0.10.1",Backbone.Form=Form})
+define(["jquery","underscore","backbone"],function($,_,Backbone){var Form=function(){return Backbone.View.extend({hasFocus:!1,initialize:function(options){if(!Form.templates.form)throw new Error("Templates not loaded");this.schema=function(){if(options.schema)return options.schema;var model=options.model;if(!model)throw new Error("Could not find schema");return _.isFunction(model.schema)?model.schema():model.schema}(),options=_.extend({template:"form",fieldsetTemplate:"fieldset",fieldTemplate:"field"},options);if(!options.fieldsets){var fields=options.fields||_.keys(this.schema);options.fieldsets=[{fields:fields}]}this.options=options,this.model=options.model,this.data=options.data,this.fields={}},render:function(){var self=this,options=this.options,template=Form.templates[options.template],$form=$(template({fieldsets:'<b class="bbf-tmp"></b>'})),$fieldsetContainer=$(".bbf-tmp",$form);return _.each(options.fieldsets,function(fieldset){$fieldsetContainer.append(self.renderFieldset(fieldset))}),$fieldsetContainer.children().unwrap(),this.setElement($form),this.hasFocus&&this.trigger("blur",this),this},renderFieldset:function(fieldset){var self=this,template=Form.templates[this.options.fieldsetTemplate],schema=this.schema,getNested=Form.helpers.getNested;_.isArray(fieldset)&&(fieldset={fields:fieldset});var $fieldset=$(template(_.extend({},fieldset,{legend:'<b class="bbf-tmp-legend"></b>',fields:'<b class="bbf-tmp-fields"></b>'})));fieldset.legend?$fieldset.find(".bbf-tmp-legend").replaceWith(fieldset.legend):$fieldset.find(".bbf-tmp-legend").parent().remove();var $fieldsContainer=$(".bbf-tmp-fields",$fieldset);return _.each(fieldset.fields,function(key){var itemSchema=function(){if(schema[key])return schema[key];var path=key.replace(/\./g,".subSchema.");return getNested(schema,path)}();if(!itemSchema)throw"Field '"+key+"' not found in schema";var field=self.fields[key]=self.createField(key,itemSchema),fieldEl=field.render().el;field.editor.on("all",function(event){args=_.toArray(arguments),args[0]=key+":"+event,args.splice(1,0,this),this.trigger.apply(this,args)},self),field.editor.on("change",function(){this.trigger("change",self)},self),field.editor.on("focus",function(){if(this.hasFocus)return;this.trigger("focus",this)},self),field.editor.on("blur",function(){if(!this.hasFocus)return;var self=this;setTimeout(function(){if(_.find(self.fields,function(field){return field.editor.hasFocus}))return;self.trigger("blur",self)},0)},self),itemSchema.type!="Hidden"&&$fieldsContainer.append(fieldEl)}),$fieldsContainer=$fieldsContainer.children().unwrap(),$fieldset},createField:function(key,schema){schema.template=schema.template||this.options.fieldTemplate;var options={form:this,key:key,schema:schema,idPrefix:this.options.idPrefix,template:this.options.fieldTemplate};return this.model?options.model=this.model:this.data?options.value=this.data[key]:options.value=null,new Form.Field(options)},validate:function(){var self=this,fields=this.fields,model=this.model,errors={};_.each(fields,function(field){var error=field.validate();error&&(errors[field.key]=error)});if(model&&model.validate){var modelErrors=model.validate(this.getValue());if(modelErrors){var isDictionary=_.isObject(modelErrors)&&!_.isArray(modelErrors);isDictionary||(errors._others=errors._others||[],errors._others.push(modelErrors)),isDictionary&&_.each(modelErrors,function(val,key){if(self.fields[key]&&!errors[key])self.fields[key].setError(val);else{errors._others=errors._others||[];var tmpErr={};tmpErr[key]=val,errors._others.push(tmpErr)}})}}return _.isEmpty(errors)?null:errors},commit:function(){var errors=this.validate();if(errors)return errors;var modelError;this.model.set(this.getValue(),{error:function(model,e){modelError=e}});if(modelError)return modelError},getValue:function(key){if(key)return this.fields[key].getValue();var values={};return _.each(this.fields,function(field){values[field.key]=field.getValue()}),values},setValue:function(data){for(var key in data)_.has(this.fields,key)&&this.fields[key].setValue(data[key])},focus:function(){if(this.hasFocus)return;var fieldset=this.options.fieldsets[0];if(fieldset){var field;_.isArray(fieldset)?field=fieldset[0]:field=fieldset.fields[0],field&&this.fields[field].editor.focus()}},blur:function(){if(!this.hasFocus)return;focusedField=_.find(this.fields,function(field){return field.editor.hasFocus}),focusedField&&focusedField.editor.blur()},remove:function(){var fields=this.fields;for(var key in fields)fields[key].remove();Backbone.View.prototype.remove.call(this)},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}})}();Form.helpers=function(){var helpers={};return helpers.getNested=function(obj,path){var fields=path.split("."),result=obj;for(var i=0,n=fields.length;i<n;i++)result=result[fields[i]];return result},helpers.keyToTitle=function(str){return str=str.replace(/([A-Z])/g," $1"),str=str.replace(/^./,function(str){return str.toUpperCase()}),str},helpers.compileTemplate=function(str){var _interpolateBackup=_.templateSettings.interpolate;_.templateSettings.interpolate=/\{\{(.+?)\}\}/g;var template=_.template(str);return _.templateSettings.interpolate=_interpolateBackup,template},helpers.createTemplate=function(str,context){var template=helpers.compileTemplate(str);return context?template(context):template},helpers.setTemplateCompiler=function(compiler){helpers.compileTemplate=compiler},helpers.setTemplates=function(templates,classNames){var createTemplate=helpers.createTemplate;Form.templates=Form.templates||{},Form.classNames=Form.classNames||{},_.each(templates,function(template,key,index){_.isString(template)&&(template=createTemplate(template)),Form.templates[key]=template}),_.extend(Form.classNames,classNames)},helpers.createEditor=function(schemaType,options){var constructorFn;return _.isString(schemaType)?constructorFn=Form.editors[schemaType]:constructorFn=schemaType,new constructorFn(options)},helpers.triggerCancellableEvent=function(subject,event,args,callback){if(!subject._callbacks||!subject._callbacks[event])return callback();var next=subject._callbacks[event].next;if(!next)return callback();var fn=next.callback,context=next.context||this;args.push(callback),fn.apply(context,args)},helpers.getValidator=function(validator){var validators=Form.validators;if(_.isRegExp(validator))return validators.regexp({regexp:validator});if(_.isString(validator)){if(!validators[validator])throw new Error('Validator "'+validator+'" not found');return validators[validator]()}if(_.isFunction(validator))return validator;if(_.isObject(validator)&&validator.type){var config=validator;return validators[config.type](config)}throw new Error("Invalid validator: "+validator)},helpers}(),Form.validators=function(){var validators={};return validators.errMessages={required:"Required",regexp:"Invalid",email:"Invalid email address",url:"Invalid URL",match:'Must match field "{{field}}"'},validators.required=function(options){return options=_.extend({type:"required",message:this.errMessages.required},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return err}},validators.regexp=function(options){if(!options.regexp)throw new Error('Missing required "regexp" option for "regexp" validator');return options=_.extend({type:"regexp",message:this.errMessages.regexp},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(!options.regexp.test(value))return err}},validators.email=function(options){return options=_.extend({type:"email",message:this.errMessages.email,regexp:/^[\w\-]{1,}([\w\-\+.]{1,1}[\w\-]{1,}){0,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/},options),validators.regexp(options)},validators.url=function(options){return 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),validators.regexp(options)},validators.match=function(options){if(!options.field)throw new Error('Missing required "field" options for "match" validator');return options=_.extend({type:"match",message:this.errMessages.match},options),function(value,attrs){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(value!=attrs[options.field])return err}},validators}(),Form.Field=function(){var helpers=Form.helpers,templates=Form.templates;return Backbone.View.extend({initialize:function(options){options=options||{},this.form=options.form,this.key=options.key,this.value=options.value,this.model=options.model,_.isString(options.schema)&&(options.schema={type:options.schema}),this.schema=_.extend({type:"Text",title:helpers.keyToTitle(this.key),template:"field"},options.schema)},render:function(){var schema=this.schema,templates=Form.templates,options={form:this.form,key:this.key,schema:schema,idPrefix:this.options.idPrefix,id:this.getId()};this.model?options.model=this.model:options.value=this.value;var editor=this.editor=helpers.createEditor(schema.type,options),$field=$(templates[schema.template]({key:this.key,title:schema.title,id:editor.id,type:schema.type,editor:'<b class="bbf-tmp-editor"></b>',help:'<b class="bbf-tmp-help"></b>'}));return $field.find(".bbf-tmp-editor").replaceWith(editor.render().el),this.$help=$(".bbf-tmp-help",$field).parent(),this.$help.empty(),this.schema.help&&this.$help.html(this.schema.help),this.schema.fieldClass&&$field.addClass(this.schema.fieldClass),this.schema.fieldAttrs&&$field.attr(this.schema.fieldAttrs),this.setElement($field),this},getId:function(){var prefix=this.options.idPrefix,id=this.key;return id=id.replace(/\./g,"_"),_.isString(prefix)||_.isNumber(prefix)?prefix+id:_.isNull(prefix)?id:this.model?this.model.cid+"_"+id:id},validate:function(){var error=this.editor.validate();return error?this.setError(error.message):this.clearError(),error},setError:function(msg){if(this.editor.hasNestedForm)return;var errClass=Form.classNames.error;this.$el.addClass(errClass),this.$help&&this.$help.html(msg)},clearError:function(){var errClass=Form.classNames.error;this.$el.removeClass(errClass);if(this.$help){this.$help.empty();var helpMsg=this.schema.help;helpMsg&&this.$help.html(helpMsg)}},commit:function(){return this.editor.commit()},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)}})}(),Form.editors=function(){var helpers=Form.helpers,editors={};return editors.Base=Backbone.View.extend({defaultValue:null,hasFocus:!1,initialize:function(options){var options=options||{};if(options.model){if(!options.key)throw"Missing option: 'key'";this.model=options.model,this.value=this.model.get(options.key)}else options.value&&(this.value=options.value);this.value===undefined&&(this.value=this.defaultValue),this.key=options.key,this.form=options.form,this.schema=options.schema||{},this.validators=options.validators||this.schema.validators,this.$el.attr("name",this.getName()),this.schema.editorClass&&this.$el.addClass(this.schema.editorClass),this.schema.editorAttrs&&this.$el.attr(this.schema.editorAttrs)},getValue:function(){throw"Not implemented. Extend and override this method."},setValue:function(){throw"Not implemented. Extend and override this method."},focus:function(){throw"Not implemented. Extend and override this method."},blur:function(){throw"Not implemented. Extend and override this method."},getName:function(){var key=this.key||"";return key.replace(/\./g,"_")},commit:function(){var error=this.validate();if(error)return error;this.model.set(this.key,this.getValue(),{error:function(model,e){error=e}});if(error)return error},validate:function(){var $el=this.$el,error=null,value=this.getValue(),formValues=this.form?this.form.getValue():{},validators=this.validators,getValidator=Form.helpers.getValidator;return validators&&_.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}}),editors.Text=editors.Base.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){editors.Base.prototype.initialize.call(this,options);var schema=this.schema,type="text";schema&&schema.editorAttrs&&schema.editorAttrs.type&&(type=schema.editorAttrs.type),schema&&schema.dataType&&(type=schema.dataType),this.$el.attr("type",type)},render:function(){return this.setValue(this.value),this},determineChange:function(event){var currentValue=this.$el.val(),changed=currentValue!=this.previousValue;changed&&(this.previousValue=currentValue,this.trigger("change",this))},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()},select:function(){this.$el.select()}}),editors.Number=editors.Text.extend({defaultValue:0,events:_.extend({},editors.Text.prototype.events,{keypress:"onKeyPress"}),initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","number")},onKeyPress:function(event){var self=this,delayedDetermineChange=function(){setTimeout(function(){self.determineChange()},0)};if(event.charCode==0){delayedDetermineChange();return}var newVal=this.$el.val()+String.fromCharCode(event.charCode),numeric=/^[0-9]*\.?[0-9]*?$/.test(newVal);numeric?delayedDetermineChange():event.preventDefault()},getValue:function(){var value=this.$el.val();return value===""?null:parseFloat(value,10)},setValue:function(value){value=function(){return _.isNumber(value)?value:_.isString(value)&&value!==""?parseFloat(value,10):null}(),_.isNaN(value)&&(value=null),editors.Text.prototype.setValue.call(this,value)}}),editors.Password=editors.Text.extend({initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","password")}}),editors.TextArea=editors.Text.extend({tagName:"textarea"}),editors.Checkbox=editors.Base.extend({defaultValue:!1,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){editors.Base.prototype.initialize.call(this,options),this.$el.attr("type","checkbox")},render:function(){return this.setValue(this.value),this},getValue:function(){return this.$el.prop("checked")},setValue:function(value){value&&this.$el.prop("checked",!0)},focus:function(){if(this.hasFocus)return;this.$el.focus()},blur:function(){if(!this.hasFocus)return;this.$el.blur()}}),editors.Hidden=editors.Base.extend({defaultValue:"",initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","hidden")},getValue:function(){return this.value},setValue:function(value){this.value=value},focus:function(){},blur:function(){}}),editors.Select=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){editors.Base.prototype.initialize.call(this,options);if(!this.schema||!this.schema.options)throw"Missing required 'schema.options'"},render:function(){return this.setOptions(this.schema.options),this},setOptions:function(options){var self=this;if(options instanceof Backbone.Collection){var collection=options;collection.length>0?this.renderOptions(options):collection.fetch({success:function(collection){self.renderOptions(options)}})}else _.isFunction(options)?options(function(result){self.renderOptions(result)}):this.renderOptions(options)},renderOptions:function(options){var $select=this.$el,html;_.isString(options)?html=options:_.isArray(options)?html=this._arrayToHtml(options):options instanceof Backbone.Collection&&(html=this._collectionToHtml(options)),$select.html(html),this.setValue(this.value)},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()},_collectionToHtml:function(collection){var array=[];collection.each(function(model){array.push({val:model.id,label:model.toString()})});var html=this._arrayToHtml(array);return html},_arrayToHtml:function(array){var html=[];return _.each(array,function(option){if(_.isObject(option)){var val=option.val?option.val:"";html.push('<option value="'+val+'">'+option.label+"</option>")}else html.push("<option>"+option+"</option>")}),html.join("")}}),editors.Radio=editors.Select.extend({tagName:"ul",className:"bbf-radio",events:{"click input[type=radio]:not(:checked)":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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="radio" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="radio" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Checkboxes=editors.Select.extend({tagName:"ul",className:"bbf-checkboxes",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=[];return this.$("input[type=checkbox]:checked").each(function(){values.push($(this).val())}),values},setValue:function(values){_.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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Object=editors.Base.extend({hasNestedForm:!0,className:"bbf-object",initialize:function(options){this.value={},editors.Base.prototype.initialize.call(this,options);if(!this.schema.subSchema)throw new Error("Missing required 'schema.subSchema' option for Object editor")},render:function(){return this.form=new Form({schema:this.schema.subSchema,data:this.value,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){return this.form?this.form.getValue():this.value},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.form.focus()},blur:function(){if(!this.hasFocus)return;this.form.blur()},remove:function(){this.form.remove(),Backbone.View.prototype.remove.call(this)},validate:function(){return this.form.validate()},_observeFormEvents:function(){this.form.on("all",function(){args=_.toArray(arguments),args[1]=this,this.trigger.apply(this,args)},this)}}),editors.NestedModel=editors.Object.extend({initialize:function(options){editors.Base.prototype.initialize.call(this,options);if(!options.schema.model)throw'Missing required "schema.model" option for NestedModel editor'},render:function(){var data=this.value||{},key=this.key,nestedModel=this.schema.model,modelInstance=data.constructor==nestedModel?data:new nestedModel(data);return this.form=new Form({model:modelInstance,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},commit:function(){var error=this.form.commit();return error?(this.$el.addClass("error"),error):editors.Object.prototype.commit.call(this)}}),editors.Date=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||{},editors.Base.prototype.initialize.call(this,options);var Self=editors.Date,today=new Date;this.options=_.extend({monthNames:Self.monthNames,showMonthNames:Self.showMonthNames},options),this.schema=_.extend({yearStart:today.getFullYear()-100,yearEnd:today.getFullYear()},options.schema||{}),this.value&&!_.isDate(this.value)&&(this.value=new Date(this.value));if(!this.value){var date=new Date;date.setSeconds(0),date.setMilliseconds(0),this.value=date}},render:function(){var options=this.options,schema=this.schema,datesOptions=_.map(_.range(1,32),function(date){return'<option value="'+date+'">'+date+"</option>"}),monthsOptions=_.map(_.range(0,12),function(month){var value=options.showMonthNames?options.monthNames[month]:month+1;return'<option value="'+month+'">'+value+"</option>"}),yearsOptions=_.map(_.range(schema.yearStart,schema.yearEnd+1),function(year){return'<option value="'+year+'">'+year+"</option>"}),$el=$(Form.templates.date({dates:datesOptions.join(""),months:monthsOptions.join(""),years:yearsOptions.join("")}));return this.$date=$el.find('select[data-type="date"]'),this.$month=$el.find('select[data-type="month"]'),this.$year=$el.find('select[data-type="year"]'),this.$hidden=$('<input type="hidden" name="'+this.key+'" />'),$el.append(this.$hidden),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var year=this.$year.val(),month=this.$month.val(),date=this.$date.val();return!year||!month||!date?null:new Date(year,month,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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)}},{showMonthNames:!0,monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"]}),editors.DateTime=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||{},editors.Base.prototype.initialize.call(this,options),this.options=_.extend({DateEditor:editors.DateTime.DateEditor},options),this.schema=_.extend({minsInterval:15},options.schema||{}),this.dateEditor=new this.options.DateEditor(options),this.value=this.dateEditor.value},render:function(){function pad(n){return n<10?"0"+n:n}var schema=this.schema,hoursOptions=_.map(_.range(0,24),function(hour){return'<option value="'+hour+'">'+pad(hour)+"</option>"}),minsOptions=_.map(_.range(0,60,schema.minsInterval),function(min){return'<option value="'+min+'">'+pad(min)+"</option>"}),$el=$(Form.templates.dateTime({date:'<b class="bbf-tmp"></b>',hours:hoursOptions.join(),mins:minsOptions.join()}));return $el.find(".bbf-tmp").replaceWith(this.dateEditor.render().el),this.$hour=$el.find('select[data-type="hour"]'),this.$min=$el.find('select[data-type="min"]'),this.$hidden=$el.find('input[type="hidden"]'),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var date=this.dateEditor.getValue(),hour=this.$hour.val(),min=this.$min.val();return!date||!hour||!min?null:(date.setHours(hour),date.setMinutes(min),date)},setValue:function(date){_.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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)},remove:function(){this.dateEditor.remove(),editors.Base.prototype.remove.call(this)}},{DateEditor:editors.Date}),editors}(),Form.setTemplates=Form.helpers.setTemplates,Form.setTemplateCompiler=Form.helpers.setTemplateCompiler,Form.templates={},Form.setTemplates({form:' <form class="bbf-form">{{fieldsets}}</form> ',fieldset:" <fieldset> <legend>{{legend}}</legend> <ul>{{fields}}</ul> </fieldset> ",field:' <li class="bbf-field field-{{key}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',nestedField:' <li class="bbf-field bbf-nested-field field-{{key}}" title="{{title}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',list:' <div class="bbf-list"> <ul>{{items}}</ul> <div class="bbf-actions"><button type="button" data-action="add">Add</div> </div> ',listItem:' <li> <button type="button" data-action="remove" class="bbf-remove">&times;</button> <div class="bbf-editor-container">{{editor}}</div> </li> ',date:' <div class="bbf-date"> <select data-type="date" class="bbf-date">{{dates}}</select> <select data-type="month" class="bbf-month">{{months}}</select> <select data-type="year" class="bbf-year">{{years}}</select> </div> ',dateTime:' <div class="bbf-datetime"> <div class="bbf-date-container">{{date}}</div> <select data-type="hour">{{hours}}</select> : <select data-type="min">{{mins}}</select> </div> ',"list.Modal":' <div class="bbf-list-modal"> {{summary}} </div> '},{error:"bbf-error"}),Form.VERSION="0.10.1",Backbone.Form=Form})
View
14 distribution.amd/editors/list.js
@@ -19,7 +19,7 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
events: {
'click [data-action="add"]': function(event) {
event.preventDefault();
- this.addItem();
+ this.addItem(null, true);
}
},
@@ -31,7 +31,8 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
//List schema defaults
this.schema = _.extend({
- listTemplate: 'list'
+ listTemplate: 'list',
+ listItemTemplate: 'listItem'
}, schema);
//Determine the editor to use
@@ -86,7 +87,8 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
/**
* Add a new item to the list
- * @param {Mixed} [value] Value for the new item editor
+ * @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;
@@ -113,7 +115,7 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
args.splice(1, 0, self);
// args = ["item:key:change", this=listEditor, itemEditor, fieldEditor]
- editors.List.prototype.trigger.apply(this, args)
+ editors.List.prototype.trigger.apply(this, args);
}, self);
item.editor.on('change', function() {
@@ -146,7 +148,7 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
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) {
@@ -291,7 +293,7 @@ define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
}).render();
//Create main element
- var $el = $(Form.templates.listItem({
+ var $el = $(Form.templates[this.schema.listItemTemplate]({
editor: '<b class="bbf-tmp"></b>'
}));
View
2  distribution.amd/editors/list.min.js
@@ -1 +1 @@
-define(["jquery","underscore","backbone"],function($,_,Backbone){(function(){var Form=Backbone.Form,editors=Form.editors;editors.List=editors.Base.extend({events:{'click [data-action="add"]':function(event){event.preventDefault(),this.addItem()}},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!schema)throw"Missing required option 'schema'";this.schema=_.extend({listTemplate:"list"},schema),this.Editor=function(){var type=schema.itemType;return type?editors.List[type]?editors.List[type]:editors[type]:editors.Text}(),this.items=[]},render:function(){var self=this,value=this.value||[],$el=$(Form.templates[this.schema.listTemplate]({items:'<b class="bbf-tmp"></b>'}));return this.$list=$el.find(".bbf-tmp").parent().empty(),value.length?_.each(value,function(itemValue){self.addItem(itemValue)}):this.Editor.isAsync||this.addItem(),this.setElement($el),this.$el.attr("id",this.id),this.$el.attr("name",this.key),this.hasFocus&&this.trigger("blur",this),this},addItem:function(value,userInitiated){var self=this,item=(new editors.List.Item({list:this,schema:this.schema,value:value,Editor:this.Editor,key:this.key})).render(),_addItem=function(){self.items.push(item),self.$list.append(item.el),item.editor.on("all",function(event){if(event=="change")return;args=_.toArray(arguments),args[0]="item:"+event,args.splice(1,0,self),editors.List.prototype.trigger.apply(this,args)},self),item.editor.on("change",function(){item.addEventTriggered||(item.addEventTriggered=!0,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=!0;userInitiated&&(self.trigger("add",self,item.editor),self.trigger("change",self))};return this.Editor.isAsync?item.editor.on("readyToAdd",_addItem,this):_addItem(),item},removeItem:function(item){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),item.addEventTriggered&&(this.trigger("remove",this,item.editor),this.trigger("change",this)),!this.items.length&&!this.Editor.isAsync&&this.addItem()},getValue:function(){var values=_.map(this.items,function(item){return item.getValue()});return _.without(values,undefined,"")},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.items[0]&&this.items[0].editor.focus()},blur:function(){if(!this.hasFocus)return;focusedItem=_.find(this.items,function(item){return item.editor.hasFocus}),focusedItem&&focusedItem.editor.blur()},remove:function(){_.invoke(this.items,"remove"),editors.Base.prototype.remove.call(this)},validate:function(){if(!this.validators)return null;var errors=_.map(this.items,function(item){return item.validate()}),hasErrors=_.compact(errors).length?!0:!1;if(!hasErrors)return null;var fieldError={type:"list",message:"Some of the items in the list failed validation",errors:errors};return fieldError}}),editors.List.Item=Backbone.View.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||editors.Text,this.key=options.key},render:function(){this.editor=(new this.Editor({key:this.key,schema:this.schema,value:this.value,list:this.list,item:this})).render();var $el=$(Form.templates.listItem({editor:'<b class="bbf-tmp"></b>'}));return $el.find(".bbf-tmp").replaceWith(this.editor.el),this.setElement($el),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=Form.helpers.getValidator;if(!validators)return null;var error=null;return _.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error?this.setError(error):this.clearError(),error?error:null},setError:function(err){this.$el.addClass(Form.classNames.error),this.$el.attr("title",err.message)},clearError:function(){this.$el.removeClass(Form.classNames.error),this.$el.attr("title",null)}}),editors.List.Modal=editors.List.Object=editors.List.NestedModel=editors.Base.extend({events:{click:"openEditor"},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!editors.List.Modal.ModalAdapter)throw"A ModalAdapter is required";if(schema.itemType=="Object"){if(!schema.subSchema)throw'Missing required option "schema.subSchema"';this.nestedSchema=schema.subSchema}if(schema.itemType=="NestedModel"){if(!schema.model)throw'Missing required option "schema.model"';this.nestedSchema=schema.model.prototype.schema,_.isFunction(this.nestedSchema)&&(this.nestedSchema=this.nestedSchema())}},render:function(){var self=this;return _.isEmpty(this.value)?this.openEditor():(this.renderSummary(),setTimeout(function(){self.trigger("readyToAdd")},0)),this.hasFocus&&this.trigger("blur",this),this},renderSummary:function(){var template=Form.templates["list.Modal"];this.$el.html(template({summary:this.getStringValue()}))},itemToString:function(value){value=value||{};var parts=[];return _.each(this.nestedSchema,function(schema,key){var desc=schema.title?schema.title:Form.helpers.keyToTitle(key),val=value[key];if(_.isUndefined(val)||_.isNull(val))val="";parts.push(desc+": "+val)}),parts.join("<br />")},getStringValue:function(){var schema=this.schema,value=this.getValue();return _.isEmpty(value)?"[Empty]":schema.itemToString?schema.itemToString(value):schema.itemType=="NestedModel"?(new schema.model(value)).toString():this.itemToString(value)},openEditor:function(){var self=this,form=new Form({schema:this.nestedSchema,data:this.value}),modal=this.modal=(new Backbone.BootstrapModal({content:form,animate:!0})).open();this.trigger("open",this),this.trigger("focus",this),modal.on("cancel",function(){this.modal=null,this.trigger("close",this),this.trigger("blur",this)},this),modal.on("ok",_.bind(this.onModalSubmitted,this,form,modal))},onModalSubmitted:function(form,modal){var isNew=!this.value,error=form.validate();if(error)return modal.preventClose();this.modal=null,this.value=form.getValue(),this.renderSummary(),isNew&&this.trigger("readyToAdd"),this.trigger("change",this),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;this.modal&&(this.modal.trigger("cancel"),this.modal.close())}},{ModalAdapter:Backbone.BootstrapModal,isAsync:!0})})()})
+define(["jquery","underscore","backbone"],function($,_,Backbone){(function(){var Form=Backbone.Form,editors=Form.editors;editors.List=editors.Base.extend({events:{'click [data-action="add"]':function(event){event.preventDefault(),this.addItem(null,!0)}},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!schema)throw"Missing required option 'schema'";this.schema=_.extend({listTemplate:"list",listItemTemplate:"listItem"},schema),this.Editor=function(){var type=schema.itemType;return type?editors.List[type]?editors.List[type]:editors[type]:editors.Text}(),this.items=[]},render:function(){var self=this,value=this.value||[],$el=$(Form.templates[this.schema.listTemplate]({items:'<b class="bbf-tmp"></b>'}));return this.$list=$el.find(".bbf-tmp").parent().empty(),value.length?_.each(value,function(itemValue){self.addItem(itemValue)}):this.Editor.isAsync||this.addItem(),this.setElement($el),this.$el.attr("id",this.id),this.$el.attr("name",this.key),this.hasFocus&&this.trigger("blur",this),this},addItem:function(value,userInitiated){var self=this,item=(new editors.List.Item({list:this,schema:this.schema,value:value,Editor:this.Editor,key:this.key})).render(),_addItem=function(){self.items.push(item),self.$list.append(item.el),item.editor.on("all",function(event){if(event=="change")return;args=_.toArray(arguments),args[0]="item:"+event,args.splice(1,0,self),editors.List.prototype.trigger.apply(this,args)},self),item.editor.on("change",function(){item.addEventTriggered||(item.addEventTriggered=!0,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=!0;userInitiated&&(self.trigger("add",self,item.editor),self.trigger("change",self))};return this.Editor.isAsync?item.editor.on("readyToAdd",_addItem,this):_addItem(),item},removeItem:function(item){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),item.addEventTriggered&&(this.trigger("remove",this,item.editor),this.trigger("change",this)),!this.items.length&&!this.Editor.isAsync&&this.addItem()},getValue:function(){var values=_.map(this.items,function(item){return item.getValue()});return _.without(values,undefined,"")},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.items[0]&&this.items[0].editor.focus()},blur:function(){if(!this.hasFocus)return;focusedItem=_.find(this.items,function(item){return item.editor.hasFocus}),focusedItem&&focusedItem.editor.blur()},remove:function(){_.invoke(this.items,"remove"),editors.Base.prototype.remove.call(this)},validate:function(){if(!this.validators)return null;var errors=_.map(this.items,function(item){return item.validate()}),hasErrors=_.compact(errors).length?!0:!1;if(!hasErrors)return null;var fieldError={type:"list",message:"Some of the items in the list failed validation",errors:errors};return fieldError}}),editors.List.Item=Backbone.View.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||editors.Text,this.key=options.key},render:function(){this.editor=(new this.Editor({key:this.key,schema:this.schema,value:this.value,list:this.list,item:this})).render();var $el=$(Form.templates[this.schema.listItemTemplate]({editor:'<b class="bbf-tmp"></b>'}));return $el.find(".bbf-tmp").replaceWith(this.editor.el),this.setElement($el),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=Form.helpers.getValidator;if(!validators)return null;var error=null;return _.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error?this.setError(error):this.clearError(),error?error:null},setError:function(err){this.$el.addClass(Form.classNames.error),this.$el.attr("title",err.message)},clearError:function(){this.$el.removeClass(Form.classNames.error),this.$el.attr("title",null)}}),editors.List.Modal=editors.List.Object=editors.List.NestedModel=editors.Base.extend({events:{click:"openEditor"},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!editors.List.Modal.ModalAdapter)throw"A ModalAdapter is required";if(schema.itemType=="Object"){if(!schema.subSchema)throw'Missing required option "schema.subSchema"';this.nestedSchema=schema.subSchema}if(schema.itemType=="NestedModel"){if(!schema.model)throw'Missing required option "schema.model"';this.nestedSchema=schema.model.prototype.schema,_.isFunction(this.nestedSchema)&&(this.nestedSchema=this.nestedSchema())}},render:function(){var self=this;return _.isEmpty(this.value)?this.openEditor():(this.renderSummary(),setTimeout(function(){self.trigger("readyToAdd")},0)),this.hasFocus&&this.trigger("blur",this),this},renderSummary:function(){var template=Form.templates["list.Modal"];this.$el.html(template({summary:this.getStringValue()}))},itemToString:function(value){value=value||{};var parts=[];return _.each(this.nestedSchema,function(schema,key){var desc=schema.title?schema.title:Form.helpers.keyToTitle(key),val=value[key];if(_.isUndefined(val)||_.isNull(val))val="";parts.push(desc+": "+val)}),parts.join("<br />")},getStringValue:function(){var schema=this.schema,value=this.getValue();return _.isEmpty(value)?"[Empty]":schema.itemToString?schema.itemToString(value):schema.itemType=="NestedModel"?(new schema.model(value)).toString():this.itemToString(value)},openEditor:function(){var self=this,form=new Form({schema:this.nestedSchema,data:this.value}),modal=this.modal=(new Backbone.BootstrapModal({content:form,animate:!0})).open();this.trigger("open",this),this.trigger("focus",this),modal.on("cancel",function(){this.modal=null,this.trigger("close",this),this.trigger("blur",this)},this),modal.on("ok",_.bind(this.onModalSubmitted,this,form,modal))},onModalSubmitted:function(form,modal){var isNew=!this.value,error=form.validate();if(error)return modal.preventClose();this.modal=null,this.value=form.getValue(),this.renderSummary(),isNew&&this.trigger("readyToAdd"),this.trigger("change",this),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;this.modal&&(this.modal.trigger("cancel"),this.modal.close())}},{ModalAdapter:Backbone.BootstrapModal,isAsync:!0})})()})
View
4 distribution/backbone-forms.js
@@ -345,7 +345,9 @@ var Form = (function() {
*/
setValue: function(data) {
for (var key in data) {
- this.fields[key].setValue(data[key]);
+ if (_.has(this.fields, key)) {
+ this.fields[key].setValue(data[key]);
+ }
}
},
View
2  distribution/backbone-forms.min.js
@@ -1 +1 @@
-(function(root){if(typeof exports!="undefined"&&typeof require!="undefined")var $=root.jQuery||root.Zepto||root.ender||require("jquery"),_=root._||require("underscore"),Backbone=root.Backbone||require("backbone");else var $=root.jQuery,_=root._,Backbone=root.Backbone;var Form=function(){return Backbone.View.extend({hasFocus:!1,initialize:function(options){if(!Form.templates.form)throw new Error("Templates not loaded");this.schema=function(){if(options.schema)return options.schema;var model=options.model;if(!model)throw new Error("Could not find schema");return _.isFunction(model.schema)?model.schema():model.schema}(),options=_.extend({template:"form",fieldsetTemplate:"fieldset",fieldTemplate:"field"},options);if(!options.fieldsets){var fields=options.fields||_.keys(this.schema);options.fieldsets=[{fields:fields}]}this.options=options,this.model=options.model,this.data=options.data,this.fields={}},render:function(){var self=this,options=this.options,template=Form.templates[options.template],$form=$(template({fieldsets:'<b class="bbf-tmp"></b>'})),$fieldsetContainer=$(".bbf-tmp",$form);return _.each(options.fieldsets,function(fieldset){$fieldsetContainer.append(self.renderFieldset(fieldset))}),$fieldsetContainer.children().unwrap(),this.setElement($form),this.hasFocus&&this.trigger("blur",this),this},renderFieldset:function(fieldset){var self=this,template=Form.templates[this.options.fieldsetTemplate],schema=this.schema,getNested=Form.helpers.getNested;_.isArray(fieldset)&&(fieldset={fields:fieldset});var $fieldset=$(template(_.extend({},fieldset,{legend:'<b class="bbf-tmp-legend"></b>',fields:'<b class="bbf-tmp-fields"></b>'})));fieldset.legend?$fieldset.find(".bbf-tmp-legend").replaceWith(fieldset.legend):$fieldset.find(".bbf-tmp-legend").parent().remove();var $fieldsContainer=$(".bbf-tmp-fields",$fieldset);return _.each(fieldset.fields,function(key){var itemSchema=function(){if(schema[key])return schema[key];var path=key.replace(/\./g,".subSchema.");return getNested(schema,path)}();if(!itemSchema)throw"Field '"+key+"' not found in schema";var field=self.fields[key]=self.createField(key,itemSchema),fieldEl=field.render().el;field.editor.on("all",function(event){args=_.toArray(arguments),args[0]=key+":"+event,args.splice(1,0,this),this.trigger.apply(this,args)},self),field.editor.on("change",function(){this.trigger("change",self)},self),field.editor.on("focus",function(){if(this.hasFocus)return;this.trigger("focus",this)},self),field.editor.on("blur",function(){if(!this.hasFocus)return;var self=this;setTimeout(function(){if(_.find(self.fields,function(field){return field.editor.hasFocus}))return;self.trigger("blur",self)},0)},self),itemSchema.type!="Hidden"&&$fieldsContainer.append(fieldEl)}),$fieldsContainer=$fieldsContainer.children().unwrap(),$fieldset},createField:function(key,schema){schema.template=schema.template||this.options.fieldTemplate;var options={form:this,key:key,schema:schema,idPrefix:this.options.idPrefix,template:this.options.fieldTemplate};return this.model?options.model=this.model:this.data?options.value=this.data[key]:options.value=null,new Form.Field(options)},validate:function(){var self=this,fields=this.fields,model=this.model,errors={};_.each(fields,function(field){var error=field.validate();error&&(errors[field.key]=error)});if(model&&model.validate){var modelErrors=model.validate(this.getValue());if(modelErrors){var isDictionary=_.isObject(modelErrors)&&!_.isArray(modelErrors);isDictionary||(errors._others=errors._others||[],errors._others.push(modelErrors)),isDictionary&&_.each(modelErrors,function(val,key){if(self.fields[key]&&!errors[key])self.fields[key].setError(val);else{errors._others=errors._others||[];var tmpErr={};tmpErr[key]=val,errors._others.push(tmpErr)}})}}return _.isEmpty(errors)?null:errors},commit:function(){var errors=this.validate();if(errors)return errors;var modelError;this.model.set(this.getValue(),{error:function(model,e){modelError=e}});if(modelError)return modelError},getValue:function(key){if(key)return this.fields[key].getValue();var values={};return _.each(this.fields,function(field){values[field.key]=field.getValue()}),values},setValue:function(data){for(var key in data)this.fields[key].setValue(data[key])},focus:function(){if(this.hasFocus)return;var fieldset=this.options.fieldsets[0];if(fieldset){var field;_.isArray(fieldset)?field=fieldset[0]:field=fieldset.fields[0],field&&this.fields[field].editor.focus()}},blur:function(){if(!this.hasFocus)return;focusedField=_.find(this.fields,function(field){return field.editor.hasFocus}),focusedField&&focusedField.editor.blur()},remove:function(){var fields=this.fields;for(var key in fields)fields[key].remove();Backbone.View.prototype.remove.call(this)},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}})}();Form.helpers=function(){var helpers={};return helpers.getNested=function(obj,path){var fields=path.split("."),result=obj;for(var i=0,n=fields.length;i<n;i++)result=result[fields[i]];return result},helpers.keyToTitle=function(str){return str=str.replace(/([A-Z])/g," $1"),str=str.replace(/^./,function(str){return str.toUpperCase()}),str},helpers.compileTemplate=function(str){var _interpolateBackup=_.templateSettings.interpolate;_.templateSettings.interpolate=/\{\{(.+?)\}\}/g;var template=_.template(str);return _.templateSettings.interpolate=_interpolateBackup,template},helpers.createTemplate=function(str,context){var template=helpers.compileTemplate(str);return context?template(context):template},helpers.setTemplateCompiler=function(compiler){helpers.compileTemplate=compiler},helpers.setTemplates=function(templates,classNames){var createTemplate=helpers.createTemplate;Form.templates=Form.templates||{},Form.classNames=Form.classNames||{},_.each(templates,function(template,key,index){_.isString(template)&&(template=createTemplate(template)),Form.templates[key]=template}),_.extend(Form.classNames,classNames)},helpers.createEditor=function(schemaType,options){var constructorFn;return _.isString(schemaType)?constructorFn=Form.editors[schemaType]:constructorFn=schemaType,new constructorFn(options)},helpers.triggerCancellableEvent=function(subject,event,args,callback){if(!subject._callbacks||!subject._callbacks[event])return callback();var next=subject._callbacks[event].next;if(!next)return callback();var fn=next.callback,context=next.context||this;args.push(callback),fn.apply(context,args)},helpers.getValidator=function(validator){var validators=Form.validators;if(_.isRegExp(validator))return validators.regexp({regexp:validator});if(_.isString(validator)){if(!validators[validator])throw new Error('Validator "'+validator+'" not found');return validators[validator]()}if(_.isFunction(validator))return validator;if(_.isObject(validator)&&validator.type){var config=validator;return validators[config.type](config)}throw new Error("Invalid validator: "+validator)},helpers}(),Form.validators=function(){var validators={};return validators.errMessages={required:"Required",regexp:"Invalid",email:"Invalid email address",url:"Invalid URL",match:'Must match field "{{field}}"'},validators.required=function(options){return options=_.extend({type:"required",message:this.errMessages.required},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return err}},validators.regexp=function(options){if(!options.regexp)throw new Error('Missing required "regexp" option for "regexp" validator');return options=_.extend({type:"regexp",message:this.errMessages.regexp},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(!options.regexp.test(value))return err}},validators.email=function(options){return options=_.extend({type:"email",message:this.errMessages.email,regexp:/^[\w\-]{1,}([\w\-\+.]{1,1}[\w\-]{1,}){0,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/},options),validators.regexp(options)},validators.url=function(options){return 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),validators.regexp(options)},validators.match=function(options){if(!options.field)throw new Error('Missing required "field" options for "match" validator');return options=_.extend({type:"match",message:this.errMessages.match},options),function(value,attrs){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(value!=attrs[options.field])return err}},validators}(),Form.Field=function(){var helpers=Form.helpers,templates=Form.templates;return Backbone.View.extend({initialize:function(options){options=options||{},this.form=options.form,this.key=options.key,this.value=options.value,this.model=options.model,_.isString(options.schema)&&(options.schema={type:options.schema}),this.schema=_.extend({type:"Text",title:helpers.keyToTitle(this.key),template:"field"},options.schema)},render:function(){var schema=this.schema,templates=Form.templates,options={form:this.form,key:this.key,schema:schema,idPrefix:this.options.idPrefix,id:this.getId()};this.model?options.model=this.model:options.value=this.value;var editor=this.editor=helpers.createEditor(schema.type,options),$field=$(templates[schema.template]({key:this.key,title:schema.title,id:editor.id,type:schema.type,editor:'<b class="bbf-tmp-editor"></b>',help:'<b class="bbf-tmp-help"></b>'}));return $field.find(".bbf-tmp-editor").replaceWith(editor.render().el),this.$help=$(".bbf-tmp-help",$field).parent(),this.$help.empty(),this.schema.help&&this.$help.html(this.schema.help),this.schema.fieldClass&&$field.addClass(this.schema.fieldClass),this.schema.fieldAttrs&&$field.attr(this.schema.fieldAttrs),this.setElement($field),this},getId:function(){var prefix=this.options.idPrefix,id=this.key;return id=id.replace(/\./g,"_"),_.isString(prefix)||_.isNumber(prefix)?prefix+id:_.isNull(prefix)?id:this.model?this.model.cid+"_"+id:id},validate:function(){var error=this.editor.validate();return error?this.setError(error.message):this.clearError(),error},setError:function(msg){if(this.editor.hasNestedForm)return;var errClass=Form.classNames.error;this.$el.addClass(errClass),this.$help&&this.$help.html(msg)},clearError:function(){var errClass=Form.classNames.error;this.$el.removeClass(errClass);if(this.$help){this.$help.empty();var helpMsg=this.schema.help;helpMsg&&this.$help.html(helpMsg)}},commit:function(){return this.editor.commit()},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)}})}(),Form.editors=function(){var helpers=Form.helpers,editors={};return editors.Base=Backbone.View.extend({defaultValue:null,hasFocus:!1,initialize:function(options){var options=options||{};if(options.model){if(!options.key)throw"Missing option: 'key'";this.model=options.model,this.value=this.model.get(options.key)}else options.value&&(this.value=options.value);this.value===undefined&&(this.value=this.defaultValue),this.key=options.key,this.form=options.form,this.schema=options.schema||{},this.validators=options.validators||this.schema.validators,this.$el.attr("name",this.getName()),this.schema.editorClass&&this.$el.addClass(this.schema.editorClass),this.schema.editorAttrs&&this.$el.attr(this.schema.editorAttrs)},getValue:function(){throw"Not implemented. Extend and override this method."},setValue:function(){throw"Not implemented. Extend and override this method."},focus:function(){throw"Not implemented. Extend and override this method."},blur:function(){throw"Not implemented. Extend and override this method."},getName:function(){var key=this.key||"";return key.replace(/\./g,"_")},commit:function(){var error=this.validate();if(error)return error;this.model.set(this.key,this.getValue(),{error:function(model,e){error=e}});if(error)return error},validate:function(){var $el=this.$el,error=null,value=this.getValue(),formValues=this.form?this.form.getValue():{},validators=this.validators,getValidator=Form.helpers.getValidator;return validators&&_.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}}),editors.Text=editors.Base.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){editors.Base.prototype.initialize.call(this,options);var schema=this.schema,type="text";schema&&schema.editorAttrs&&schema.editorAttrs.type&&(type=schema.editorAttrs.type),schema&&schema.dataType&&(type=schema.dataType),this.$el.attr("type",type)},render:function(){return this.setValue(this.value),this},determineChange:function(event){var currentValue=this.$el.val(),changed=currentValue!=this.previousValue;changed&&(this.previousValue=currentValue,this.trigger("change",this))},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()},select:function(){this.$el.select()}}),editors.Number=editors.Text.extend({defaultValue:0,events:_.extend({},editors.Text.prototype.events,{keypress:"onKeyPress"}),initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","number")},onKeyPress:function(event){var self=this,delayedDetermineChange=function(){setTimeout(function(){self.determineChange()},0)};if(event.charCode==0){delayedDetermineChange();return}var newVal=this.$el.val()+String.fromCharCode(event.charCode),numeric=/^[0-9]*\.?[0-9]*?$/.test(newVal);numeric?delayedDetermineChange():event.preventDefault()},getValue:function(){var value=this.$el.val();return value===""?null:parseFloat(value,10)},setValue:function(value){value=function(){return _.isNumber(value)?value:_.isString(value)&&value!==""?parseFloat(value,10):null}(),_.isNaN(value)&&(value=null),editors.Text.prototype.setValue.call(this,value)}}),editors.Password=editors.Text.extend({initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","password")}}),editors.TextArea=editors.Text.extend({tagName:"textarea"}),editors.Checkbox=editors.Base.extend({defaultValue:!1,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){editors.Base.prototype.initialize.call(this,options),this.$el.attr("type","checkbox")},render:function(){return this.setValue(this.value),this},getValue:function(){return this.$el.prop("checked")},setValue:function(value){value&&this.$el.prop("checked",!0)},focus:function(){if(this.hasFocus)return;this.$el.focus()},blur:function(){if(!this.hasFocus)return;this.$el.blur()}}),editors.Hidden=editors.Base.extend({defaultValue:"",initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","hidden")},getValue:function(){return this.value},setValue:function(value){this.value=value},focus:function(){},blur:function(){}}),editors.Select=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){editors.Base.prototype.initialize.call(this,options);if(!this.schema||!this.schema.options)throw"Missing required 'schema.options'"},render:function(){return this.setOptions(this.schema.options),this},setOptions:function(options){var self=this;if(options instanceof Backbone.Collection){var collection=options;collection.length>0?this.renderOptions(options):collection.fetch({success:function(collection){self.renderOptions(options)}})}else _.isFunction(options)?options(function(result){self.renderOptions(result)}):this.renderOptions(options)},renderOptions:function(options){var $select=this.$el,html;_.isString(options)?html=options:_.isArray(options)?html=this._arrayToHtml(options):options instanceof Backbone.Collection&&(html=this._collectionToHtml(options)),$select.html(html),this.setValue(this.value)},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()},_collectionToHtml:function(collection){var array=[];collection.each(function(model){array.push({val:model.id,label:model.toString()})});var html=this._arrayToHtml(array);return html},_arrayToHtml:function(array){var html=[];return _.each(array,function(option){if(_.isObject(option)){var val=option.val?option.val:"";html.push('<option value="'+val+'">'+option.label+"</option>")}else html.push("<option>"+option+"</option>")}),html.join("")}}),editors.Radio=editors.Select.extend({tagName:"ul",className:"bbf-radio",events:{"click input[type=radio]:not(:checked)":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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="radio" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="radio" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Checkboxes=editors.Select.extend({tagName:"ul",className:"bbf-checkboxes",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=[];return this.$("input[type=checkbox]:checked").each(function(){values.push($(this).val())}),values},setValue:function(values){_.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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Object=editors.Base.extend({hasNestedForm:!0,className:"bbf-object",initialize:function(options){this.value={},editors.Base.prototype.initialize.call(this,options);if(!this.schema.subSchema)throw new Error("Missing required 'schema.subSchema' option for Object editor")},render:function(){return this.form=new Form({schema:this.schema.subSchema,data:this.value,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){return this.form?this.form.getValue():this.value},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.form.focus()},blur:function(){if(!this.hasFocus)return;this.form.blur()},remove:function(){this.form.remove(),Backbone.View.prototype.remove.call(this)},validate:function(){return this.form.validate()},_observeFormEvents:function(){this.form.on("all",function(){args=_.toArray(arguments),args[1]=this,this.trigger.apply(this,args)},this)}}),editors.NestedModel=editors.Object.extend({initialize:function(options){editors.Base.prototype.initialize.call(this,options);if(!options.schema.model)throw'Missing required "schema.model" option for NestedModel editor'},render:function(){var data=this.value||{},key=this.key,nestedModel=this.schema.model,modelInstance=data.constructor==nestedModel?data:new nestedModel(data);return this.form=new Form({model:modelInstance,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},commit:function(){var error=this.form.commit();return error?(this.$el.addClass("error"),error):editors.Object.prototype.commit.call(this)}}),editors.Date=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||{},editors.Base.prototype.initialize.call(this,options);var Self=editors.Date,today=new Date;this.options=_.extend({monthNames:Self.monthNames,showMonthNames:Self.showMonthNames},options),this.schema=_.extend({yearStart:today.getFullYear()-100,yearEnd:today.getFullYear()},options.schema||{}),this.value&&!_.isDate(this.value)&&(this.value=new Date(this.value));if(!this.value){var date=new Date;date.setSeconds(0),date.setMilliseconds(0),this.value=date}},render:function(){var options=this.options,schema=this.schema,datesOptions=_.map(_.range(1,32),function(date){return'<option value="'+date+'">'+date+"</option>"}),monthsOptions=_.map(_.range(0,12),function(month){var value=options.showMonthNames?options.monthNames[month]:month+1;return'<option value="'+month+'">'+value+"</option>"}),yearsOptions=_.map(_.range(schema.yearStart,schema.yearEnd+1),function(year){return'<option value="'+year+'">'+year+"</option>"}),$el=$(Form.templates.date({dates:datesOptions.join(""),months:monthsOptions.join(""),years:yearsOptions.join("")}));return this.$date=$el.find('select[data-type="date"]'),this.$month=$el.find('select[data-type="month"]'),this.$year=$el.find('select[data-type="year"]'),this.$hidden=$('<input type="hidden" name="'+this.key+'" />'),$el.append(this.$hidden),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var year=this.$year.val(),month=this.$month.val(),date=this.$date.val();return!year||!month||!date?null:new Date(year,month,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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)}},{showMonthNames:!0,monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"]}),editors.DateTime=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||{},editors.Base.prototype.initialize.call(this,options),this.options=_.extend({DateEditor:editors.DateTime.DateEditor},options),this.schema=_.extend({minsInterval:15},options.schema||{}),this.dateEditor=new this.options.DateEditor(options),this.value=this.dateEditor.value},render:function(){function pad(n){return n<10?"0"+n:n}var schema=this.schema,hoursOptions=_.map(_.range(0,24),function(hour){return'<option value="'+hour+'">'+pad(hour)+"</option>"}),minsOptions=_.map(_.range(0,60,schema.minsInterval),function(min){return'<option value="'+min+'">'+pad(min)+"</option>"}),$el=$(Form.templates.dateTime({date:'<b class="bbf-tmp"></b>',hours:hoursOptions.join(),mins:minsOptions.join()}));return $el.find(".bbf-tmp").replaceWith(this.dateEditor.render().el),this.$hour=$el.find('select[data-type="hour"]'),this.$min=$el.find('select[data-type="min"]'),this.$hidden=$el.find('input[type="hidden"]'),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var date=this.dateEditor.getValue(),hour=this.$hour.val(),min=this.$min.val();return!date||!hour||!min?null:(date.setHours(hour),date.setMinutes(min),date)},setValue:function(date){_.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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)},remove:function(){this.dateEditor.remove(),editors.Base.prototype.remove.call(this)}},{DateEditor:editors.Date}),editors}(),Form.setTemplates=Form.helpers.setTemplates,Form.setTemplateCompiler=Form.helpers.setTemplateCompiler,Form.templates={},Form.setTemplates({form:' <form class="bbf-form">{{fieldsets}}</form> ',fieldset:" <fieldset> <legend>{{legend}}</legend> <ul>{{fields}}</ul> </fieldset> ",field:' <li class="bbf-field field-{{key}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',nestedField:' <li class="bbf-field bbf-nested-field field-{{key}}" title="{{title}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',list:' <div class="bbf-list"> <ul>{{items}}</ul> <div class="bbf-actions"><button type="button" data-action="add">Add</div> </div> ',listItem:' <li> <button type="button" data-action="remove" class="bbf-remove">&times;</button> <div class="bbf-editor-container">{{editor}}</div> </li> ',date:' <div class="bbf-date"> <select data-type="date" class="bbf-date">{{dates}}</select> <select data-type="month" class="bbf-month">{{months}}</select> <select data-type="year" class="bbf-year">{{years}}</select> </div> ',dateTime:' <div class="bbf-datetime"> <div class="bbf-date-container">{{date}}</div> <select data-type="hour">{{hours}}</select> : <select data-type="min">{{mins}}</select> </div> ',"list.Modal":' <div class="bbf-list-modal"> {{summary}} </div> '},{error:"bbf-error"}),Form.VERSION="0.10.1",Backbone.Form=Form})(this)
+(function(root){if(typeof exports!="undefined"&&typeof require!="undefined")var $=root.jQuery||root.Zepto||root.ender||require("jquery"),_=root._||require("underscore"),Backbone=root.Backbone||require("backbone");else var $=root.jQuery,_=root._,Backbone=root.Backbone;var Form=function(){return Backbone.View.extend({hasFocus:!1,initialize:function(options){if(!Form.templates.form)throw new Error("Templates not loaded");this.schema=function(){if(options.schema)return options.schema;var model=options.model;if(!model)throw new Error("Could not find schema");return _.isFunction(model.schema)?model.schema():model.schema}(),options=_.extend({template:"form",fieldsetTemplate:"fieldset",fieldTemplate:"field"},options);if(!options.fieldsets){var fields=options.fields||_.keys(this.schema);options.fieldsets=[{fields:fields}]}this.options=options,this.model=options.model,this.data=options.data,this.fields={}},render:function(){var self=this,options=this.options,template=Form.templates[options.template],$form=$(template({fieldsets:'<b class="bbf-tmp"></b>'})),$fieldsetContainer=$(".bbf-tmp",$form);return _.each(options.fieldsets,function(fieldset){$fieldsetContainer.append(self.renderFieldset(fieldset))}),$fieldsetContainer.children().unwrap(),this.setElement($form),this.hasFocus&&this.trigger("blur",this),this},renderFieldset:function(fieldset){var self=this,template=Form.templates[this.options.fieldsetTemplate],schema=this.schema,getNested=Form.helpers.getNested;_.isArray(fieldset)&&(fieldset={fields:fieldset});var $fieldset=$(template(_.extend({},fieldset,{legend:'<b class="bbf-tmp-legend"></b>',fields:'<b class="bbf-tmp-fields"></b>'})));fieldset.legend?$fieldset.find(".bbf-tmp-legend").replaceWith(fieldset.legend):$fieldset.find(".bbf-tmp-legend").parent().remove();var $fieldsContainer=$(".bbf-tmp-fields",$fieldset);return _.each(fieldset.fields,function(key){var itemSchema=function(){if(schema[key])return schema[key];var path=key.replace(/\./g,".subSchema.");return getNested(schema,path)}();if(!itemSchema)throw"Field '"+key+"' not found in schema";var field=self.fields[key]=self.createField(key,itemSchema),fieldEl=field.render().el;field.editor.on("all",function(event){args=_.toArray(arguments),args[0]=key+":"+event,args.splice(1,0,this),this.trigger.apply(this,args)},self),field.editor.on("change",function(){this.trigger("change",self)},self),field.editor.on("focus",function(){if(this.hasFocus)return;this.trigger("focus",this)},self),field.editor.on("blur",function(){if(!this.hasFocus)return;var self=this;setTimeout(function(){if(_.find(self.fields,function(field){return field.editor.hasFocus}))return;self.trigger("blur",self)},0)},self),itemSchema.type!="Hidden"&&$fieldsContainer.append(fieldEl)}),$fieldsContainer=$fieldsContainer.children().unwrap(),$fieldset},createField:function(key,schema){schema.template=schema.template||this.options.fieldTemplate;var options={form:this,key:key,schema:schema,idPrefix:this.options.idPrefix,template:this.options.fieldTemplate};return this.model?options.model=this.model:this.data?options.value=this.data[key]:options.value=null,new Form.Field(options)},validate:function(){var self=this,fields=this.fields,model=this.model,errors={};_.each(fields,function(field){var error=field.validate();error&&(errors[field.key]=error)});if(model&&model.validate){var modelErrors=model.validate(this.getValue());if(modelErrors){var isDictionary=_.isObject(modelErrors)&&!_.isArray(modelErrors);isDictionary||(errors._others=errors._others||[],errors._others.push(modelErrors)),isDictionary&&_.each(modelErrors,function(val,key){if(self.fields[key]&&!errors[key])self.fields[key].setError(val);else{errors._others=errors._others||[];var tmpErr={};tmpErr[key]=val,errors._others.push(tmpErr)}})}}return _.isEmpty(errors)?null:errors},commit:function(){var errors=this.validate();if(errors)return errors;var modelError;this.model.set(this.getValue(),{error:function(model,e){modelError=e}});if(modelError)return modelError},getValue:function(key){if(key)return this.fields[key].getValue();var values={};return _.each(this.fields,function(field){values[field.key]=field.getValue()}),values},setValue:function(data){for(var key in data)_.has(this.fields,key)&&this.fields[key].setValue(data[key])},focus:function(){if(this.hasFocus)return;var fieldset=this.options.fieldsets[0];if(fieldset){var field;_.isArray(fieldset)?field=fieldset[0]:field=fieldset.fields[0],field&&this.fields[field].editor.focus()}},blur:function(){if(!this.hasFocus)return;focusedField=_.find(this.fields,function(field){return field.editor.hasFocus}),focusedField&&focusedField.editor.blur()},remove:function(){var fields=this.fields;for(var key in fields)fields[key].remove();Backbone.View.prototype.remove.call(this)},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}})}();Form.helpers=function(){var helpers={};return helpers.getNested=function(obj,path){var fields=path.split("."),result=obj;for(var i=0,n=fields.length;i<n;i++)result=result[fields[i]];return result},helpers.keyToTitle=function(str){return str=str.replace(/([A-Z])/g," $1"),str=str.replace(/^./,function(str){return str.toUpperCase()}),str},helpers.compileTemplate=function(str){var _interpolateBackup=_.templateSettings.interpolate;_.templateSettings.interpolate=/\{\{(.+?)\}\}/g;var template=_.template(str);return _.templateSettings.interpolate=_interpolateBackup,template},helpers.createTemplate=function(str,context){var template=helpers.compileTemplate(str);return context?template(context):template},helpers.setTemplateCompiler=function(compiler){helpers.compileTemplate=compiler},helpers.setTemplates=function(templates,classNames){var createTemplate=helpers.createTemplate;Form.templates=Form.templates||{},Form.classNames=Form.classNames||{},_.each(templates,function(template,key,index){_.isString(template)&&(template=createTemplate(template)),Form.templates[key]=template}),_.extend(Form.classNames,classNames)},helpers.createEditor=function(schemaType,options){var constructorFn;return _.isString(schemaType)?constructorFn=Form.editors[schemaType]:constructorFn=schemaType,new constructorFn(options)},helpers.triggerCancellableEvent=function(subject,event,args,callback){if(!subject._callbacks||!subject._callbacks[event])return callback();var next=subject._callbacks[event].next;if(!next)return callback();var fn=next.callback,context=next.context||this;args.push(callback),fn.apply(context,args)},helpers.getValidator=function(validator){var validators=Form.validators;if(_.isRegExp(validator))return validators.regexp({regexp:validator});if(_.isString(validator)){if(!validators[validator])throw new Error('Validator "'+validator+'" not found');return validators[validator]()}if(_.isFunction(validator))return validator;if(_.isObject(validator)&&validator.type){var config=validator;return validators[config.type](config)}throw new Error("Invalid validator: "+validator)},helpers}(),Form.validators=function(){var validators={};return validators.errMessages={required:"Required",regexp:"Invalid",email:"Invalid email address",url:"Invalid URL",match:'Must match field "{{field}}"'},validators.required=function(options){return options=_.extend({type:"required",message:this.errMessages.required},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return err}},validators.regexp=function(options){if(!options.regexp)throw new Error('Missing required "regexp" option for "regexp" validator');return options=_.extend({type:"regexp",message:this.errMessages.regexp},options),function(value){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(!options.regexp.test(value))return err}},validators.email=function(options){return options=_.extend({type:"email",message:this.errMessages.email,regexp:/^[\w\-]{1,}([\w\-\+.]{1,1}[\w\-]{1,}){0,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/},options),validators.regexp(options)},validators.url=function(options){return 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),validators.regexp(options)},validators.match=function(options){if(!options.field)throw new Error('Missing required "field" options for "match" validator');return options=_.extend({type:"match",message:this.errMessages.match},options),function(value,attrs){options.value=value;var err={type:options.type,message:Form.helpers.createTemplate(options.message,options)};if(value===null||value===undefined||value==="")return;if(value!=attrs[options.field])return err}},validators}(),Form.Field=function(){var helpers=Form.helpers,templates=Form.templates;return Backbone.View.extend({initialize:function(options){options=options||{},this.form=options.form,this.key=options.key,this.value=options.value,this.model=options.model,_.isString(options.schema)&&(options.schema={type:options.schema}),this.schema=_.extend({type:"Text",title:helpers.keyToTitle(this.key),template:"field"},options.schema)},render:function(){var schema=this.schema,templates=Form.templates,options={form:this.form,key:this.key,schema:schema,idPrefix:this.options.idPrefix,id:this.getId()};this.model?options.model=this.model:options.value=this.value;var editor=this.editor=helpers.createEditor(schema.type,options),$field=$(templates[schema.template]({key:this.key,title:schema.title,id:editor.id,type:schema.type,editor:'<b class="bbf-tmp-editor"></b>',help:'<b class="bbf-tmp-help"></b>'}));return $field.find(".bbf-tmp-editor").replaceWith(editor.render().el),this.$help=$(".bbf-tmp-help",$field).parent(),this.$help.empty(),this.schema.help&&this.$help.html(this.schema.help),this.schema.fieldClass&&$field.addClass(this.schema.fieldClass),this.schema.fieldAttrs&&$field.attr(this.schema.fieldAttrs),this.setElement($field),this},getId:function(){var prefix=this.options.idPrefix,id=this.key;return id=id.replace(/\./g,"_"),_.isString(prefix)||_.isNumber(prefix)?prefix+id:_.isNull(prefix)?id:this.model?this.model.cid+"_"+id:id},validate:function(){var error=this.editor.validate();return error?this.setError(error.message):this.clearError(),error},setError:function(msg){if(this.editor.hasNestedForm)return;var errClass=Form.classNames.error;this.$el.addClass(errClass),this.$help&&this.$help.html(msg)},clearError:function(){var errClass=Form.classNames.error;this.$el.removeClass(errClass);if(this.$help){this.$help.empty();var helpMsg=this.schema.help;helpMsg&&this.$help.html(helpMsg)}},commit:function(){return this.editor.commit()},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)}})}(),Form.editors=function(){var helpers=Form.helpers,editors={};return editors.Base=Backbone.View.extend({defaultValue:null,hasFocus:!1,initialize:function(options){var options=options||{};if(options.model){if(!options.key)throw"Missing option: 'key'";this.model=options.model,this.value=this.model.get(options.key)}else options.value&&(this.value=options.value);this.value===undefined&&(this.value=this.defaultValue),this.key=options.key,this.form=options.form,this.schema=options.schema||{},this.validators=options.validators||this.schema.validators,this.$el.attr("name",this.getName()),this.schema.editorClass&&this.$el.addClass(this.schema.editorClass),this.schema.editorAttrs&&this.$el.attr(this.schema.editorAttrs)},getValue:function(){throw"Not implemented. Extend and override this method."},setValue:function(){throw"Not implemented. Extend and override this method."},focus:function(){throw"Not implemented. Extend and override this method."},blur:function(){throw"Not implemented. Extend and override this method."},getName:function(){var key=this.key||"";return key.replace(/\./g,"_")},commit:function(){var error=this.validate();if(error)return error;this.model.set(this.key,this.getValue(),{error:function(model,e){error=e}});if(error)return error},validate:function(){var $el=this.$el,error=null,value=this.getValue(),formValues=this.form?this.form.getValue():{},validators=this.validators,getValidator=Form.helpers.getValidator;return validators&&_.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error},trigger:function(event){return event=="focus"?this.hasFocus=!0:event=="blur"&&(this.hasFocus=!1),Backbone.View.prototype.trigger.apply(this,arguments)}}),editors.Text=editors.Base.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){editors.Base.prototype.initialize.call(this,options);var schema=this.schema,type="text";schema&&schema.editorAttrs&&schema.editorAttrs.type&&(type=schema.editorAttrs.type),schema&&schema.dataType&&(type=schema.dataType),this.$el.attr("type",type)},render:function(){return this.setValue(this.value),this},determineChange:function(event){var currentValue=this.$el.val(),changed=currentValue!=this.previousValue;changed&&(this.previousValue=currentValue,this.trigger("change",this))},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()},select:function(){this.$el.select()}}),editors.Number=editors.Text.extend({defaultValue:0,events:_.extend({},editors.Text.prototype.events,{keypress:"onKeyPress"}),initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","number")},onKeyPress:function(event){var self=this,delayedDetermineChange=function(){setTimeout(function(){self.determineChange()},0)};if(event.charCode==0){delayedDetermineChange();return}var newVal=this.$el.val()+String.fromCharCode(event.charCode),numeric=/^[0-9]*\.?[0-9]*?$/.test(newVal);numeric?delayedDetermineChange():event.preventDefault()},getValue:function(){var value=this.$el.val();return value===""?null:parseFloat(value,10)},setValue:function(value){value=function(){return _.isNumber(value)?value:_.isString(value)&&value!==""?parseFloat(value,10):null}(),_.isNaN(value)&&(value=null),editors.Text.prototype.setValue.call(this,value)}}),editors.Password=editors.Text.extend({initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","password")}}),editors.TextArea=editors.Text.extend({tagName:"textarea"}),editors.Checkbox=editors.Base.extend({defaultValue:!1,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){editors.Base.prototype.initialize.call(this,options),this.$el.attr("type","checkbox")},render:function(){return this.setValue(this.value),this},getValue:function(){return this.$el.prop("checked")},setValue:function(value){value&&this.$el.prop("checked",!0)},focus:function(){if(this.hasFocus)return;this.$el.focus()},blur:function(){if(!this.hasFocus)return;this.$el.blur()}}),editors.Hidden=editors.Base.extend({defaultValue:"",initialize:function(options){editors.Text.prototype.initialize.call(this,options),this.$el.attr("type","hidden")},getValue:function(){return this.value},setValue:function(value){this.value=value},focus:function(){},blur:function(){}}),editors.Select=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){editors.Base.prototype.initialize.call(this,options);if(!this.schema||!this.schema.options)throw"Missing required 'schema.options'"},render:function(){return this.setOptions(this.schema.options),this},setOptions:function(options){var self=this;if(options instanceof Backbone.Collection){var collection=options;collection.length>0?this.renderOptions(options):collection.fetch({success:function(collection){self.renderOptions(options)}})}else _.isFunction(options)?options(function(result){self.renderOptions(result)}):this.renderOptions(options)},renderOptions:function(options){var $select=this.$el,html;_.isString(options)?html=options:_.isArray(options)?html=this._arrayToHtml(options):options instanceof Backbone.Collection&&(html=this._collectionToHtml(options)),$select.html(html),this.setValue(this.value)},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()},_collectionToHtml:function(collection){var array=[];collection.each(function(model){array.push({val:model.id,label:model.toString()})});var html=this._arrayToHtml(array);return html},_arrayToHtml:function(array){var html=[];return _.each(array,function(option){if(_.isObject(option)){var val=option.val?option.val:"";html.push('<option value="'+val+'">'+option.label+"</option>")}else html.push("<option>"+option+"</option>")}),html.join("")}}),editors.Radio=editors.Select.extend({tagName:"ul",className:"bbf-radio",events:{"click input[type=radio]:not(:checked)":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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="radio" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="radio" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Checkboxes=editors.Select.extend({tagName:"ul",className:"bbf-checkboxes",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=[];return this.$("input[type=checkbox]:checked").each(function(){values.push($(this).val())}),values},setValue:function(values){_.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()},_arrayToHtml:function(array){var html=[],self=this;return _.each(array,function(option,index){var itemHtml="<li>";if(_.isObject(option)){var val=option.val?option.val:"";itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+val+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option.label+"</label>"}else itemHtml+='<input type="checkbox" name="'+self.id+'" value="'+option+'" id="'+self.id+"-"+index+'" />',itemHtml+='<label for="'+self.id+"-"+index+'">'+option+"</label>";itemHtml+="</li>",html.push(itemHtml)}),html.join("")}}),editors.Object=editors.Base.extend({hasNestedForm:!0,className:"bbf-object",initialize:function(options){this.value={},editors.Base.prototype.initialize.call(this,options);if(!this.schema.subSchema)throw new Error("Missing required 'schema.subSchema' option for Object editor")},render:function(){return this.form=new Form({schema:this.schema.subSchema,data:this.value,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){return this.form?this.form.getValue():this.value},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.form.focus()},blur:function(){if(!this.hasFocus)return;this.form.blur()},remove:function(){this.form.remove(),Backbone.View.prototype.remove.call(this)},validate:function(){return this.form.validate()},_observeFormEvents:function(){this.form.on("all",function(){args=_.toArray(arguments),args[1]=this,this.trigger.apply(this,args)},this)}}),editors.NestedModel=editors.Object.extend({initialize:function(options){editors.Base.prototype.initialize.call(this,options);if(!options.schema.model)throw'Missing required "schema.model" option for NestedModel editor'},render:function(){var data=this.value||{},key=this.key,nestedModel=this.schema.model,modelInstance=data.constructor==nestedModel?data:new nestedModel(data);return this.form=new Form({model:modelInstance,idPrefix:this.id+"_",fieldTemplate:"nestedField"}),this._observeFormEvents(),this.$el.html(this.form.render().el),this.hasFocus&&this.trigger("blur",this),this},commit:function(){var error=this.form.commit();return error?(this.$el.addClass("error"),error):editors.Object.prototype.commit.call(this)}}),editors.Date=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||{},editors.Base.prototype.initialize.call(this,options);var Self=editors.Date,today=new Date;this.options=_.extend({monthNames:Self.monthNames,showMonthNames:Self.showMonthNames},options),this.schema=_.extend({yearStart:today.getFullYear()-100,yearEnd:today.getFullYear()},options.schema||{}),this.value&&!_.isDate(this.value)&&(this.value=new Date(this.value));if(!this.value){var date=new Date;date.setSeconds(0),date.setMilliseconds(0),this.value=date}},render:function(){var options=this.options,schema=this.schema,datesOptions=_.map(_.range(1,32),function(date){return'<option value="'+date+'">'+date+"</option>"}),monthsOptions=_.map(_.range(0,12),function(month){var value=options.showMonthNames?options.monthNames[month]:month+1;return'<option value="'+month+'">'+value+"</option>"}),yearsOptions=_.map(_.range(schema.yearStart,schema.yearEnd+1),function(year){return'<option value="'+year+'">'+year+"</option>"}),$el=$(Form.templates.date({dates:datesOptions.join(""),months:monthsOptions.join(""),years:yearsOptions.join("")}));return this.$date=$el.find('select[data-type="date"]'),this.$month=$el.find('select[data-type="month"]'),this.$year=$el.find('select[data-type="year"]'),this.$hidden=$('<input type="hidden" name="'+this.key+'" />'),$el.append(this.$hidden),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var year=this.$year.val(),month=this.$month.val(),date=this.$date.val();return!year||!month||!date?null:new Date(year,month,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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)}},{showMonthNames:!0,monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"]}),editors.DateTime=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||{},editors.Base.prototype.initialize.call(this,options),this.options=_.extend({DateEditor:editors.DateTime.DateEditor},options),this.schema=_.extend({minsInterval:15},options.schema||{}),this.dateEditor=new this.options.DateEditor(options),this.value=this.dateEditor.value},render:function(){function pad(n){return n<10?"0"+n:n}var schema=this.schema,hoursOptions=_.map(_.range(0,24),function(hour){return'<option value="'+hour+'">'+pad(hour)+"</option>"}),minsOptions=_.map(_.range(0,60,schema.minsInterval),function(min){return'<option value="'+min+'">'+pad(min)+"</option>"}),$el=$(Form.templates.dateTime({date:'<b class="bbf-tmp"></b>',hours:hoursOptions.join(),mins:minsOptions.join()}));return $el.find(".bbf-tmp").replaceWith(this.dateEditor.render().el),this.$hour=$el.find('select[data-type="hour"]'),this.$min=$el.find('select[data-type="min"]'),this.$hidden=$el.find('input[type="hidden"]'),this.setValue(this.value),this.setElement($el),this.$el.attr("id",this.id),this.hasFocus&&this.trigger("blur",this),this},getValue:function(){var date=this.dateEditor.getValue(),hour=this.$hour.val(),min=this.$min.val();return!date||!hour||!min?null:(date.setHours(hour),date.setMinutes(min),date)},setValue:function(date){_.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()},updateHidden:function(){var val=this.getValue();_.isDate(val)&&(val=val.toISOString()),this.$hidden.val(val)},remove:function(){this.dateEditor.remove(),editors.Base.prototype.remove.call(this)}},{DateEditor:editors.Date}),editors}(),Form.setTemplates=Form.helpers.setTemplates,Form.setTemplateCompiler=Form.helpers.setTemplateCompiler,Form.templates={},Form.setTemplates({form:' <form class="bbf-form">{{fieldsets}}</form> ',fieldset:" <fieldset> <legend>{{legend}}</legend> <ul>{{fields}}</ul> </fieldset> ",field:' <li class="bbf-field field-{{key}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',nestedField:' <li class="bbf-field bbf-nested-field field-{{key}}" title="{{title}}"> <label for="{{id}}">{{title}}</label> <div class="bbf-editor">{{editor}}</div> <div class="bbf-help">{{help}}</div> </li> ',list:' <div class="bbf-list"> <ul>{{items}}</ul> <div class="bbf-actions"><button type="button" data-action="add">Add</div> </div> ',listItem:' <li> <button type="button" data-action="remove" class="bbf-remove">&times;</button> <div class="bbf-editor-container">{{editor}}</div> </li> ',date:' <div class="bbf-date"> <select data-type="date" class="bbf-date">{{dates}}</select> <select data-type="month" class="bbf-month">{{months}}</select> <select data-type="year" class="bbf-year">{{years}}</select> </div> ',dateTime:' <div class="bbf-datetime"> <div class="bbf-date-container">{{date}}</div> <select data-type="hour">{{hours}}</select> : <select data-type="min">{{mins}}</select> </div> ',"list.Modal":' <div class="bbf-list-modal"> {{summary}} </div> '},{error:"bbf-error"}),Form.VERSION="0.10.1",Backbone.Form=Form})(this)
View
14 distribution/editors/list.js
@@ -17,7 +17,7 @@
events: {
'click [data-action="add"]': function(event) {
event.preventDefault();
- this.addItem();
+ this.addItem(null, true);
}
},
@@ -29,7 +29,8 @@
//List schema defaults
this.schema = _.extend({
- listTemplate: 'list'
+ listTemplate: 'list',
+ listItemTemplate: 'listItem'
}, schema);
//Determine the editor to use
@@ -84,7 +85,8 @@
/**
* Add a new item to the list
- * @param {Mixed} [value] Value for the new item editor
+ * @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;
@@ -111,7 +113,7 @@
args.splice(1, 0, self);
// args = ["item:key:change", this=listEditor, itemEditor, fieldEditor]
- editors.List.prototype.trigger.apply(this, args)
+ editors.List.prototype.trigger.apply(this, args);
}, self);
item.editor.on('change', function() {
@@ -144,7 +146,7 @@
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) {
@@ -289,7 +291,7 @@
}).render();
//Create main element
- var $el = $(Form.templates.listItem({
+ var $el = $(Form.templates[this.schema.listItemTemplate]({
editor: '<b class="bbf-tmp"></b>'
}));
View
2  distribution/editors/list.min.js
@@ -1 +1 @@
-(function(){var Form=Backbone.Form,editors=Form.editors;editors.List=editors.Base.extend({events:{'click [data-action="add"]':function(event){event.preventDefault(),this.addItem()}},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!schema)throw"Missing required option 'schema'";this.schema=_.extend({listTemplate:"list"},schema),this.Editor=function(){var type=schema.itemType;return type?editors.List[type]?editors.List[type]:editors[type]:editors.Text}(),this.items=[]},render:function(){var self=this,value=this.value||[],$el=$(Form.templates[this.schema.listTemplate]({items:'<b class="bbf-tmp"></b>'}));return this.$list=$el.find(".bbf-tmp").parent().empty(),value.length?_.each(value,function(itemValue){self.addItem(itemValue)}):this.Editor.isAsync||this.addItem(),this.setElement($el),this.$el.attr("id",this.id),this.$el.attr("name",this.key),this.hasFocus&&this.trigger("blur",this),this},addItem:function(value,userInitiated){var self=this,item=(new editors.List.Item({list:this,schema:this.schema,value:value,Editor:this.Editor,key:this.key})).render(),_addItem=function(){self.items.push(item),self.$list.append(item.el),item.editor.on("all",function(event){if(event=="change")return;args=_.toArray(arguments),args[0]="item:"+event,args.splice(1,0,self),editors.List.prototype.trigger.apply(this,args)},self),item.editor.on("change",function(){item.addEventTriggered||(item.addEventTriggered=!0,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=!0;userInitiated&&(self.trigger("add",self,item.editor),self.trigger("change",self))};return this.Editor.isAsync?item.editor.on("readyToAdd",_addItem,this):_addItem(),item},removeItem:function(item){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),item.addEventTriggered&&(this.trigger("remove",this,item.editor),this.trigger("change",this)),!this.items.length&&!this.Editor.isAsync&&this.addItem()},getValue:function(){var values=_.map(this.items,function(item){return item.getValue()});return _.without(values,undefined,"")},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.items[0]&&this.items[0].editor.focus()},blur:function(){if(!this.hasFocus)return;focusedItem=_.find(this.items,function(item){return item.editor.hasFocus}),focusedItem&&focusedItem.editor.blur()},remove:function(){_.invoke(this.items,"remove"),editors.Base.prototype.remove.call(this)},validate:function(){if(!this.validators)return null;var errors=_.map(this.items,function(item){return item.validate()}),hasErrors=_.compact(errors).length?!0:!1;if(!hasErrors)return null;var fieldError={type:"list",message:"Some of the items in the list failed validation",errors:errors};return fieldError}}),editors.List.Item=Backbone.View.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||editors.Text,this.key=options.key},render:function(){this.editor=(new this.Editor({key:this.key,schema:this.schema,value:this.value,list:this.list,item:this})).render();var $el=$(Form.templates.listItem({editor:'<b class="bbf-tmp"></b>'}));return $el.find(".bbf-tmp").replaceWith(this.editor.el),this.setElement($el),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=Form.helpers.getValidator;if(!validators)return null;var error=null;return _.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error?this.setError(error):this.clearError(),error?error:null},setError:function(err){this.$el.addClass(Form.classNames.error),this.$el.attr("title",err.message)},clearError:function(){this.$el.removeClass(Form.classNames.error),this.$el.attr("title",null)}}),editors.List.Modal=editors.List.Object=editors.List.NestedModel=editors.Base.extend({events:{click:"openEditor"},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!editors.List.Modal.ModalAdapter)throw"A ModalAdapter is required";if(schema.itemType=="Object"){if(!schema.subSchema)throw'Missing required option "schema.subSchema"';this.nestedSchema=schema.subSchema}if(schema.itemType=="NestedModel"){if(!schema.model)throw'Missing required option "schema.model"';this.nestedSchema=schema.model.prototype.schema,_.isFunction(this.nestedSchema)&&(this.nestedSchema=this.nestedSchema())}},render:function(){var self=this;return _.isEmpty(this.value)?this.openEditor():(this.renderSummary(),setTimeout(function(){self.trigger("readyToAdd")},0)),this.hasFocus&&this.trigger("blur",this),this},renderSummary:function(){var template=Form.templates["list.Modal"];this.$el.html(template({summary:this.getStringValue()}))},itemToString:function(value){value=value||{};var parts=[];return _.each(this.nestedSchema,function(schema,key){var desc=schema.title?schema.title:Form.helpers.keyToTitle(key),val=value[key];if(_.isUndefined(val)||_.isNull(val))val="";parts.push(desc+": "+val)}),parts.join("<br />")},getStringValue:function(){var schema=this.schema,value=this.getValue();return _.isEmpty(value)?"[Empty]":schema.itemToString?schema.itemToString(value):schema.itemType=="NestedModel"?(new schema.model(value)).toString():this.itemToString(value)},openEditor:function(){var self=this,form=new Form({schema:this.nestedSchema,data:this.value}),modal=this.modal=(new Backbone.BootstrapModal({content:form,animate:!0})).open();this.trigger("open",this),this.trigger("focus",this),modal.on("cancel",function(){this.modal=null,this.trigger("close",this),this.trigger("blur",this)},this),modal.on("ok",_.bind(this.onModalSubmitted,this,form,modal))},onModalSubmitted:function(form,modal){var isNew=!this.value,error=form.validate();if(error)return modal.preventClose();this.modal=null,this.value=form.getValue(),this.renderSummary(),isNew&&this.trigger("readyToAdd"),this.trigger("change",this),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;this.modal&&(this.modal.trigger("cancel"),this.modal.close())}},{ModalAdapter:Backbone.BootstrapModal,isAsync:!0})})()
+(function(){var Form=Backbone.Form,editors=Form.editors;editors.List=editors.Base.extend({events:{'click [data-action="add"]':function(event){event.preventDefault(),this.addItem(null,!0)}},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!schema)throw"Missing required option 'schema'";this.schema=_.extend({listTemplate:"list",listItemTemplate:"listItem"},schema),this.Editor=function(){var type=schema.itemType;return type?editors.List[type]?editors.List[type]:editors[type]:editors.Text}(),this.items=[]},render:function(){var self=this,value=this.value||[],$el=$(Form.templates[this.schema.listTemplate]({items:'<b class="bbf-tmp"></b>'}));return this.$list=$el.find(".bbf-tmp").parent().empty(),value.length?_.each(value,function(itemValue){self.addItem(itemValue)}):this.Editor.isAsync||this.addItem(),this.setElement($el),this.$el.attr("id",this.id),this.$el.attr("name",this.key),this.hasFocus&&this.trigger("blur",this),this},addItem:function(value,userInitiated){var self=this,item=(new editors.List.Item({list:this,schema:this.schema,value:value,Editor:this.Editor,key:this.key})).render(),_addItem=function(){self.items.push(item),self.$list.append(item.el),item.editor.on("all",function(event){if(event=="change")return;args=_.toArray(arguments),args[0]="item:"+event,args.splice(1,0,self),editors.List.prototype.trigger.apply(this,args)},self),item.editor.on("change",function(){item.addEventTriggered||(item.addEventTriggered=!0,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=!0;userInitiated&&(self.trigger("add",self,item.editor),self.trigger("change",self))};return this.Editor.isAsync?item.editor.on("readyToAdd",_addItem,this):_addItem(),item},removeItem:function(item){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),item.addEventTriggered&&(this.trigger("remove",this,item.editor),this.trigger("change",this)),!this.items.length&&!this.Editor.isAsync&&this.addItem()},getValue:function(){var values=_.map(this.items,function(item){return item.getValue()});return _.without(values,undefined,"")},setValue:function(value){this.value=value,this.render()},focus:function(){if(this.hasFocus)return;this.items[0]&&this.items[0].editor.focus()},blur:function(){if(!this.hasFocus)return;focusedItem=_.find(this.items,function(item){return item.editor.hasFocus}),focusedItem&&focusedItem.editor.blur()},remove:function(){_.invoke(this.items,"remove"),editors.Base.prototype.remove.call(this)},validate:function(){if(!this.validators)return null;var errors=_.map(this.items,function(item){return item.validate()}),hasErrors=_.compact(errors).length?!0:!1;if(!hasErrors)return null;var fieldError={type:"list",message:"Some of the items in the list failed validation",errors:errors};return fieldError}}),editors.List.Item=Backbone.View.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||editors.Text,this.key=options.key},render:function(){this.editor=(new this.Editor({key:this.key,schema:this.schema,value:this.value,list:this.list,item:this})).render();var $el=$(Form.templates[this.schema.listItemTemplate]({editor:'<b class="bbf-tmp"></b>'}));return $el.find(".bbf-tmp").replaceWith(this.editor.el),this.setElement($el),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=Form.helpers.getValidator;if(!validators)return null;var error=null;return _.every(validators,function(validator){return error=getValidator(validator)(value,formValues),continueLoop=error?!1:!0}),error?this.setError(error):this.clearError(),error?error:null},setError:function(err){this.$el.addClass(Form.classNames.error),this.$el.attr("title",err.message)},clearError:function(){this.$el.removeClass(Form.classNames.error),this.$el.attr("title",null)}}),editors.List.Modal=editors.List.Object=editors.List.NestedModel=editors.Base.extend({events:{click:"openEditor"},initialize:function(options){editors.Base.prototype.initialize.call(this,options);var schema=this.schema;if(!editors.List.Modal.ModalAdapter)throw"A ModalAdapter is required";if(schema.itemType=="Object"){if(!schema.subSchema)throw'Missing required option "schema.subSchema"';this.nestedSchema=schema.subSchema}if(schema.itemType=="NestedModel"){if(!schema.model)throw'Missing required option "schema.model"';this.nestedSchema=schema.model.prototype.schema,_.isFunction(this.nestedSchema)&&(this.nestedSchema=this.nestedSchema())}},render:function(){var self=this;return _.isEmpty(this.value)?this.openEditor():(this.renderSummary(),setTimeout(function(){self.trigger("readyToAdd")},0)),this.hasFocus&&this.trigger("blur",this),this},renderSummary:function(){var template=Form.templates["list.Modal"];this.$el.html(template({summary:this.getStringValue()}))},itemToString:function(value){value=value||{};var parts=[];return _.each(this.nestedSchema,function(schema,key){var desc=schema.title?schema.title:Form.helpers.keyToTitle(key),val=value[key];if(_.isUndefined(val)||_.isNull(val))val="";parts.push(desc+": "+val)}),parts.join("<br />")},getStringValue:function(){var schema=this.schema,value=this.getValue();return _.isEmpty(value)?"[Empty]":schema.itemToString?schema.itemToString(value):schema.itemType=="NestedModel"?(new schema.model(value)).toString():this.itemToString(value)},openEditor:function(){var self=this,form=new Form({schema:this.nestedSchema,data:this.value}),modal=this.modal=(new Backbone.BootstrapModal({content:form,animate:!0})).open();this.trigger("open",this),this.trigger("focus",this),modal.on("cancel",function(){this.modal=null,this.trigger("close",this),this.trigger("blur",this)},this),modal.on("ok",_.bind(this.onModalSubmitted,this,form,modal))},onModalSubmitted:function(form,modal){var isNew=!this.value,error=form.validate();if(error)return modal.preventClose();this.modal=null,this.value=form.getValue(),this.renderSummary(),isNew&&this.trigger("readyToAdd"),this.trigger("change",this),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;this.modal&&(this.modal.trigger("cancel"),this.modal.close())}},{ModalAdapter:Backbone.BootstrapModal,isAsync:!0})})()
View
9 src/editors/list.js
@@ -17,7 +17,7 @@
events: {
'click [data-action="add"]': function(event) {
event.preventDefault();
- this.addItem();
+ this.addItem(null, true);
}
},
@@ -85,7 +85,8 @@
/**
* Add a new item to the list
- * @param {Mixed} [value] Value for the new item editor
+ * @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;
@@ -112,7 +113,7 @@
args.splice(1, 0, self);
// args = ["item:key:change", this=listEditor, itemEditor, fieldEditor]
- editors.List.prototype.trigger.apply(this, args)
+ editors.List.prototype.trigger.apply(this, args);
}, self);
item.editor.on('change', function() {
@@ -145,7 +146,7 @@
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) {
Please sign in to comment.
Something went wrong with that request. Please try again.