// Invalid/missing elements are highlighted using
// this background colour:
var MJS_COLOUR_ERROR = '#ffdddd';
// Indexes into validation param hash for each validated element,
// first general ones, then those specifi to type:
var MJS_TYPE = 0;
var MJS_NAME = 1; // display name
var MJS_REQUIRED = 2;
var MJS_REGEXP = 3;
var MJS_name = 4; // 'real' name
var MJS_TEXT_MIN = 4;
var MJS_TEXT_MAX = 5;
var MJS_TEXT_REQ_IF = 6;
var MJS_TEXTAREA_REQ_IF = 4;
var MJS_SEL_IGNORE_FIRST = 3; // Select element has hint as first option -
// ignore when looking for value
var MJS_SEL_REQ_IF = 4;
var MJS_DATE_REQ_IF = 3;
var MJS_DATE_RANGE_START = 4;
var MJS_DEFAULT_REQ_IF = 3;
var MOCA_REGEXP_all = '';
var MOCA_REGEXP_words = /^[a-z 0-9]+$/i;
var MOCA_REGEXP_alphanumeric = /^[a-z0-9_]+$/i;
var MOCA_REGEXP_alphanumericplusspace = /^[a-z0-9_ -]+$/i;
var MOCA_REGEXP_filename = /^[a-z0-9_.-]+$/i;
var MOCA_REGEXP_email = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/;
var MOCA_REGEXP_phone = /^[0-9 +\(\)-]{7,}$/;
var MOCA_REGEXP_integer = /^[0-9]+$/;
var MOCA_REGEXP_hex = /^[0-9a-f]+$/i;
var MOCA_REGEXP_number = /^[0-9.,-]+$/;
var MOCA_REGEXP_float = /^[0-9.]+$/;
var MOCA_REGEXP_cc = /^\d{4}(-| )?\d{4}(-| )?\d{4}(-| )?\d{4}$/;
var MOCA_REGEXP_cc_exp = /^\d{2}\/\d{2}$/;
var MOCA_REGEXP_date = /^(\d{2})-(\d{2})-(\d{4})$/;
var MOCA_REGEXP_time = /^[0-9]+:[0-9]+$/;
var MOCA_REGEXP_expiry = /^\d+\s+(week|day|month|year)s?$/i;
var MOCA_REGEXP_names = /^[ $a-z0-9_&@()'.,";~-]{0,255}$/i;
var MOCA_REGEXP_simple_names = /^[ a-z,0-9"'.-]{0,255}$/i;
var MOCA_REGEXP_labels = /^[ $#a-z0-9_&%\/@()'."+!,:;~?>-]{0,255}$/i;
var MOCA_REGEXP_data = /^[a-z#?0-9_&%@()-;:,'~".!+\n\r ]{0,1024}$/i;
var MOCA_REGEXP_url = /^[a-z=0-9_&@%-:\/;,'".~!+?+]{0,255}$/i;
var MOCA_REGEXP_keywords = /^[a-z0-9, '-]+$/i;
function MocaForm(fields){
this.fields = fields; // All MocaFields {name:[prop,prop,prop,etc],...}
this.values = {}; // Cache of processed element values
this.els = {}; // Cache of element refs
this.name = ''; // current actual name value
this.field = []; // Current MocaField (being validated)
this.element = null; // Currently element
this.value = ''; // Current element value
}
MocaForm.prototype = {
MJS_T: 'text', // == EL_ID_TEXT
MJS_TA: 'textarea',
MJS_S: 'select',
MJS_RG: 'radio',
MJS_C: 'checkbox',
MJS_CG: 'checkboxgroup',
MJS_D: 'date',
MJS_P: 'password',
MJS_H: 'hidden',
MJS_L: 'label',
MJS_DS: 'dateselector',
MJS_PH: 'phone',
required_if: {},
require:function(fields,preserve){
var flds = fields.splice ? fields : [fields];
if(!preserve){
for(var f in this.fields){
f[MJS_REQUIRED] = false;
}
}
for(var i=0; ithis.field[MJS_TEXT_MAX]){
return this.max_error();
}
break;
case this.MJS_P: // password
req_if_ord = MJS_TEXT_REQ_IF;
var val = this.values[f] = this.value = $.trim(this.element.value);
if(this.field[MJS_REQUIRED] && !val){
return this.req_error();
}
if(val && this.field[MJS_REGEXP]){
if(!this.field[MJS_REGEXP].test(val)){
return this.val_error();
}
}
if(this.field[MJS_TEXT_MIN] && val && val.lengththis.field[MJS_TEXT_MAX]){
return this.max_error();
}
break;
case this.MJS_TA: // textarea
req_if_ord = MJS_TEXTAREA_REQ_IF;
var val = this.values[f] = this.value = $.trim(this.element.value);
if(this.field[MJS_REQUIRED] && !val){
return this.req_error();
}
if(val && this.field[MJS_REGEXP]){
if(!this.field[MJS_REGEXP].test(val)){
return this.val_error();
}
}
break;
case this.MJS_S: // select
req_if_ord = MJS_SEL_REQ_IF;
idx = this.element.selectedIndex;
if(this.field[MJS_REQUIRED]){
if((idx == -1) || (!idx && this.field[MJS_SEL_IGNORE_FIRST])){
return this.sel_error();
}
}
this.value = this.values[f] = $('#mf_select_'+f).val();
break;
case this.MJS_RG: // radio group
if(this.field[MJS_REQUIRED]){
var val = false;
var chk = document.getElementsByName('mf_'+ this.MJS_RG + '_' + f );
for (var i = 0; i < chk.length; i++){
if (chk[i].checked) {val = chk[i].value;break;}
}
if(!val){
this.element = chk;
return this.grp_error();
}
}
break;
case this.MJS_CG: // checkbox group
if(this.field[MJS_REQUIRED]){
this.values[f] = []; // don't really need these otherwise
var chk = document.getElementsByName('mf_'+ this.MJS_CG + '_' + f + '[]');
for (var i = 0; i < chk.length; i++){
if (chk[i].checked) {this.values[f].push(chk[i].value);}
}
if(!this.values[f].length){
this.element = chk;
return this.grp_error();
}
}
break;
case this.MJS_C: // checkbox
if(this.element.checked){
this.value = this.values[f] = this.element.value;
}
else{
if(this.field[MJS_REQUIRED]){
return this.chk_error();
}
}
break;
case this.MJS_D: // date
req_if_ord = MJS_DATE_REQ_IF;
var val = $.trim(this.element.value);
this.values[f] = this.value = val = val.replace(/^\s+|\s+$/g,'');
if(val){
if(!MOCA_REGEXP_date.test(val)){return this.val_error();}
if(start_date = this.field[MJS_DATE_RANGE_START]){
if(sval = $('#mf_'+ this.field[MJS_TYPE] + '_' + start_date).val()){
var sD,eD;
if(bits = MOCA_REGEXP_date.exec(sval)){
sD = new Date(bits[3],bits[2]-1,bits[1]);
}
if(bits = MOCA_REGEXP_date.exec(val)){
eD = new Date(bits[3],bits[2]-1,bits[1]);
}
if(sD && eD){ // If not true will get caught above
if(sD > eD){
return this.range_error(sD,eD);
}
}
}
}
}
else {
if(this.field[MJS_REQUIRED]){return this.chk_error();}
}
break;
case this.MJS_DS: // date selectors
var _d = [document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_d'),
document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_m'),
document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_y')];
this.element = _d[0];// for errors
for(i=0; i<_d.length; i++){
idx = _d[i].selectedIndex;
if((idx == -1) || (!idx && this.field[MJS_SEL_IGNORE_FIRST])){
if(this.field[MJS_REQUIRED]){return this.dsel_error();}
}
}
this.value = _d.join('-');
break;
case this.MJS_PH: // phone boxes
var _p = [document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_i'),
document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_a'),
document.getElementById('mf_'+ this.field[MJS_TYPE] + '_' + f + '_p')];
this.element = _p[2];// for errors
var empty = false; var has_default=false;var vals = [];
for(i=0; i<_p.length; i++){
if(v=$.trim(_p[i].value)){
vals.push(v);
if(i==0){has_default=true;}
}
}
if(vals.length==1 && has_default && !this.field[MJS_REQUIRED]){
// Just default int code - remove for submission
_p.value = '';
break;
}
if(vals.length==2){return this.ph_error();}
if(this.field[MJS_REQUIRED] && (vals.length<3)){return this.req_error();}
if(vals.length){
if(!MOCA_REGEXP_integer.test(vals[0])){
this.value = vals[0];
return this.val_error();
}
if(!MOCA_REGEXP_integer.test(vals[1])){
this.value = vals[1];
return this.val_error();
}
if(!MOCA_REGEXP_phone.test(vals[2])){
this.value = vals[2];
return this.val_error();
}
}
break;
}
if(this.field[req_if_ord] && !this.value){
// eg: this.required_if['component_key'] = 'has_component'
this.required_if[f] = this.field[req_if_ord];
}
}
if(this.required_if){
for(var f in this.required_if){
if(this.values[this.required_if[f]]){
this.field = this.fields[f];
this.element = this.els[f];
this.that = this.fields[this.required_if[f]];
return this.req_if_error();
}
}
}
return true;
},
req_error:function(){
return this.error('"'+this.field[MJS_NAME] + '" is a required field');
},
val_error:function(){
return this.error('"' + this.value + '" is not a valid value for the "' + this.field[MJS_NAME] + '" field');
},
min_error:function(){
return this.error('Your "' + this.field[MJS_NAME] + '" value must be at least ' +
this.field[MJS_TEXT_MIN] + ' characters in length');
},
max_error:function(){
return this.error('Your "' + this.field[MJS_NAME] + '" value must be less than ' +
this.field[MJS_TEXT_MAX] + ' characters in length');
},
sel_error:function(){
return this.error('You must select a value from the "' + this.field[MJS_NAME] + '" list to continue');
},
dsel_error:function(){
return this.error('You must select a valid date with the "' + this.field[MJS_NAME] + '" selector to continue');
},
ph_error:function(){
return this.error('Your "' + this.field[MJS_NAME] + '" value must include international and area codes, and the local phone number');
},
grp_error:function(){
return this.error('You must select a value for "' + this.field[MJS_NAME] + '" to continue');
},
chk_error:function(){
return this.error('You must tick the "' + this.field[MJS_NAME] + '" checkbox to continue');
},
req_if_error:function(){
var verbed = this.field[MJS_TYPE]==this.MJS_C || this.field[MJS_TYPE]==this.MJS_S ? 'selected' : 'entered';
return this.error('You must provide a value for ' + this.field[MJS_NAME] + ' if ' +
this.that[MJS_NAME] + ' is ' + verbed);
},
range_error:function(s,e){
return this.error('You have entered an invalid date range:
('+s.mocaToString() +
' to '+e.mocaToString()+')',true);
},
error:function(str,no_focus){
if(this.field[MJS_TYPE] != this.MJS_RG && this.field[MJS_TYPE] != this.MJS_CG){
var bg = this.element.style.backgroundColor;
this.element.style.backgroundColor = MJS_COLOUR_ERROR;
var revert = function(){this.style.backgroundColor=bg;};
this.element.onclick = revert;
this.element.onkeydown = revert;
no_focus || this.element.focus();
}
mocaAlert(str);
}
};
function formError(el_id,msg,no_focus){
// formError('element_id','error_msg' [, 'title', 'functionOnDismiss']);
// Class function - first arg must be element_id,
// second is alert message, third is alert title,
// fourth is function to be executed on alert
// dismissal. Only the first two are mandatory
var title = arguments[2] ? arguments[2] : '';
var func = arguments[3] ? arguments[3] : null;
var el = document.getElementById(el_id);
var bg = el.style.backgroundColor;
el.style.backgroundColor = MJS_COLOUR_ERROR;
var revert = function(){this.style.backgroundColor=bg;};
el.onclick = revert;
el.onkeydown = revert;
no_focus || el.focus();
mocaAlert(msg,title,func);
return false;
}