PageSourceSearch

https://qualityinfo.org/combo?browserId=chrome&minifierType=js&the…let%2Fjs%2Fqisitetoolsof.js&t=1785884398000

js qualityinfo.org collected 2026-09-24 09:02:48 UTC 136,546 bytes, 3,809 lines download raw bytes

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&rsquo;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&rsquo;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.