arma-server-web-admin/public/js/lib/marionette-formview.js
2014-02-09 03:19:48 +00:00

330 lines
10 KiB
JavaScript

/*! marionette-formview - v1.0.1 - 2014-01-27 */
/*global Backbone,define*/
;(function (root, factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['marionette','jquery','underscore'], factory);
} else {
// Browser globals
root.Marionette.FormView = factory(root.Marionette,root.jQuery,root._);
}
}(this, function (Marionette,$,_) {
"use strict";
/**
* FormView Extension of Backbone.Marionette.ItemView
*
* @param {Object} options Options defining this FormView
* @param {Object} [options.data] Form Data. (Required if options.model is not set)
* @param {Object} [options.fields] Which Fields to include
*
*/
var FormView = Marionette.FormView = Marionette.ItemView.extend({
className : "formView",
rules : {}, //Custom Field Validation Rules
fields : {},
constructor : function(){
Marionette.ItemView.prototype.constructor.apply(this, arguments);
//Allow Passing In Fields by extending with a fields hash
if (!this.fields) throw new Error("Fields Must Be Provided");
if (!this.model) this.model = new Backbone.Model();
this.listenTo(this.model, 'change', this.changeFieldVal,this);
if (this.data) this.model.set(this.data);
//Attach Events to preexisting elements if we don't have a template
if (!this.template) this.runInitializers();
this.on('item:rendered',this.runInitializers, this);
},
changeFieldVal : function(model, fields) {
if(!_.isEmpty(fields) && fields.changes) {
var modelProperty = Object.keys(fields.changes);
this.inputVal(modelProperty, this.model.get(modelProperty));
} else if (fields.unset) {
_(this.fields).each(function(options, field) {
var elem = this.$('[data-field="'+field+'"]');
this.inputVal(elem, this.model.get(field));
},this);
}
},
populateFields : function () {
_(this.fields).each(function(options, field) {
var elem = this.$('[data-field="'+field+'"]');
this.inputVal(elem, this.model.get(field));
if (options.autoFocus) elem.focus();
},this);
},
serializeFormData : function () {
var data = {}, self = this;
_(this.fields).each(function(options, field){
data[field] = self.inputVal(field);
});
return data;
},
beforeFormSubmit : function (e) {
var errors = this.validate();
var success = _.isEmpty(errors);
if (success) {
if (_.isFunction(this.onSubmit)) return this.onSubmit.apply(this, [e]);
} else {
if (_.isFunction(this.onSubmitFail)) this.onSubmitFail.apply(this, [errors]);
e.stopImmediatePropagation();
return false;
}
},
onFieldEvent : function(evt) {
this.handleFieldEvent(evt, evt.type);
},
handleFieldEvent : function(evt, eventName) {
var el = evt.target || evt.srcElement,
field = $(el).attr('data-field'),
fieldOptions = this.fields[field];
if (fieldOptions && fieldOptions.validateOn === eventName) {
var errors = this.validateField(field);
if (!_.isEmpty(errors) && _.isFunction(this.onValidationFail)) this.onValidationFail(errors);
}
},
validate : function () {
var errors = {},
fields = _(this.fields).keys();
_(fields).each(function (field) {
var fieldErrors = this.validateField(field);
if (!_.isEmpty(fieldErrors)) errors[field] = fieldErrors;
},this);
return errors;
},
validateField : function(field) {
var fieldOptions = this.fields[field],
validations = fieldOptions && fieldOptions.validations ? fieldOptions.validations : {},
fieldErrors = [],
isValid = true;
var val = this.inputVal(field);
if (fieldOptions.required) {
isValid = this.validateRule(val,'required');
var errorMessage = typeof fieldOptions.required === 'string' ? fieldOptions.required : 'This field is required';
if (!isValid) fieldErrors.push(errorMessage);
}
// Don't bother with other validations if failed 'required' already
if (isValid && validations) {
_.each(validations, function (errorMsg, validateWith) {
isValid = this.validateRule(val, validateWith);
if (!isValid) fieldErrors.push(errorMsg);
},this);
}
if (!_.isEmpty(fieldErrors)) {
var errorObject = {
field : field,
el : this.fields[field].el,
error : fieldErrors
};
return errorObject;
}
},
inputVal : function(input, val) {
//takes field name or jQuery object
var el = input.jquery ? input : this.$('[data-field="'+input+'"]');
var self = this, mode = typeof val === 'undefined' ? 'get' : 'set';
if (el.data('fieldtype') === 'object'){
if (mode === 'get') val = {};
el.find('[data-property]').each(function(){
var elem = $(this);
var prop = elem.attr('data-property');
if (mode === 'get'){
val[prop] = self.inputVal(elem);
} else if (val){
self.inputVal(elem, val[prop]);
}
});
} else if (el.data('fieldtype') === 'array'){
if (mode === 'get') val = [];
el.find('[data-index]').each(function(){
var elem = $(this);
var index = elem.data('index');
if (mode === 'get'){
val[index] = self.inputVal(elem);
} else if (val){
self.inputVal(elem, val[index]);
}
});
} else if (el.is('input')) {
var inputType = el.attr('type').toLowerCase();
switch (inputType) {
case "radio":
el.each(function(){
var radio = $(this);
if (mode === 'get'){
if (radio.is(':checked')){
val = radio.val();
return false;
}
} else {
if (radio.val() === val){
radio.prop('checked', true);
return false;
}
}
});
break;
case "checkbox":
if (mode === 'get'){
val = el.is(':checked');
} else {
el.prop('checked', !!val);
}
break;
case "password":
if (mode === 'get'){
val = el.val();
} else {
el.val(val);
}
break;
default :
if (mode === 'get'){
val = $.trim(el.val());
} else {
el.val(val);
}
break;
}
} else {
if (mode === 'get'){
val = $.trim(el.val());
} else {
el.val(val);
}
//Handle Select / MultiSelect Etc
//@todo
}
return val;
},
validateRule : function (val,validationRule) {
var options;
// throw an error because it could be tough to troubleshoot if we just return false
if (!validationRule) throw new Error('Not passed a validation to test');
if (validationRule === 'required') return FormValidator.required(val);
if (validationRule.indexOf(':') !== -1) {
options = validationRule.split(":");
validationRule = options.shift();
}
if (this.rules && this.rules[validationRule]) {
return _(this.rules[validationRule]).bind(this)(val);
} else {
return _(FormValidator.validate).bind(this)(validationRule, val, options);
}
return true;
},
submit : function () {
this.form.submit();
},
bindFormEvents : function() {
var form = (this.$el.is('form')) ? this.$el : this.$('form').first();
this.form = form;
this.$('input')
.blur(_(this.onFieldEvent).bind(this))
.keyup(_(this.onFieldEvent).bind(this))
.keydown(_(this.onFieldEvent).bind(this))
.change(_(this.onFieldEvent).bind(this));
form.submit(_(this.beforeFormSubmit).bind(this));
},
runInitializers : function() {
this.populateFields();
this.bindFormEvents();
if (_.isFunction(this.onReady)) this.onReady();
}
});
var FormValidator = {
regex : {
//RFC 2822
email : /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i,
alpha : /^[a-zA-Z]+$/,
alphanum : /^[a-zA-Z0-9]+$/
},
validate : function(validator, val, options) {
if (_.isFunction(FormValidator[validator])) return _(FormValidator[validator]).bind(this)(val,options);
throw new Error('Validator does not exist : ' + validator);
},
matches : function(val,field) {
/*jshint eqeqeq:false*/
return val == this.inputVal(field);
},
min : function(val,minLength) {
if (val.length < minLength) return false;
return true;
},
max : function(val, maxLength) {
if (val.length > maxLength) return false;
return true;
},
numeric : function(val) {
return _.isNumber(val);
},
alpha : function(val) {
return FormValidator.regex.alpha.test(val);
},
alphanum : function (val) {
return FormValidator.regex.alphanum.test(val);
},
email : function(val) {
return FormValidator.regex.email.test(val);
},
required : function(val) {
if (val === false || _.isNull(val) || _.isUndefined(val) || (_.isString(val) && val.length === 0)) return false;
return true;
},
boolean : function(val) {
return _.isBoolean(val);
}
};
return FormView;
}));