// Invalid/missing elements are highlighted using
// this background colour:
var MJS_COLOUR_ERROR = '#ffdddd';
// Indexes into 'fields' validation param object for each element,
// to be validated - first general ones, then those specific 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 = 6;
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\u00C0-\u024F]+$/i;
var MOCA_REGEXP_alphanumeric = /^[a-z0-9_\.-]+$/i;
var MOCA_REGEXP_event_anchor = /^[.a-z0-9_-]+$/i;
var MOCA_REGEXP_filename = /^[a-z0-9_#.]+$/i;
var MOCA_REGEXP_email = /^[\w'&"]+([\.-]?[\w&'"]+)*@\w+([\.-]?\w+)*(\.\w{2,64})+$/;
var MOCA_REGEXP_phone = /^\s*\+?[0-9 +\(\)-]{6,16}$/;
var MOCA_REGEXP_integer = /^[0-9]+$/;
var MOCA_REGEXP_hex = /^[0-9a-f]+$/i;
var MOCA_REGEXP_number = /^[0-9.,-]+$/;
var MOCA_REGEXP_numberic_list = /^[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]+(:[0-9]+)?$/;
var MOCA_REGEXP_expiry = /^\d+\s+(week|day|month|year)s?$/i;
var MOCA_REGEXP_names = /^[ \u00C0-\u024F!'\?a-z0-9_&@()'.,":;~-]{0,255}$/i;
var MOCA_REGEXP_simple_names = /^[ \u00C0-\u024Fa-z,0-9"'.-]{0,255}$/i;
var MOCA_REGEXP_labels = /^[ $#\u00C0-\u024Fa-z0-9_&%\/@()'."+!,:;~?>-]{0,255}$/i;
var MOCA_REGEXP_data = /^[\u00C0-\u024Fa-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;
var MOCA_REGEXP_username = /^[.@a-z0-9_]+$/i;
var MOCA_REGEXP_password = /^\S+$/;
var MOCA_REGEXP_gst = /^[0-9]{8,11}$/;
var MOCA_REGEXP_coupons = /^[0-9a-z_-]+$/i;
var MOCA_REGEXP_nsn = /^[0-9]{2,9}$/;
var MOCA_REGEXP_db = /^[0-9a-z_]+$/i;
function MocaForm(fields,prefix){
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
this.prefix = prefix ? prefix : 'mf'; // Current MocaForm (php) prefix, 'mf' by default
this.suppress_messages = false; // In csome error contexts we want highlighting os failing fields, but no alert
this.strict = false; // eg: no "dodgy_phone" checks if false
this.dodgy_phone = false; // set to true if ph number is technically valid but looks not - eg: too many digits
this.arbitrary_id = ''; // used for single-element validation outside of MocaForm context
}
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_TM: 'time',
MJS_P: 'password',
MJS_H: 'hidden',
MJS_L: 'label',
MJS_DS: 'date_selectors',
MJS_PH: 'phone',
MJS_HT: 'html',
required_if: {},
require:function(fields,preserve){
var flds = !fields ? [] : (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_HT: // html
req_if_ord = MJS_TEXT_REQ_IF;
var val = this.value = this.values[f] =
$.trim(CKEDITOR.instances[this.prefix+'_'+ this.field[MJS_TYPE] + '_' + f].getData());
if(this.field[MJS_REQUIRED] && !val){
return this.req_error();
}
break;
case this.MJS_P: // password
req_if_ord = MJS_TEXT_REQ_IF;
var val = this.value = this.values[f] = $.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.value = this.values[f] = $.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_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(this.prefix+'_'+ 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(this.prefix+'_'+ 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);
var val = 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();}
var sD,eD;
// Check for valid format, no such date 31-06-2020
if(bits = MOCA_REGEXP_date.exec(val)){
sD = new Date(bits[3],bits[2]-1,bits[1]);
}
if(start_date = this.field[MJS_DATE_RANGE_START]){
if(sval = $('#mf_'+ this.field[MJS_TYPE] + '_' + start_date).val()){
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.req_error();}
}
break;
case this.MJS_TM: // date
//req_if_ord = MJS_DATE_REQ_IF;
//var val = $.trim(this.element.value);
var val = this.element.value;
this.values[f] = this.value = val = val.replace(/^\s+|\s+$/g,'');
if(val){
if(!MOCA_REGEXP_time.test(val)){return this.val_error();}
}
else {
if(this.field[MJS_REQUIRED]){return this.req_error();}
}
break;
case this.MJS_DS: // date selectors
var _d = [document.getElementById(this.prefix+'_'+ this.field[MJS_TYPE] + '_' + f + '_d'),
document.getElementById(this.prefix+'_'+ this.field[MJS_TYPE] + '_' + f + '_m'),
document.getElementById(this.prefix+'_'+ 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 arb_id = this.arbitrary_id ? this.arbitrary_id : this.prefix+'_'+ this.field[MJS_TYPE] + '_' + f;
var _p = [document.getElementById(arb_id + '_i'),
document.getElementById(arb_id + '_a'),
document.getElementById(arb_id + '_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;
}
else{
if(vals.length && vals.length<3){
return this.ph_error();
}
}
if(this.field[MJS_REQUIRED] && (vals.length<3)){return this.req_error();}
if(vals.length){
if(vals[0].charAt(0)=='+'){
vals[0] = vals[0].substr(1);
}
if(!MOCA_REGEXP_integer.test(vals[0])){
this.value = vals[0];
return this.val_error();
}
else{
if(this.strict){
if(vals[0].length >3){
this.dodgy_phone = true;
}
}
}
if(!MOCA_REGEXP_integer.test(vals[1])){
this.value = vals[1];
return this.val_error();
}
else{
if(this.strict){
if(vals[1].length >3){
this.dodgy_phone = true;
}
}
}
if(!MOCA_REGEXP_phone.test(vals[2])){
this.value = vals[2];
return this.val_error();
}
else{
if(this.strict){
if(vals[2].length >9){
this.dodgy_phone = true;
}
}
}
}
if(this.dodgy_phone){
if(!confirm('The number you have entered for "'+this.field[MJS_NAME]+'" looks a bit unusual.\n\nPress "Cancel" if you wish to change it.')){
return false;
}
}
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.field[MJS_TYPE]==this.MJS_P ?
this.error('You have entered an invalid password. Space characters are not permitted')
:
this.error('"' + this.value + '" is not a valid value for the "' + this.field[MJS_NAME] + '" field');
},
min_error:function(){
return this.field[MJS_TEXT_MAX] && this.field[MJS_TEXT_MIN]==this.field[MJS_TEXT_MAX] ? this.error('Your "' + this.field[MJS_NAME] + '" value must be ' + this.field[MJS_TEXT_MIN] + ' characters in length') : 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.field[MJS_TEXT_MIN] && this.field[MJS_TEXT_MIN]==this.field[MJS_TEXT_MAX] ? this.error('Your "' + this.field[MJS_NAME] + '" value must be ' + this.field[MJS_TEXT_MAX] + ' characters in length') : 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,title){
if(this.field[MJS_TYPE] != this.MJS_RG && this.field[MJS_TYPE] != this.MJS_CG && this.field[MJS_TYPE] != this.MJS_HT){
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();
}
if(!this.suppress_messages){
mocaAlert(str,'Try again?');
}
}
};
function formError(el_id,msg,title,no_focus,suppress_msg){
//var no_focus = arguments[2] ? arguments[2] : '';
//var title = arguments[3] ? arguments[3] : '';
//var suppress_msg = arguments[4] ? arguments[4] : false;
unthrob();
var el = document.getElementById(el_id);
if(el){
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();
el.scrollIntoView();
}
if(!suppress_msg){mocaAlert(msg,title);}
return false;
}
/*
Allow non-OOP validation of phone elements
possible params: id, req, lbl, suppress_msg, use_arbitrary id
*/
function phoneIsValid(){
var lbl = arguments[2] ? arguments[2] : 'Phone Number';
var args = {};args[arguments[0]] = ['phone',lbl,arguments[1]];
var mf = new MocaForm(args);
if(arguments[4]){
mf.arbitrary_id=arguments[0];
}
if(arguments[3]){mf.suppress_messages = true;}
if(!mf.validate()){return false;}
mf.arbitrary_id='';
return true;
}
String.prototype.mocaIsEmail = function(){
return this.match( MOCA_REGEXP_email );
};
String.prototype.mocaIsPhone = function(){
return this.match( /^\+?[0-9 -]{7,24}$/ );
};
String.prototype.mocaIsPhoneNumber = function(){
var _num = this.replace(/[^\d]/,'');
return _num.match(/^[0-9]{6,8}$/);
};
String.prototype.mocaIsPhoneArea = function(){
return this.match( /^[0-9]{1,3}$/ );
};
String.prototype.mocaIsDateOfBirth = function(){
if(bits = MOCA_REGEXP_date.exec(this)){
var _today = new Date().getFullYear();
var _dob = new Date(bits[3],bits[2]-1,bits[1]).getFullYear();
_test = _today - _dob;
return (_test > 0) && (_test < 110);
}
return false;
};