PageSourceSearch

https://jornaltribunalivre.com.br/fale-conosco/js/SpryValidationTextField.js

js jornaltribunalivre.com.br collected 2026-10-02 14:22:47 UTC 75,208 bytes, 2,357 lines download raw bytes

1// SpryValidationTextField.js - version 0.37 - Spry Pre-Release 1.6.1
2//
3// Copyright (c) 2006. Adobe Systems Incorporated.
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9//   * Redistributions of source code must retain the above copyright notice,
10//     this list of conditions and the following disclaimer.
11//   * Redistributions in binary form must reproduce the above copyright notice,
12//     this list of conditions and the following disclaimer in the documentation
13//     and/or other materials provided with the distribution.
14//   * Neither the name of Adobe Systems Incorporated nor the names of its
15//     contributors may be used to endorse or promote products derived from this
16//     software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28// POSSIBILITY OF SUCH DAMAGE.
29
30var Spry;
31if (!Spry) Spry = {};
32if (!Spry.Widget) Spry.Widget = {};
33
34Spry.Widget.BrowserSniff = function()
35{
36	var b = navigator.appName.toString();
37	var up = navigator.platform.toString();
38	var ua = navigator.userAgent.toString();
39
40	this.mozilla = this.ie = this.opera = this.safari = false;
41	var re_opera = /Opera.([0-9\.]*)/i;
42	var re_msie = /MSIE.([0-9\.]*)/i;
43	var re_gecko = /gecko/i;
44	var re_safari = /(applewebkit|safari)\/([\d\.]*)/i;
45	var r = false;
46
47	if ( (r = ua.match(re_opera))) {
48		this.opera = true;
49		this.version = parseFloat(r[1]);
50	} else if ( (r = ua.match(re_msie))) {
51		this.ie = true;
52		this.version = parseFloat(r[1]);
53	} else if ( (r = ua.match(re_safari))) {
54		this.safari = true;
55		this.version = parseFloat(r[2]);
56	} else if (ua.match(re_gecko)) {
57		var re_gecko_version = /rv:\s*([0-9\.]+)/i;
58		r = ua.match(re_gecko_version);
59		this.mozilla = true;
60		this.version = parseFloat(r[1]);
61	}
62	this.windows = this.mac = this.linux = false;
63
64	this.Platform = ua.match(/windows/i) ? "windows" :
65					(ua.match(/linux/i) ? "linux" :
66					(ua.match(/mac/i) ? "mac" :
67					ua.match(/unix/i)? "unix" : "unknown"));
68	this[this.Platform] = true;
69	this.v = this.version;
70
71	if (this.safari && this.mac && this.mozilla) {
72		this.mozilla = false;
73	}
74};
75
76Spry.is = new Spry.Widget.BrowserSniff();
77
78Spry.Widget.ValidationTextField = function(element, type, options)
79{
80	type = Spry.Widget.Utils.firstValid(type, "none");
81	if (typeof type != 'string') {
82		this.showError('The second parameter in the constructor should be the validation type, the options are the third parameter.');
83		return;
84	}
85	if (typeof Spry.Widget.ValidationTextField.ValidationDescriptors[type] == 'undefined') {
86		this.showError('Unknown validation type received as the second parameter.');
87		return;
88	}
89	options = Spry.Widget.Utils.firstValid(options, {});
90	this.type = type;
91	if (!this.isBrowserSupported()) {
92		//disable character masking and pattern behaviors for low level browsers
93		options.useCharacterMasking = false;
94	}
95	this.init(element, options);
96
97	//make sure we validate at least on submit
98	var validateOn = ['submit'].concat(Spry.Widget.Utils.firstValid(this.options.validateOn, []));
99	validateOn = validateOn.join(",");
100
101	this.validateOn = 0;
102	this.validateOn = this.validateOn | (validateOn.indexOf('submit') != -1 ? Spry.Widget.ValidationTextField.ONSUBMIT : 0);
103	this.validateOn = this.validateOn | (validateOn.indexOf('blur') != -1 ? Spry.Widget.ValidationTextField.ONBLUR : 0);
104	this.validateOn = this.validateOn | (validateOn.indexOf('change') != -1 ? Spry.Widget.ValidationTextField.ONCHANGE : 0);
105
106	if (Spry.Widget.ValidationTextField.onloadDidFire)
107		this.attachBehaviors();
108	else
109		Spry.Widget.ValidationTextField.loadQueue.push(this);
110};
111
112Spry.Widget.ValidationTextField.ONCHANGE = 1;
113Spry.Widget.ValidationTextField.ONBLUR = 2;
114Spry.Widget.ValidationTextField.ONSUBMIT = 4;
115
116Spry.Widget.ValidationTextField.ERROR_REQUIRED = 1;
117Spry.Widget.ValidationTextField.ERROR_FORMAT = 2;
118Spry.Widget.ValidationTextField.ERROR_RANGE_MIN = 4;
119Spry.Widget.ValidationTextField.ERROR_RANGE_MAX = 8;
120Spry.Widget.ValidationTextField.ERROR_CHARS_MIN = 16;
121Spry.Widget.ValidationTextField.ERROR_CHARS_MAX = 32;
122
123/* validation parameters:
124 *  - characterMasking : prevent typing of characters not matching an regular expression
125 *  - regExpFilter : additional regular expression to disalow typing of characters 
126 *		(like the "-" sign in the middle of the value); use for partial matching of the currently typed value;
127 * 		the typed value must match regExpFilter at any moment
128 *  - pattern : enforce character on each position inside a pattern (AX0?)
129 *  - validation : function performing logic validation; return false if failed and the typedValue value on success
130 *  - minValue, maxValue : range validation; check if typedValue inside the specified range
131 *  - minChars, maxChars : value length validation; at least/at most number of characters
132 * */
133Spry.Widget.ValidationTextField.ValidationDescriptors = {
134	'none': {
135	},
136	'custom': {
137	},
138	'integer': {
139		characterMasking: /[\-\+\d]/,
140		regExpFilter: /^[\-\+]?\d*$/,
141		validation: function(value, options) {
142			if (value == '' || value == '-' || value == '+') {
143				return false;
144			}
145			var regExp = /^[\-\+]?\d*$/;
146			if (!regExp.test(value)) {
147				return false;
148			}
149			options = options || {allowNegative:false};
150			var ret = parseInt(value, 10);
151			if (!isNaN(ret)) {
152				var allowNegative = true;
153				if (typeof options.allowNegative != 'undefined' && options.allowNegative == false) {
154					allowNegative = false;
155				}
156				if (!allowNegative && value < 0) {
157					ret = false;
158				}
159			} else {
160				ret = false;
161			}
162			return ret;
163		}
164	},
165	'real': {
166		characterMasking: /[\d\.,\-\+e]/i,
167		regExpFilter: /^[\-\+]?\d(?:|\.,\d{0,2})|(?:|e{0,1}[\-\+]?\d{0,})$/i,
168		validation: function (value, options) {
169			var regExp = /^[\+\-]?[0-9]+([\.,][0-9]+)?([eE]{0,1}[\-\+]?[0-9]+)?$/;
170			if (!regExp.test(value)) {
171				return false;
172			}
173			var ret = parseFloat(value);
174			if (isNaN(ret)) {
175				ret = false;
176			}
177			return ret;
178		}
179	},
180	'currency': {
181		formats: {
182			'dot_comma': {
183				characterMasking: /[\d\.\,\-\+\$]/,
184				regExpFilter: /^[\-\+]?(?:[\d\.]*)+(|\,\d{0,2})$/,
185				validation: function(value, options) {
186					var ret = false;
187					//2 or no digits after the comma
188					if (/^(\-|\+)?\d{1,3}(?:\.\d{3})*(?:\,\d{2}|)$/.test(value) || /^(\-|\+)?\d+(?:\,\d{2}|)$/.test(value)) {
189						value = value.toString().replace(/\./gi, '').replace(/\,/, '.');
190						ret = parseFloat(value);
191					}
192					return ret;
193				}
194			},
195			'comma_dot': {
196				characterMasking: /[\d\.\,\-\+\$]/,
197				regExpFilter: /^[\-\+]?(?:[\d\,]*)+(|\.\d{0,2})$/,
198				validation: function(value, options) {
199					var ret = false;
200					//2 or no digits after the comma
201					if (/^(\-|\+)?\d{1,3}(?:\,\d{3})*(?:\.\d{2}|)$/.test(value) || /^(\-|\+)?\d+(?:\.\d{2}|)$/.test(value)) {
202						value = value.toString().replace(/\,/gi, '');
203						ret = parseFloat(value);
204					}
205					return ret;
206				}
207			}
208		}
209	},
210	'email': {
211		characterMasking: /[^\s]/,
212		validation: function(value, options) {
213			var rx = /^[\w\.-]+@[\w\.-]+\.\w+$/i;
214			return rx.test(value);
215		}
216	},
217	'date': {
218		validation: function(value, options) {
219			var formatRegExp = /^([mdy]+)[\.\-\/\\\s]+([mdy]+)[\.\-\/\\\s]+([mdy]+)$/i;
220			var valueRegExp = this.dateValidationPattern;
221			var formatGroups = options.format.match(formatRegExp);
222			var valueGroups = value.match(valueRegExp);
223			if (formatGroups !== null && valueGroups !== null) {
224				var dayIndex = -1;
225				var monthIndex = -1;
226				var yearIndex = -1;
227				for (var i=1; i<formatGroups.length; i++) {
228					switch (formatGroups[i].toLowerCase()) {
229						case "dd":
230							dayIndex = i;
231							break;
232						case "mm":
233							monthIndex = i;
234							break;
235						case "yy":
236						case "yyyy":
237							yearIndex = i;
238							break;
239					}
240				}
241				if (dayIndex != -1 && monthIndex != -1 && yearIndex != -1) {
242					var maxDay = -1;
243					var theDay = parseInt(valueGroups[dayIndex], 10);
244					var theMonth = parseInt(valueGroups[monthIndex], 10);
245					var theYear = parseInt(valueGroups[yearIndex], 10);
246
247					// Check month value to be between 1..12
248					if (theMonth < 1 || theMonth > 12) {
249						return false;
250					}
251					
252					// Calculate the maxDay according to the current month
253					switch (theMonth) {
254						case 1:	// January
255						case 3: // March
256						case 5: // May
257						case 7: // July
258						case 8: // August
259						case 10: // October
260						case 12: // December
261							maxDay = 31;
262							break;
263						case 4:	// April
264						case 6: // June
265						case 9: // September
266						case 11: // November
267							maxDay = 30;
268							break;
269						case 2: // February
270							if ((parseInt(theYear/4, 10) * 4 == theYear) && (theYear % 100 != 0 || theYear % 400 == 0)) {
271								maxDay = 29;
272							} else {
273								maxDay = 28;
274							}
275							break;
276					}
277
278					// Check day value to be between 1..maxDay
279					if (theDay < 1 || theDay > maxDay) {
280						return false;
281					}
282					
283					// If successfull we'll return the date object
284					return (new Date(theYear, theMonth - 1, theDay));   //JavaScript requires a month between 0 and 11
285				}
286			} else {
287				return false;
288			}
289		}
290	},
291	'time': {
292		validation: function(value, options) {
293			//	HH:MM:SS T
294			var formatRegExp = /([hmst]+)/gi;
295			var valueRegExp = /(\d+|AM?|PM?)/gi;
296			var formatGroups = options.format.match(formatRegExp);
297			var valueGroups = value.match(valueRegExp);
298			//mast match and have same length
299			if (formatGroups !== null && valueGroups !== null) {
300				if (formatGroups.length != valueGroups.length) {
301					return false;
302				}
303
304				var hourIndex = -1;
305				var minuteIndex = -1;
306				var secondIndex = -1;
307				//T is AM or PM
308				var tIndex = -1;
309				var theHour = 0, theMinute = 0, theSecond = 0, theT = 'AM';
310				for (var i=0; i<formatGroups.length; i++) {
311					switch (formatGroups[i].toLowerCase()) {
312						case "hh":
313							hourIndex = i;
314							break;
315						case "mm":
316							minuteIndex = i;
317							break;
318						case "ss":
319							secondIndex = i;
320							break;
321						case "t":
322						case "tt":
323							tIndex = i;
324							break;
325					}
326				}
327				if (hourIndex != -1) {
328					var theHour = parseInt(valueGroups[hourIndex], 10);
329					if (isNaN(theHour) || theHour > (formatGroups[hourIndex] == 'HH' ? 23 : 12 )) {
330						return false;
331					}
332				}
333				if (minuteIndex != -1) {
334					var theMinute = parseInt(valueGroups[minuteIndex], 10);
335					if (isNaN(theMinute) || theMinute > 59) {
336						return false;
337					}
338				}
339				if (secondIndex != -1) {
340					var theSecond = parseInt(valueGroups[secondIndex], 10);
341					if (isNaN(theSecond) || theSecond > 59) {
342						return false;
343					}
344				}
345				if (tIndex != -1) {
346					var theT = valueGroups[tIndex].toUpperCase();
347					if (
348						formatGroups[tIndex].toUpperCase() == 'TT' && !/^a|pm$/i.test(theT) || 
349						formatGroups[tIndex].toUpperCase() == 'T' && !/^a|p$/i.test(theT)
350					) {
351						return false;
352					}
353				}
354				var date = new Date(2000, 0, 1, theHour + (theT.charAt(0) == 'P'?12:0), theMinute, theSecond);
355				return date;
356			} else {
357				return false;
358			}
359		}
360	},
361	'credit_card': {
362		characterMasking: /\d/,
363		validation: function(value, options) {
364			var regExp = null;
365			options.format = options.format || 'ALL';
366			switch (options.format.toUpperCase()) {
367				case 'ALL': regExp = /^[3-6]{1}[0-9]{12,18}$/; break;
368				case 'VISA': regExp = /^4(?:[0-9]{12}|[0-9]{15})$/; break;
369				case 'MASTERCARD': regExp = /^5[1-5]{1}[0-9]{14}$/; break;
370				case 'AMEX': regExp = /^3(4|7){1}[0-9]{13}$/; break;
371				case 'DISCOVER': regExp = /^6011[0-9]{12}$/; break;
372				case 'DINERSCLUB': regExp = /^3(?:(0[0-5]{1}[0-9]{11})|(6[0-9]{12})|(8[0-9]{12}))$/; break;
373			}
374			if (!regExp.test(value)) {
375				return false;
376			}
377			var digits = [];
378			var j = 1, digit = '';
379			for (var i = value.length - 1; i >= 0; i--) {
380				if ((j%2) == 0) {
381					digit = parseInt(value.charAt(i), 10) * 2;
382					digits[digits.length] = digit.toString().charAt(0);
383					if (digit.toString().length == 2) {
384						digits[digits.length] = digit.toString().charAt(1);
385					}
386				} else {
387					digit = value.charAt(i);
388					digits[digits.length] = digit;
389				}
390				j++;
391			}
392			var sum = 0;
393			for(i=0; i < digits.length; i++ ) {
394				sum += parseInt(digits[i], 10);
395			}
396			if ((sum%10) == 0) {
397				return true;
398			}
399			return false;
400		}
401	},
402	'zip_code': {
403		formats: {
404			'zip_us9': {
405				pattern:'00000-0000'
406			},
407			'zip_us5': {
408				pattern:'00000'
409			},
410			'zip_uk': {
411				characterMasking: /[\dA-Z\s]/,
412				validation: function(value, options) {
413					//check one of the following masks
414					// AN NAA, ANA NAA, ANN NAA, AAN NAA, AANA NAA, AANN NAA
415					return /^[A-Z]{1,2}\d[\dA-Z]?\s?\d[A-Z]{2}$/.test(value);
416				}
417			},
418			'zip_canada': {
419				characterMasking: /[\dA-Z\s]/,
420				pattern: 'A0A 0A0'
421			},
422			'zip_custom': {}
423		}
424	},
425	'phone_number': {
426		formats: {
427			//US phone number; 10 digits
428			'phone_us': {
429				pattern:'(000) 000-0000'
430			},
431			'phone_custom': {
432				pattern:'000000000'}
433		}
434	},
435	'social_security_number': {
436		pattern:'000-00-0000'
437	},
438	'ip': {
439		characterMaskingFormats: {
440			'ipv4': /[\d\.]/i,
441			'ipv6_ipv4': /[\d\.\:A-F\/]/i,
442			'ipv6': /[\d\.\:A-F\/]/i
443		},
444		validation: function (value, options) {
445			return Spry.Widget.ValidationTextField.validateIP(value, options.format);
446		}
447	},
448
449	'url': {
450		characterMasking: /[^\s]/,
451		validation: function(value, options) {
452			//fix for ?ID=223429 and ?ID=223387
453			/* the following regexp matches components of an URI as specified in http://tools.ietf.org/html/rfc3986#page-51 page 51, Appendix B.
454				scheme    = $2
455				authority = $4
456				path      = $5
457				query     = $7
458				fragment  = $9
459			*/
460			var URI_spliter = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
461			var parts = value.match(URI_spliter);
462			if (parts && parts[4]) {
463				//encode each component of the domain name using Punycode encoding scheme: http://tools.ietf.org/html/rfc3492
464				var host  = parts[4].split(".");
465				var punyencoded = '';
466				for (var i=0; i<host.length; i++) {
467					punyencoded = Spry.Widget.Utils.punycode_encode(host[i], 64);
468					if (!punyencoded) {
469						return false;
470					} else {
471						if (punyencoded != (host[i] + "-")) {
472							host[i] = 'xn--' + punyencoded;
473						}
474					}
475				}
476				host = host .join(".");
477				//the encoded domain name is replaced into the original URL to be validated again later as URL
478				value = value.replace(URI_spliter, "$1//" + host + "$5$6$8");
479			}
480
481			//fix for ?ID=223358 and ?ID=223594
482			//the following validates an URL using ABNF rules as defined in http://tools.ietf.org/html/rfc3986 , Appendix A., page 49
483			//except host which is extracted by match[1] and validated separately
484			/*
485			 * userinfo=	(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?
486			 * host=			(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))
487			 * pathname=	(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*
488			 * query=			(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
489			 * anchor=		(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
490			 */
491			var regExp = /^(?:https?|ftp)\:\/\/(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))(?:\:[0-9]*)?)(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?$/i;
492
493			var valid = value.match(regExp);
494			if (valid) {
495				//extract the  address from URL
496				var address = valid[1];
497
498				if (address) {
499					if (address == '[]') {
500						return false;
501					}
502					if (address.charAt(0) == '[' ) {
503						//IPv6 address or IPv4 enclosed in square brackets
504						address = address.replace(/^\[|\]$/gi, '');
505						return Spry.Widget.ValidationTextField.validateIP(address, 'ipv6_ipv4');
506					} else {
507						if (/[^0-9\.]/.test(address)) {
508							return true;
509						} else {
510							//check if hostname is all digits and dots and then check for IPv4
511							return Spry.Widget.ValidationTextField.validateIP(address, 'ipv4');
512						}
513					}
514				} else {
515					return true;
516				}
517			} else {
518				return false;
519			}
520		}
521	}
522};
523
524/*
5252.2.1. Preferred
526x:x:x:x:x:x:x:x, where the 'x's are the hexadecimal values of the eight 16-bit pieces of the address.
527Examples:
528	FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
529	1080:0:0:0:8:800:200C:417A
530Note that it is not necessary to write the leading zeros in an
531individual field, but there must be at least one numeral in every
532field (except for the case described in 2.2.2.).
533
5342.2.2. Compressed
535The use of "::" indicates multiple groups of 16-bits of zeros.
536The "::" can only appear once in an address.  The "::" can also be
537used to compress the leading and/or trailing zeros in an address.
538	1080:0:0:0:8:800:200C:417A --> 1080::8:800:200C:417A
539	FF01:0:0:0:0:0:0:101 --> FF01::101
540	0:0:0:0:0:0:0:1 --> ::1
541	0:0:0:0:0:0:0:0 --> ::
542
5432.5.4 IPv6 Addresses with Embedded IPv4 Addresses
544	IPv4-compatible IPv6 address (tunnel IPv6 packets over IPv4 routing infrastructures)
545	::0:129.144.52.38
546	IPv4-mapped IPv6 address (represent the addresses of IPv4-only nodes as IPv6 addresses)
547	::ffff:129.144.52.38
548
549The text representation of IPv6 addresses and prefixes in Augmented BNF (Backus-Naur Form) [ABNF] for reference purposes.
550[ABNF http://tools.ietf.org/html/rfc2234]
551      IPv6address = hexpart [ ":" IPv4address ]
552      IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
553
554      IPv6prefix  = hexpart "/" 1*2DIGIT
555
556      hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
557      hexseq  = hex4 *( ":" hex4)
558      hex4    = 1*4HEXDIG
559*/
560Spry.Widget.ValidationTextField.validateIP = function (value, format)
561{
562	var validIPv6Addresses = [
563		//preferred
564		/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}(?:\/\d{1,3})?$/i,
565
566		//various compressed
567		/^[a-f0-9]{0,4}::(?:\/\d{1,3})?$/i,
568		/^:(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
569		/^(?:[a-f0-9]{1,4}:){1,6}:(?:\/\d{1,3})?$/i,
570		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
571		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,5}(?:\/\d{1,3})?$/i,
572		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,4}(?:\/\d{1,3})?$/i,
573		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}){1,3}(?:\/\d{1,3})?$/i,
574		/^(?:[a-f0-9]{1,4}:){5}(?::[a-f0-9]{1,4}){1,2}(?:\/\d{1,3})?$/i,
575		/^(?:[a-f0-9]{1,4}:){6}(?::[a-f0-9]{1,4})(?:\/\d{1,3})?$/i,
576
577
578		//IPv6 mixes with IPv4
579		/^(?:[a-f0-9]{1,4}:){6}(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
580		/^:(?::[a-f0-9]{1,4}){0,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
581		/^(?:[a-f0-9]{1,4}:){1,5}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
582		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
583		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,3}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,	
584		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,2}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
585		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}):(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i
586	];
587	var validIPv4Addresses = [
588		//IPv4
589		/^(\d{1,3}\.){3}\d{1,3}$/i
590	];
591	var validAddresses = [];
592	if (format == 'ipv6' || format == 'ipv6_ipv4') {
593		validAddresses = validAddresses.concat(validIPv6Addresses);
594	}
595	if (format == 'ipv4' || format == 'ipv6_ipv4') {
596		validAddresses = validAddresses.concat(validIPv4Addresses);
597	}
598
599	var ret = false;
600	for (var i=0; i<validAddresses.length; i++) {
601		if (validAddresses[i].test(value)) {
602			ret = true;
603			break;
604		}
605	}
606
607	if (ret && value.indexOf(".") != -1) {
608		//if address contains IPv4 fragment, it must be valid; all 4 groups must be less than 256
609		var ipv4 = value.match(/:?(?:\d{1,3}\.){3}\d{1,3}/i);
610		if(!ipv4) {
611			return false;
612		}
613		ipv4 = ipv4[0].replace(/^:/, '');
614		var pieces = ipv4.split('.');
615		if (pieces.length != 4) {
616			return false;
617		}
618		var regExp = /^[\-\+]?\d*$/;
619		for (var i=0; i< pieces.length; i++) {
620			if (pieces[i] == '') {
621				return false;
622			}
623			var piece = parseInt(pieces[i], 10);
624			if (isNaN(piece) || piece > 255 || !regExp.test(pieces[i]) || pieces[i].length>3 || /^0{2,3}$/.test(pieces[i])) {
625				return false;
626			}
627		}
628	}
629	if (ret && value.indexOf("/") != -1) {
630		// if prefix-length is specified must be in [1-128]
631		var prefLen = value.match(/\/\d{1,3}$/);
632		if (!prefLen) return false;
633		var prefLenVal = parseInt(prefLen[0].replace(/^\//,''), 10);
634		if (isNaN(prefLenVal) || prefLenVal > 128 || prefLenVal < 1) {
635			return false;
636		}
637	}
638	return ret;
639};
640
641Spry.Widget.ValidationTextField.onloadDidFire = false;
642Spry.Widget.ValidationTextField.loadQueue = [];
643
644Spry.Widget.ValidationTextField.prototype.isBrowserSupported = function()
645{
646	return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows
647		||
648	Spry.is.mozilla && Spry.is.v >= 1.4
649		||
650	Spry.is.safari
651		||
652	Spry.is.opera && Spry.is.v >= 9;
653};
654
655Spry.Widget.ValidationTextField.prototype.init = function(element, options)
656{
657	this.element = this.getElement(element);
658	this.errors = 0;
659	this.flags = {locked: false, restoreSelection: true};
660	this.options = {};
661	this.event_handlers = [];
662
663	this.validClass = "textfieldValidState";
664	this.focusClass = "textfieldFocusState";
665	this.requiredClass = "textfieldRequiredState";
666	this.hintClass = "textfieldHintState";
667	this.invalidFormatClass = "textfieldInvalidFormatState";
668	this.invalidRangeMinClass = "textfieldMinValueState";
669	this.invalidRangeMaxClass = "textfieldMaxValueState";
670	this.invalidCharsMinClass = "textfieldMinCharsState";
671	this.invalidCharsMaxClass = "textfieldMaxCharsState";
672	this.textfieldFlashTextClass = "textfieldFlashText";
673	if (Spry.is.safari) {
674		this.flags.lastKeyPressedTimeStamp = 0;
675	}
676
677	switch (this.type) {
678		case 'phone_number':options.format = Spry.Widget.Utils.firstValid(options.format, 'phone_us');break;
679		case 'currency':options.format = Spry.Widget.Utils.firstValid(options.format, 'comma_dot');break;
680		case 'zip_code':options.format = Spry.Widget.Utils.firstValid(options.format, 'zip_us5');break;
681		case 'date':
682			options.format = Spry.Widget.Utils.firstValid(options.format, 'mm/dd/yy');
683			break;
684		case 'time':
685			options.format = Spry.Widget.Utils.firstValid(options.format, 'HH:mm');
686			options.pattern = options.format.replace(/[hms]/gi, "0").replace(/TT/gi, 'AM').replace(/T/gi, 'A');
687			break;
688		case 'ip':
689			options.format = Spry.Widget.Utils.firstValid(options.format, 'ipv4');
690			options.characterMasking = Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].characterMaskingFormats[options.format]; 
691			break;
692	}
693
694	//retrieve the validation type descriptor to be used with this instance (base on type and format)
695	//widgets may have different validations depending on format (like zip_code with formats)
696	var validationDescriptor = {};
697	if (options.format && Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats) {
698		if (Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]) {
699			Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]);
700		}
701	} else {
702		Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type]);
703	}
704
705	//set default values for some parameters which were not aspecified
706	options.useCharacterMasking = Spry.Widget.Utils.firstValid(options.useCharacterMasking, false);
707	options.hint = Spry.Widget.Utils.firstValid(options.hint, '');
708	options.isRequired = Spry.Widget.Utils.firstValid(options.isRequired, true);
709	options.additionalError = Spry.Widget.Utils.firstValid(options.additionalError, false);
710	if (options.additionalError)
711		options.additionalError = this.getElement(options.additionalError);
712
713	//set widget validation parameters
714	//get values from validation type descriptor
715	//use the user specified values, if defined
716	options.characterMasking = Spry.Widget.Utils.firstValid(options.characterMasking, validationDescriptor.characterMasking);
717	options.regExpFilter = Spry.Widget.Utils.firstValid(options.regExpFilter, validationDescriptor.regExpFilter);
718	options.pattern = Spry.Widget.Utils.firstValid(options.pattern, validationDescriptor.pattern);
719	options.validation = Spry.Widget.Utils.firstValid(options.validation, validationDescriptor.validation);
720	if (typeof options.validation == 'string') {
721		options.validation = eval(options.validation);
722	}
723
724	options.minValue = Spry.Widget.Utils.firstValid(options.minValue, validationDescriptor.minValue);
725	options.maxValue = Spry.Widget.Utils.firstValid(options.maxValue, validationDescriptor.maxValue);
726
727	options.minChars = Spry.Widget.Utils.firstValid(options.minChars, validationDescriptor.minChars);
728	options.maxChars = Spry.Widget.Utils.firstValid(options.maxChars, validationDescriptor.maxChars);
729
730	Spry.Widget.Utils.setOptions(this, options);
731	Spry.Widget.Utils.setOptions(this.options, options);
732};
733
734Spry.Widget.ValidationTextField.prototype.destroy = function() {
735	if (this.event_handlers)
736		for (var i=0; i<this.event_handlers.length; i++) {
737			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
738		}
739	try { delete this.element; } catch(err) {}
740	try { delete this.input; } catch(err) {}
741	try { delete this.form; } catch(err) {}
742	try { delete this.event_handlers; } catch(err) {}
743	try { this.selection.destroy(); } catch(err) {}
744	try { delete this.selection; } catch(err) {}
745
746	var q = Spry.Widget.Form.onSubmitWidgetQueue;
747	var qlen = q.length;
748	for (var i = 0; i < qlen; i++) {
749		if (q[i] == this) {
750			q.splice(i, 1);
751			break;
752		}
753	}
754};
755
756Spry.Widget.ValidationTextField.prototype.attachBehaviors = function()
757{
758	if (this.element) {
759		if (this.element.nodeName == "INPUT") {
760			this.input = this.element;
761		} else {
762			this.input = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.element, "INPUT");
763		}
764	}
765
766	if (this.input) {
767		if (this.maxChars) {
768			this.input.removeAttribute("maxLength");
769		}
770		this.putHint();
771		this.compilePattern();
772		if (this.type == 'date') {
773			this.compileDatePattern();
774		}
775		this.input.setAttribute("AutoComplete", "off");
776		this.selection = new Spry.Widget.SelectionDescriptor(this.input);
777		this.oldValue = this.input.value;
778
779		var self = this;
780		this.event_handlers = [];
781
782		this.event_handlers.push([this.input, "keydown", function(e) { if (self.isDisabled()) return true; return self.onKeyDown(e || event); }]);
783		this.event_handlers.push([this.input, "keypress", function(e) { if (self.isDisabled()) return true; return self.onKeyPress(e || event); }]);
784		if (Spry.is.opera) {
785			this.event_handlers.push([this.input, "keyup", function(e) { if (self.isDisabled()) return true; return self.onKeyUp(e || event); }]);
786		}
787
788		this.event_handlers.push([this.input, "focus", function(e) { if (self.isDisabled()) return true; return self.onFocus(e || event); }]);
789		this.event_handlers.push([this.input, "blur", function(e) { if (self.isDisabled()) return true; return self.onBlur(e || event); }]);
790
791		this.event_handlers.push([this.input, "mousedown", function(e) { if (self.isDisabled()) return true; return self.onMouseDown(e || event); }]);
792
793		var changeEvent = 
794			Spry.is.mozilla || Spry.is.opera || Spry.is.safari?"input":
795			Spry.is.ie?"propertychange":
796			"change";
797		this.event_handlers.push([this.input, changeEvent, function(e) { if (self.isDisabled()) return true; return self.onChange(e || event); }]);
798
799		if (Spry.is.mozilla || Spry.is.safari) {
800			//oninput event on mozilla does not fire ondragdrop
801			this.event_handlers.push([this.input, "dragdrop", function(e) { if (self.isDisabled()) return true; self.removeHint();return self.onChange(e || event); }]);
802		} else if (Spry.is.ie){
803			//ondrop&onpropertychange crash on IE 
804			this.event_handlers.push([this.input, "drop", function(e) { if (self.isDisabled()) return true; return self.onDrop(e || event); }]);
805		}
806
807		for (var i=0; i<this.event_handlers.length; i++) {
808			Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
809		}
810
811		// submit
812		this.form = Spry.Widget.Utils.getFirstParentWithNodeName(this.input, "FORM");
813		if (this.form) {
814			// if no "onSubmit" handler has been attached to the current form, attach one
815			if (!this.form.attachedSubmitHandler && !this.form.onsubmit) {
816				this.form.onsubmit = function(e) { e = e || event; return Spry.Widget.Form.onSubmit(e, e.srcElement || e.currentTarget) };
817				this.form.attachedSubmitHandler = true;                 
818			}
819			if (!this.form.attachedResetHandler) {
820				Spry.Widget.Utils.addEventListener(this.form, "reset", function(e) { e = e || event; return Spry.Widget.Form.onReset(e, e.srcElement || e.currentTarget) }, false);
821				this.form.attachedResetHandler = true;                 
822			}
823			// add the currrent widget to the "onSubmit" check queue;
824			Spry.Widget.Form.onSubmitWidgetQueue.push(this);
825		}
826	}	
827};
828
829Spry.Widget.ValidationTextField.prototype.isDisabled = function() {
830	return this.input && (this.input.disabled || this.input.readOnly) || !this.input;
831};
832
833Spry.Widget.ValidationTextField.prototype.getElement = function(ele)
834{
835	if (ele && typeof ele == "string")
836		return document.getElementById(ele);
837	return ele;
838};
839
840Spry.Widget.ValidationTextField.addLoadListener = function(handler)
841{
842	if (typeof window.addEventListener != 'undefined')
843		window.addEventListener('load', handler, false);
844	else if (typeof document.addEventListener != 'undefined')
845		document.addEventListener('load', handler, false);
846	else if (typeof window.attachEvent != 'undefined')
847		window.attachEvent('onload', handler);
848};
849
850Spry.Widget.ValidationTextField.processLoadQueue = function(handler)
851{
852	Spry.Widget.ValidationTextField.onloadDidFire = true;
853	var q = Spry.Widget.ValidationTextField.loadQueue;
854	var qlen = q.length;
855	for (var i = 0; i < qlen; i++)
856		q[i].attachBehaviors();
857};
858
859Spry.Widget.ValidationTextField.addLoadListener(Spry.Widget.ValidationTextField.processLoadQueue);
860Spry.Widget.ValidationTextField.addLoadListener(function(){
861	Spry.Widget.Utils.addEventListener(window, "unload", Spry.Widget.Form.destroyAll, false);
862});
863
864Spry.Widget.ValidationTextField.prototype.setValue = function(newValue) {
865	this.flags.locked = true;
866	this.input.value = newValue;
867	this.flags.locked = false;
868	this.oldValue = newValue;
869	if (!Spry.is.ie) {
870		this.onChange();
871	}
872};
873
874/**
875 * save the state of the input (selection and value) so we can revert to it
876 * should call this just before modifying the input value
877 */
878Spry.Widget.ValidationTextField.prototype.saveState = function()
879{
880	this.oldValue = this.input.value;
881	this.selection.update();
882};
883
884Spry.Widget.ValidationTextField.prototype.revertState = function(revertValue)
885{
886	if (revertValue != this.input.value) {
887		this.input.readOnly = true;
888		this.input.value = revertValue;
889		this.input.readOnly = false;
890		if (Spry.is.safari && this.flags.active) {
891			this.input.focus();
892		}
893	}
894  if (this.flags.restoreSelection) {
895	this.selection.moveTo(this.selection.start, this.selection.end);
896  }
897
898	this.redTextFlash();
899};
900
901Spry.Widget.ValidationTextField.prototype.removeHint = function()
902{
903	if (this.flags.hintOn) {
904		this.input.value = "";
905		this.flags.hintOn = false;
906		this.removeClassName(this.element, this.hintClass);
907		this.removeClassName(this.additionalError, this.hintClass);
908	}
909};
910
911Spry.Widget.ValidationTextField.prototype.putHint = function()
912{
913	if(this.hint && this.input && this.input.type == "text" && this.input.value == "") {
914		this.flags.hintOn = true;
915		this.input.value = this.hint;
916		this.addClassName(this.element, this.hintClass);
917		this.addClassName(this.additionalError, this.hintClass);
918	}
919};
920
921Spry.Widget.ValidationTextField.prototype.redTextFlash = function()
922{
923	var self = this;
924	this.addClassName(this.element, this.textfieldFlashTextClass);
925	setTimeout(function() {
926		self.removeClassName(self.element, self.textfieldFlashTextClass)
927	}, 100);
928};
929
930Spry.Widget.ValidationTextField.prototype.doValidations = function(testValue, revertValue)
931{
932	if (this.isDisabled()) return false;
933
934	if (this.flags.locked) {
935		return false;
936	}
937
938	if (testValue.length == 0 && !this.isRequired) {
939		this.errors = 0;
940		return false;
941	}
942	this.flags.locked = true;
943
944	var mustRevert = false;
945	var continueValidations = true;
946	if (!this.options.isRequired && testValue.length == 0) {
947		continueValidations = false;
948	}
949
950	var errors = 0;
951	var fixedValue = testValue;
952
953	//characterMasking - test if all characters are valid with the characterMasking (keyboard filter)
954	if (this.useCharacterMasking && this.characterMasking) {
955		for(var i=0; i<testValue.length; i++) {
956			if (!this.characterMasking.test(testValue.charAt(i))) {
957				errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
958				fixedValue = revertValue;
959				mustRevert = true;
960				break;
961			}
962		}
963	}
964
965	//regExpFilter - character mask positioning (additional mask to restrict some characters only in some position)
966	if (!mustRevert && this.useCharacterMasking && this.regExpFilter) {
967		if (!this.regExpFilter.test(fixedValue)) {
968			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
969			mustRevert = true;
970		}
971	}
972
973	//pattern - testValue matches the pattern so far
974	if (!mustRevert && this.pattern) {
975		var currentRegExp = this.patternToRegExp(testValue.length);
976		if (!currentRegExp.test(testValue)) {
977			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
978			mustRevert = true;
979		} else if (this.patternLength != testValue.length) {
980			//testValue matches pattern so far, but it's not ok if it does not have the proper length
981			//do not revert, but should show the error
982			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
983		}
984	}
985
986	if (fixedValue == '') {
987		errors = errors | Spry.Widget.ValidationTextField.ERROR_REQUIRED;
988	}
989
990	if (!mustRevert && this.pattern && this.useCharacterMasking) {
991		var n = this.getAutoComplete(testValue.length);
992		if (n) {
993			fixedValue += n;
994		}
995	}
996
997	if(!mustRevert && this.minChars !== null  && continueValidations) {
998		if (testValue.length < this.minChars) {
999			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MIN;
1000			continueValidations = false;
1001		}
1002	}
1003
1004	if(!mustRevert && this.maxChars !== null && continueValidations) {
1005		if (testValue.length > this.maxChars) {
1006			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MAX;
1007			continueValidations = false;
1008		}
1009	}
1010
1011	//validation - testValue passes widget validation function
1012	if (!mustRevert && this.validation && continueValidations) {
1013		var value = this.validation(fixedValue, this.options);
1014		if (false === value) {
1015			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
1016			continueValidations = false;
1017		} else {
1018			this.typedValue = value;
1019		}
1020	}
1021
1022	if(!mustRevert && this.validation && this.minValue !== null && continueValidations) {
1023		var minValue = this.validation(this.minValue.toString(), this.options);
1024		if (minValue !== false) {
1025			if (this.typedValue < minValue) {
1026				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MIN;
1027				continueValidations = false;
1028			}
1029		}
1030	}
1031
1032	if(!mustRevert && this.validation && this.maxValue !== null && continueValidations) {
1033		var maxValue = this.validation(this.maxValue.toString(), this.options);
1034		if (maxValue !== false) {
1035			if( this.typedValue > maxValue) {
1036				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MAX;
1037				continueValidations = false;
1038			}
1039		}
1040	}
1041
1042	//an invalid value was tested; must make sure it does not get inside the input
1043	if (this.useCharacterMasking && mustRevert) {
1044		this.revertState(revertValue);
1045	}
1046
1047	this.errors = errors;
1048	this.fixedValue = fixedValue;
1049
1050	this.flags.locked = false;
1051
1052	return mustRevert;
1053};
1054
1055Spry.Widget.ValidationTextField.prototype.onChange = function(e)
1056{
1057	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
1058		return true;
1059	}
1060	if (Spry.is.ie && e && e.propertyName != 'value') {
1061		return true;
1062	}
1063
1064	if (this.flags.drop) {
1065		//delay this if it's a drop operation
1066		var self = this;
1067		setTimeout(function() {
1068			self.flags.drop = false;
1069			self.onChange(null);
1070		}, 0);
1071		return;
1072	}
1073
1074	if (this.flags.hintOn) {
1075		return true;
1076	}
1077
1078	if (this.keyCode == 8 || this.keyCode == 46 ) {
1079		var mustRevert = this.doValidations(this.input.value, this.input.value);
1080		this.oldValue = this.input.value;
1081		if ((mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
1082			var self = this;
1083			setTimeout(function() {self.validate();}, 0);
1084			return true;
1085		}
1086	}
1087
1088	var mustRevert = this.doValidations(this.input.value, this.oldValue);
1089	if ((!mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
1090		var self = this;
1091		setTimeout(function() {self.validate();}, 0);
1092	}
1093	return true;
1094};
1095
1096Spry.Widget.ValidationTextField.prototype.onKeyUp = function(e) {
1097	if (this.flags.operaRevertOnKeyUp) {
1098		this.setValue(this.oldValue);
1099		Spry.Widget.Utils.stopEvent(e);
1100		this.selection.moveTo(this.selection.start, this.selection.start);
1101		this.flags.operaRevertOnKeyUp = false;
1102		return false;
1103	}
1104	if (this.flags.operaPasteOperation) {
1105		window.clearInterval(this.flags.operaPasteOperation);
1106		this.flags.operaPasteOperation = null;
1107	}
1108};
1109
1110Spry.Widget.ValidationTextField.prototype.operaPasteMonitor = function() {
1111	if (this.input.value != this.oldValue) {
1112		var mustRevert = this.doValidations(this.input.value, this.input.value);
1113		if (mustRevert) {
1114			this.setValue(this.oldValue);
1115			this.selection.moveTo(this.selection.start, this.selection.start);
1116		} else {
1117			this.onChange();
1118		}
1119	}
1120};
1121
1122
1123Spry.Widget.ValidationTextField.prototype.compileDatePattern = function () 
1124{
1125	var dateValidationPatternString = "";
1126	var groupPatterns = [];
1127	var fullGroupPatterns = [];
1128	var autocompleteCharacters = [];
1129	
1130	
1131	var formatRegExp = /^([mdy]+)([\.\-\/\\\s]+)([mdy]+)([\.\-\/\\\s]+)([mdy]+)$/i;
1132	var formatGroups = this.options.format.match(formatRegExp);
1133	if (formatGroups !== null) {
1134		for (var i=1; i<formatGroups.length; i++) {
1135			switch (formatGroups[i].toLowerCase()) {
1136				case "dd":
1137					groupPatterns[i-1] = "\\d{1,2}";
1138					fullGroupPatterns[i-1] = "\\d\\d";
1139					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
1140					autocompleteCharacters[i-1] = null;
1141					break;
1142				case "mm":
1143					groupPatterns[i-1] = "\\d{1,2}";
1144					fullGroupPatterns[i-1] = "\\d\\d";
1145					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
1146					autocompleteCharacters[i-1] = null;
1147					break;
1148				case "yy":
1149					groupPatterns[i-1] = "\\d{1,2}";
1150					fullGroupPatterns[i-1] = "\\d\\d";
1151					dateValidationPatternString += "(\\d\\d)";
1152					autocompleteCharacters[i-1] = null;
1153					break;
1154				case "yyyy":
1155					groupPatterns[i-1] = "\\d{1,4}";
1156					fullGroupPatterns[i-1] = "\\d\\d\\d\\d";
1157					dateValidationPatternString += "(\\d\\d\\d\\d)";
1158					autocompleteCharacters[i-1] = null;
1159					break;
1160				default:
1161					groupPatterns[i-1] = fullGroupPatterns[i-1] = Spry.Widget.ValidationTextField.regExpFromChars(formatGroups[i]);
1162					dateValidationPatternString += "["+ groupPatterns[i-1] + "]";
1163					autocompleteCharacters[i-1] = formatGroups[i];
1164			}
1165		}
1166	}
1167	this.dateValidationPattern = new RegExp("^" + dateValidationPatternString + "$" , "");
1168	this.dateAutocompleteCharacters = autocompleteCharacters;
1169	this.dateGroupPatterns = groupPatterns;
1170	this.dateFullGroupPatterns = fullGroupPatterns;
1171	this.lastDateGroup = formatGroups.length-2;
1172};
1173
1174Spry.Widget.ValidationTextField.prototype.getRegExpForGroup = function (group) 
1175{
1176	var ret = '^';
1177	for (var j = 0; j <= group; j++) ret += this.dateGroupPatterns[j];
1178	ret += '$';
1179	return new RegExp(ret, "");	
1180};
1181
1182Spry.Widget.ValidationTextField.prototype.getRegExpForFullGroup = function (group) 
1183{
1184	var ret = '^';
1185	for (var j = 0; j < group; j++) ret += this.dateGroupPatterns[j];
1186	ret += this.dateFullGroupPatterns[group];
1187	return new RegExp(ret, "");	
1188};
1189
1190Spry.Widget.ValidationTextField.prototype.getDateGroup = function(value, pos) 
1191{
1192	if (pos == 0) return 0;
1193	var test_value = value.substring(0, pos);
1194	for (var i=0; i <= this.lastDateGroup; i++) 
1195		if (this.getRegExpForGroup(i).test(test_value)) return i;
1196	return -1;
1197};
1198
1199
1200Spry.Widget.ValidationTextField.prototype.isDateGroupFull = function(value, group) 
1201{
1202	return this.getRegExpForFullGroup(group).test(value);
1203};
1204
1205Spry.Widget.ValidationTextField.prototype.isValueValid = function(value, pos, group) 
1206{
1207	var test_value = value.substring(0, pos);
1208	return this.getRegExpForGroup(group).test(test_value);
1209};
1210
1211
1212Spry.Widget.ValidationTextField.prototype.isPositionAtEndOfGroup = function (value, pos, group)
1213{
1214	var test_value = value.substring(0, pos);
1215	return this.getRegExpForFullGroup(group).test(test_value);
1216};
1217
1218Spry.Widget.ValidationTextField.prototype.nextDateDelimiterExists = function (value, pos, group)
1219{
1220	var autocomplete = this.dateAutocompleteCharacters[group+1];
1221	if (value.length < pos  + autocomplete.length) 
1222		return false;
1223	else 
1224	{
1225		var test_value = value.substring(pos, pos+autocomplete.length);
1226		if (test_value == autocomplete) 
1227			return true;
1228	}
1229	return false;
1230};
1231
1232
1233
1234Spry.Widget.ValidationTextField.prototype.onKeyPress = function(e)
1235{
1236	if (this.flags.skp) {
1237		this.flags.skp = false;
1238		Spry.Widget.Utils.stopEvent(e);
1239		return false;
1240	}
1241
1242	if (e.ctrlKey || e.metaKey || !this.useCharacterMasking) {
1243		return true;
1244	}
1245/*
1246	if (Spry.is.safari) {
1247		if ( (e.timeStamp - this.flags.lastKeyPressedTimeStamp)<10 ) {
1248			return true;
1249		}
1250		this.flags.lastKeyPressedTimeStamp = e.timeStamp;
1251	}
1252*/
1253	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
1254		Spry.Widget.Utils.stopEvent(e);
1255		return false;
1256	}
1257
1258	if (this.keyCode == 8 || this.keyCode == 46) {
1259		var mr = this.doValidations(this.input.value, this.input.value);
1260		if (mr) {
1261			return true;
1262		}
1263	}
1264
1265	var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);
1266
1267	if (pressed && this.characterMasking) {
1268		if (!this.characterMasking.test(pressed)) {
1269			Spry.Widget.Utils.stopEvent(e);
1270			this.redTextFlash();
1271			return false;
1272		}
1273	}
1274
1275	if(pressed && this.pattern) {
1276		var currentPatternChar = this.patternCharacters[this.selection.start];
1277		if (/[ax]/i.test(currentPatternChar)) {
1278			//convert the entered character to the pattern character case
1279			if (currentPatternChar.toLowerCase() == currentPatternChar) {
1280				pressed = pressed.toLowerCase();
1281			} else {
1282				pressed = pressed.toUpperCase();
1283			}
1284		}
1285
1286		var autocomplete = this.getAutoComplete(this.selection.start);
1287		if (this.selection.start == this.oldValue.length) {
1288			if (this.oldValue.length < this.patternLength) {
1289				if (autocomplete) {
1290					Spry.Widget.Utils.stopEvent(e);
1291					var futureValue = this.oldValue.substring(0, this.selection.start) + autocomplete + pressed;
1292					var mustRevert = this.doValidations(futureValue, this.oldValue);
1293					if (!mustRevert) {
1294						this.setValue(this.fixedValue);
1295						this.selection.moveTo(this.fixedValue.length, this.fixedValue.length);
1296					} else {
1297						this.setValue(this.oldValue.substring(0, this.selection.start) + autocomplete);
1298						this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1299					}
1300					return false;
1301				}
1302			} else {
1303				Spry.Widget.Utils.stopEvent(e);
1304				this.setValue(this.input.value);
1305				return false;
1306			}
1307		} else if (autocomplete) {
1308			Spry.Widget.Utils.stopEvent(e);
1309			this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1310			return false;
1311		}
1312
1313		Spry.Widget.Utils.stopEvent(e);
1314
1315		var futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1316		var mustRevert = this.doValidations(futureValue, this.oldValue);
1317
1318		if (!mustRevert) {
1319			autocomplete = this.getAutoComplete(this.selection.start + 1);
1320			this.setValue(this.fixedValue);
1321			this.selection.moveTo(this.selection.start + 1 + autocomplete.length, this.selection.start + 1 + autocomplete.length);
1322		} else {
1323			this.selection.moveTo(this.selection.start, this.selection.start);
1324		}
1325
1326		return false;
1327	}
1328	
1329	
1330	if (pressed && this.type == 'date' && this.useCharacterMasking) 
1331	{
1332		var group = this.getDateGroup(this.oldValue, this.selection.start);
1333		if (group != -1) {
1334			Spry.Widget.Utils.stopEvent(e);
1335			if ( (group % 2) !=0 ) 
1336				group ++;
1337			
1338			if (this.isDateGroupFull(this.oldValue, group)) 
1339			{
1340				if(this.isPositionAtEndOfGroup(this.oldValue, this.selection.start, group))
1341				{
1342					if(group == this.lastDateGroup) 
1343					{
1344						this.redTextFlash(); return false;
1345					}
1346					else 
1347					{
1348						// add or jump over autocomplete delimiter
1349						var autocomplete = this.dateAutocompleteCharacters[group+1];
1350						
1351						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
1352						{
1353							var autocomplete = this.dateAutocompleteCharacters[group+1];
1354							
1355							this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1356							if (pressed == autocomplete) 
1357								return false;
1358							
1359							if (this.isDateGroupFull(this.oldValue, group+2)) 
1360								// need to overwrite first char in the next digit group
1361								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1362							else
1363								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
1364								
1365							if (!this.isValueValid(futureValue, this.selection.start + 1, group +2 )) 
1366							{
1367								this.redTextFlash(); return false;						
1368							}
1369							else
1370							{
1371								this.setValue (futureValue);
1372								this.selection.moveTo(this.selection.start + 1, this.selection.start + 1);									
1373							}
1374							return false;					
1375						}
1376						else 
1377						{
1378							var autocomplete = this.dateAutocompleteCharacters[group+1];
1379							
1380							var insertedValue = autocomplete + pressed;
1381							futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
1382							if (!this.isValueValid(futureValue, this.selection.start + insertedValue.length, group +2 )) 
1383							{
1384								// block this type
1385								insertedValue = autocomplete;
1386								futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
1387								this.setValue (futureValue);
1388								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
1389								this.redTextFlash(); return false;
1390							}
1391							else 
1392							{
1393								this.setValue (futureValue);
1394								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
1395								return false;
1396							}
1397						}
1398						
1399					}
1400				}
1401				else
1402				{
1403					// it's not the end of the full digits group
1404					
1405					// overwrite
1406					var movePosition = 1;
1407					futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1408					if (!this.isValueValid(futureValue, this.selection.start + 1, group)) 
1409					{
1410						this.redTextFlash(); return false;
1411					}
1412					else 
1413					{
1414						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
1415						{
1416							if (group != this.lastDateGroup)
1417							{
1418								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
1419								{
1420									var autocomplete = this.dateAutocompleteCharacters[group+1];
1421									movePosition = 1 + autocomplete.length;
1422								}
1423								else
1424								{
1425									var autocomplete = this.dateAutocompleteCharacters[group+1];
1426									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
1427									movePosition = 1 + autocomplete.length;
1428								}
1429							}
1430						}
1431						this.setValue (futureValue);
1432						this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
1433						return false;							
1434					}			
1435				}
1436			}
1437			else
1438			{
1439				// date group is not full
1440				// insert
1441				futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
1442				var movePosition = 1;
1443				if (!this.isValueValid(futureValue, this.selection.start + 1, group) && !this.isValueValid(futureValue, this.selection.start + 1, group+1)) 
1444				{
1445					this.redTextFlash(); return false;
1446				}
1447				else 
1448				{
1449					var autocomplete = this.dateAutocompleteCharacters[group+1];
1450					if (pressed == autocomplete) 
1451					{
1452						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
1453						{
1454							futureValue = this.oldValue;
1455							movePosition = 1;
1456						}
1457					}
1458					else
1459					{
1460						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
1461						{
1462							if (group != this.lastDateGroup)
1463							{
1464								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
1465								{
1466									var autocomplete = this.dateAutocompleteCharacters[group+1];
1467									movePosition = 1 + autocomplete.length;
1468								}
1469								else
1470								{
1471									var autocomplete = this.dateAutocompleteCharacters[group+1];
1472									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
1473									movePosition = 1 + autocomplete.length;
1474								}
1475							}
1476						}
1477					}
1478					this.setValue (futureValue);
1479					this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
1480					return false;						
1481				}	
1482			}
1483		}
1484		return false;
1485	}
1486	
1487};
1488
1489Spry.Widget.ValidationTextField.prototype.onKeyDown = function(e)
1490{
1491	this.saveState();
1492	this.keyCode = e.keyCode;
1493
1494	if (Spry.is.opera) {
1495		if (this.flags.operaPasteOperation) {
1496			window.clearInterval(this.flags.operaPasteOperation);
1497			this.flags.operaPasteOperation = null;
1498		}
1499		if (e.ctrlKey) {
1500			var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);
1501			if (pressed && 'vx'.indexOf(pressed.toLowerCase()) != -1) {
1502				var self = this;
1503				this.flags.operaPasteOperation = window.setInterval(function() { self.operaPasteMonitor();}, 1);
1504				return true;
1505			}
1506		}
1507	}
1508
1509	if (this.keyCode != 8 && this.keyCode != 46 && Spry.Widget.Utils.isSpecialKey(e)) {
1510		return true;
1511	}
1512	if (this.keyCode == 8 || this.keyCode == 46 ) {
1513		var mr = this.doValidations(this.input.value, this.input.value);
1514		if (mr) {
1515			return true;
1516		}
1517	}
1518
1519	//DELETE
1520	if (this.useCharacterMasking && this.pattern && this.keyCode == 46) {
1521		if (e.ctrlKey) {
1522			//delete from selection until end
1523			this.setValue(this.input.value.substring(0, this.selection.start));
1524		} else if (this.selection.end == this.input.value.length || this.selection.start == this.input.value.length-1){
1525			//allow key if selection is at end (will delete selection)
1526			return true;
1527		} else {
1528			this.flags.operaRevertOnKeyUp = true;
1529		}
1530		if (Spry.is.mozilla && Spry.is.mac) {
1531			this.flags.skp = true;
1532		}
1533		Spry.Widget.Utils.stopEvent(e);
1534		return false;
1535	}
1536
1537	//BACKSPACE
1538	if (this.useCharacterMasking && this.pattern && !e.ctrlKey && this.keyCode == 8) {
1539		if (this.selection.start == this.input.value.length) {
1540			//delete with BACKSPACE from the end of the input value only
1541			var n = this.getAutoComplete(this.selection.start, -1);
1542			this.setValue(this.input.value.substring(0, this.input.value.length - (Spry.is.opera?0:1) - n.length));
1543			if (Spry.is.opera) {
1544				//cant stop the event on Opera, we'll just preserve the selection so delete will act on it
1545				this.selection.start = this.selection.start - 1 - n.length;
1546				this.selection.end = this.selection.end - 1 - n.length;
1547			}
1548		} else if (this.selection.end == this.input.value.length){
1549			//allow BACKSPACE if selection is at end (will delete selection)
1550			return true;
1551		} else {
1552			this.flags.operaRevertOnKeyUp = true;
1553		}
1554		if (Spry.is.mozilla && Spry.is.mac) {
1555			this.flags.skp = true;
1556		} 
1557		Spry.Widget.Utils.stopEvent(e);
1558		return false;
1559	}
1560
1561	return true;
1562};
1563
1564Spry.Widget.ValidationTextField.prototype.onMouseDown = function(e)
1565{
1566	if (this.flags.active) {
1567		//mousedown fires before focus
1568		//avoid double saveState on first focus by mousedown by checking if the control has focus
1569		//do nothing if it's not focused because saveState will be called onfocus
1570		this.saveState();
1571	}
1572};
1573
1574Spry.Widget.ValidationTextField.prototype.onDrop = function(e)
1575{
1576	//mark that a drop operation is in progress to avoid race conditions with event handlers for other events
1577	//especially onchange and onfocus
1578	this.flags.drop = true;
1579	this.removeHint();
1580	this.saveState();
1581	this.flags.active = true;
1582	this.addClassName(this.element, this.focusClass);
1583	this.addClassName(this.additionalError, this.focusClass);
1584};
1585
1586Spry.Widget.ValidationTextField.prototype.onFocus = function(e)
1587{
1588	if (this.flags.drop) {
1589		return;
1590	}
1591	this.removeHint();
1592
1593	if (this.pattern && this.useCharacterMasking) {
1594		var autocomplete = this.getAutoComplete(this.selection.start);
1595		this.setValue(this.input.value + autocomplete);
1596		this.selection.moveTo(this.input.value.length, this.input.value.length);
1597	}
1598	
1599	this.saveState();
1600	this.flags.active = true;
1601	this.addClassName(this.element, this.focusClass);
1602	this.addClassName(this.additionalError, this.focusClass);
1603};
1604	
1605Spry.Widget.ValidationTextField.prototype.onBlur = function(e)
1606{
1607	this.flags.active = false;
1608	this.removeClassName(this.element, this.focusClass);
1609	this.removeClassName(this.additionalError, this.focusClass);
1610	this.flags.restoreSelection = false;
1611	var mustRevert = this.doValidations(this.input.value, this.input.value);
1612	this.flags.restoreSelection = true;
1613
1614	if (this.validateOn & Spry.Widget.ValidationTextField.ONBLUR) {
1615		this.validate();
1616	}
1617	var self = this;
1618	setTimeout(function() {self.putHint();}, 10);
1619	return true;
1620};
1621
1622Spry.Widget.ValidationTextField.prototype.compilePattern = function() {
1623	if (!this.pattern) {
1624		return;
1625	}
1626	var compiled = [];
1627	var regexps = [];
1628	var patternCharacters = [];
1629	var idx = 0;
1630	var c = '', p = '';
1631	for (var i=0; i<this.pattern.length; i++) {
1632		c = this.pattern.charAt(i);
1633		if (p == '\\') {
1634			if (/[0ABXY\?]/i.test(c)) {
1635				regexps[idx - 1] = c;
1636			} else {
1637				regexps[idx - 1] = Spry.Widget.ValidationTextField.regExpFromChars(c);
1638			}
1639			compiled[idx - 1] = c;
1640			patternCharacters[idx - 1] = null;
1641			p = '';
1642			continue;
1643		}
1644		regexps[idx] = Spry.Widget.ValidationTextField.regExpFromChars(c);
1645		if (/[0ABXY\?]/i.test(c)) {
1646			compiled[idx] = null;
1647			patternCharacters[idx] = c;
1648		} else if (c == '\\') {
1649			compiled[idx] = c;
1650			patternCharacters[idx] = '\\';
1651		} else {
1652			compiled[idx] = c;
1653			patternCharacters[idx] = null;
1654		}
1655		idx++;
1656		p = c;
1657	}
1658
1659	this.autoCompleteCharacters = compiled;
1660	this.compiledPattern = regexps;
1661	this.patternCharacters = patternCharacters;
1662	this.patternLength = compiled.length;
1663};
1664
1665Spry.Widget.ValidationTextField.prototype.getAutoComplete = function(from, direction) {
1666	if (direction == -1) {
1667		var n = '', m = '';
1668		while(from && (n = this.getAutoComplete(--from) )) {
1669			m = n;
1670		}
1671		return m;
1672	}
1673	var ret = '', c = '';
1674	for (var i=from; i<this.autoCompleteCharacters.length; i++) {
1675		c = this.autoCompleteCharacters[i];
1676		if (c) {
1677			ret += c;
1678		} else {
1679			break;
1680		}
1681	}
1682	return ret;
1683};
1684
1685Spry.Widget.ValidationTextField.regExpFromChars = function (string) {
1686	//string contains pattern characters
1687	var ret = '', character = '';
1688	for (var i = 0; i<string.length; i++) {
1689		character = string.charAt(i);
1690		switch (character) {
1691			case '0': ret += '\\d';break;
1692			case 'A': ret += '[A-Z]';break;
1693//			case 'A': ret += '[\u0041-\u005A\u0061-\u007A\u0100-\u017E\u0180-\u0233\u0391-\u03CE\u0410-\u044F\u05D0-\u05EA\u0621-\u063A\u0641-\u064A\u0661-\u06D3\u06F1-\u06FE]';break;
1694			case 'a': ret += '[a-z]';break;
1695//			case 'a': ret += '[\u0080-\u00FF]';break;
1696			case 'B': case 'b': ret += '[a-zA-Z]';break;
1697			case 'x': ret += '[0-9a-z]';break;
1698			case 'X': ret += '[0-9A-Z]';break;
1699			case 'Y': case 'y': ret += '[0-9a-zA-Z]';break;
1700			case '?': ret += '.';break;
1701			case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
1702				ret += character;
1703				break;
1704			case 'c': case 'C': case 'e': case 'E': case 'f': case 'F':case 'r':case 'd': case 'D':case 'n':case 's':case 'S':case 'w':case 'W':case 't':case 'v':
1705				ret += character;
1706				break;
1707			default: ret += '\\' + character;
1708		}
1709	}
1710	return ret;
1711};
1712
1713Spry.Widget.ValidationTextField.prototype.patternToRegExp = function(len) {
1714	var ret = '^';
1715	var end = Math.min(this.compiledPattern.length, len);
1716	for (var i=0; i < end; i++) {
1717		ret += this.compiledPattern[i];
1718	}
1719	ret += '$';
1720	ret = new RegExp(ret, "");
1721	return ret;
1722};
1723
1724Spry.Widget.ValidationTextField.prototype.resetClasses = function() {
1725	var classes = [this.requiredClass, this.invalidFormatClass, this.invalidRangeMinClass, this.invalidRangeMaxClass, this.invalidCharsMinClass, this.invalidCharsMaxClass, this.validClass];
1726	for (var i=0; i < classes.length; i++)
1727	{
1728		this.removeClassName(this.element, classes[i]);
1729		this.removeClassName(this.additionalError, classes[i]);
1730	}
1731};
1732
1733Spry.Widget.ValidationTextField.prototype.reset = function() {
1734	this.removeHint();
1735	this.oldValue = this.input.defaultValue;
1736	
1737	this.resetClasses();
1738	if (Spry.is.ie) {
1739		//this will fire the onpropertychange event right after the className changed on the container element
1740		//IE6 will not fire the first onpropertychange on an input type text after a onreset handler if inside that handler the className of one of the elements inside the form has been changed
1741		//to reproduce: change the className of one of the elements inside the form from within the onreset handler; then the onpropertychange does not fire the first time
1742		this.input.forceFireFirstOnPropertyChange = true;
1743		this.input.removeAttribute("forceFireFirstOnPropertyChange");
1744	}
1745	var self = this;
1746	setTimeout(function() {self.putHint();}, 10);
1747};
1748
1749Spry.Widget.ValidationTextField.prototype.validate = function() {
1750
1751	this.resetClasses();
1752	//possible states: required, format, rangeMin, rangeMax, charsMin, charsMax
1753	if (this.validateOn & Spry.Widget.ValidationTextField.ONSUBMIT) {
1754
1755		this.removeHint();
1756		this.doValidations(this.input.value, this.input.value);
1757
1758		if(!this.flags.active) {
1759			var self = this;
1760			setTimeout(function() {self.putHint();}, 10);
1761		}
1762	}
1763
1764	if (this.isRequired && this.errors & Spry.Widget.ValidationTextField.ERROR_REQUIRED) {
1765		this.addClassName(this.element, this.requiredClass);
1766		this.addClassName(this.additionalError, this.requiredClass);
1767		return false;
1768	}
1769
1770	if (this.errors & Spry.Widget.ValidationTextField.ERROR_FORMAT) {
1771		this.addClassName(this.element, this.invalidFormatClass);
1772		this.addClassName(this.additionalError, this.invalidFormatClass);
1773		return false;
1774	}
1775
1776	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MIN) {
1777		this.addClassName(this.element, this.invalidRangeMinClass);
1778		this.addClassName(this.additionalError, this.invalidRangeMinClass);
1779		return false;
1780	}
1781
1782	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MAX) {
1783		this.addClassName(this.element, this.invalidRangeMaxClass);
1784		this.addClassName(this.additionalError, this.invalidRangeMaxClass);
1785		return false;
1786	}
1787
1788	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MIN) {
1789		this.addClassName(this.element, this.invalidCharsMinClass);
1790		this.addClassName(this.additionalError, this.invalidCharsMinClass);
1791		return false;
1792	}
1793
1794	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MAX) {
1795		this.addClassName(this.element, this.invalidCharsMaxClass);
1796		this.addClassName(this.additionalError, this.invalidCharsMaxClass);
1797		return false;
1798	}
1799
1800	this.addClassName(this.element, this.validClass);
1801	this.addClassName(this.additionalError, this.validClass);
1802	return true;
1803};
1804
1805Spry.Widget.ValidationTextField.prototype.addClassName = function(ele, className)
1806{
1807	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
1808		return;
1809	ele.className += (ele.className ? " " : "") + className;
1810};
1811
1812Spry.Widget.ValidationTextField.prototype.removeClassName = function(ele, className)
1813{
1814	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))
1815		return;
1816	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
1817};
1818Spry.Widget.ValidationTextField.prototype.showError = function(msg)
1819{
1820	alert('Spry.Widget.TextField ERR: ' + msg);
1821};
1822/**
1823 * SelectionDescriptor is a wrapper for input type text selection methods and properties 
1824 * as implemented by various  browsers
1825 */
1826Spry.Widget.SelectionDescriptor = function (element)
1827{
1828	this.element = element;
1829	this.update();
1830};
1831
1832Spry.Widget.SelectionDescriptor.prototype.update = function()
1833{
1834	if (Spry.is.ie && Spry.is.windows) {
1835		var sel = this.element.ownerDocument.selection;
1836		if (this.element.nodeName == "TEXTAREA") {
1837			if (sel.type != 'None') {
1838				try{var range = sel.createRange();}catch(err){return;}
1839				if (range.parentElement() == this.element){
1840					var range_all = this.element.ownerDocument.body.createTextRange();
1841					range_all.moveToElementText(this.element);
1842					for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++){
1843						range_all.moveStart('character', 1);
1844					}
1845					this.start = sel_start;
1846					// create a selection of the whole this.element
1847					range_all = this.element.ownerDocument.body.createTextRange();
1848					range_all.moveToElementText(this.element);
1849					for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; sel_end++){
1850						range_all.moveStart('character', 1);
1851					}
1852					this.end = sel_end;
1853					this.length = this.end - this.start;
1854					// get selected and surrounding text
1855					this.text = range.text;
1856		 		}
1857			}        
1858		} else if (this.element.nodeName == "INPUT"){
1859			try{this.range = sel.createRange();}catch(err){return;}
1860			this.length = this.range.text.length;
1861			var clone = this.range.duplicate();
1862			this.start = -clone.moveStart("character", -10000);
1863			clone = this.range.duplicate();
1864			clone.collapse(false);
1865			this.end = -clone.moveStart("character", -10000);
1866			this.text = this.range.text;
1867		}
1868	} else {
1869		var tmp = this.element;
1870		var selectionStart = 0;
1871		var selectionEnd = 0;
1872        
1873		try { selectionStart = tmp.selectionStart;} catch(err) {}
1874		try { selectionEnd = tmp.selectionEnd;} catch(err) {}
1875
1876		if (Spry.is.safari) {
1877			if (selectionStart == 2147483647) {
1878				selectionStart = 0;
1879			}
1880			if (selectionEnd == 2147483647) {
1881				selectionEnd = 0;
1882			}
1883		}
1884		this.start = selectionStart;
1885		this.end = selectionEnd;
1886		this.length = selectionEnd - selectionStart;
1887		this.text = this.element.value.substring(selectionStart, selectionEnd);
1888	}
1889};
1890
1891Spry.Widget.SelectionDescriptor.prototype.destroy = function() {
1892	try { delete this.range} catch(err) {}
1893	try { delete this.element} catch(err) {}
1894};
1895
1896Spry.Widget.SelectionDescriptor.prototype.move = function(amount)
1897{
1898	if (Spry.is.ie && Spry.is.windows) {
1899		this.range.move("character", amount);
1900		this.range.select();
1901	} else {
1902		try { this.element.selectionStart++;}catch(err) {}
1903	}
1904	this.update();
1905};
1906
1907Spry.Widget.SelectionDescriptor.prototype.moveTo = function(start, end)
1908{
1909	if (Spry.is.ie && Spry.is.windows) {
1910		if (this.element.nodeName == "TEXTAREA") {
1911			var ta_range = this.element.createTextRange();
1912			this.range = this.element.createTextRange();
1913			this.range.move("character", start);
1914			this.range.moveEnd("character", end - start);
1915			
1916			var c1 = this.range.compareEndPoints("StartToStart", ta_range);
1917			if (c1 < 0) {
1918				this.range.setEndPoint("StartToStart", ta_range);
1919			}
1920
1921			var c2 = this.range.compareEndPoints("EndToEnd", ta_range);
1922			if (c2 > 0) {
1923				this.range.setEndPoint("EndToEnd", ta_range);
1924			}
1925		} else if (this.element.nodeName == "INPUT"){
1926			this.range = this.element.ownerDocument.selection.createRange();
1927			this.range.move("character", -10000);
1928			this.start = this.range.moveStart("character", start);
1929			this.end = this.start + this.range.moveEnd("character", end - start);
1930		}
1931		this.range.select();
1932	} else {
1933		this.start = start;
1934		try { this.element.selectionStart = start;} catch(err) {}
1935		this.end = end;
1936		try { this.element.selectionEnd = end;} catch(err) {}
1937	}
1938	this.ignore = true;
1939	this.update();
1940};
1941
1942Spry.Widget.SelectionDescriptor.prototype.moveEnd = function(amount)
1943{
1944	if (Spry.is.ie && Spry.is.windows) {
1945		this.range.moveEnd("character", amount);
1946		this.range.select();
1947	} else {
1948		try { this.element.selectionEnd++;} catch(err) {}
1949	}
1950	this.update();
1951};
1952
1953Spry.Widget.SelectionDescriptor.prototype.collapse = function(begin)
1954{
1955	if (Spry.is.ie && Spry.is.windows) {
1956		this.range = this.element.ownerDocument.selection.createRange();
1957		this.range.collapse(begin);
1958		this.range.select();
1959	} else {
1960		if (begin) {
1961			try { this.element.selectionEnd = this.element.selectionStart;} catch(err) {}
1962		} else {
1963			try { this.element.selectionStart = this.element.selectionEnd;} catch(err) {}
1964		}
1965	}
1966
1967	this.update();
1968};
1969
1970//////////////////////////////////////////////////////////////////////
1971//
1972// Spry.Widget.Form - common for all widgets
1973//
1974//////////////////////////////////////////////////////////////////////
1975
1976if (!Spry.Widget.Form) Spry.Widget.Form = {};
1977if (!Spry.Widget.Form.onSubmitWidgetQueue) Spry.Widget.Form.onSubmitWidgetQueue = [];
1978
1979if (!Spry.Widget.Form.validate) {
1980	Spry.Widget.Form.validate = function(vform) {
1981		var isValid = true;
1982		var isElementValid = true;
1983		var q = Spry.Widget.Form.onSubmitWidgetQueue;
1984		var qlen = q.length;
1985		for (var i = 0; i < qlen; i++) {
1986			if (!q[i].isDisabled() && q[i].form == vform) {
1987				isElementValid = q[i].validate();
1988				isValid = isElementValid && isValid;
1989			}
1990		}
1991		return isValid;
1992	}
1993};
1994
1995if (!Spry.Widget.Form.onSubmit) {
1996	Spry.Widget.Form.onSubmit = function(e, form)
1997	{
1998		if (Spry.Widget.Form.validate(form) == false) {
1999			return false;
2000		}
2001		return true;
2002	};
2003};
2004
2005if (!Spry.Widget.Form.onReset) {
2006	Spry.Widget.Form.onReset = function(e, vform)
2007	{
2008		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2009		var qlen = q.length;
2010		for (var i = 0; i < qlen; i++) {
2011			if (!q[i].isDisabled() && q[i].form == vform && typeof(q[i].reset) == 'function') {
2012				q[i].reset();
2013			}
2014		}
2015		return true;
2016	};
2017};
2018
2019if (!Spry.Widget.Form.destroy) {
2020	Spry.Widget.Form.destroy = function(form)
2021	{
2022		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2023		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2024			if (q[i].form == form && typeof(q[i].destroy) == 'function') {
2025				q[i].destroy();
2026				i--;
2027			}
2028		}
2029	}
2030};
2031
2032if (!Spry.Widget.Form.destroyAll) {
2033	Spry.Widget.Form.destroyAll = function()
2034	{
2035		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2036		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2037			if (typeof(q[i].destroy) == 'function') {
2038				q[i].destroy();
2039				i--;
2040			}
2041		}
2042	}
2043};
2044
2045//////////////////////////////////////////////////////////////////////
2046//
2047// Spry.Widget.Utils
2048//
2049//////////////////////////////////////////////////////////////////////
2050
2051if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};
2052
2053Spry.Widget.Utils.punycode_constants = {
2054	base : 36, tmin : 1, tmax : 26, skew : 38, damp : 700,
2055  initial_bias : 72, initial_n : 0x80, delimiter : 0x2D,
2056  maxint : 2<<26-1
2057};
2058
2059Spry.Widget.Utils.punycode_encode_digit = function (d) {
2060  return String.fromCharCode(d + 22 + 75 * (d < 26));
2061};
2062
2063Spry.Widget.Utils.punycode_adapt = function (delta, numpoints, firsttime) {
2064	delta = firsttime ? delta / this.punycode_constants.damp : delta >> 1;
2065	delta += delta / numpoints;
2066	
2067	for (var k = 0; delta > ((this.punycode_constants.base - this.punycode_constants.tmin) * this.punycode_constants.tmax) / 2; k += this.punycode_constants.base) {
2068		delta /= this.punycode_constants.base - this.punycode_constants.tmin;
2069	}
2070	return k + (this.punycode_constants.base - this.punycode_constants.tmin + 1) * delta / (delta + this.punycode_constants.skew);
2071};
2072
2073/**
2074 * returns a 	Punicode representation of a UTF-8 string
2075 * adapted from http://tools.ietf.org/html/rfc3492
2076 */
2077Spry.Widget.Utils.punycode_encode = function (input, max_out) {
2078	var inputc = input.split("");
2079	input = [];
2080	for(var i=0; i<inputc.length; i++) {
2081		input.push(inputc[i].charCodeAt(0));
2082	}
2083	var output = '';
2084
2085  var h, b, j, m, q, k, t;
2086	var input_len = input.length;
2087  var n = this.punycode_constants.initial_n;
2088  var delta = 0;
2089  var bias = this.punycode_constants.initial_bias;
2090  var out = 0;
2091
2092  for (j = 0; j < input_len; j++) {
2093		if (input[j] < 128) {
2094			if (max_out - out < 2) {
2095				return false;
2096			}
2097			output += String.fromCharCode(input[j]);
2098			out++;
2099		}
2100	}
2101
2102	h = b = out;
2103	if (b > 0) {
2104		output += String.fromCharCode(this.punycode_constants.delimiter);
2105		out++;
2106	}
2107
2108  while (h < input_len)	{
2109		for (m = this.punycode_constants.maxint, j = 0; j < input_len; j++) {
2110			if (input[j] >= n && input[j] < m) {
2111				m = input[j];
2112			}
2113		}
2114		if (m - n > (this.punycode_constants.maxint - delta) / (h + 1)) {
2115			return false;
2116		}
2117		
2118		delta += (m - n) * (h + 1);
2119		n = m;
2120
2121		for (j = 0; j < input_len; j++) {
2122			if (input[j] < n ) {
2123				if (++delta == 0) {
2124					return false;
2125				}
2126			}
2127
2128			if (input[j] == n) {
2129				for (q = delta, k = this.punycode_constants.base; true; k += this.punycode_constants.base) {
2130					if (out >= max_out) {
2131						return false;
2132					}
2133
2134					t = k <= bias ? this.punycode_constants.tmin : k >= bias + this.punycode_constants.tmax ? this.punycode_constants.tmax : k - bias;
2135					if (q < t) {
2136						break;
2137					}
2138
2139					output += this.punycode_encode_digit(t + (q - t) % (this.punycode_constants.base - t));
2140					out++;
2141					q = (q - t) / (this.punycode_constants.base - t);
2142				}
2143
2144				output += this.punycode_encode_digit(q);
2145				out++;
2146				bias = this.punycode_adapt(delta, h + 1, h == b);
2147				delta = 0;
2148				h++;
2149			}
2150		}
2151		delta++, n++;
2152	}
2153
2154  return output;
2155};
2156
2157Spry.Widget.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
2158{
2159	if (!optionsObj)
2160		return;
2161	for (var optionName in optionsObj)
2162	{
2163		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
2164			continue;
2165		obj[optionName] = optionsObj[optionName];
2166	}
2167};
2168
2169Spry.Widget.Utils.firstValid = function() {
2170	var ret = null;
2171	for(var i=0; i<Spry.Widget.Utils.firstValid.arguments.length; i++) {
2172		if (typeof(Spry.Widget.Utils.firstValid.arguments[i]) != 'undefined') {
2173			ret = Spry.Widget.Utils.firstValid.arguments[i];
2174			break;
2175		}
2176	}
2177	return ret;
2178};
2179
2180
2181Spry.Widget.Utils.specialCharacters = ",8,9,16,17,18,20,27,33,34,35,36,37,38,40,45,144,192,63232,";
2182Spry.Widget.Utils.specialSafariNavKeys = "63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,";
2183Spry.Widget.Utils.specialNotSafariCharacters = "39,46,91,92,93,";
2184
2185Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys;
2186
2187if (!Spry.is.safari) {
2188	Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialNotSafariCharacters;
2189}
2190
2191Spry.Widget.Utils.isSpecialKey = function (ev) {
2192	return Spry.Widget.Utils.specialCharacters.indexOf("," + ev.keyCode + ",") != -1;
2193};
2194
2195Spry.Widget.Utils.getCharacterFromEvent = function(e){
2196	var keyDown = e.type == "keydown";
2197
2198	var code = null;
2199	var character = null;
2200	if(Spry.is.mozilla && !keyDown){
2201		if(e.charCode){
2202			character = String.fromCharCode(e.charCode);
2203		} else {
2204			code = e.keyCode;
2205		}
2206	} else {
2207		code = e.keyCode || e.which;
2208		if (code != 13) {
2209			character = String.fromCharCode(code);
2210		}
2211	}
2212
2213	if (Spry.is.safari) {
2214		if (keyDown) {
2215			code = e.keyCode || e.which;
2216			character = String.fromCharCode(code);
2217		} else {
2218			code = e.keyCode || e.which;
2219			if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
2220				character = null;
2221			} else {
2222				character = String.fromCharCode(code);
2223			}
2224		}
2225	}
2226
2227	if(Spry.is.opera) {
2228		if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
2229			character = null;
2230		} else {
2231			character = String.fromCharCode(code);
2232		}
2233	}
2234
2235	return character;
2236};
2237
2238Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName)
2239{
2240	var elements  = node.getElementsByTagName(nodeName);
2241	if (elements) {
2242		return elements[0];
2243	}
2244	return null;
2245};
2246
2247Spry.Widget.Utils.getFirstParentWithNodeName = function(node, nodeName)
2248{
2249	while (node.parentNode
2250			&& node.parentNode.nodeName.toLowerCase() != nodeName.toLowerCase()
2251			&& node.parentNode.nodeName != 'BODY') {
2252		node = node.parentNode;
2253	}
2254
2255	if (node.parentNode && node.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {
2256		return node.parentNode;
2257	} else {
2258		return null;
2259	}
2260};
2261
2262Spry.Widget.Utils.destroyWidgets = function (container)
2263{
2264	if (typeof container == 'string') {
2265		container = document.getElementById(container);
2266	}
2267
2268	var q = Spry.Widget.Form.onSubmitWidgetQueue;
2269	for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2270		if (typeof(q[i].destroy) == 'function' && Spry.Widget.Utils.contains(container, q[i].element)) {
2271			q[i].destroy();
2272			i--;
2273		}
2274	}
2275};
2276
2277Spry.Widget.Utils.contains = function (who, what)
2278{
2279	if (typeof who.contains == 'object') {
2280		return what && who && (who == what || who.contains(what));
2281	} else {
2282		var el = what;
2283		while(el) {
2284			if (el == who) {
2285				return true;
2286			}
2287			el = el.parentNode;
2288		}
2289		return false;
2290	}
2291};
2292
2293Spry.Widget.Utils.addEventListener = function(element, eventType, handler, capture)
2294{
2295	try
2296	{
2297		if (element.addEventListener)
2298			element.addEventListener(eventType, handler, capture);
2299		else if (element.attachEvent)
2300			element.attachEvent("on" + eventType, handler, capture);
2301	}
2302	catch (e) {}
2303};
2304
2305Spry.Widget.Utils.removeEventListener = function(element, eventType, handler, capture)
2306{
2307	try
2308	{
2309		if (element.removeEventListener)
2310			element.removeEventListener(eventType, handler, capture);
2311		else if (element.detachEvent)
2312			element.detachEvent("on" + eventType, handler, capture);
2313	}
2314	catch (e) {}
2315};
2316
2317Spry.Widget.Utils.stopEvent = function(ev)
2318{
2319	try
2320	{
2321		this.stopPropagation(ev);
2322		this.preventDefault(ev);
2323	}
2324	catch (e) {}
2325};
2326
2327/**
2328 * Stops event propagation
2329 * @param {Event} ev the event
2330 */
2331Spry.Widget.Utils.stopPropagation = function(ev)
2332{
2333	if (ev.stopPropagation)
2334	{
2335		ev.stopPropagation();
2336	}
2337	else
2338	{
2339		ev.cancelBubble = true;
2340	}
2341};
2342
2343/**
2344 * Prevents the default behavior of the event
2345 * @param {Event} ev the event
2346 */
2347Spry.Widget.Utils.preventDefault = function(ev)
2348{
2349	if (ev.preventDefault)
2350	{
2351		ev.preventDefault();
2352	}
2353	else
2354	{
2355		ev.returnValue = false;
2356	}
2357};

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.