mirror of
https://github.com/Dahlgren/arma-server-web-admin.git
synced 2024-08-30 17:22:10 +00:00
330 lines
10 KiB
JavaScript
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;
|
|
})); |