1// json2.js 2// 2016-10-28 3// Public Domain. 4// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 5// See http://www.JSON.org/js.html 6// This code should be minified before deployment. 7// See http://javascript.crockford.com/jsmin.html 8 9// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 10// NOT CONTROL. 11 12// This file creates a global JSON object containing two methods: stringify 13// and parse. This file provides the ES5 JSON capability to ES3 systems. 14// If a project might run on IE8 or earlier, then this file should be included. 15// This file does nothing on ES5 systems. 16 17// JSON.stringify(value, replacer, space) 18// value any JavaScript value, usually an object or array. 19// replacer an optional parameter that determines how object 20// values are stringified for objects. It can be a 21// function or an array of strings. 22// space an optional parameter that specifies the indentation 23// of nested structures. If it is omitted, the text will 24// be packed without extra whitespace. If it is a number, 25// it will specify the number of spaces to indent at each 26// level. If it is a string (such as "\t" or " "), 27// it contains the characters used to indent at each level. 28// This method produces a JSON text from a JavaScript value. 29// When an object value is found, if the object contains a toJSON 30// method, its toJSON method will be called and the result will be 31// stringified. A toJSON method does not serialize: it returns the 32// value represented by the name/value pair that should be serialized, 33// or undefined if nothing should be serialized. The toJSON method 34// will be passed the key associated with the value, and this will be 35// bound to the value. 36 37// For example, this would serialize Dates as ISO strings. 38 39// Date.prototype.toJSON = function (key) { 40// function f(n) { 41// // Format integers to have at least two digits. 42// return (n < 10) 43// ? "0" + n 44// : n; 45// } 46// return this.getUTCFullYear() + "-" + 47// f(this.getUTCMonth() + 1) + "-" + 48// f(this.getUTCDate()) + "T" + 49// f(this.getUTCHours()) + ":" + 50// f(this.getUTCMinutes()) + ":" + 51// f(this.getUTCSeconds()) + "Z"; 52// }; 53 54// You can provide an optional replacer method. It will be passed the 55// key and value of each member, with this bound to the containing 56// object. The value that is returned from your method will be 57// serialized. If your method returns undefined, then the member will 58// be excluded from the serialization. 59 60// If the replacer parameter is an array of strings, then it will be 61// used to select the members to be serialized. It filters the results 62// such that only members with keys listed in the replacer array are 63// stringified. 64 65// Values that do not have JSON representations, such as undefined or 66// functions, will not be serialized. Such values in objects will be 67// dropped; in arrays they will be replaced with null. You can use 68// a replacer function to replace those with JSON values. 69 70// JSON.stringify(undefined) returns undefined. 71 72// The optional space parameter produces a stringification of the 73// value that is filled with line breaks and indentation to make it 74// easier to read. 75 76// If the space parameter is a non-empty string, then that string will 77// be used for indentation. If the space parameter is a number, then 78// the indentation will be that many spaces. 79 80// Example: 81 82// text = JSON.stringify(["e", {pluribus: "unum"}]);
83// // text is '["e",{"pluribus":"unum"}]' 84 85// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); 86// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 87 88// text = JSON.stringify([new Date()], function (key, value) { 89// return this[key] instanceof Date 90// ? "Date(" + this[key] + ")" 91// : value; 92// }); 93// // text is '["Date(---current time---)"]' 94 95// JSON.parse(text, reviver) 96// This method parses a JSON text to produce an object or array. 97// It can throw a SyntaxError exception. 98 99// The optional reviver parameter is a function that can filter and 100// transform the results. It receives each of the keys and values, 101// and its return value is used instead of the original value. 102// If it returns what it received, then the structure is not modified. 103// If it returns undefined then the member is deleted. 104 105// Example: 106 107// // Parse the text. Values that look like ISO date strings will 108// // be converted to Date objects. 109 110// myData = JSON.parse(text, function (key, value) { 111// var a; 112// if (typeof value === "string") { 113// a = 114// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 115// if (a) { 116// return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 117// +a[5], +a[6])); 118// } 119// } 120// return value; 121// }); 122 123// myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 124// var d; 125// if (typeof value === "string" && 126// value.slice(0, 5) === "Date(" && 127// value.slice(-1) === ")") { 128// d = new Date(value.slice(5, -1)); 129// if (d) { 130// return d; 131// } 132// } 133// return value; 134// }); 135 136// This is a reference implementation. You are free to copy, modify, or 137// redistribute. 138 139/*jslint 140 eval, for, this 141*/ 142 143/*property 144 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 145 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 146 lastIndex, length, parse, prototype, push, replace, slice, stringify, 147 test, toJSON, toString, valueOf 148*/ 149 150 151// Create a JSON object only if one does not already exist. We create the 152// methods in a closure to avoid creating global variables. 153 154if (typeof JSON !== "object") { 155 JSON = {}; 156} 157 158(function () { 159 "use strict"; 160 161 var rx_one = /^[\],:{}\s]*$/; 162 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; 163 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 164 var rx_four = /(?:^|:|,)(?:\s*\[)+/g; 165 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 166 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 167 168 function f(n) { 169 // Format integers to have at least two digits. 170 return n < 10 171 ? "0" + n 172 : n; 173 } 174 175 function this_value() { 176 return this.valueOf(); 177 } 178 179 if (typeof Date.prototype.toJSON !== "function") { 180 181 Date.prototype.toJSON = function () { 182 183 return isFinite(this.valueOf()) 184 ? this.getUTCFullYear() + "-" + 185 f(this.getUTCMonth() + 1) + "-" + 186 f(this.getUTCDate()) + "T" + 187 f(this.getUTCHours()) + ":" + 188 f(this.getUTCMinutes()) + ":" + 189 f(this.getUTCSeconds()) + "Z" 190 : null; 191 }; 192 193 Boolean.prototype.toJSON = this_value; 194 Number.prototype.toJSON = this_value; 195 String.prototype.toJSON = this_value; 196 } 197 198 var gap; 199 var indent; 200 var meta; 201 var rep; 202 203 204 function quote(string) { 205
206// If the string contains no control characters, no quote characters, and no 207// backslash characters, then we can safely slap some quotes around it. 208// Otherwise we must also replace the offending characters with safe escape 209// sequences. 210 211 rx_escapable.lastIndex = 0; 212 return rx_escapable.test(string) 213 ? "\"" + string.replace(rx_escapable, function (a) { 214 var c = meta[a]; 215 return typeof c === "string" 216 ? c 217 : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 218 }) + "\"" 219 : "\"" + string + "\""; 220 } 221 222 223 function str(key, holder) { 224 225// Produce a string from holder[key]. 226 227 var i; // The loop counter. 228 var k; // The member key. 229 var v; // The member value. 230 var length; 231 var mind = gap; 232 var partial; 233 var value = holder[key]; 234 235// If the value has a toJSON method, call it to obtain a replacement value. 236 237 if (value && typeof value === "object" && 238 typeof value.toJSON === "function") { 239 value = value.toJSON(key); 240 } 241 242// If we were called with a replacer function, then call the replacer to 243// obtain a replacement value. 244 245 if (typeof rep === "function") { 246 value = rep.call(holder, key, value); 247 } 248 249// What happens next depends on the value's type. 250 251 switch (typeof value) { 252 case "string": 253 return quote(value); 254 255 case "number": 256 257// JSON numbers must be finite. Encode non-finite numbers as null. 258 259 return isFinite(value) 260 ? String(value) 261 : "null"; 262 263 case "boolean": 264 case "null": 265 266// If the value is a boolean or null, convert it to a string. Note: 267// typeof null does not produce "null". The case is included here in 268// the remote chance that this gets fixed someday. 269 270 return String(value); 271 272// If the type is "object", we might be dealing with an object or an array or 273// null. 274 275 case "object": 276 277// Due to a specification blunder in ECMAScript, typeof null is "object", 278// so watch out for that case. 279 280 if (!value) { 281 return "null"; 282 } 283 284// Make an array to hold the partial results of stringifying this object value. 285 286 gap += indent; 287 partial = []; 288 289// Is the value an array? 290 291 if (Object.prototype.toString.apply(value) === "[object Array]") { 292 293// The value is an array. Stringify every element. Use null as a placeholder 294// for non-JSON values. 295 296 length = value.length; 297 for (i = 0; i < length; i += 1) { 298 partial[i] = str(i, value) || "null"; 299 } 300 301// Join all of the elements together, separated with commas, and wrap them in 302// brackets. 303 304 v = partial.length === 0 305 ? "[]" 306 : gap 307 ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" 308 : "[" + partial.join(",") + "]"; 309 gap = mind; 310 return v; 311 } 312 313// If the replacer is an array, use it to select the members to be stringified. 314 315 if (rep && typeof rep === "object") { 316 length = rep.length; 317 for (i = 0; i < length; i += 1) { 318 if (typeof rep[i] === "string") { 319 k = rep[i]; 320 v = str(k, value); 321 if (v) { 322 partial.push(quote(k) + ( 323 gap 324 ? ": " 325 : ":" 326 ) + v); 327 } 328 } 329 } 330 } else { 331 332// Otherwise, iterate through all of the keys in the object. 333 334 for (k in value) { 335 if (Object.prototype.hasOwnProperty.call(value, k)) { 336 v = str(k, value); 337 if (v) { 338 partial.push(quote(k) + ( 339 gap 340 ? ": " 341 : ":" 342 ) + v); 343 } 344 } 345 } 346 } 347 348// Join all of the member texts together, separated with commas, 349// and wrap them in braces. 350 351 v = partial.length === 0 352 ? "{}" 353 : gap 354 ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 355 : "{" + partial.join(",") + "}"; 356 gap = mind; 357 return v; 358 } 359 } 360 361// If the JSON object does not yet have a stringify method, give it one. 362 363 if (typeof JSON.stringify !== "function") { 364 meta = { // table of character substitutions 365 "\b": "\\b", 366 "\t": "\\t", 367 "\n": "\\n", 368 "\f": "\\f", 369 "\r": "\\r", 370 "\"": "\\\"", 371 "\\": "\\\\" 372 }; 373 JSON.stringify = function (value, replacer, space) { 374 375// The stringify method takes a value and an optional replacer, and an optional 376// space parameter, and returns a JSON text. The replacer can be a function
377// that can replace values, or an array of strings that will select the keys. 378// A default replacer method can be provided. Use of the space parameter can 379// produce text that is more easily readable. 380 381 var i; 382 gap = ""; 383 indent = ""; 384 385// If the space parameter is a number, make an indent string containing that 386// many spaces. 387 388 if (typeof space === "number") { 389 for (i = 0; i < space; i += 1) { 390 indent += " "; 391 } 392 393// If the space parameter is a string, it will be used as the indent string. 394 395 } else if (typeof space === "string") { 396 indent = space; 397 } 398 399// If there is a replacer, it must be a function or an array. 400// Otherwise, throw an error. 401 402 rep = replacer; 403 if (replacer && typeof replacer !== "function" && 404 (typeof replacer !== "object" || 405 typeof replacer.length !== "number")) { 406 throw new Error("JSON.stringify"); 407 } 408 409// Make a fake root object containing our value under the key of "". 410// Return the result of stringifying the value. 411 412 return str("", {"": value}); 413 }; 414 } 415 416 417// If the JSON object does not yet have a parse method, give it one. 418 419 if (typeof JSON.parse !== "function") { 420 JSON.parse = function (text, reviver) { 421 422// The parse method takes a text and an optional reviver function, and returns 423// a JavaScript value if the text is a valid JSON text. 424 425 var j; 426 427 function walk(holder, key) { 428 429// The walk method is used to recursively walk the resulting structure so 430// that modifications can be made. 431 432 var k; 433 var v; 434 var value = holder[key]; 435 if (value && typeof value === "object") { 436 for (k in value) { 437 if (Object.prototype.hasOwnProperty.call(value, k)) { 438 v = walk(value, k); 439 if (v !== undefined) { 440 value[k] = v; 441 } else { 442 delete value[k]; 443 } 444 } 445 } 446 } 447 return reviver.call(holder, key, value); 448 } 449 450 451// Parsing happens in four stages. In the first stage, we replace certain 452// Unicode characters with escape sequences. JavaScript handles many characters 453// incorrectly, either silently deleting them, or treating them as line endings. 454 455 text = String(text); 456 rx_dangerous.lastIndex = 0; 457 if (rx_dangerous.test(text)) { 458 text = text.replace(rx_dangerous, function (a) { 459 return "\\u" + 460 ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 461 }); 462 } 463 464// In the second stage, we run the text against regular expressions that look 465// for non-JSON patterns. We are especially concerned with "()" and "new" 466// because they can cause invocation, and "=" because it can cause mutation. 467// But just to be safe, we want to reject all unexpected forms. 468 469// We split the second stage into 4 regexp operations in order to work around 470// crippling inefficiencies in IE's and Safari's regexp engines. First we 471// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we 472// replace all simple value tokens with "]" characters. Third, we delete all 473// open brackets that follow a colon or comma or that begin the text. Finally, 474// we look to see that the remaining characters are only whitespace or "]" or 475// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. 476 477 if ( 478 rx_one.test( 479 text 480 .replace(rx_two, "@") 481 .replace(rx_three, "]") 482 .replace(rx_four, "") 483 ) 484 ) { 485 486// In the third stage we use the eval function to compile the text into a 487// JavaScript structure. The "{" operator is subject to a syntactic ambiguity 488// in JavaScript: it can begin a block or an object literal. We wrap the text 489// in parens to eliminate the ambiguity. 490
491 j = eval("(" + text + ")"); 492 493// In the optional fourth stage, we recursively walk the new structure, passing 494// each name/value pair to a reviver function for possible transformation. 495 496 return (typeof reviver === "function") 497 ? walk({"": j}, "") 498 : j; 499 } 500 501// If the text is not JSON parseable, then a SyntaxError is thrown. 502 503 throw new SyntaxError("JSON.parse"); 504 }; 505 } 506}()); 507 508(function($){ 509 $.fn.validationEngineLanguage = function(){ 510 }; 511 $.validationEngineLanguage = { 512 newLang: function(){ 513 $.validationEngineLanguage.allRules = { 514 "required": { // Add your regex rules here, you can take telephone as an example 515 "regex": "none", 516 "alertText": "* This field is required", 517 "alertTextCheckboxMultiple": "* Please select an option", 518 "alertTextCheckboxe": "* This checkbox is required", 519 "alertTextDateRange": "* Both date range fields are required" 520 }, 521 "requiredInFunction": { 522 "func": function(field, rules, i, options){ 523 return (field.val() == "test") ? true : false; 524 }, 525 "alertText": "* Field must equal test" 526 }, 527 "dateRange": { 528 "regex": "none", 529 "alertText": "* Invalid ", 530 "alertText2": "Date Range" 531 }, 532 "dateTimeRange": { 533 "regex": "none", 534 "alertText": "* Invalid ", 535 "alertText2": "Date Time Range" 536 }, 537 "minSize": { 538 "regex": "none", 539 "alertText": "* Minimum ", 540 "alertText2": " characters required" 541 }, 542 "maxSize": { 543 "regex": "none", 544 "alertText": "* Maximum ", 545 "alertText2": " characters allowed" 546 }, 547 "groupRequired": { 548 "regex": "none", 549 "alertText": "* You must fill one of the following fields", 550 "alertTextCheckboxMultiple": "* Please select an option", 551 "alertTextCheckboxe": "* This checkbox is required" 552 }, 553 "min": { 554 "regex": "none", 555 "alertText": "* Minimum value is " 556 }, 557 "max": { 558 "regex": "none", 559 "alertText": "* Maximum value is " 560 }, 561 "past": { 562 "regex": "none", 563 "alertText": "* Date prior to " 564 }, 565 "future": { 566 "regex": "none", 567 "alertText": "* Date past " 568 }, 569 "maxCheckbox": { 570 "regex": "none", 571 "alertText": "* Maximum ", 572 "alertText2": " options allowed" 573 }, 574 "minCheckbox": { 575 "regex": "none", 576 "alertText": "* Please select ", 577 "alertText2": " options" 578 }, 579 "equals": { 580 "regex": "none", 581 "alertText": "* Fields do not match" 582 }, 583 "creditCard": { 584 "regex": "none", 585 "alertText": "* Invalid credit card number" 586 }, 587 "phone": { 588 // credit: jquery.h5validate.js / orefalo 589 "regex": /^([\+][0-9]{1,3}([ \.\-])?)?([\(][0-9]{1,6}[\)])?([0-9 \.\-]{1,32})(([A-Za-z \:]{1,11})?[0-9]{1,4}?)$/, 590 "alertText": "* Invalid phone number" 591 }, 592 "email": { 593 // HTML5 compatible email regex ( http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html# e-mail-state-%28type=email%29 ) 594 "regex": /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 595 "alertText": "* Invalid email address" 596 }, 597 "fullname": { 598 "regex":/^([a-zA-Z]+[\'\,\.\-]?[a-zA-Z ]*)+[ ]([a-zA-Z]+[\'\,\.\-]?[a-zA-Z ]+)+$/, 599 "alertText":"* Must be first and last name" 600 }, 601 "zip": { 602 "regex":/^\d{5}$|^\d{5}-\d{4}$/, 603 "alertText":"* Invalid zip format" 604 }, 605 "integer": { 606 "regex": /^[\-\+]?\d+$/, 607 "alertText": "* Not a valid integer" 608 }, 609 "number": { 610 // Number, including positive, negative, and floating decimal. credit: orefalo 611 "regex": /^[\-\+]?((([0-9]{1,3})([,][0-9]{3})*)|([0-9]+))?([\.]([0-9]+))?$/, 612 "alertText": "* Invalid floating decimal number" 613 }, 614 "date": { 615 // Check if date is valid by leap year 616 "func": function (field) { 617 var pattern = new RegExp(/^(\d{4})[\/\-\.](0?[1-9]|1[012])[\/\-\.](0?[1-9]|[12][0-9]|3[01])$/); 618 var match = pattern.exec(field.val()); 619 if (match == null) 620 return false; 621 622 var year = match[1];
623 var month = match[2]*1; 624 var day = match[3]*1; 625 var date = new Date(year, month - 1, day); // because months starts from 0. 626 627 return (date.getFullYear() == year && date.getMonth() == (month - 1) && date.getDate() == day); 628 }, 629 "alertText": "* Invalid date, must be in YYYY-MM-DD format" 630 }, 631 "ipv4": { 632 "regex": /^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))[.]){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/, 633 "alertText": "* Invalid IP address" 634 }, 635 "url": { 636 "regex": /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([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])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, 637 "alertText": "* Invalid URL" 638 }, 639 "onlyNumberSp": { 640 "regex": /^[0-9\ ]+$/, 641 "alertText": "* Numbers only" 642 }, 643 "onlyLetterSp": { 644 "regex": /^[a-zA-Z\ \']+$/, 645 "alertText": "* Letters only" 646 }, 647 "onlyLetterAccentSp":{ 648 "regex": /^[a-z\u00C0-\u017F\ ]+$/i, 649 "alertText": "* Letters only (accents allowed)" 650 }, 651 "onlyLetterNumber": { 652 "regex": /^[0-9a-zA-Z]+$/, 653 "alertText": "* No special characters allowed" 654 }, 655 // --- CUSTOM RULES -- Those are specific to the demos, they can be removed or changed to your likings 656 "ajaxUserCall": { 657 "url": "ajaxValidateFieldUser", 658 // you may want to pass extra data on the ajax call 659 "extraData": "name=eric", 660 "alertText": "* This user is already taken", 661 "alertTextLoad": "* Validating, please wait" 662 }, 663 "ajaxUserCallPhp": { 664 "url": "phpajax/ajaxValidateFieldUser.php", 665 // you may want to pass extra data on the ajax call 666 "extraData": "name=eric", 667 // if you provide an "alertTextOk", it will show as a green prompt when the field validates 668 "alertTextOk": "* This username is available", 669 "alertText": "* This user is already taken", 670 "alertTextLoad": "* Validating, please wait" 671 }, 672 "ajaxNameCall": { 673 // remote json service location 674 "url": "ajaxValidateFieldName", 675 // error 676 "alertText": "* This name is already taken", 677 // if you provide an "alertTextOk", it will show as a green prompt when the field validates 678 "alertTextOk": "* This name is available", 679 // speaks by itself 680 "alertTextLoad": "* Validating, please wait" 681 }, 682 "ajaxNameCallPhp": { 683 // remote json service location 684 "url": "phpajax/ajaxValidateFieldName.php", 685 // error 686 "alertText": "* This name is already taken", 687 // speaks by itself 688 "alertTextLoad": "* Validating, please wait" 689 }, 690 "validate2fields": { 691 "alertText": "* Please input HELLO" 692 }, 693 //tls warning:homegrown not fielded 694 "dateFormat":{ 695 "regex": /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?
695:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/, 696 "alertText": "* Invalid Date" 697 }, 698 //tls warning:homegrown not fielded 699 "dateTimeFormat": { 700 "regex": /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/, 701 "alertText": "* Invalid Date or Date Format", 702 "alertText2": "Expected Format: ", 703 "alertText3": "mm/dd/yyyy hh:mm:ss AM|PM or ", 704 "alertText4": "yyyy-mm-dd hh:mm:ss AM|PM" 705 } 706 }; 707 708 } 709 }; 710 711 $.validationEngineLanguage.newLang(); 712 713})(jQuery); 714 715/* 716 * Inline Form Validation Engine 2.6.2, jQuery plugin 717 * 718 * Copyright(c) 2010, Cedric Dugas 719 * http://www.position-absolute.com 720 * 721 * 2.0 Rewrite by Olivier Refalo 722 * http://www.crionics.com 723 * 724 * Form validation engine allowing custom regex rules to be added. 725 * Licensed under the MIT License 726 */ 727 (function($) { 728 729 "use strict"; 730 731 var methods = { 732 733 /** 734 * Kind of the constructor, called before any action 735 * @param {Map} user options 736 */ 737 init: function(options) { 738 var form = this; 739 if (!form.data('jqv') || form.data('jqv') == null ) { 740 options = methods._saveOptions(form, options); 741 // bind all formError elements to close on click 742 $(document).on("click", ".formError", function() { 743 $(this).fadeOut(150, function() { 744 // remove prompt once invisible 745 $(this).closest('.formErrorOuter').remove(); 746 }); 747 }); 748 } 749 return this; 750 }, 751 /** 752 * Attachs jQuery.validationEngine to form.submit and field.blur events 753 * Takes an optional params: a list of options 754 * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"}); 755 */ 756 attach: function(userOptions) { 757 758 var form = this; 759 var options; 760 761 if(userOptions) 762 options = methods._saveOptions(form, userOptions); 763 else 764 options = form.data('jqv'); 765 766 options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class"; 767 if (options.binded) { 768 769 // delegate fields 770 form.on(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent); 771 form.on("click", "["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent); 772 form.on(options.validationEventTrigger,"["+options.validateAttribute+"*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent); 773 } 774 if (options.autoPositionUpdate) { 775 $(window).bind("resize", { 776 "noAnimation": true, 777 "formElem": form 778 }, methods.updatePromptsPosition); 779 } 780 form.on("click","a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick); 781 form.removeData('jqv_submitButton'); 782 783 // bind form.submit 784 form.on("submit", methods._onSubmitEvent); 785 return this; 786 }, 787 /** 788 * Unregisters any bindings that may point to jQuery.validaitonEngine 789 */ 790 detach: function() { 791 792 var form = this; 793 var options = form.data('jqv'); 794 795 // unbind fields 796 form.off(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent); 797 form.off("click", "["+options.validateAttribute+"*=validate][type=c
797heckbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent); 798 form.off(options.validationEventTrigger,"["+options.validateAttribute+"*=validate][class*=datepicker]", methods._onFieldEvent); 799 800 // unbind form.submit 801 form.off("submit", methods._onSubmitEvent); 802 form.removeData('jqv'); 803 804 form.off("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick); 805 form.removeData('jqv_submitButton'); 806 807 if (options.autoPositionUpdate) 808 $(window).off("resize", methods.updatePromptsPosition); 809 810 return this; 811 }, 812 /** 813 * Validates either a form or a list of fields, shows prompts accordingly. 814 * Note: There is no ajax form validation with this method, only field ajax validation are evaluated 815 * 816 * @return true if the form validates, false if it fails 817 */ 818 validate: function(userOptions) { 819 var element = $(this); 820 var valid = null; 821 var options; 822
823 if (element.is("form") || element.hasClass("validationEngineContainer")) { 824 if (element.hasClass('validating')) { 825 // form is already validating. 826 // Should abort old validation and start new one. I don't know how to implement it. 827 return false; 828 } else { 829 element.addClass('validating'); 830 if(userOptions) 831 options = methods._saveOptions(element, userOptions); 832 else 833 options = element.data('jqv'); 834 var valid = methods._validateFields(this); 835 836 // If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again 837 setTimeout(function(){ 838 element.removeClass('validating'); 839 }, 100); 840 if (valid && options.onSuccess) { 841 options.onSuccess(); 842 } else if (!valid && options.onFailure) { 843 options.onFailure(); 844 } 845 } 846 } else if (element.is('form') || element.hasClass('validationEngineContainer')) { 847 element.removeClass('validating'); 848 } else { 849 // field validation 850 var form = element.closest('form, .validationEngineContainer'), 851 options = (form.data('jqv')) ? form.data('jqv') : $.validationEngine.defaults, 852 valid = methods._validateField(element, options); 853 } 854 if(options.onValidationComplete) { 855 // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing 856 return !!options.onValidationComplete(form, valid); 857 } 858 return valid; 859 }, 860 /** 861 * Redraw prompts position, useful when you change the DOM state when validating 862 */ 863 updatePromptsPosition: function(event) { 864 865 if (event && this == window) { 866 var form = event.data.formElem; 867 var noAnimation = event.data.noAnimation; 868 } 869 else 870 var form = $(this.closest('form, .validationEngineContainer')); 871 872 var options = form.data('jqv'); 873 // No option, take default one 874 if (!options) 875 options = methods._saveOptions(form, options); 876 form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each(function(){ 877 var field = $(this); 878 if (options.prettySelect && field.is(":hidden")) 879 field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix); 880 var prompt = methods._getPrompt(field); 881 var promptText = $(prompt).find(".formErrorContent").html(); 882 883 if(prompt) 884 methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation); 885 }); 886 return this; 887 }, 888 /** 889 * Displays a prompt on a element. 890 * Note that the element needs an id! 891 * 892 * @param {String} promptText html text to display type 893 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 894 * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight 895 */ 896 showPrompt: function(promptText, type, promptPosition, showArrow) { 897 var form = this.closest('form, .validationEngineContainer'); 898 var options = form.data('jqv'); 899 // No option, take default one 900 if(!options) 901 options = methods._saveOptions(this, options); 902 if(promptPosition) 903 options.promptPosition=promptPosition; 904 options.showArrow = showArrow==true; 905 906 methods._showPrompt(this, promptText, type, false, options); 907 return this; 908 }, 909 /** 910 * Closes form error prompts, CAN be invidual 911 */ 912 hide: function() { 913 var form = $(this).closest('form, .validationEngineContainer'); 914 var options = form.data('jqv'); 915 // No option, take default one 916 if (!options) 917 options = methods._saveOptions(form, options); 918 var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3; 919 var closingtag; 920
921 if($(this).is("form") || $(this).hasClass("validationEngineContainer")) { 922 closingtag = "parentForm"+methods._getClassName($(this).attr("id")); 923 } else { 924 closingtag = methods._getClassName($(this).attr("id")) +"formError"; 925 } 926 $('.'+closingtag).fadeTo(fadeDuration, options.hideOpacity, function() { 927 $(this).closest('.formErrorOuter').remove(); 928 }); 929 return this; 930 }, 931 /** 932 * Closes all error prompts on the page 933 */ 934 hideAll: function() { 935 936 var form = this; 937 var options = form.data('jqv'); 938 var duration = options ? options.fadeDuration:300; 939 $('.formError').fadeTo(duration, 300, function() { 940 $(this).closest('.formErrorOuter').remove(); 941 }); 942 return this; 943 }, 944 /** 945 * Typically called when user exists a field using tab or a mouse click, triggers a field 946 * validation 947 */ 948 _onFieldEvent: function(event) { 949 var field = $(this); 950 var form = field.closest('form, .validationEngineContainer'); 951 var options = form.data('jqv'); 952 // No option, take default one 953 if (!options) 954 options = methods._saveOptions(form, options); 955 options.eventTrigger = "field"; 956 957 if (options.notEmpty == true){ 958 959 if(field.val().length > 0){ 960 // validate the current field 961 window.setTimeout(function() { 962 methods._validateField(field, options); 963 }, (event.data) ? event.data.delay : 0); 964 965 } 966 967 }else{ 968 969 // validate the current field 970 window.setTimeout(function() { 971 methods._validateField(field, options); 972 }, (event.data) ? event.data.delay : 0); 973 974 } 975 976 977 978 979 }, 980 /** 981 * Called when the form is submited, shows prompts accordingly 982 * 983 * @param {jqObject} 984 * form 985 * @return false if form submission needs to be cancelled 986 */ 987 _onSubmitEvent: function() { 988 var form = $(this); 989 var options = form.data('jqv'); 990 991 //check if it is trigger from skipped button 992 if (form.data("jqv_submitButton")){ 993 var submitButton = $("#" + form.data("jqv_submitButton")); 994 if (submitButton){ 995 if (submitButton.length > 0){
996 if (submitButton.hasClass("validate-skip") || submitButton.attr("data-validation-engine-skip") == "true") 997 return true; 998 } 999 } 1000 } 1001 1002 options.eventTrigger = "submit"; 1003 1004 // validate each field 1005 // (- skip field ajax validation, not necessary IF we will perform an ajax form validation) 1006 var r=methods._validateFields(form); 1007 1008 if (r && options.ajaxFormValidation) { 1009 methods._validateFormWithAjax(form, options); 1010 // cancel form auto-submission - process with async call onAjaxFormComplete 1011 return false; 1012 } 1013 1014 if(options.onValidationComplete) { 1015 // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing 1016 return !!options.onValidationComplete(form, r); 1017 } 1018 return r; 1019 }, 1020 /** 1021 * Return true if the ajax field validations passed so far 1022 * @param {Object} options 1023 * @return true, is all ajax validation passed so far (remember ajax is async) 1024 */ 1025 _checkAjaxStatus: function(options) { 1026 var status = true; 1027 $.each(options.ajaxValidCache, function(key, value) { 1028 if (!value) { 1029 status = false; 1030 // break the each 1031 return false; 1032 } 1033 }); 1034 return status; 1035 }, 1036 1037 /** 1038 * Return true if the ajax field is validated 1039 * @param {String} fieldid 1040 * @param {Object} options 1041 * @return true, if validation passed, false if false or doesn't exist 1042 */ 1043 _checkAjaxFieldStatus: function(fieldid, options) { 1044 return options.ajaxValidCache[fieldid] == true; 1045 }, 1046 /** 1047 * Validates form fields, shows prompts accordingly 1048 * 1049 * @param {jqObject} 1050 * form 1051 * @param {skipAjaxFieldValidation} 1052 * boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked 1053 * 1054 * @return true if form is valid, false if not, undefined if ajax form validation is done 1055 */ 1056 _validateFields: function(form) { 1057 var options = form.data('jqv'); 1058 1059 // this variable is set to true if an error is found 1060 var errorFound = false; 1061 1062 // Trigger hook, start validation 1063 form.trigger("jqv.form.validating"); 1064 // first, evaluate status of non ajax fields 1065 var first_err=null; 1066 form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each( function() { 1067 var field = $(this); 1068 var names = []; 1069 if ($.inArray(field.attr('name'), names) < 0) { 1070 errorFound |= methods._validateField(field, options); 1071 if (errorFound && first_err==null) 1072 if (field.is(":hidden") && options.prettySelect) 1073 first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); 1074 else { 1075 1076 //Check if we need to adjust what element to show the prompt on 1077 //and and such scroll to instead 1078 if(field.data('jqv-prompt-at') instanceof jQuery ){ 1079 field = field.data('jqv-prompt-at'); 1080 } else if(field.data('jqv-prompt-at')) { 1081 field = $(field.data('jqv-prompt-at')); 1082 } 1083 first_err=field; 1084 } 1085 if (options.doNotShowAllErrosOnSubmit) 1086 return false; 1087 names.push(field.attr('name')); 1088 1089 //if option set, stop checking validation rules after one error is found 1090 if(options.showOneMessage == true && errorFound){ 1091 return false; 1092 } 1093 } 1094 }); 1095 1096 // second, check to see if all ajax calls completed ok 1097 // errorFound |= !methods._checkAjaxStatus(options); 1098 1099 // third, check status and scroll the container accordingly 1100 form.trigger("jqv.form.result", [errorFound]); 1101 1102 if (errorFound) { 1103 if (options.scroll) { 1104 var destination=first_err.offset().top; 1105 var fixleft = first_err.offset().left; 1106 1107 //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10) 1108 var positionType=options.promptPosition; 1109 if (typeof(positionType)=='string' && positionType.indexOf(":")!=-1) 1110 positionType=positionType.substring(0,positionType.indexOf(":")); 1111 1112 if (positionType!="bottomRight" && positionType!="bottomLeft") { 1113 var prompt_err= methods._getPrompt(first_err); 1114 if (prompt_err) { 1115 destination=prompt_err.offset().top; 1116 } 1117 } 1118 1119 // Offset the amount the page scrolls by an amount in px to accomodate fixed elements at top of page 1120 if (options.scrollOffset) { 1121 destination -= options.scrollOffset; 1122 } 1123 1124 // get the position of the first error, there should be at least one, no need to check this 1125 //var destination = form.find(".formError:not('.greenPopup'):first").offset().top; 1126 if (options.isOverflown) { 1127 var overflowDIV = $(options.overflownDIV); 1128 if(!overflowDIV.length) return false; 1129 var scrollContainerScroll = overflowDIV.scrollTop(); 1130 var scrollContainerPos = -parseInt(overflowDIV.offset().top); 1131
1132 destination += scrollContainerScroll + scrollContainerPos - 5; 1133 var scrollContainer = $(options.overflownDIV).filter(":not(:animated)"); 1134 1135 scrollContainer.animate({ scrollTop: destination }, 1100, function(){ 1136 if(options.focusFirstField) first_err.focus(); 1137 }); 1138 1139 } else { 1140 $("html, body").animate({ 1141 scrollTop: destination 1142 }, 1100, function(){ 1143 if(options.focusFirstField) first_err.focus(); 1144 }); 1145 $("html, body").animate({scrollLeft: fixleft},1100) 1146 } 1147 1148 } else if(options.focusFirstField) 1149 first_err.focus(); 1150 return false; 1151 } 1152 return true; 1153 }, 1154 /** 1155 * This method is called to perform an ajax form validation. 1156 * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true 1157 * 1158 * @param {jqObject} form 1159 * @param {Map} options 1160 */ 1161 _validateFormWithAjax: function(form, options) { 1162 1163 var data = form.serialize(); 1164 var type = (options.ajaxFormValidationMethod) ? options.ajaxFormValidationMethod : "GET"; 1165 var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action"); 1166 var dataType = (options.dataType) ? options.dataType : "json"; 1167 $.ajax({ 1168 type: type, 1169 url: url, 1170 cache: false, 1171 dataType: dataType, 1172 data: data, 1173 form: form, 1174 methods: methods, 1175 options: options, 1176 beforeSend: function() { 1177 return options.onBeforeAjaxFormValidation(form, options); 1178 }, 1179 error: function(data, transport) { 1180 if (options.onFailure) { 1181 options.onFailure(data, transport); 1182 } else { 1183 methods._ajaxError(data, transport); 1184 } 1185 }, 1186 success: function(json) { 1187 if ((dataType == "json") && (json !== true)) { 1188 // getting to this case doesn't necessary means that the form is invalid 1189 // the server may return green or closing prompt actions 1190 // this flag helps figuring it out 1191 var errorInForm=false; 1192 for (var i = 0; i < json.length; i++) { 1193 var value = json[i]; 1194 1195 var errorFieldId = value[0]; 1196 var errorField = $($("#" + errorFieldId)[0]); 1197 1198 // make sure we found the element 1199 if (errorField.length == 1) { 1200 1201 // promptText or selector 1202 var msg = value[2]; 1203 // if the field is valid 1204 if (value[1] == true) { 1205 1206 if (msg == "" || !msg){ 1207 // if for some reason, status==true and error="", just close the prompt 1208 methods._closePrompt(errorField); 1209 } else { 1210 // the field is valid, but we are displaying a green prompt 1211 if (options.allrules[msg]) { 1212 var txt = options.allrules[msg].alertTextOk; 1213 if (txt) 1214 msg = txt; 1215 } 1216 if (options.showPrompts) methods._showPrompt(errorField, msg, "pass", false, options, true); 1217 } 1218 } else { 1219 // the field is invalid, show the red error prompt 1220 errorInForm|=true; 1221 if (options.allrules[msg]) { 1222 var txt = options.allrules[msg].alertText; 1223 if (txt) 1224 msg = txt; 1225 } 1226 if(options.showPrompts) methods._showPrompt(errorField, msg, "", false, options, true); 1227 } 1228 } 1229 } 1230 options.onAjaxFormComplete(!errorInForm, form, json, options); 1231 } else 1232 options.onAjaxFormComplete(true, form, json, options); 1233 1234 } 1235 }); 1236 1237 }, 1238 /** 1239 * Validates field, shows prompts accordingly 1240 * 1241 * @param {jqObject} 1242 * field 1243 * @param {Array[String]} 1244 * field's validation rules 1245 * @param {Map} 1246 * user options 1247 * @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.) 1248 */ 1249 _validateField: function(field, options, skipAjaxValidation) { 1250 if (!field.attr("id")) { 1251 field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter); 1252 ++$.validationEngine.fieldIdCounter; 1253 } 1254 1255 if(field.hasClass(options.ignoreFieldsWithClass)) 1256 return false; 1257 1258 if (!options.validateNonVisibleFields && (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden"))) 1259 return false; 1260 1261 var rulesParsing = field.attr(options.validateAttribute); 1262 var getRules = /validate\[(.*)\]/.exec(rulesParsing); 1263 1264 if (!getRules) 1265 return false; 1266 var str = getRules[1]; 1267 var rules = str.split(/\[|,|\]/); 1268
1269 // true if we ran the ajax validation, tells the logic to stop messing with prompts 1270 var isAjaxValidator = false; 1271 var fieldName = field.attr("name"); 1272 var promptText = ""; 1273 var promptType = ""; 1274 var required = false; 1275 var limitErrors = false; 1276 options.isError = false; 1277 options.showArrow = true; 1278 1279 // If the programmer wants to limit the amount of error messages per field, 1280 if (options.maxErrorsPerField > 0) { 1281 limitErrors = true; 1282 } 1283 1284 var form = $(field.closest("form, .validationEngineContainer")); 1285 // Fix for adding spaces in the rules 1286 for (var i = 0; i < rules.length; i++) { 1287 rules[i] = rules[i].replace(" ", ""); 1288 // Remove any parsing errors 1289 if (rules[i] === '') { 1290 delete rules[i]; 1291 } 1292 } 1293 1294 for (var i = 0, field_errors = 0; i < rules.length; i++) { 1295 1296 // If we are limiting errors, and have hit the max, break 1297 if (limitErrors && field_errors >= options.maxErrorsPerField) { 1298 // If we haven't hit a required yet, check to see if there is one in the validation rules for this 1299 // field and that it's index is greater or equal to our current index 1300 if (!required) { 1301 var have_required = $.inArray('required', rules); 1302 required = (have_required != -1 && have_required >= i); 1303 } 1304 break; 1305 } 1306 1307 1308 var errorMsg = undefined; 1309 switch (rules[i]) { 1310 1311 case "required": 1312 required = true; 1313 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required); 1314 break; 1315 case "custom": 1316 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom); 1317 break; 1318 case "groupRequired": 1319 // Check is its the first of group, if not, reload validation with first field 1320 // AND continue normal validation on present field 1321 var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]"; 1322 var firstOfGroup = form.find(classGroup).eq(0); 1323 if(firstOfGroup[0] != field[0]){ 1324 1325 methods._validateField(firstOfGroup, options, skipAjaxValidation); 1326 options.showArrow = true; 1327 1328 } 1329 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired); 1330 if(errorMsg) required = true; 1331 options.showArrow = false; 1332 break; 1333 case "ajax": 1334 // AJAX defaults to returning it's loading message 1335 errorMsg = methods._ajax(field, rules, i, options); 1336 if (errorMsg) { 1337 promptType = "load"; 1338 } 1339 break; 1340 case "minSize": 1341 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize); 1342 break; 1343 case "maxSize": 1344 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize); 1345 break; 1346 case "min": 1347 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min); 1348 break; 1349 case "max": 1350 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max); 1351 break; 1352 case "past": 1353 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past); 1354 break; 1355 case "future": 1356 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future); 1357 break; 1358 case "dateRange": 1359 var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; 1360 options.firstOfGroup = form.find(classGroup).eq(0); 1361 options.secondOfGroup = form.find(classGroup).eq(1); 1362 1363 //if one entry out of the pair has value then proceed to run through validation 1364 if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { 1365 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange); 1366 } 1367 if (errorMsg) required = true; 1368 options.showArrow = false; 1369 break; 1370 1371 case "dateTimeRange": 1372 var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; 1373 options.firstOfGroup = form.find(classGroup).eq(0); 1374 options.secondOfGroup = form.find(classGroup).eq(1); 1375 1376 //if one entry out of the pair has value then proceed to run through validation 1377 if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { 1378 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange); 1379 } 1380 if (errorMsg) required = true; 1381 options.showArrow = false; 1382 break; 1383 case "maxCheckbox": 1384 field = $(form.find("input[name='" + fieldName + "']")); 1385 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox); 1386 break; 1387 case "minCheckbox": 1388 field = $(form.find("input[name='" + fieldName + "']")); 1389 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox); 1390 break; 1391 case "equals": 1392 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals); 1393 break; 1394 case "funcCall": 1395 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall); 1396 break; 1397 case "creditCard": 1398 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard); 1399 break; 1400 case "condRequired": 1401 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._c
1401ondRequired); 1402 if (errorMsg !== undefined) { 1403 required = true; 1404 } 1405 break; 1406 case "funcCallRequired": 1407 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCallRequired); 1408 if (errorMsg !== undefined) { 1409 required = true; 1410 } 1411 break; 1412 1413 default: 1414 } 1415 1416 var end_validation = false; 1417 1418 // If we were passed back an message object, check what the status was to determine what to do 1419 if (typeof errorMsg == "object") { 1420 switch (errorMsg.status) { 1421 case "_break": 1422 end_validation = true; 1423 break; 1424 // If we have an error message, set errorMsg to the error message 1425 case "_error": 1426 errorMsg = errorMsg.message; 1427 break; 1428 // If we want to throw an error, but not show a prompt, return early with true 1429 case "_error_no_prompt": 1430 return true; 1431 break; 1432 // Anything else we continue on 1433 default: 1434 break; 1435 } 1436 } 1437 1438 //funcCallRequired, first in rules, and has error, skip anything else 1439 if( i==0 && str.indexOf('funcCallRequired')==0 && errorMsg !== undefined ){ 1440 promptText += errorMsg + "<br/>"; 1441 options.isError=true; 1442 field_errors++; 1443 end_validation=true; 1444 } 1445 1446 // If it has been specified that validation should end now, break 1447 if (end_validation) { 1448 break; 1449 } 1450 1451 // If we have a string, that means that we have an error, so add it to the error message. 1452 if (typeof errorMsg == 'string') { 1453 promptText += errorMsg + "<br/>"; 1454 options.isError = true; 1455 field_errors++; 1456 } 1457 } 1458 // If the rules required is not added, an empty field is not validated 1459 //the 3rd condition is added so that even empty password fields should be equal 1460 //otherwise if one is filled and another left empty, the "equal" condition would fail 1461 //which does not make any sense 1462 if(!required && !(field.val()) && field.val().length < 1 && $.inArray('equals', rules) < 0) options.isError = false; 1463 1464 // Hack for radio/checkbox group button, the validation go into the 1465 // first radio/checkbox of the group 1466 var fieldType = field.prop("type"); 1467 var positionType=field.data("promptPosition") || options.promptPosition; 1468 1469 if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) { 1470 if(positionType === 'inline') { 1471 field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:last")); 1472 } else { 1473 field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first")); 1474 } 1475 options.showArrow = options.showArrowOnRadioAndCheckbox; 1476 } 1477 1478 if(field.is(":hidden") && options.prettySelect) { 1479 field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); 1480 } 1481 1482 if (options.isError && options.showPrompts){ 1483 methods._showPrompt(field, promptText, promptType, false, options); 1484 }else{ 1485 if (!isAjaxValidator) methods._closePrompt(field); 1486 } 1487 1488 if (!isAjaxValidator) { 1489 field.trigger("jqv.field.result", [field, options.isError, promptText]); 1490 } 1491 1492 /* Record error */ 1493 var errindex = $.inArray(field[0], options.InvalidFields); 1494 if (errindex == -1) { 1495 if (options.isError) 1496 options.InvalidFields.push(field[0]); 1497 } else if (!options.isError) { 1498 options.InvalidFields.splice(errindex, 1); 1499 } 1500 1501 methods._handleStatusCssClasses(field, options); 1502 1503 /* run callback function for each field */ 1504 if (options.isError && options.onFieldFailure) 1505 options.onFieldFailure(field); 1506 1507 if (!options.isError && options.onFieldSuccess) 1508 options.onFieldSuccess(field); 1509 1510 return options.isError; 1511 }, 1512 /** 1513 * Handling css classes of fields indicating result of validation 1514 * 1515 * @param {jqObject} 1516 * field 1517 * @param {Array[String]} 1518 * field's validation rules 1519 * @private 1520 */ 1521 _handleStatusCssClasses: function(field, options) { 1522 /* remove all classes */ 1523 if(options.addSuccessCssClassToField) 1524 field.removeClass(options.addSuccessCssClassToField); 1525 1526 if(options.addFailureCssClassToField) 1527 field.removeClass(options.addFailureCssClassToField); 1528 1529 /* Add classes */ 1530 if (options.addSuccessCssClassToField && !options.isError) 1531 field.addClass(options.addSuccessCssClassToField); 1532 1533 if (options.addFailureCssClassToField && options.isError) 1534 field.addClass(options.addFailureCssClassToField); 1535 }, 1536 1537 /******************** 1538 * _getErrorMessage 1539 * 1540 * @param form 1541 * @param field 1542 * @param rule 1543 * @param rules 1544 * @param i 1545 * @param options 1546 * @param originalValidationMethod 1547 * @return {*} 1548 * @private 1549 */ 1550 _getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) { 1551 // If we are using the custon validation type, build the index for the rule. 1552 // Otherwise if we are doing a function call, make the call and return the object 1553 // that is passed back. 1554 var rule_index = jQuery.inArray(rule, rules); 1555 if (rule === "custom" || rule === "funcCall" || rule === "funcCallRequired") { 1556 var custom_validation_type = rules[rule_index + 1]; 1557 rule = rule + "[" + custom_validation_type + "]"; 1558 // Delete the rule from the rules array so that it doesn't try to call the 1559 // same rule over again 1560 delete(rules[rule_index]); 1561 } 1562 // Change the rule to the composite rule, if it was different from the original 1563 var alteredRule = rule; 1564 1565 1566 var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class"); 1567 var element_classes_array = element_classes.split(" "); 1568 1569 // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form 1570 var errorMsg; 1571 if (rule == "future" || rule == "past" || rule == "maxCheckbox" || rule == "minCheckbox") { 1572 errorMsg = originalValidationMethod(form, field, rules, i, options); 1573 } else { 1574 errorMsg = originalValidationMethod(field, rules, i, options); 1575 } 1576 1577 // If the original validation method returned an error and we have a custom error message, 1578 // return the custom message instead. Otherwise return the original error message. 1579 if (errorMsg != undefined) { 1580 var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options); 1581 if (custom_message) errorMsg = custom_message; 1582 } 1583 return errorMsg; 1584 1585 }, 1586 _getCustomErrorMessage:function (field, classes, rule, options) { 1587 var custom_message = false;
1588 var validityProp = /^custom\[.*\]$/.test(rule) ? methods._validityProp["custom"] : methods._validityProp[rule]; 1589 // If there is a validityProp for this rule, check to see if the field has an attribute for it 1590 if (validityProp != undefined) { 1591 custom_message = field.attr("data-errormessage-"+validityProp); 1592 // If there was an error message for it, return the message 1593 if (custom_message != undefined) 1594 return custom_message; 1595 } 1596 custom_message = field.attr("data-errormessage"); 1597 // If there is an inline custom error message, return it 1598 if (custom_message != undefined) 1599 return custom_message; 1600 var id = '#' + field.attr("id"); 1601 // If we have custom messages for the element's id, get the message for the rule from the id. 1602 // Otherwise, if we have custom messages for the element's classes, use the first class message we find instead. 1603 if (typeof options.custom_error_messages[id] != "undefined" && 1604 typeof options.custom_error_messages[id][rule] != "undefined" ) { 1605 custom_message = options.custom_error_messages[id][rule]['message']; 1606 } else if (classes.length > 0) { 1607 for (var i = 0; i < classes.length && classes.length > 0; i++) { 1608 var element_class = "." + classes[i]; 1609 if (typeof options.custom_error_messages[element_class] != "undefined" && 1610 typeof options.custom_error_messages[element_class][rule] != "undefined") { 1611 custom_message = options.custom_error_messages[element_class][rule]['message']; 1612 break; 1613 } 1614 } 1615 } 1616 if (!custom_message && 1617 typeof options.custom_error_messages[rule] != "undefined" && 1618 typeof options.custom_error_messages[rule]['message'] != "undefined"){ 1619 custom_message = options.custom_error_messages[rule]['message']; 1620 } 1621 return custom_message; 1622 }, 1623 _validityProp: { 1624 "required": "value-missing", 1625 "custom": "custom-error", 1626 "groupRequired": "value-missing", 1627 "ajax": "custom-error", 1628 "minSize": "range-underflow", 1629 "maxSize": "range-overflow", 1630 "min": "range-underflow", 1631 "max": "range-overflow", 1632 "past": "type-mismatch", 1633 "future": "type-mismatch", 1634 "dateRange": "type-mismatch", 1635 "dateTimeRange": "type-mismatch", 1636 "maxCheckbox": "range-overflow", 1637 "minCheckbox": "range-underflow", 1638 "equals": "pattern-mismatch", 1639 "funcCall": "custom-error", 1640 "funcCallRequired": "custom-error", 1641 "creditCard": "pattern-mismatch", 1642 "condRequired": "value-missing" 1643 }, 1644 /** 1645 * Required validation 1646 * 1647 * @param {jqObject} field 1648 * @param {Array[String]} rules 1649 * @param {int} i rules index 1650 * @param {Map} 1651 * user options 1652 * @param {bool} condRequired flag when method is used for internal purpose in condRequired check 1653 * @return an error string if validation failed 1654 */ 1655 _required: function(field, rules, i, options, condRequired) { 1656 switch (field.prop("type")) { 1657 case "radio": 1658 case "checkbox": 1659 // new validation style to only check dependent field 1660 if (condRequired) { 1661 if (!field.prop('checked')) { 1662 return options.allrules[rules[i]].alertTextCheckboxMultiple; 1663 } 1664 break; 1665 } 1666 // old validation style 1667 var form = field.closest("form, .validationEngineContainer"); 1668 var name = field.attr("name"); 1669 if (form.find("input[name='" + name + "']:checked").size() == 0) { 1670 if (form.find("input[name='" + name + "']:visible").size() == 1) 1671 return options.allrules[rules[i]].alertTextCheckboxe; 1672 else 1673 return options.allrules[rules[i]].alertTextCheckboxMultiple; 1674 } 1675 break; 1676 case "text": 1677 case "password": 1678 case "textarea": 1679 case "file": 1680 case "select-one": 1681 case "select-multiple": 1682 default: 1683 var field_val = $.trim( field.val() ); 1684 var dv_placeholder = $.trim( field.attr("data-validation-placeholder") ); 1685 var placeholder = $.trim( field.attr("placeholder") ); 1686 if ( 1687 ( !field_val ) 1688 || ( dv_placeholder && field_val == dv_placeholder ) 1689 || ( placeholder && field_val == placeholder ) 1690 ) { 1691 return options.allrules[rules[i]].alertText; 1692 } 1693 break; 1694 } 1695 }, 1696 /** 1697 * Validate that 1 from the group field is required 1698 * 1699 * @param {jqObject} field 1700 * @param {Array[String]} rules 1701 * @param {int} i rules index 1702 * @param {Map} 1703 * user options 1704 * @return an error string if validation failed 1705 */ 1706 _groupRequired: function(field, rules, i, options) { 1707 var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]"; 1708 var isValid = false; 1709 // Check all fields from the group 1710 field.closest("form, .validationEngineContainer").find(classGroup).each(function(){ 1711 if(!methods._required($(this), rules, i, options)){ 1712 isValid = true; 1713 return false; 1714 } 1715 }); 1716 1717 if(!isValid) { 1718 return options.allrules[rules[i]].alertText; 1719 } 1720 }, 1721 /** 1722 * Validate rules 1723 * 1724 * @param {jqObject} field 1725 * @param {Array[String]} rules 1726 * @param {int} i rules index 1727 * @param {Map} 1728 * user options 1729 * @return an error string if validation failed 1730 */ 1731 _custom: function(field, rules, i, options) { 1732 var customRule = rules[i + 1];
1733 var rule = options.allrules[customRule]; 1734 var fn; 1735 if(!rule) { 1736 alert("jqv:custom rule not found - "+customRule); 1737 return; 1738 } 1739 1740 if(rule["regex"]) { 1741 var ex=rule.regex; 1742 if(!ex) { 1743 alert("jqv:custom regex not found - "+customRule); 1744 return; 1745 } 1746 var pattern = new RegExp(ex); 1747 1748 if (!pattern.test(field.val())) return options.allrules[customRule].alertText; 1749 1750 } else if(rule["func"]) { 1751 fn = rule["func"]; 1752 1753 if (typeof(fn) !== "function") { 1754 alert("jqv:custom parameter 'function' is no function - "+customRule); 1755 return; 1756 } 1757 1758 if (!fn(field, rules, i, options)) 1759 return options.allrules[customRule].alertText; 1760 } else { 1761 alert("jqv:custom type not allowed "+customRule); 1762 return; 1763 } 1764 }, 1765 /** 1766 * Validate custom function outside of the engine scope 1767 * 1768 * @param {jqObject} field 1769 * @param {Array[String]} rules 1770 * @param {int} i rules index 1771 * @param {Map} 1772 * user options 1773 * @return an error string if validation failed 1774 */ 1775 _funcCall: function(field, rules, i, options) { 1776 var functionName = rules[i + 1]; 1777 var fn; 1778 if(functionName.indexOf('.') >-1) 1779 { 1780 var namespaces = functionName.split('.'); 1781 var scope = window; 1782 while(namespaces.length) 1783 { 1784 scope = scope[namespaces.shift()]; 1785 } 1786 fn = scope; 1787 } 1788 else 1789 fn = window[functionName] || options.customFunctions[functionName]; 1790 if (typeof(fn) == 'function') 1791 return fn(field, rules, i, options); 1792 1793 }, 1794 _funcCallRequired: function(field, rules, i, options) { 1795 return methods._funcCall(field,rules,i,options); 1796 }, 1797 /** 1798 * Field match 1799 * 1800 * @param {jqObject} field 1801 * @param {Array[String]} rules 1802 * @param {int} i rules index 1803 * @param {Map} 1804 * user options 1805 * @return an error string if validation failed 1806 */ 1807 _equals: function(field, rules, i, options) { 1808 var equalsField = rules[i + 1]; 1809 1810 if (field.val() != $("#" + equalsField).val()) 1811 return options.allrules.equals.alertText; 1812 }, 1813 /** 1814 * Check the maximum size (in characters) 1815 * 1816 * @param {jqObject} field 1817 * @param {Array[String]} rules 1818 * @param {int} i rules index 1819 * @param {Map} 1820 * user options 1821 * @return an error string if validation failed 1822 */ 1823 _maxSize: function(field, rules, i, options) { 1824 var max = rules[i + 1]; 1825 var len = field.val().length; 1826 1827 if (len > max) { 1828 var rule = options.allrules.maxSize; 1829 return rule.alertText + max + rule.alertText2; 1830 } 1831 }, 1832 /** 1833 * Check the minimum size (in characters) 1834 * 1835 * @param {jqObject} field 1836 * @param {Array[String]} rules 1837 * @param {int} i rules index 1838 * @param {Map} 1839 * user options 1840 * @return an error string if validation failed 1841 */ 1842 _minSize: function(field, rules, i, options) { 1843 var min = rules[i + 1]; 1844 var len = field.val().length; 1845 1846 if (len < min) { 1847 var rule = options.allrules.minSize; 1848 return rule.alertText + min + rule.alertText2; 1849 } 1850 }, 1851 /** 1852 * Check number minimum value 1853 * 1854 * @param {jqObject} field 1855 * @param {Array[String]} rules 1856 * @param {int} i rules index 1857 * @param {Map} 1858 * user options 1859 * @return an error string if validation failed 1860 */ 1861 _min: function(field, rules, i, options) { 1862 var min = parseFloat(rules[i + 1]); 1863 var len = parseFloat(field.val()); 1864 1865 if (len < min) { 1866 var rule = options.allrules.min; 1867 if (rule.alertText2) return rule.alertText + min + rule.alertText2; 1868 return rule.alertText + min; 1869 } 1870 }, 1871 /** 1872 * Check number maximum value 1873 * 1874 * @param {jqObject} field 1875 * @param {Array[String]} rules 1876 * @param {int} i rules index 1877 * @param {Map} 1878 * user options 1879 * @return an error string if validation failed 1880 */ 1881 _max: function(field, rules, i, options) { 1882 var max = parseFloat(rules[i + 1]); 1883 var len = parseFloat(field.val()); 1884 1885 if (len >max ) { 1886 var rule = options.allrules.max; 1887 if (rule.alertText2) return rule.alertText + max + rule.alertText2; 1888 //orefalo: to review, also do the translations 1889 return rule.alertText + max; 1890 } 1891 }, 1892 /** 1893 * Checks date is in the past 1894 * 1895 * @param {jqObject} field 1896 * @param {Array[String]} rules 1897 * @param {int} i rules index 1898 * @param {Map} 1899 * user options 1900 * @return an error string if validation failed 1901 */ 1902 _past: function(form, field, rules, i, options) { 1903 1904 var p=rules[i + 1];
1905 var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']")); 1906 var pdate; 1907 1908 if (p.toLowerCase() == "now") { 1909 pdate = new Date(); 1910 } else if (undefined != fieldAlt.val()) { 1911 if (fieldAlt.is(":disabled")) 1912 return; 1913 pdate = methods._parseDate(fieldAlt.val()); 1914 } else { 1915 pdate = methods._parseDate(p); 1916 } 1917 var vdate = methods._parseDate(field.val()); 1918 1919 if (vdate > pdate ) { 1920 var rule = options.allrules.past; 1921 if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 1922 return rule.alertText + methods._dateToString(pdate); 1923 } 1924 }, 1925 /** 1926 * Checks date is in the future 1927 * 1928 * @param {jqObject} field 1929 * @param {Array[String]} rules 1930 * @param {int} i rules index 1931 * @param {Map} 1932 * user options 1933 * @return an error string if validation failed 1934 */ 1935 _future: function(form, field, rules, i, options) { 1936 1937 var p=rules[i + 1]; 1938 var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']")); 1939 var pdate; 1940 1941 if (p.toLowerCase() == "now") { 1942 pdate = new Date(); 1943 } else if (undefined != fieldAlt.val()) { 1944 if (fieldAlt.is(":disabled")) 1945 return; 1946 pdate = methods._parseDate(fieldAlt.val()); 1947 } else { 1948 pdate = methods._parseDate(p); 1949 } 1950 var vdate = methods._parseDate(field.val()); 1951 1952 if (vdate < pdate ) { 1953 var rule = options.allrules.future; 1954 if (rule.alertText2) 1955 return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 1956 return rule.alertText + methods._dateToString(pdate); 1957 } 1958 }, 1959 /** 1960 * Checks if valid date 1961 * 1962 * @param {string} date string 1963 * @return a bool based on determination of valid date 1964 */ 1965 _isDate: function (value) { 1966 var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/); 1967 return dateRegEx.test(value); 1968 }, 1969 /** 1970 * Checks if valid date time 1971 * 1972 * @param {string} date string 1973 * @return a bool based on determination of valid date time 1974 */ 1975 _isDateTime: function (value){ 1976 var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/); 1977 return dateTimeRegEx.test(value); 1978 }, 1979 //Checks if the start date is before the end date 1980 //returns true if end is later than start 1981 _dateCompare: function (start, end) { 1982 return (new Date(start.toString()) < new Date(end.toString())); 1983 }, 1984 /** 1985 * Checks date range 1986 * 1987 * @param {jqObject} first field name 1988 * @param {jqObject} second field name 1989 * @return an error string if validation failed 1990 */ 1991 _dateRange: function (field, rules, i, options) { 1992 //are not both populated 1993 if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) { 1994 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1995 } 1996 1997 //are not both dates 1998 if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) { 1999 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 2000 } 2001 2002 //are both dates but range is off 2003 if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) { 2004 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 2005 } 2006 }, 2007 /** 2008 * Checks date time range 2009 * 2010 * @param {jqObject} first field name 2011 * @param {jqObject} second field name 2012 * @return an error string if validation failed 2013 */ 2014 _dateTimeRange: function (field, rules, i, options) { 2015 //are not both populated 2016 if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) { 2017 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 2018 } 2019 //are not both dates 2020 if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) { 2021 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 2022 } 2023 //are both dates but range is off 2024 if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) { 2025 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 2026 } 2027 }, 2028 /** 2029 * Max number of checkbox selected 2030 * 2031 * @param {jqObject} field 2032 * @param {Array[String]} rules 2033 * @param {int} i rules index 2034 * @param {Map} 2035 * user options 2036 * @return an error string if validation failed 2037 */ 2038 _maxCheckbox: function(form, field, rules, i, options) { 2039 2040 var nbCheck = rules[i + 1];
2041 var groupname = field.attr("name"); 2042 var groupSize = form.find("input[name='" + groupname + "']:checked").size(); 2043 if (groupSize > nbCheck) { 2044 options.showArrow = false; 2045 if (options.allrules.maxCheckbox.alertText2) 2046 return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2; 2047 return options.allrules.maxCheckbox.alertText; 2048 } 2049 }, 2050 /** 2051 * Min number of checkbox selected 2052 * 2053 * @param {jqObject} field 2054 * @param {Array[String]} rules 2055 * @param {int} i rules index 2056 * @param {Map} 2057 * user options 2058 * @return an error string if validation failed 2059 */ 2060 _minCheckbox: function(form, field, rules, i, options) { 2061 2062 var nbCheck = rules[i + 1]; 2063 var groupname = field.attr("name"); 2064 var groupSize = form.find("input[name='" + groupname + "']:checked").size(); 2065 if (groupSize < nbCheck) { 2066 options.showArrow = false; 2067 return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2; 2068 } 2069 }, 2070 /** 2071 * Checks that it is a valid credit card number according to the 2072 * Luhn checksum algorithm. 2073 * 2074 * @param {jqObject} field 2075 * @param {Array[String]} rules 2076 * @param {int} i rules index 2077 * @param {Map} 2078 * user options 2079 * @return an error string if validation failed 2080 */ 2081 _creditCard: function(field, rules, i, options) { 2082 //spaces and dashes may be valid characters, but must be stripped to calculate the checksum. 2083 var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, ''); 2084 2085 var numDigits = cardNumber.length; 2086 if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) { 2087 2088 var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String(); 2089 do { 2090 digit = parseInt(cardNumber.charAt(i)); 2091 luhn += (pos++ % 2 == 0) ? digit * 2 : digit; 2092 } while (--i >= 0) 2093 2094 for (i = 0; i < luhn.length; i++) { 2095 sum += parseInt(luhn.charAt(i)); 2096 } 2097 valid = sum % 10 == 0; 2098 } 2099 if (!valid) return options.allrules.creditCard.alertText; 2100 }, 2101 /** 2102 * Ajax field validation 2103 * 2104 * @param {jqObject} field 2105 * @param {Array[String]} rules 2106 * @param {int} i rules index 2107 * @param {Map} 2108 * user options 2109 * @return nothing! the ajax validator handles the prompts itself 2110 */ 2111 _ajax: function(field, rules, i, options) { 2112 2113 var errorSelector = rules[i + 1]; 2114 var rule = options.allrules[errorSelector]; 2115 var extraData = rule.extraData; 2116 var extraDataDynamic = rule.extraDataDynamic; 2117 var data = { 2118 "fieldId" : field.attr("id"), 2119 "fieldValue" : field.val() 2120 }; 2121 2122 if (typeof extraData === "object") { 2123 $.extend(data, extraData); 2124 } else if (typeof extraData === "string") { 2125 var tempData = extraData.split("&"); 2126 for(var i = 0; i < tempData.length; i++) { 2127 var values = tempData[i].split("="); 2128 if (values[0] && values[0]) { 2129 data[values[0]] = values[1]; 2130 } 2131 } 2132 } 2133 2134 if (extraDataDynamic) { 2135 var tmpData = []; 2136 var domIds = String(extraDataDynamic).split(","); 2137 for (var i = 0; i < domIds.length; i++) { 2138 var id = domIds[i]; 2139 if ($(id).length) { 2140 var inputValue = field.closest("form, .validationEngineContainer").find(id).val(); 2141 var keyValue = id.replace('#', '') + '=' + escape(inputValue); 2142 data[id.replace('#', '')] = inputValue; 2143 } 2144 } 2145 } 2146 2147 // If a field change event triggered this we want to clear the cache for this ID 2148 if (options.eventTrigger == "field") { 2149 delete(options.ajaxValidCache[field.attr("id")]); 2150 } 2151 2152 // If there is an error or if the the field is already validated, do not re-execute AJAX 2153 if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) { 2154 $.ajax({
2155 type: options.ajaxFormValidationMethod, 2156 url: rule.url, 2157 cache: false, 2158 dataType: "json", 2159 data: data, 2160 field: field, 2161 rule: rule, 2162 methods: methods, 2163 options: options, 2164 beforeSend: function() {}, 2165 error: function(data, transport) { 2166 if (options.onFailure) { 2167 options.onFailure(data, transport); 2168 } else { 2169 methods._ajaxError(data, transport); 2170 } 2171 }, 2172 success: function(json) { 2173 2174 // asynchronously called on success, data is the json answer from the server 2175 var errorFieldId = json[0]; 2176 //var errorField = $($("#" + errorFieldId)[0]); 2177 var errorField = $("#"+ errorFieldId).eq(0); 2178 2179 // make sure we found the element 2180 if (errorField.length == 1) { 2181 var status = json[1]; 2182 // read the optional msg from the server 2183 var msg = json[2]; 2184 if (!status) { 2185 // Houston we got a problem - display an red prompt 2186 options.ajaxValidCache[errorFieldId] = false; 2187 options.isError = true; 2188 2189 // resolve the msg prompt 2190 if(msg) { 2191 if (options.allrules[msg]) { 2192 var txt = options.allrules[msg].alertText; 2193 if (txt) { 2194 msg = txt; 2195 } 2196 } 2197 } 2198 else 2199 msg = rule.alertText; 2200 2201 if (options.showPrompts) methods._showPrompt(errorField, msg, "", true, options); 2202 } else { 2203 options.ajaxValidCache[errorFieldId] = true; 2204 2205 // resolves the msg prompt 2206 if(msg) { 2207 if (options.allrules[msg]) { 2208 var txt = options.allrules[msg].alertTextOk; 2209 if (txt) { 2210 msg = txt; 2211 } 2212 } 2213 } 2214 else 2215 msg = rule.alertTextOk; 2216 2217 if (options.showPrompts) { 2218 // see if we should display a green prompt 2219 if (msg) 2220 methods._showPrompt(errorField, msg, "pass", true, options); 2221 else 2222 methods._closePrompt(errorField); 2223 } 2224 2225 // If a submit form triggered this, we want to re-submit the form 2226 if (options.eventTrigger == "submit") 2227 field.closest("form").submit(); 2228 } 2229 } 2230 errorField.trigger("jqv.field.result", [errorField, options.isError, msg]); 2231 } 2232 }); 2233 2234 return rule.alertTextLoad; 2235 } 2236 }, 2237 /** 2238 * Common method to handle ajax errors 2239 * 2240 * @param {Object} data 2241 * @param {Object} transport 2242 */ 2243 _ajaxError: function(data, transport) { 2244 if(data.status == 0 && transport == null) 2245 alert("The page is not served from a server! ajax call failed"); 2246 else if(typeof console != "undefined") 2247 console.log("Ajax error: " + data.status + " " + transport); 2248 }, 2249 /** 2250 * date -> string 2251 * 2252 * @param {Object} date 2253 */ 2254 _dateToString: function(date) { 2255 return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate(); 2256 }, 2257 /** 2258 * Parses an ISO date 2259 * @param {String} d 2260 */ 2261 _parseDate: function(d) { 2262 2263 var dateParts = d.split("-"); 2264 if(dateParts==d) 2265 dateParts = d.split("/"); 2266 if(dateParts==d) { 2267 dateParts = d.split("."); 2268 return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]); 2269 } 2270 return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]); 2271 }, 2272 /** 2273 * Builds or updates a prompt with the given information 2274 * 2275 * @param {jqObject} field 2276 * @param {String} promptText html text to display type 2277 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 2278 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 2279 * @param {Map} options user options 2280 */ 2281 _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) { 2282 //Check if we need to adjust what element to show the prompt on 2283 if(field.data('jqv-prompt-at') instanceof jQuery ){ 2284 field = field.data('jqv-prompt-at'); 2285 } else if(field.data('jqv-prompt-at')) { 2286 field = $(field.data('jqv-prompt-at')); 2287 } 2288 2289 var prompt = methods._getPrompt(field); 2290 // The ajax submit errors are not see has an error in the form, 2291 // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time 2292 // Because no error was found befor submitting 2293 if(ajaxform) prompt = false; 2294 // Check that there is indded text 2295 if($.trim(promptText)){ 2296 if (prompt) 2297 methods._updatePrompt(field, prompt, promptText, type, ajaxed, options); 2298 else 2299 methods._buildPrompt(field, promptText, type, ajaxed, options); 2300 } 2301 }, 2302 /** 2303 * Builds and shades a prompt for the given field. 2304 * 2305 * @param {jqObject} field 2306 * @param {String} promptText html text to display type 2307 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 2308 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 2309 * @param {Map} options user options 2310 */ 2311 _buildPrompt: function(field, promptText, type, ajaxed, options) { 2312 2313 // create the prompt 2314 var prompt = $('<div>'); 2315 prompt.addClass(methods._getClassName(field.attr("id")) + "formError"); 2316 // add a class name to identify the parent form of the prompt 2317 prompt.addClass("parentForm"+methods._getClassName(field.closest('form, .validationEngineContainer').attr("id"))); 2318 prompt.addClass("formError"); 2319 2320 switch (type) { 2321 case "pass": 2322 prompt.addClass("greenPopup"); 2323 break; 2324 case "load": 2325 prompt.addClass("blackPopup"); 2326 break; 2327 default: 2328 /* it has error */ 2329 //alert("unknown popup type:"+type); 2330 } 2331 if (ajaxed) 2332 prompt.addClass("ajaxed"); 2333 2334 // create the prompt content 2335 var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt); 2336 2337 // determine position type 2338 var positionType=field.data("promptPosition") || options.promptPosition; 2339 2340 // create the css arrow pointing at the field 2341 // note that there is no triangle on max-checkbox and radio 2342 if (options.showArrow) { 2343 var arrow = $('<div>').addClass("formErrorArrow"); 2344 2345 //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10) 2346 if (typeof(positionType)=='string') 2347 { 2348 var pos=positionType.indexOf(":"); 2349 if(pos!=-1) 2350 positionType=positionType.substring(0,pos); 2351 } 2352 2353 switch (positionType) { 2354 case "bottomLeft": 2355 case "bottomRight": 2356 prompt.find(".formErrorContent").before(arrow); 2357 arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>'); 2358 break; 2359 case "topLeft": 2360 case "topRight": 2361 arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>'); 2362 prompt.append(arrow); 2363 break; 2364 } 2365 } 2366 // Add custom prompt class 2367 if (options.addPromptClass) 2368 prompt.addClass(options.addPromptClass); 2369 2370 // Add custom prompt class defined in element 2371 var requiredOverride = field.attr('data-required-class'); 2372 if(requiredOverride !== undefined) { 2373 prompt.addClass(requiredOverride); 2374 } else { 2375 if(options.prettySelect) { 2376 if($('#' + field.attr('id')).next().is('select')) { 2377 var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class'); 2378 if(prettyOverrideClass !== undefined) { 2379 prompt.addClass(prettyOverrideClass); 2380 } 2381 } 2382 } 2383 } 2384 2385 prompt.css({ 2386 "opacity": 0 2387 }); 2388 if(positionType === 'inline') { 2389 prompt.addClass("inline"); 2390 if(typeof field.attr('data-prompt-target') !== 'undefined' && $('#'+field.attr('data-prompt-target')).length > 0) { 2391 prompt.appendTo($('#'+field.attr('data-prompt-target'))); 2392 } else { 2393 field.after(prompt); 2394 } 2395 } else { 2396 field.before(prompt); 2397 } 2398 2399 var pos = methods._calculatePosition(field, prompt, options); 2400 prompt.css({ 2401 'position': positionType === 'inline' ? 'relative' : 'absolute', 2402 "top": pos.callerTopPosition, 2403 "left": pos.callerleftPosition, 2404 "marginTop": pos.marginTopSize, 2405 "opacity": 0 2406 }).data("callerField", field); 2407 2408 2409 if (options.autoHidePrompt) { 2410 setTimeout(function(){ 2411 prompt.animate({ 2412 "opacity": 0 2413 },function(){ 2414 prompt.closest('.formErrorOuter').remove(); 2415 });
2416 }, options.autoHideDelay); 2417 } 2418 return prompt.animate({ 2419 "opacity": 0.87 2420 }); 2421 }, 2422 /** 2423 * Updates the prompt text field - the field for which the prompt 2424 * @param {jqObject} field 2425 * @param {String} promptText html text to display type 2426 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 2427 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 2428 * @param {Map} options user options 2429 */ 2430 _updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) { 2431 2432 if (prompt) { 2433 if (typeof type !== "undefined") { 2434 if (type == "pass") 2435 prompt.addClass("greenPopup"); 2436 else 2437 prompt.removeClass("greenPopup"); 2438 2439 if (type == "load") 2440 prompt.addClass("blackPopup"); 2441 else 2442 prompt.removeClass("blackPopup"); 2443 } 2444 if (ajaxed) 2445 prompt.addClass("ajaxed"); 2446 else 2447 prompt.removeClass("ajaxed"); 2448 2449 prompt.find(".formErrorContent").html(promptText); 2450 2451 var pos = methods._calculatePosition(field, prompt, options); 2452 var css = {"top": pos.callerTopPosition, 2453 "left": pos.callerleftPosition, 2454 "marginTop": pos.marginTopSize, 2455 "opacity": 0.87}; 2456 2457 if (noAnimation) 2458 prompt.css(css); 2459 else 2460 prompt.animate(css); 2461 } 2462 }, 2463 /** 2464 * Closes the prompt associated with the given field 2465 * 2466 * @param {jqObject} 2467 * field 2468 */ 2469 _closePrompt: function(field) { 2470 var prompt = methods._getPrompt(field); 2471 if (prompt) 2472 prompt.fadeTo("fast", 0, function() { 2473 prompt.closest('.formErrorOuter').remove(); 2474 }); 2475 }, 2476 closePrompt: function(field) { 2477 return methods._closePrompt(field); 2478 }, 2479 /** 2480 * Returns the error prompt matching the field if any 2481 * 2482 * @param {jqObject} 2483 * field 2484 * @return undefined or the error prompt (jqObject) 2485 */ 2486 _getPrompt: function(field) { 2487 var formId = $(field).closest('form, .validationEngineContainer').attr('id'); 2488 var className = methods._getClassName(field.attr("id")) + "formError"; 2489 var match = $("." + methods._escapeExpression(className) + '.parentForm' + methods._getClassName(formId))[0]; 2490 if (match) 2491 return $(match); 2492 }, 2493 /** 2494 * Returns the escapade classname 2495 * 2496 * @param {selector} 2497 * className 2498 */ 2499 _escapeExpression: function (selector) { 2500 return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1"); 2501 }, 2502 /** 2503 * returns true if we are in a RTLed document 2504 * 2505 * @param {jqObject} field 2506 */ 2507 isRTL: function(field) 2508 { 2509 var $document = $(document); 2510 var $body = $('body'); 2511 var rtl = 2512 (field && field.hasClass('rtl')) || 2513 (field && (field.attr('dir') || '').toLowerCase()==='rtl') || 2514 $document.hasClass('rtl') || 2515 ($document.attr('dir') || '').toLowerCase()==='rtl' || 2516 $body.hasClass('rtl') || 2517 ($body.attr('dir') || '').toLowerCase()==='rtl'; 2518 return Boolean(rtl); 2519 }, 2520 /** 2521 * Calculates prompt position 2522 * 2523 * @param {jqObject} 2524 * field 2525 * @param {jqObject} 2526 * the prompt 2527 * @param {Map} 2528 * options 2529 * @return positions 2530 */ 2531 _calculatePosition: function (field, promptElmt, options) { 2532 2533 var promptTopPosition, promptleftPosition, marginTopSize; 2534 var fieldWidth = field.width(); 2535 var fieldLeft = field.position().left; 2536 var fieldTop = field.position().top; 2537 var fieldHeight = field.height(); 2538 var promptHeight = promptElmt.height(); 2539 2540 2541 // is the form contained in an overflown container? 2542 promptTopPosition = promptleftPosition = 0; 2543 // compensation for the arrow 2544 marginTopSize = -promptHeight; 2545 2546 2547 //prompt positioning adjustment support 2548 //now you can adjust prompt position 2549 //usage: positionType:Xshift,Yshift 2550 //for example: 2551 // bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally 2552 // topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top 2553 //You can use +pixels, - pixels. If no sign is provided than + is default. 2554 var positionType=field.data("promptPosition") || options.promptPosition; 2555 var shift1=""; 2556 var shift2=""; 2557 var shiftX=0; 2558 var shiftY=0; 2559 if (typeof(positionType)=='string') { 2560 //do we have any position adjustments ? 2561 if (positionType.indexOf(":")!=-1) { 2562 shift1=positionType.substring(positionType.indexOf(":")+1); 2563 positionType=positionType.substring(0,positionType.indexOf(":")); 2564 2565 //if any advanced positioning will be needed (percents or something else) - parser should be added here 2566 //for now we use simple parseInt() 2567 2568 //do we have second parameter? 2569 if (shift1.indexOf(",") !=-1) { 2570 shift2=shift1.substring(shift1.indexOf(",") +1); 2571 shift1=shift1.substring(0,shift1.indexOf(",")); 2572 shiftY=parseInt(shift2); 2573 if (isNaN(shiftY)) shiftY=0; 2574 }; 2575 2576 shiftX=parseInt(shift1); 2577 if (isNaN(shift1)) shift1=0; 2578 2579 }; 2580 }; 2581 2582 2583 switch (positionType) { 2584 default: 2585 case "topRight": 2586 promptleftPosition += fieldLeft + fieldWidth - 27; 2587 promptTopPosition += fieldTop; 2588 break; 2589 2590 case "topLeft": 2591 promptTopPosition += fieldTop; 2592 promptleftPosition += fieldLeft; 2593 break; 2594 2595 case "centerRight": 2596 promptTopPosition = fieldTop+4; 2597 marginTopSize = 0; 2598 promptleftPosition= fieldLeft + field.outerWidth(true)+5; 2599 break; 2600 case "centerLeft": 2601 promptleftPosition = fieldLeft - (promptElmt.width() + 2); 2602 promptTopPosition = fieldTop+4; 2603 marginTopSize = 0; 2604 2605 break; 2606 2607 case "bottomLeft": 2608 promptTopPosition = fieldTop + field.height() + 5; 2609 marginTopSize = 0; 2610 promptleftPosition = fieldLeft; 2611 break; 2612 case "bottomRight": 2613 promptleftPosition = fieldLeft + fieldWidth - 27; 2614 promptTopPosition = fieldTop + field.height() + 5; 2615 marginTopSize = 0; 2616 break; 2617 case "inline": 2618 promptleftPosition = 0; 2619 promptTopPosition = 0; 2620 marginTopSize = 0; 2621 }; 2622 2623 2624 2625 //apply adjusments if any 2626 promptleftPosition += shiftX; 2627 promptTopPosition += shiftY; 2628 2629 return { 2630 "callerTopPosition": promptTopPosition + "px", 2631 "callerleftPosition": promptleftPosition + "px", 2632 "marginTopSize": marginTopSize + "px" 2633 }; 2634 }, 2635 /** 2636 * Saves the user options and variables in the form.data 2637 * 2638 * @param {jqObject} 2639 * form - the form where the user option should be saved 2640 * @param {Map} 2641 * options - the user options 2642 * @return the user options (extended from the defaults) 2643 */ 2644 _saveOptions: function(form, options) { 2645 2646 // is there a language localisation ? 2647 if ($.validationEngineLanguage) 2648 var allRules = $.validationEngineLanguage.allRules; 2649 else 2650 $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page"); 2651 // --- Internals DO NOT TOUCH or OVERLOAD --- 2652 // validation rules and i18 2653 $.validationEngine.defaults.allrules = allRules; 2654 2655 var userOptions = $.extend(true,{},$.validationEngine.defaults,options); 2656 2657 form.data('jqv', userOptions); 2658 return userOptions; 2659 }, 2660 2661 /** 2662 * Removes forbidden characters from class name 2663 * @param {String} className 2664 */ 2665 _getClassName: function(className) { 2666 if(className) 2667 return className.replace(/:/g, "_").replace(/\./g, "_"); 2668 }, 2669 /** 2670 * Escape special character for jQuery selector 2671 * http://totaldev.com/content/escaping-characters-get-valid-jquery-id 2672 * @param {String} selector 2673 */ 2674 _jqSelector: function(str){ 2675 return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); 2676 }, 2677 /** 2678 * Conditionally required field 2679 * 2680 * @param {jqObject} field 2681 * @param {Array[String]} rules 2682 * @param {int} i rules index 2683 * @param {Map} 2684 * user options 2685 * @return an error string if validation failed 2686 */ 2687 _condRequired: function(field, rules, i, options) { 2688 var idx, dependingField; 2689 2690 for(idx = (i + 1); idx < rules.length; idx++) { 2691 dependingField = jQuery("#" + rules[idx]).first(); 2692 2693 /* Use _required for determining wether dependingField has a value. 2694 * There is logic there for handling all field types, and default value; so we won't replicate that here 2695 * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462) 2696 */ 2697 if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) == undefined) { 2698 /* We now know any of the depending fields has a value, 2699 * so we can validate this field as per normal required code 2700 */ 2701 return methods._required(field, ["required"], 0, options); 2702 } 2703 } 2704 }, 2705 2706 _submitButtonClick: function(event) { 2707 var button = $(this); 2708 var form = button.closest('form, .validationEngineContainer'); 2709 form.data("jqv_submitButton", button.attr("id")); 2710 } 2711 }; 2712 2713 /** 2714 * Plugin entry point. 2715 * You may pass an action as a parameter or a list of options. 2716 * if none, the init and attach methods are being called. 2717 * Remember: if you pass options, the attached method is NOT called automatically 2718 * 2719 * @param {String} 2720 * method (optional) action 2721 */ 2722 $.fn.validationEngine = function(method) { 2723 2724 var form = $(this); 2725 if(!form[0]) return form; // stop here if the form does not exist 2726 2727 if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) { 2728 2729 // make sure init is called once 2730 if(method != "showPrompt" && method != "hide" && method != "hideAll") 2731 methods.init.apply(form); 2732 2733 return methods[method].apply(form, Array.prototype.slice.call(arguments, 1)); 2734 } else if (typeof method == 'object' || !method) { 2735 2736 // default constructor with or without arguments 2737 methods.init.apply(form, arguments); 2738 return methods.attach.apply(form); 2739 } else { 2740 $.error('Method ' + method + ' does not exist in jQuery.validationEngine'); 2741 } 2742 }; 2743 2744 2745 2746 // LEAK GLOBAL OPTIONS 2747 $.validationEngine= {fieldIdCounter: 0,defaults:{ 2748 2749 // Name of the event triggering field validation 2750 validationEventTrigger: "blur", 2751 // Automatically scroll viewport to the first error 2752 scroll: true, 2753 // Focus on the first input 2754 focusFirstField:true, 2755 // Show prompts, set to false to disable prompts 2756 showPrompts: true, 2757 // Should we attempt to validate non-visible input fields contained in the form? (Useful in cases of tabbed containers, e.g. jQuery-UI tabs) 2758 validateNonVisibleFields: false, 2759 // ignore the validation for fields with this specific class (Useful in cases of tabbed containers AND hidden fields we don't want to validate) 2760 ignoreFieldsWithClass: 'ignoreMe', 2761 // Opening box position, possible locations are: topLeft, 2762 // topRight, bottomLeft, centerRight, bottomRight, inline 2763 // inline gets inserted after the validated field or into an element specified in data-prompt-target
2764 promptPosition: "topRight", 2765 bindMethod:"bind", 2766 // internal, automatically set to true when it parse a _ajax rule 2767 inlineAjax: false, 2768 // if set to true, the form data is sent asynchronously via ajax to the form.action url (get) 2769 ajaxFormValidation: false, 2770 // The url to send the submit ajax validation (default to action) 2771 ajaxFormValidationURL: false, 2772 // HTTP method used for ajax validation 2773 ajaxFormValidationMethod: 'get', 2774 // Ajax form validation callback method: boolean onComplete(form, status, errors, options) 2775 // retuns false if the form.submit event needs to be canceled. 2776 onAjaxFormComplete: $.noop, 2777 // called right before the ajax call, may return false to cancel 2778 onBeforeAjaxFormValidation: $.noop, 2779 // Stops form from submitting and execute function assiciated with it 2780 onValidationComplete: false, 2781 2782 // Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages 2783 doNotShowAllErrosOnSubmit: false, 2784 // Object where you store custom messages to override the default error messages 2785 custom_error_messages:{}, 2786 // true if you want to validate the input fields on blur event 2787 binded: true, 2788 // set to true if you want to validate the input fields on blur only if the field it's not empty 2789 notEmpty: false, 2790 // set to true, when the prompt arrow needs to be displayed 2791 showArrow: true, 2792 // set to false, determines if the prompt arrow should be displayed when validating 2793 // checkboxes and radio buttons 2794 showArrowOnRadioAndCheckbox: false, 2795 // did one of the validation fail ? kept global to stop further ajax validations 2796 isError: false, 2797 // Limit how many displayed errors a field can have 2798 maxErrorsPerField: false, 2799 2800 // Caches field validation status, typically only bad status are created. 2801 // the array is used during ajax form validation to detect issues early and prevent an expensive submit 2802 ajaxValidCache: {}, 2803 // Auto update prompt position after window resize 2804 autoPositionUpdate: false, 2805 2806 InvalidFields: [], 2807 onFieldSuccess: false, 2808 onFieldFailure: false, 2809 onSuccess: false, 2810 onFailure: false, 2811 validateAttribute: "class", 2812 addSuccessCssClassToField: "", 2813 addFailureCssClassToField: "", 2814 2815 // Auto-hide prompt 2816 autoHidePrompt: false, 2817 // Delay before auto-hide 2818 autoHideDelay: 10000, 2819 // Opacity of hidden prompt !Added by John Janes on Sept. 18th 2014 2820 hideOpacity: 0, 2821 // Fade out duration while hiding the validations 2822 fadeDuration: 0.3, 2823 // Use Prettify select library 2824 prettySelect: false, 2825 // Add css class on prompt 2826 addPromptClass : "", 2827 // Custom ID uses prefix 2828 usePrefix: "", 2829 // Custom ID uses suffix 2830 useSuffix: "", 2831 // Only show one message per error prompt 2832 showOneMessage: false 2833 }}; 2834 $(function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"}); 2835})(jQuery); 2836 2837/* 2838 * QualityInfo Sitetool Subscription Order Form Portlet 2839 * qisitetoolsof.js 2840 * 2841 * Dual licensed under the MIT (MIT-LICENSE.txt) 2842 * 2843 * jQueryDate: 10/19/2021 10:12:00 $ 2844 * $Rev: 2.0 $ 2845 */ 2846var stateVersion = 0; 2847var loadingArray = []; 2848var urlState = ""; 2849var wasState = false; 2850var isPoppingState = false; 2851var pushHistory = 0; 2852var replaceHistory = 1; 2853 2854/** 2855 * Performs all necessary portlet initialization 2856 * by calling required init methods. 2857 */ 2858async function init_Sof_Portlet() { 2859 // Show AJAX loading image 2860 toggle_Sof_Loading("initializing",true,"Initializing<br/>Please wait..."); 2861 2862 // Initialize view. 2863 await init_Sof_View(); 2864 2865 // Initialize the tools state. 2866 init_Sof_ToolState(); 2867 2868 // Initialize event bindings. 2869 init_Sof_EventBindings(); 2870 2871 // In this portlet, the state needs to be 2872 // popped prior to initialization complete. 2873 // The customer email needs to be in the 2874 // form so the user can be logged in. 2875 pop_Sof_State(); 2876 2877 // Initialization is now complete. 2878 init_Sof_Complete(); 2879 2880 // Hide AJAX loading image. 2881 toggle_Sof_Loading("initializing",false); 2882} 2883 2884/** 2885 * Performs view initialization. Initializes page 2886 * elements such as the GUI layout, tabs, etc. This 2887 * function also calls initialization of filtering 2888 * options. 2889 * 2890 * @returns 2891 */ 2892async function init_Sof_View() { 2893 return new Promise(async (resolve) => { 2894 // Initialize form validation. 2895 jQuery("#sof_form").validationEngine( 2896 'attach', 2897 { 2898 scroll: false, 2899 promptPosition: "bottomLeft", 2900 showArrow: true 2901 } 2902 ); 2903 2904 // Initialize all bootstrap selects. 2905 jQuery('.selectpicker').selectpicker({ 2906 dropupAuto: false 2907 }); 2908 2909 // Retrieve all Publication and Geographic Area 2910 // category subscription items. 2911 await get_Sof_Subitems("Publications"); 2912 await get_Sof_Subitems("Areas"); 2913 resolve(true); 2914 }); 2915} 2916 2917/** 2918 * Initializes the state array from the URL 2919 * query string. If state is found, browser 2920 * history is replaced. If no state is found 2921 * state will be created later and placed on 2922 * the URL. 2923 * 2924 * @returns 2925 */ 2926function init_Sof_ToolState() { 2927 var urlState = decodeURIComponent(window.location.search.toString().replace("?","")); 2928 var parmMap = getJsonFromUrl(urlState); 2929 2930 // Determine if there are state parameters or not. 2931 if(Object.keys(parmMap).length >= 1 && Object.keys(parmMap)[0] != '') { 2932 // Set that there was state parameters at startup. All 2933 // state will be pushed to URL later in startup. 2934 wasState = true; 2935 } else { 2936 // Push the state and replace browser history. 2937 push_Sof_State(replaceHistory); 2938 } 2939} 2940 2941/** 2942 * Performs UI control event bindings. 2943 * 2944 * @returns 2945 */ 2946function init_Sof_EventBindings() { 2947 // Add listener for back button event 2948 window.addEventListener("popstate", function() { 2949 urlState = decodeURIComponent(window.location.search.toString().replace("?","")); 2950 pop_Sof_State(); 2951 }); 2952 2953 // Add expand/collapse click event 2954 jQuery("i.expander").on("click", function() { 2955 const expander = jQuery(this); 2956 expander.nextAll(".expandable:first").slideToggle("fast"); 2957 2958 // Toggle between plus and minus icons 2959 expander.toggleClass("fa-plus-square fa-minus-square"); 2960 }); 2961 2962 jQuery("#sofSignInBtn").on("click", function(evt) { 2963 var isErrors = jQuery("#sof_CustEmail").validationEngine('validate'); 2964 2965 if(!isErrors) { 2966 evt.preventDefault(); 2967 jQuery('#sof_CustEmail').val(jQuery('#sof_CustEmail').val().toLowerCase()); 2968 get_Sof_Subscriber(); 2969 push_Sof_State(replaceHistory); 2970 } 2971 }); 2972 2973 jQuery("#sofSignOutBtn").on("click", function(evt) { 2974 evt.preventDefault(); 2975 reset_Sof_Form(); 2976 }); 2977 2978 $("#sofUnsubscribeBtn").on("click", function(evt) { 2979 evt.preventDefault(); 2980 do_Sof_Unsubscribe(); 2981 }); 2982 2983 $("#sofSubmitBtn").on("click", function(evt) { 2984 var isNotErrors = $("#sof_form").validationEngine('validate'); 2985 2986 evt.preventDefault(); 2987 2988 if(isNotErrors) { 2989 do_Sof_Persist_Order(); 2990 } 2991 2992 return false;
2993 }); 2994 2995 jQuery('input[id^="pcat_"]').on("click", function() { 2996 var cbTotal = jQuery(this).parent().next('.expandable').find(':checkbox').length; 2997 var h6Title = jQuery(this).attr("title"); 2998 2999 if(jQuery(this).is(':checked')) { 3000 jQuery(this).parent().next('.expandable').find(':checkbox').prop('checked', true); 3001 jQuery(this).next().html(h6Title+" - "+cbTotal+ " Publication(s) Selected"); 3002 } else { 3003 cbTotal = 0; 3004 jQuery(this).parent().next('.expandable').find(':checkbox').prop('checked', false); 3005 jQuery(this).next().html(h6Title); 3006 } 3007 }); 3008 3009 jQuery('input[id^="pcats_"]').on("click", function() { 3010 var cbTotal = jQuery(this).parent().parent().parent().find(':checkbox').length, 3011 cbChecked = jQuery(this).parent().parent().parent().find(':checkbox:checked').length; 3012 3013 var cbElemH6 = jQuery(this).parent().parent().parent().prev(); 3014 var h6Title = cbElemH6.find("input").attr("title"); 3015 3016 if(cbChecked > 0) { 3017 if(cbChecked == cbTotal) { 3018 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop("indeterminate", false); 3019 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop('checked', true); 3020 } else { 3021 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop('checked', false); 3022 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop("indeterminate", true); 3023 } 3024 cbElemH6.find("span").html(h6Title+" - "+cbChecked+ " Publication(s) Selected"); 3025 } else { 3026 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop("indeterminate", false); 3027 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="pcat_"]').prop('checked', false); 3028 cbElemH6.find("span").html(h6Title); 3029 } 3030 }); 3031 3032 jQuery('input[id^="gcat_"]').on("click", function() { 3033 var cbTotal = jQuery(this).parent().next('.expandable').find(':checkbox').length; 3034 var h6Title = jQuery(this).attr("title"); 3035 3036 if(jQuery(this).is(':checked')) { 3037 jQuery(this).parent().next('.expandable').find(':checkbox').prop('checked', true); 3038 if(h6Title != "Statewide") { 3039 jQuery(this).next().html(h6Title+" - "+cbTotal+ " County(ies) Selected"); 3040 } else { 3041 jQuery(this).next().html(h6Title); 3042 } 3043 } else { 3044 cbTotal = 0; 3045 jQuery(this).parent().next('.expandable').find(':checkbox').prop('checked', false); 3046 jQuery(this).next().html(h6Title); 3047 } 3048 }); 3049 3050 jQuery('input[id^="gcats_"]').on("click", function() { 3051 var cbTotal = jQuery(this).parent().parent().parent().find(':checkbox').length, 3052 cbChecked = jQuery(this).parent().parent().parent().find(':checkbox:checked').length; 3053 3054 var cbElemH6 = jQuery(this).parent().parent().parent().prev(); 3055 var h6Title = cbElemH6.find("input").attr("title"); 3056 3057 if(cbChecked > 0) { 3058 if(cbChecked == cbTotal) { 3059 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop("indeterminate", false); 3060 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop('checked', true); 3061 } else { 3062 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop('checked', false); 3063 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop("indeterminate", true); 3064 } 3065 if(h6Title != "Statewide") { 3066 cbElemH6.find("span").html(h6Title+" - "+cbChecked+ " County(ies) Selected"); 3067 } else { 3068 cbElemH6.find("span").html(h6Title); 3069 } 3070 } else { 3071 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop("indeterminate", false); 3072 jQuery(this).parent().parent().parent().prev('.expander').find('input[id^="gcat_"]').prop('checked', false); 3073 cbElemH6.find("span").html(h6Title); 3074 } 3075 }); 3076 3077 // Add event handler for print button. 3078 jQuery("i#info").on("click", function() { 3079 showAbout(); 3080 }); 3081 3082 // Add event handlers for clear input box. 3083 jQuery('.form-search').on('click','.form-text + .clear-text', function(e) { 3084 e.preventDefault(); 3085 jQuery(this).prev('input').val(''); 3086 return false;
3087 }); 3088 3089 jQuery('.form-search').on('input','.form-text', function(e) { 3090 e.preventDefault(); 3091 if(jQuery(this).val().length) { 3092 jQuery(this).addClass('valid-text'); 3093 } else { 3094 jQuery(this).removeClass('valid-text'); 3095 } 3096 }); 3097 3098 jQuery('.form-search').on('click','.form-text + .clear-text', function(e) { 3099 e.preventDefault(); 3100 jQuery(this).prev('input').removeClass('valid-text'); 3101 jQuery(this).prev('input').focus(); 3102 return false; 3103 }); 3104 3105 // Add event handler for dynamic input validation 3106 jQuery('#sof_CustEmail').on('keyup', function() { 3107 jQuery(this).validationEngine('hide'); 3108 }); 3109} 3110 3111/** 3112 * Performs any final initialization before waiting on 3113 * user interaction. 3114 * 3115 * @returns 3116 */ 3117function init_Sof_Complete() { 3118 var custEmail = jQuery("#sof_CustEmail").val(); 3119 3120 // Determine if a customer email is present 3121 // as a parameter. If it is, then place it 3122 // in the form and log the user in. 3123 if(custEmail != '') { 3124 jQuery("#sofSignInBtn").trigger('click'); 3125 } 3126} 3127 3128function get_Sof_Subitems(vocabulary) { 3129 return new Promise((resolve) => { 3130 // Show AJAX loading image 3131 toggle_Sof_Loading("get_Sof_Subitems",true,"Retrieving subscription items<br/>Please wait..."); 3132 3133 // Retrieve this vocabularies subscription items 3134 // user interface. 3135 jQuery.ajax({ 3136 url: jQuery("#getSofSubcriptionItemsUrl").val(), 3137 type: "GET", 3138 async: true, 3139 dataType: "html", 3140 data: "&vocab=" + vocabulary, 3141 success: function(subItemsHtml) { 3142 if(vocabulary == "Publications") { 3143 jQuery("#sof_pubSubItems").empty().append(subItemsHtml); 3144 jQuery("#sof_pubSubItems .expandable").slideToggle("fast"); 3145 } else if(vocabulary == "Areas") { 3146 jQuery("#sof_geoSubItems").empty().append(subItemsHtml); 3147 jQuery("#sof_geoSubItems .expandable").slideToggle("fast"); 3148 } 3149 resolve(true); 3150 }, 3151 error: function(jqXHR, status, exception) { 3152 var errorThrown = ''; 3153 if (jqXHR.status === 0) { 3154 errorThrown = 'Not connect.\n Verify Network.'; 3155 } else if (jqXHR.status == 404) { 3156 errorThrown = 'Requested page not found. [404]'; 3157 } else if (jqXHR.status == 500) { 3158 errorThrown = 'Internal Server Error [500].'; 3159 } else if (exception === 'parsererror') { 3160 errorThrown = 'Requested JSON parse failed.'; 3161 } else if (exception === 'timeout') { 3162 errorThrown = 'Time out error.'; 3163 } else if (exception === 'abort') { 3164 errorThrown = 'Ajax request aborted.'; 3165 } else { 3166 errorThrown = 'Uncaught Error.\n' + jqXHR.responseText; 3167 } 3168 3169 var errMsg = 3170 '<div class=\"pcatContainer\">\n' + 3171 ' <span>Error retrieving subscription items.</span>\n' + 3172 '</div>\n' 3173 3174 if(vocabulary == "Publications") { 3175 jQuery("#sof_pubSubItems").empty().append(errMsg); 3176 } else if(vocabulary == "Areas") { 3177 jQuery("#sof_geoSubItems").empty().append(errMsg); 3178 } 3179 resolve(false); 3180 }, 3181 complete: function() { 3182 toggle_Sof_Loading("get_Sof_Subitems",false); 3183 } 3184 }); 3185 }); 3186} 3187 3188function get_Sof_Subscriber() { 3189 // Show AJAX loading image 3190 toggle_Sof_Loading("get_Sof_Subscriber",true,"Retrieving your items<br/>Please wait..."); 3191 3192 jQuery.ajax({ 3193 url: jQuery('#getSofSubcriberByEmailUrl').val(), 3194 async: true, 3195 dataType: "json", 3196 data: 3197 "email=" + encodeURIComponent(jQuery('#sof_CustEmail').val().toLowerCase()), 3198 success: function(jsonSubscribers) { 3199 // Process return result. 3200 if(jsonSubscribers != null && jsonSubscribers != undefined) { 3201 var subscribers = jsonSubscribers.SofSubscriber; 3202 jQuery.each(subscribers, function(sId, subscriber) { 3203 jQuery("#sof_custFrequency").val(subscriber.frequency).selectpicker('refresh'); 3204 jQuery("#custIsSubscribed").val(subscriber.isSubscribed); 3205 jQuery("#custLastSubscribed").val(subscriber.lastSubscribed); 3206 jQuery("#custLastUnsubscribed").val(subscriber.lastUnsubscribed); 3207 jQuery("#custLastSentDigest").val(subscriber.lastSentDigest); 3208 jQuery("#ai_lastSubscribed").html(subscriber.lastSubscribed); 3209 jQuery("#ai_lastUnsubscribed").html(subscriber.lastUnsubscribed); 3210 jQuery("#ai_pcatNotificationsSent").html(subscriber.pcatNotificationsSent); 3211 jQuery("#ai_gcatNotificationsSent").html(subscriber.gcatNotificationsSent); 3212 if(JSON.parse(subscriber.isSubscribed)) { 3213 var orders = subscriber.SofSubscriberOrder; 3214 if(orders != undefined) { 3215 for(var idx=0; idx < orders.length; idx++) { 3216 var cbElem = jQuery("#sofOrderForm :checkbox[value='"+orders[idx].parentCategoryId+","+orders[idx].categoryId+"']"); 3217 cbElem.trigger("click"); 3218 } 3219 } 3220 jQuery("#lblSigninNote").css("display", "none"); 3221 jQuery("#lblNewSubscriberNote").css("display", "none"); 3222 jQuery("#sofUnsubscribeBtn").css("display", "block"); 3223 jQuery("#sofSignOutBtn").css("display", "block"); 3224 jQuery("#sofSignInBtn").css("display", "none"); 3225 jQuery(".overlay").css("display", "none");
3226 jQuery("#sofSubmitBtn").prop("disabled",false); 3227 jQuery("#sofOrderForm").css("opacity","1"); 3228 } else { 3229 var orders = subscriber.SofSubscriberOrder; 3230 if(orders != undefined) { 3231 for(var idx=0; idx < orders.length; idx++) { 3232 var cbElem = jQuery("#sofOrderForm :checkbox[value='"+orders[idx].parentCategoryId+","+orders[idx].categoryId+"']"); 3233 cbElem.trigger("click"); 3234 } 3235 } 3236 jQuery("#lblSigninNote").css("display", "none"); 3237 jQuery("#lblNewSubscriberNote").css("display", "block"); 3238 jQuery("#sofUnsubscribeBtn").css("display", "none"); 3239 jQuery("#sofSignOutBtn").css("display", "none"); 3240 jQuery("#sofSignInBtn").css("display", "none"); 3241 jQuery(".overlay").css("display", "none"); 3242 jQuery("#sofSubmitBtn").prop("disabled",false); 3243 jQuery("#sofOrderForm").css("opacity","1"); 3244 } 3245 }); 3246 } 3247 }, 3248 complete: function() { 3249 toggle_Sof_Loading("get_Sof_Subscriber",false); 3250 } 3251 }); 3252} 3253 3254function do_Sof_Unsubscribe() { 3255 // Show AJAX loading image 3256 toggle_Sof_Loading("do_Sof_Unsubscribe",true,"Unsubscribing your account<br/>Please wait..."); 3257 3258 var request = $.ajax({ 3259 url: jQuery('#doSofUnsubscribeUrl').val(), 3260 async: false, 3261 type: "POST", 3262 dataType: "json", 3263 data: 3264 "&operation=1" + 3265 "&custEmail=" + jQuery("#sof_CustEmail").val().toLowerCase() + 3266 "&custLastSubscribed=" + jQuery("#custLastSubscribed").val() + 3267 "&custLastUnsubscribed=" + jQuery("#custLastUnsubscribed").val() + 3268 "&custLastSentDigest=" + jQuery("#custLastSentDigest").val() + 3269 "&custFrequency=" + jQuery("#sof_custFrequency").val() 3270 }); 3271 3272 request.done(function( msg ) { 3273 // Toggle loading state. 3274 toggle_Sof_Loading("do_Sof_Unsubscribe",false); 3275 3276 // Tell user the unsubscribe was successful. 3277 show_Sof_Unsubscribe_Results(msg.success, msg.status); 3278 }); 3279 3280 request.fail(function( jqXHR, textStatus ) { 3281 // Toggle loading state. 3282 toggle_Sof_Loading("do_Sof_Unsubscribe",false); 3283 3284 // Tell user the unsubscribe was unsuccessful. 3285 show_Sof_Unsubscribe_Results(false, textStatus); 3286 }); 3287} 3288 3289function do_Sof_Persist_Order() { 3290 // Show AJAX loading image 3291 toggle_Sof_Loading("do_Sof_Persist_Order",true,'Saving your order.<br/>This may take a moment.<br/>Please wait...'); 3292
3293 var request = $.ajax({ 3294 url: jQuery('#doSofPersistOrderUrl').val(), 3295 async: false, 3296 type: "POST", 3297 data: 3298 jQuery('#sofOrderForm :input[name="pcsItems"], #sofOrderForm :input[name="gcsItems"]').serialize() + 3299 "&custEmail=" + jQuery("#sof_CustEmail").val().toLowerCase() + 3300 "&custFrequency=" + jQuery("#sof_custFrequency").val() + 3301 "&custIsSubscribed=" + jQuery("#custIsSubscribed").val() + 3302 "&custLastSubscribed=" + jQuery("#custLastSubscribed").val() + 3303 "&custLastUnsubscribed=" + jQuery("#custLastUnsubscribed").val() + 3304 "&custLastSentDigest=" + jQuery("#custLastSentDigest").val() 3305 }); 3306 3307 request.done(function( msg ) { 3308 // Toggle loading state. 3309 toggle_Sof_Loading("do_Sof_Persist_Order",false); 3310 3311 // Toggle certain UI controls. 3312 jQuery("#lblNewSubscriberNote, #sofUnsubscribeBtn").toggle(); 3313 3314 // Tell user the submit was successful. 3315 show_Sof_Order_Results(true, ""); 3316 }); 3317 3318 request.fail(function( jqXHR, textStatus ) { 3319 // Toggle loading state. 3320 toggle_Sof_Loading("do_Sof_Persist_Order",false); 3321 3322 // Tell user the submit was unsuccessful. 3323 show_Sof_Order_Results(false, textStatus); 3324 }); 3325} 3326 3327/** 3328 * Shows the unsubscribe results popup 3329 * modal dialog. 3330 */ 3331function show_Sof_Unsubscribe_Results(isSuccess, textStatus) { 3332 var jsOrderResultsHtml = ""; 3333 3334 if(isSuccess) { 3335 jsOrderResultsHtml += "<div style=\"font-weight:600;margin-bottom: 1rem;\">"; 3336 jsOrderResultsHtml += " <div>Email:"; 3337 jsOrderResultsHtml += " <span style=\"font-weight:500;margin-left:0.5rem;\">" + jQuery("#sof_CustEmail").val() + "</span>"; 3338 jsOrderResultsHtml += " </div>"; 3339 jsOrderResultsHtml += "</div>"; 3340 jsOrderResultsHtml += "<div>"; 3341 jsOrderResultsHtml += " You have been successfully unsubscribed from our system."; 3342 jsOrderResultsHtml += " We would appreciate you telling us why you no longer wish to receive"; 3343 jsOrderResultsHtml += " our email updates by using the Contact Us link at the top of the page."; 3344 jsOrderResultsHtml += "</div>"; 3345 jsOrderResultsHtml += "<div>"; 3346 jsOrderResultsHtml += " <hr/>"; 3347 jsOrderResultsHtml += " <button id=\"sofDialogCloseBtn\" class=\"btn btn-primary pull-right\" style=\"margin: 0rem 1rem 0.5em 0rem;\">Close</button>"; 3348 jsOrderResultsHtml += "</div>"; 3349 } else { 3350 jsOrderResultsHtml += "<div>"; 3351 jsOrderResultsHtml += " I’m sorry, we were unable to unsubscribe your account "; 3352 jsOrderResultsHtml += " at this time.<br/><br/>"; 3353 jsOrderResultsHtml += " Error: "+textStatus+"<br/><br/>"; 3354 jsOrderResultsHtml += " Please notify us using the Contact Us link at the top of the page."; 3355 jsOrderResultsHtml += "</div>"; 3356 jsOrderResultsHtml += "<div>"; 3357 jsOrderResultsHtml += " <hr/>"; 3358 jsOrderResultsHtml += " <button id=\"sofDialogCloseBtn\" class=\"btn btn-primary pull-right\" style=\"margin: 0rem 1rem 0.5em 0rem;\">Close</button>"; 3359 jsOrderResultsHtml += "</div>"; 3360 } 3361 3362 AUI().use('aui-dialog', 'aui-io', function(A) { 3363 Liferay.Util.openWindow({ 3364 id: jsNamespace+'tfo', 3365 title: "We are sorry you're unsubscribing.", 3366 dialog: { 3367 cache: false, 3368 width: 499, 3369 modal: true, 3370 toolbars: false, 3371 destroyOnHide: true, 3372 bodyContent: jsOrderResultsHtml, 3373 after: { 3374 render: function() { 3375 var dialog = Liferay.Util.getWindow(jsNamespace+'tfo'); 3376 3377 jQuery('#'+jsNamespace+'tfo.modal-dialog').css({ 3378 'height':'unset' 3379 }); 3380 3381 jQuery('#'+jsNamespace+'tfo div.modal-content').css({ 3382 'height':'unset' 3383 }); 3384 3385 jQuery('#'+jsNamespace+'tfo div.modal-header').css({ 3386 'height':'2.5rem', 3387 'paddingLeft':'1rem' 3388 }); 3389 3390 jQuery('#'+jsNamespace+'tfo h3.modal-title').css({ 3391 'fontSize':'1rem' 3392 }) 3393 3394 jQuery('#'+jsNamespace+'tfo h3.modal-body').css({ 3395 'height':'unset', 3396 'padding':'0.5rem 1.5rem' 3397 }); 3398 3399 jQuery('#sofDialogCloseBtn').on('click', function() { 3400 reset_Sof_Form(); 3401 dialog.destroy(); 3402 }); 3403 } 3404 } 3405 } 3406 }); 3407 }); 3408} 3409 3410/** 3411 * Shows the order form results popup 3412 * modal dialog. 3413 */ 3414function show_Sof_Order_Results(isSuccess, textStatus) { 3415 var jsOrderResultsHtml = ""; 3416 3417 if(isSuccess) {
3418 jsOrderResultsHtml += "<div style=\"font-size:0.9rem;font-weight:600\">"; 3419 jsOrderResultsHtml += " <div>Email:"; 3420 jsOrderResultsHtml += " <span style=\"font-weight:500;margin-left:0.5rem;\">" + jQuery("#sof_CustEmail").val() + "</span>"; 3421 jsOrderResultsHtml += " </div>"; 3422 jsOrderResultsHtml += " <div style=\"margin:1rem 0rem;\">Article Frequency:"; 3423 jsOrderResultsHtml += " <span style=\"font-weight:500;margin-left:0.5rem;\">"+ jQuery("#sof_custFrequency option:selected").text(); +"</span>"; 3424 jsOrderResultsHtml += " </div>"; 3425 3426 3427 jsOrderResultsHtml += " <table style=\"width:100%;margin:1rem 0rem;border-collapse:collapse;\">"; 3428 jsOrderResultsHtml += " <thead>"; 3429 jsOrderResultsHtml += " <tr>"; 3430 jsOrderResultsHtml += " <th style=\"border-bottom:1px solid\">Publications Selected:</th>"; 3431 jsOrderResultsHtml += " </tr>"; 3432 jsOrderResultsHtml += " </thead>"; 3433 jsOrderResultsHtml += " <tbody>"; 3434 if(jQuery('.pcatContainer input:checkbox:checked').length > 0) { 3435 jQuery('.pcatContainer input:checkbox:checked').each(function () { 3436 if(jQuery(this).attr('name') != "pcItems") { 3437 jsOrderResultsHtml += " <tr>"; 3438 jsOrderResultsHtml += " <td style=\"font-weight:500;\">"+this.title+"</td>"; 3439 jsOrderResultsHtml += " </tr>"; 3440 } 3441 }); 3442 } else { 3443 jsOrderResultsHtml += " <tr>"; 3444 jsOrderResultsHtml += " <td style=\"font-weight:500;\">No publications selected.</td>"; 3445 jsOrderResultsHtml += " </tr>"; 3446 } 3447 jsOrderResultsHtml += " </tbody>"; 3448 jsOrderResultsHtml += " </table>"; 3449 3450 jsOrderResultsHtml += "<table style=\"width:100%;margin:1rem 0rem;border-collapse:collapse;\">"; 3451 jsOrderResultsHtml += " <thead>"; 3452 jsOrderResultsHtml += " <tr>"; 3453 jsOrderResultsHtml += " <th style=\"border-bottom: 1px solid;\">Areas Selected:</th>"; 3454 jsOrderResultsHtml += " </tr>"; 3455 jsOrderResultsHtml += " </thead>"; 3456 jsOrderResultsHtml += " <tbody>"; 3457 if(jQuery('.gcatContainer input:checkbox:checked').length > 0) { 3458 jQuery('.gcatContainer input:checkbox:checked').each(function () {
3459 if(jQuery(this).attr('name') != "gcItems") { 3460 jsOrderResultsHtml += " <tr>"; 3461 jsOrderResultsHtml += " <td style=\"font-weight:500;\">"+this.title+"</td>"; 3462 jsOrderResultsHtml += " </tr>"; 3463 } 3464 }); 3465 } else { 3466 jsOrderResultsHtml += " <tr>"; 3467 jsOrderResultsHtml += " <td style=\"font-weight:500;\">No Article areas selected.</td>"; 3468 jsOrderResultsHtml += " </tr>"; 3469 } 3470 jsOrderResultsHtml += " </tbody>"; 3471 jsOrderResultsHtml += " </table>"; 3472 3473 jsOrderResultsHtml += " <div>"; 3474 jsOrderResultsHtml += " If this is incorrect, please re-enter your selections and click submit."; 3475 jsOrderResultsHtml += " </div>"; 3476 jsOrderResultsHtml += " <div>"; 3477 jsOrderResultsHtml += " <hr/>"; 3478 jsOrderResultsHtml += " <button id=\"sofDialogCloseBtn\" class=\"btn btn-primary pull-right\" style=\"margin: 0rem 1rem 0.5em 0rem;\">Close</button>"; 3479 jsOrderResultsHtml += " </div>"; 3480 jsOrderResultsHtml += "</div>"; 3481 } else { 3482 jsOrderResultsHtml += "<div>"; 3483 jsOrderResultsHtml += " I’m sorry, we were unable to save "; 3484 jsOrderResultsHtml += " your order at this time.<br/><br/>"; 3485 jsOrderResultsHtml += " Error: "+textStatus+"<br/><br/>"; 3486 jsOrderResultsHtml += " Please notify us using the Contact Us link at the top of the page."; 3487 jsOrderResultsHtml += "</div>"; 3488 jsOrderResultsHtml += "<div>"; 3489 jsOrderResultsHtml += " <hr/>"; 3490 jsOrderResultsHtml += " <button id=\"sofDialogCloseBtn\" class=\"btn btn-primary pull-right\" style=\"margin: 0rem 1rem 0.5em 0rem;\">Close</button>"; 3491 jsOrderResultsHtml += "</div>"; 3492 } 3493 3494 AUI().use('aui-dialog', 'aui-io', function(A) { 3495 Liferay.Util.openWindow({ 3496 id: jsNamespace+'tfo', 3497 title: 'Thanks for subscribing.', 3498 dialog: { 3499 cache: false, 3500 width: 499, 3501 modal: true, 3502 toolbars: false, 3503 destroyOnHide: true, 3504 bodyContent: jsOrderResultsHtml, 3505 after: { 3506 render: function() { 3507 var dialog = Liferay.Util.getWindow(jsNamespace+'tfo'); 3508 3509 jQuery('#'+jsNamespace+'tfo.modal-dialog').css({ 3510 'height':'unset' 3511 }); 3512 3513 jQuery('#'+jsNamespace+'tfo div.modal-content').css({ 3514 'height':'unset' 3515 }); 3516 3517 jQuery('#'+jsNamespace+'tfo div.modal-header').css({ 3518 'height':'2.5rem', 3519 'paddingLeft':'1rem' 3520 }); 3521 3522 jQuery('#'+jsNamespace+'tfo h3.modal-title').css({ 3523 'fontSize':'1rem' 3524 }) 3525 3526 jQuery('#'+jsNamespace+'tfo h3.modal-body').css({ 3527 'height':'unset', 3528 'padding':'0.5rem 1.5rem' 3529 }); 3530 3531 jQuery('#sofDialogCloseBtn').on('click', function() { 3532 jQuery("#lblSigninNote").css("display", "none"); 3533 jQuery("#lblNewSubscriberNote").css("display", "none"); 3534 jQuery("#sofUnsubscribeBtn").css("display", "block"); 3535 jQuery("#sofSignOutBtn").css("display", "block"); 3536 dialog.destroy(); 3537 }); 3538 } 3539 } 3540 } 3541 }); 3542 }); 3543} 3544 3545/** 3546 * Reset the user interface back to initial 3547 * state. 3548 * 3549 * @returns 3550 */ 3551function reset_Sof_Form() { 3552 // Clear the email field. 3553 jQuery('#sof_CustEmail').val('').removeClass('valid-text'); 3554 3555 // Clear statistics section. 3556 jQuery('#ai_lastSubscribed').html('Never'); 3557 jQuery('#ai_lastUnsubscribed').html('Never'); 3558 jQuery('#ai_pcatNotificationsSent').html('0'); 3559 jQuery('#ai_gcatNotificationsSent').html('0'); 3560 3561 // Uncheck all checkboxes. 3562 jQuery('input:checkbox').prop('checked', false); 3563 jQuery('input:checkbox').prop('indeterminate', false); 3564 3565 // Reset hidden field. 3566 jQuery("#sof_custFrequency").val(''); 3567 jQuery("#custIsSubscribed").val(''); 3568 jQuery("#custLastSubscribed").val(''); 3569 jQuery("#custLastUnsubscribed").val(''); 3570 jQuery("#custLastSentDigest").val(''); 3571 3572 // Reset buttons and cover input area. 3573 jQuery("#lblSigninNote").css("display", "block"); 3574 jQuery("#lblNewSubscriberNote").css("display", "none"); 3575 jQuery("#sofUnsubscribeBtn").css("display", "none"); 3576 jQuery("#sofSignOutBtn").css("display", "none"); 3577 jQuery("#sofSignInBtn").css("display", "block"); 3578 jQuery(".overlay").css("display", "block"); 3579 jQuery("#sofSubmitBtn").prop("disabled",true); 3580 jQuery("#sofOrderForm").css("opacity","0.5"); 3581 3582 // Push state. 3583 push_Sof_State(pushHistory); 3584} 3585 3586/** 3587 * Performs history array push or replace of the current 3588 * GUI state into the history array for later retrieval. 3589 * 3590 * @returns 3591 */ 3592function push_Sof_State(historyAction) { 3593 var custEmail = jQuery("#sof_CustEmail").val(); 3594 3595 var newState = 3596 "?custEmail=" + custEmail; 3597 3598 // Push or set state for the selected tab. 3599 // TODO: Determine if this is still needed. 3600 urlState = newState.replace(/null/g, ''); 3601
3602 // Determine what type of History action should 3603 // be performed. 3604 if(historyAction == pushHistory) { 3605 push_Sof_History(); 3606 } else { 3607 replace_Sof_History(); 3608 } 3609} 3610 3611/** 3612 * Builds a new query string for the active tab and 3613 * replaces the browsers history state from the created 3614 * query string. 3615 * 3616 * @returns 3617 */ 3618function push_Sof_History() { 3619// Replace the browser's history with the new state. 3620 try { 3621 history.pushState({sv:0},window.document.title,urlState); 3622 } catch(err) { 3623 window.console && 3624 window.console.error && 3625 console.error(err.message); 3626 } 3627} 3628 3629/** 3630 * Builds a new query string for the active tab and 3631 * replaces the browsers history state from the created 3632 * query string. 3633 */ 3634function replace_Sof_History() { 3635 // Replace the browser's history with the new state. 3636 try { 3637 history.replaceState({sv:0},window.document.title,urlState); 3638 } catch(err) { 3639 window.console && 3640 window.console.error && 3641 console.error(err.message); 3642 } 3643} 3644 3645/** 3646 * This function is responsible for popping the history 3647 * state back into the GUI. 3648 * 3649 * @returns 3650 */ 3651function pop_Sof_State() { 3652 // Set the isPoppingState flag. This is needed due to the 3653 // select boxes being changed, which initiates a call to 3654 // get the report. Since there are multiple change events, 3655 // the get report would be called more times then necessary 3656 // when popping state. Setting this flag notifies the change 3657 // events to not get the report. 3658 isPoppingState = true; 3659 3660 urlState = decodeURIComponent(window.location.search.toString().replace("?","")); 3661 3662 // Retrieve parameter map from urlState. 3663 parmMap = getJsonFromUrl(urlState); 3664 3665 // Populate all state values into the options panels. Either perform 3666 // necessary function or trigger event change to get interface where 3667 // is needs to be. 3668 jQuery("#sof_CustEmail").val(parmMap.custEmail).trigger("change"); 3669
3670 // Set the isPoppingState flag back to false. 3671 isPoppingState = false; 3672} 3673 3674/** 3675 * Shows the tool about popup modal dialog. 3676 */ 3677function showAbout() { 3678 AUI().use('aui-dialog', 'aui-io', function(A) { 3679 Liferay.Util.openWindow({ 3680 dialog: { 3681 cache: false, 3682 width: 499, 3683 height: 281, 3684 modal: true, 3685 bodyContent: decodeURIComponent(jsAboutHtmlPref) 3686 }, 3687 id: jsNamespace+'about', 3688 title: 'About' 3689 }); 3690 }); 3691} 3692 3693/** 3694 * Gets a decoded JSON object from URL. 3695 * 3696 * @param url 3697 * @returns 3698 */ 3699function getJsonFromUrl(url) { 3700 var query = (url != null ? url : location.search); 3701 var result = {}; 3702 3703 query.split("&").forEach(function(part) { 3704 var item = part.split("="); 3705 result[item[0]] = decodeURIComponent(item[1]); 3706 }); 3707 3708 return result; 3709} 3710 3711/** 3712 * Toggles the jquery mobile loading indicator and shows 3713 * or hides an overlay. 3714 * 3715 * @param func The function name to track. 3716 * @param isLoading Whether the loading indicator should be showing 3717 * isLoading=true or not showing isLoading=false. 3718 */ 3719function toggle_Sof_Loading(func, isLoading, message) { 3720 // Make sure there is a message. 3721 if(typeof message === 'undefined') { 3722 liMessage = 'Please wait...'; 3723 } else { 3724 if(message != null && message.length > 0) { 3725 liMessage = message; 3726 } else { 3727 liMessage = "Please wait..."; 3728 } 3729 } 3730 3731 // Perform loading count logic. 3732 if(isLoading) { 3733 loadingArray.push(func); 3734 if(loadingArray.length>0) { 3735 try { 3736 jQuery('div.sof_Page').block({ 3737 message: liMessage, 3738 fadeIn: 0, 3739 centerX: false, 3740 centerY: false, 3741 css: { 3742 position: 'fixed', 3743 top: '50%', 3744 left: 'calc(50% - 150px)', 3745 width: '300px', 3746 margin: '0 auto', 3747 border: 'none', 3748 padding: '10px', 3749 color: '#ffffff', 3750 backgroundColor: blockUI_bgColor1, 3751 '-webkit-border-radius': '6px', 3752 '-moz-border-radius': '6px', 3753 borderRadius: '6px', 3754 opacity: 0.9 3755 }, 3756 overlayCSS: { 3757 backgroundColor: blockUI_bgColor2, 3758 opacity: 0.3, 3759 borderRadius: '6px', 3760 zIndex: 900 3761 } 3762 }); 3763 } catch(error) {} 3764 } 3765 } else { 3766 loadingArray.splice(jQuery.inArray(func,loadingArray),1); 3767 if(loadingArray.length<=0) { 3768 jQuery('div.sof_Page').unblock(); 3769 } 3770 } 3771} 3772 3773/** 3774 * Detects Print Requests with JavaScript. Using these two 3775 * approaches, it can detect print requests in IE 5+, Firefox 6+, 3776 * Chrome 9+, and Safari 5.1+ (Opera as of 2012 doesnât support 3777 * either approach, not sure where Opera is at). 3778 * 3779 * TODO: 3780 * Try to test in Opera to see if they support either 3781 * approach now. 3782 * 3783 * @returns 3784 */ 3785(function() { 3786 var beforePrint = function() { 3787 for (var i = 0; i < Highcharts.charts.length; i++) { 3788 Highcharts.charts[i].reflow(); 3789 } 3790 }; 3791 var afterPrint = function() { 3792 }; 3793 3794 if (window.matchMedia) { 3795 var mediaQueryList = window.matchMedia('print'); 3796 mediaQueryList.addListener(function(mql) { 3797 if (mql.matches) { 3798 beforePrint(); 3799 return false; 3800 } else { 3801 afterPrint(); 3802 } 3803 }); 3804 } 3805 3806 window.onbeforeprint = beforePrint; 3807 window.onafterprint = afterPrint; 3808}()); 3809
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.