// 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-]{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,2048}$/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_vat = ''; var MOCA_REGEXP_coupons = /^[0-9a-z_-]+$/i; var MOCA_REGEXP_nsn = /^[0-9]{2,10}$/; var MOCA_REGEXP_db = /^[0-9a-z_]+$/i; var MOCA_REGEXP_ppo = /[0-9]{9}$/; var MOCA_REGEXP_sort_code = /^\d\d-\d\d-\d\d$/; 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_phone.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,10}$/); }; 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; };