PageSourceSearch

https://www.dotwnews.com/SpryAssets/SpryValidationTextField.js

js dotwnews.com collected 2026-10-03 21:19:20 UTC 75,266 bytes, 2,358 lines download raw bytes

1// SpryValidationTextField.js - version 0.38 - 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
30(function() { // BeginSpryComponent
31
32if (typeof Spry == "undefined") window.Spry = {}; if (!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		}
433	},
434	'social_security_number': {
435		pattern:'000-00-0000'
436	},
437	'ip': {
438		characterMaskingFormats: {
439			'ipv4': /[\d\.]/i,
440			'ipv6_ipv4': /[\d\.\:A-F\/]/i,
441			'ipv6': /[\d\.\:A-F\/]/i
442		},
443		validation: function (value, options) {
444			return Spry.Widget.ValidationTextField.validateIP(value, options.format);
445		}
446	},
447
448	'url': {
449		characterMasking: /[^\s]/,
450		validation: function(value, options) {
451			//fix for ?ID=223429 and ?ID=223387
452			/* the following regexp matches components of an URI as specified in http://tools.ietf.org/html/rfc3986#page-51 page 51, Appendix B.
453				scheme    = $2
454				authority = $4
455				path      = $5
456				query     = $7
457				fragment  = $9
458			*/
459			var URI_spliter = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
460			var parts = value.match(URI_spliter);
461			if (parts && parts[4]) {
462				//encode each component of the domain name using Punycode encoding scheme: http://tools.ietf.org/html/rfc3492
463				var host  = parts[4].split(".");
464				var punyencoded = '';
465				for (var i=0; i<host.length; i++) {
466					punyencoded = Spry.Widget.Utils.punycode_encode(host[i], 64);
467					if (!punyencoded) {
468						return false;
469					} else {
470						if (punyencoded != (host[i] + "-")) {
471							host[i] = 'xn--' + punyencoded;
472						}
473					}
474				}
475				host = host .join(".");
476				//the encoded domain name is replaced into the original URL to be validated again later as URL
477				value = value.replace(URI_spliter, "$1//" + host + "$5$6$8");
478			}
479
480			//fix for ?ID=223358 and ?ID=223594
481			//the following validates an URL using ABNF rules as defined in http://tools.ietf.org/html/rfc3986 , Appendix A., page 49
482			//except host which is extracted by match[1] and validated separately
483			/*
484			 * userinfo=	(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?
485			 * host=			(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))
486			 * pathname=	(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*
487			 * query=			(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
488			 * anchor=		(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?
489			 */
490			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;
491
492			var valid = value.match(regExp);
493			if (valid) {
494				//extract the  address from URL
495				var address = valid[1];
496
497				if (address) {
498					if (address == '[]') {
499						return false;
500					}
501					if (address.charAt(0) == '[' ) {
502						//IPv6 address or IPv4 enclosed in square brackets
503						address = address.replace(/^\[|\]$/gi, '');
504						return Spry.Widget.ValidationTextField.validateIP(address, 'ipv6_ipv4');
505					} else {
506						if (/[^0-9\.]/.test(address)) {
507							return true;
508						} else {
509							//check if hostname is all digits and dots and then check for IPv4
510							return Spry.Widget.ValidationTextField.validateIP(address, 'ipv4');
511						}
512					}
513				} else {
514					return true;
515				}
516			} else {
517				return false;
518			}
519		}
520	}
521};
522
523/*
5242.2.1. Preferred
525x:x:x:x:x:x:x:x, where the 'x's are the hexadecimal values of the eight 16-bit pieces of the address.
526Examples:
527	FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
528	1080:0:0:0:8:800:200C:417A
529Note that it is not necessary to write the leading zeros in an
530individual field, but there must be at least one numeral in every
531field (except for the case described in 2.2.2.).
532
5332.2.2. Compressed
534The use of "::" indicates multiple groups of 16-bits of zeros.
535The "::" can only appear once in an address.  The "::" can also be
536used to compress the leading and/or trailing zeros in an address.
537	1080:0:0:0:8:800:200C:417A --> 1080::8:800:200C:417A
538	FF01:0:0:0:0:0:0:101 --> FF01::101
539	0:0:0:0:0:0:0:1 --> ::1
540	0:0:0:0:0:0:0:0 --> ::
541
5422.5.4 IPv6 Addresses with Embedded IPv4 Addresses
543	IPv4-compatible IPv6 address (tunnel IPv6 packets over IPv4 routing infrastructures)
544	::0:129.144.52.38
545	IPv4-mapped IPv6 address (represent the addresses of IPv4-only nodes as IPv6 addresses)
546	::ffff:129.144.52.38
547
548The text representation of IPv6 addresses and prefixes in Augmented BNF (Backus-Naur Form) [ABNF] for reference purposes.
549[ABNF http://tools.ietf.org/html/rfc2234]
550      IPv6address = hexpart [ ":" IPv4address ]
551      IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
552
553      IPv6prefix  = hexpart "/" 1*2DIGIT
554
555      hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
556      hexseq  = hex4 *( ":" hex4)
557      hex4    = 1*4HEXDIG
558*/
559Spry.Widget.ValidationTextField.validateIP = function (value, format)
560{
561	var validIPv6Addresses = [
562		//preferred
563		/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}(?:\/\d{1,3})?$/i,
564
565		//various compressed
566		/^[a-f0-9]{0,4}::(?:\/\d{1,3})?$/i,
567		/^:(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
568		/^(?:[a-f0-9]{1,4}:){1,6}:(?:\/\d{1,3})?$/i,
569		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,6}(?:\/\d{1,3})?$/i,
570		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,5}(?:\/\d{1,3})?$/i,
571		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,4}(?:\/\d{1,3})?$/i,
572		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}){1,3}(?:\/\d{1,3})?$/i,
573		/^(?:[a-f0-9]{1,4}:){5}(?::[a-f0-9]{1,4}){1,2}(?:\/\d{1,3})?$/i,
574		/^(?:[a-f0-9]{1,4}:){6}(?::[a-f0-9]{1,4})(?:\/\d{1,3})?$/i,
575
576
577		//IPv6 mixes with IPv4
578		/^(?:[a-f0-9]{1,4}:){6}(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
579		/^:(?::[a-f0-9]{1,4}){0,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
580		/^(?:[a-f0-9]{1,4}:){1,5}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
581		/^(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,4}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
582		/^(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,3}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,	
583		/^(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,2}:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i,
584		/^(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}):(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,3})?$/i
585	];
586	var validIPv4Addresses = [
587		//IPv4
588		/^(\d{1,3}\.){3}\d{1,3}$/i
589	];
590	var validAddresses = [];
591	if (format == 'ipv6' || format == 'ipv6_ipv4') {
592		validAddresses = validAddresses.concat(validIPv6Addresses);
593	}
594	if (format == 'ipv4' || format == 'ipv6_ipv4') {
595		validAddresses = validAddresses.concat(validIPv4Addresses);
596	}
597
598	var ret = false;
599	for (var i=0; i<validAddresses.length; i++) {
600		if (validAddresses[i].test(value)) {
601			ret = true;
602			break;
603		}
604	}
605
606	if (ret && value.indexOf(".") != -1) {
607		//if address contains IPv4 fragment, it must be valid; all 4 groups must be less than 256
608		var ipv4 = value.match(/:?(?:\d{1,3}\.){3}\d{1,3}/i);
609		if(!ipv4) {
610			return false;
611		}
612		ipv4 = ipv4[0].replace(/^:/, '');
613		var pieces = ipv4.split('.');
614		if (pieces.length != 4) {
615			return false;
616		}
617		var regExp = /^[\-\+]?\d*$/;
618		for (var i=0; i< pieces.length; i++) {
619			if (pieces[i] == '') {
620				return false;
621			}
622			var piece = parseInt(pieces[i], 10);
623			if (isNaN(piece) || piece > 255 || !regExp.test(pieces[i]) || pieces[i].length>3 || /^0{2,3}$/.test(pieces[i])) {
624				return false;
625			}
626		}
627	}
628	if (ret && value.indexOf("/") != -1) {
629		// if prefix-length is specified must be in [1-128]
630		var prefLen = value.match(/\/\d{1,3}$/);
631		if (!prefLen) return false;
632		var prefLenVal = parseInt(prefLen[0].replace(/^\//,''), 10);
633		if (isNaN(prefLenVal) || prefLenVal > 128 || prefLenVal < 1) {
634			return false;
635		}
636	}
637	return ret;
638};
639
640Spry.Widget.ValidationTextField.onloadDidFire = false;
641Spry.Widget.ValidationTextField.loadQueue = [];
642
643Spry.Widget.ValidationTextField.prototype.isBrowserSupported = function()
644{
645	return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows
646		||
647	Spry.is.mozilla && Spry.is.v >= 1.4
648		||
649	Spry.is.safari
650		||
651	Spry.is.opera && Spry.is.v >= 9;
652};
653
654Spry.Widget.ValidationTextField.prototype.init = function(element, options)
655{
656	this.element = this.getElement(element);
657	this.errors = 0;
658	this.flags = {locked: false, restoreSelection: true};
659	this.options = {};
660	this.event_handlers = [];
661
662	this.validClass = "textfieldValidState";
663	this.focusClass = "textfieldFocusState";
664	this.requiredClass = "textfieldRequiredState";
665	this.hintClass = "textfieldHintState";
666	this.invalidFormatClass = "textfieldInvalidFormatState";
667	this.invalidRangeMinClass = "textfieldMinValueState";
668	this.invalidRangeMaxClass = "textfieldMaxValueState";
669	this.invalidCharsMinClass = "textfieldMinCharsState";
670	this.invalidCharsMaxClass = "textfieldMaxCharsState";
671	this.textfieldFlashTextClass = "textfieldFlashText";
672	if (Spry.is.safari) {
673		this.flags.lastKeyPressedTimeStamp = 0;
674	}
675
676	switch (this.type) {
677		case 'phone_number':options.format = Spry.Widget.Utils.firstValid(options.format, 'phone_us');break;
678		case 'currency':options.format = Spry.Widget.Utils.firstValid(options.format, 'comma_dot');break;
679		case 'zip_code':options.format = Spry.Widget.Utils.firstValid(options.format, 'zip_us5');break;
680		case 'date':
681			options.format = Spry.Widget.Utils.firstValid(options.format, 'mm/dd/yy');
682			break;
683		case 'time':
684			options.format = Spry.Widget.Utils.firstValid(options.format, 'HH:mm');
685			options.pattern = options.format.replace(/[hms]/gi, "0").replace(/TT/gi, 'AM').replace(/T/gi, 'A');
686			break;
687		case 'ip':
688			options.format = Spry.Widget.Utils.firstValid(options.format, 'ipv4');
689			options.characterMasking = Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].characterMaskingFormats[options.format]; 
690			break;
691	}
692
693	//retrieve the validation type descriptor to be used with this instance (base on type and format)
694	//widgets may have different validations depending on format (like zip_code with formats)
695	var validationDescriptor = {};
696	if (options.format && Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats) {
697		if (Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]) {
698			Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type].formats[options.format]);
699		}
700	} else {
701		Spry.Widget.Utils.setOptions(validationDescriptor, Spry.Widget.ValidationTextField.ValidationDescriptors[this.type]);
702	}
703
704	//set default values for some parameters which were not aspecified
705	options.useCharacterMasking = Spry.Widget.Utils.firstValid(options.useCharacterMasking, false);
706	options.hint = Spry.Widget.Utils.firstValid(options.hint, '');
707	options.isRequired = Spry.Widget.Utils.firstValid(options.isRequired, true);
708	options.additionalError = Spry.Widget.Utils.firstValid(options.additionalError, false);
709	if (options.additionalError)
710		options.additionalError = this.getElement(options.additionalError);
711
712	//set widget validation parameters
713	//get values from validation type descriptor
714	//use the user specified values, if defined
715	options.characterMasking = Spry.Widget.Utils.firstValid(options.characterMasking, validationDescriptor.characterMasking);
716	options.regExpFilter = Spry.Widget.Utils.firstValid(options.regExpFilter, validationDescriptor.regExpFilter);
717	options.pattern = Spry.Widget.Utils.firstValid(options.pattern, validationDescriptor.pattern);
718	options.validation = Spry.Widget.Utils.firstValid(options.validation, validationDescriptor.validation);
719	if (typeof options.validation == 'string') {
720		options.validation = eval(options.validation);
721	}
722
723	options.minValue = Spry.Widget.Utils.firstValid(options.minValue, validationDescriptor.minValue);
724	options.maxValue = Spry.Widget.Utils.firstValid(options.maxValue, validationDescriptor.maxValue);
725
726	options.minChars = Spry.Widget.Utils.firstValid(options.minChars, validationDescriptor.minChars);
727	options.maxChars = Spry.Widget.Utils.firstValid(options.maxChars, validationDescriptor.maxChars);
728
729	Spry.Widget.Utils.setOptions(this, options);
730	Spry.Widget.Utils.setOptions(this.options, options);
731};
732
733Spry.Widget.ValidationTextField.prototype.destroy = function() {
734	if (this.event_handlers)
735		for (var i=0; i<this.event_handlers.length; i++) {
736			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
737		}
738	try { delete this.element; } catch(err) {}
739	try { delete this.input; } catch(err) {}
740	try { delete this.form; } catch(err) {}
741	try { delete this.event_handlers; } catch(err) {}
742	try { this.selection.destroy(); } catch(err) {}
743	try { delete this.selection; } catch(err) {}
744
745	var q = Spry.Widget.Form.onSubmitWidgetQueue;
746	var qlen = q.length;
747	for (var i = 0; i < qlen; i++) {
748		if (q[i] == this) {
749			q.splice(i, 1);
750			break;
751		}
752	}
753};
754
755Spry.Widget.ValidationTextField.prototype.attachBehaviors = function()
756{
757	if (this.element) {
758		if (this.element.nodeName == "INPUT") {
759			this.input = this.element;
760		} else {
761			this.input = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.element, "INPUT");
762		}
763	}
764
765	if (this.input) {
766		if (this.maxChars) {
767			this.input.removeAttribute("maxLength");
768		}
769		this.putHint();
770		this.compilePattern();
771		if (this.type == 'date') {
772			this.compileDatePattern();
773		}
774		this.input.setAttribute("AutoComplete", "off");
775		this.selection = new Spry.Widget.SelectionDescriptor(this.input);
776		this.oldValue = this.input.value;
777
778		var self = this;
779		this.event_handlers = [];
780
781		this.event_handlers.push([this.input, "keydown", function(e) { if (self.isDisabled()) return true; return self.onKeyDown(e || event); }]);
782		this.event_handlers.push([this.input, "keypress", function(e) { if (self.isDisabled()) return true; return self.onKeyPress(e || event); }]);
783		if (Spry.is.opera) {
784			this.event_handlers.push([this.input, "keyup", function(e) { if (self.isDisabled()) return true; return self.onKeyUp(e || event); }]);
785		}
786
787		this.event_handlers.push([this.input, "focus", function(e) { if (self.isDisabled()) return true; return self.onFocus(e || event); }]);
788		this.event_handlers.push([this.input, "blur", function(e) { if (self.isDisabled()) return true; return self.onBlur(e || event); }]);
789
790		this.event_handlers.push([this.input, "mousedown", function(e) { if (self.isDisabled()) return true; return self.onMouseDown(e || event); }]);
791
792		var changeEvent = 
793			Spry.is.mozilla || Spry.is.opera || Spry.is.safari?"input":
794			Spry.is.ie?"propertychange":
795			"change";
796		this.event_handlers.push([this.input, changeEvent, function(e) { if (self.isDisabled()) return true; return self.onChange(e || event); }]);
797
798		if (Spry.is.mozilla || Spry.is.safari) {
799			//oninput event on mozilla does not fire ondragdrop
800			this.event_handlers.push([this.input, "dragdrop", function(e) { if (self.isDisabled()) return true; self.removeHint();return self.onChange(e || event); }]);
801		} else if (Spry.is.ie){
802			//ondrop&onpropertychange crash on IE 
803			this.event_handlers.push([this.input, "drop", function(e) { if (self.isDisabled()) return true; return self.onDrop(e || event); }]);
804		}
805
806		for (var i=0; i<this.event_handlers.length; i++) {
807			Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
808		}
809
810		// submit
811		this.form = Spry.Widget.Utils.getFirstParentWithNodeName(this.input, "FORM");
812		if (this.form) {
813			// if no "onSubmit" handler has been attached to the current form, attach one
814			if (!this.form.attachedSubmitHandler && !this.form.onsubmit) {
815				this.form.onsubmit = function(e) { e = e || event; return Spry.Widget.Form.onSubmit(e, e.srcElement || e.currentTarget) };
816				this.form.attachedSubmitHandler = true;                 
817			}
818			if (!this.form.attachedResetHandler) {
819				Spry.Widget.Utils.addEventListener(this.form, "reset", function(e) { e = e || event; return Spry.Widget.Form.onReset(e, e.srcElement || e.currentTarget) }, false);
820				this.form.attachedResetHandler = true;                 
821			}
822			// add the currrent widget to the "onSubmit" check queue;
823			Spry.Widget.Form.onSubmitWidgetQueue.push(this);
824		}
825	}	
826};
827
828Spry.Widget.ValidationTextField.prototype.isDisabled = function() {
829	return this.input && (this.input.disabled || this.input.readOnly) || !this.input;
830};
831
832Spry.Widget.ValidationTextField.prototype.getElement = function(ele)
833{
834	if (ele && typeof ele == "string")
835		return document.getElementById(ele);
836	return ele;
837};
838
839Spry.Widget.ValidationTextField.addLoadListener = function(handler)
840{
841	if (typeof window.addEventListener != 'undefined')
842		window.addEventListener('load', handler, false);
843	else if (typeof document.addEventListener != 'undefined')
844		document.addEventListener('load', handler, false);
845	else if (typeof window.attachEvent != 'undefined')
846		window.attachEvent('onload', handler);
847};
848
849Spry.Widget.ValidationTextField.processLoadQueue = function(handler)
850{
851	Spry.Widget.ValidationTextField.onloadDidFire = true;
852	var q = Spry.Widget.ValidationTextField.loadQueue;
853	var qlen = q.length;
854	for (var i = 0; i < qlen; i++)
855		q[i].attachBehaviors();
856};
857
858Spry.Widget.ValidationTextField.addLoadListener(Spry.Widget.ValidationTextField.processLoadQueue);
859Spry.Widget.ValidationTextField.addLoadListener(function(){
860	Spry.Widget.Utils.addEventListener(window, "unload", Spry.Widget.Form.destroyAll, false);
861});
862
863Spry.Widget.ValidationTextField.prototype.setValue = function(newValue) {
864	this.flags.locked = true;
865	this.input.value = newValue;
866	this.flags.locked = false;
867	this.oldValue = newValue;
868	if (!Spry.is.ie) {
869		this.onChange();
870	}
871};
872
873/**
874 * save the state of the input (selection and value) so we can revert to it
875 * should call this just before modifying the input value
876 */
877Spry.Widget.ValidationTextField.prototype.saveState = function()
878{
879	this.oldValue = this.input.value;
880	this.selection.update();
881};
882
883Spry.Widget.ValidationTextField.prototype.revertState = function(revertValue)
884{
885	if (revertValue != this.input.value) {
886		this.input.readOnly = true;
887		this.input.value = revertValue;
888		this.input.readOnly = false;
889		if (Spry.is.safari && this.flags.active) {
890			this.input.focus();
891		}
892	}
893  if (this.flags.restoreSelection) {
894	this.selection.moveTo(this.selection.start, this.selection.end);
895  }
896
897	this.redTextFlash();
898};
899
900Spry.Widget.ValidationTextField.prototype.removeHint = function()
901{
902	if (this.flags.hintOn) {
903		this.input.value = "";
904		this.flags.hintOn = false;
905		this.removeClassName(this.element, this.hintClass);
906		this.removeClassName(this.additionalError, this.hintClass);
907	}
908};
909
910Spry.Widget.ValidationTextField.prototype.putHint = function()
911{
912	if(this.hint && this.input && this.input.type == "text" && this.input.value == "") {
913		this.flags.hintOn = true;
914		this.input.value = this.hint;
915		this.addClassName(this.element, this.hintClass);
916		this.addClassName(this.additionalError, this.hintClass);
917	}
918};
919
920Spry.Widget.ValidationTextField.prototype.redTextFlash = function()
921{
922	var self = this;
923	this.addClassName(this.element, this.textfieldFlashTextClass);
924	setTimeout(function() {
925		self.removeClassName(self.element, self.textfieldFlashTextClass)
926	}, 100);
927};
928
929Spry.Widget.ValidationTextField.prototype.doValidations = function(testValue, revertValue)
930{
931	if (this.isDisabled()) return false;
932
933	if (this.flags.locked) {
934		return false;
935	}
936
937	if (testValue.length == 0 && !this.isRequired) {
938		this.errors = 0;
939		return false;
940	}
941	this.flags.locked = true;
942
943	var mustRevert = false;
944	var continueValidations = true;
945	if (!this.options.isRequired && testValue.length == 0) {
946		continueValidations = false;
947	}
948
949	var errors = 0;
950	var fixedValue = testValue;
951
952	//characterMasking - test if all characters are valid with the characterMasking (keyboard filter)
953	if (this.useCharacterMasking && this.characterMasking) {
954		for(var i=0; i<testValue.length; i++) {
955			if (!this.characterMasking.test(testValue.charAt(i))) {
956				errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
957				fixedValue = revertValue;
958				mustRevert = true;
959				break;
960			}
961		}
962	}
963
964	//regExpFilter - character mask positioning (additional mask to restrict some characters only in some position)
965	if (!mustRevert && this.useCharacterMasking && this.regExpFilter) {
966		if (!this.regExpFilter.test(fixedValue)) {
967			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
968			mustRevert = true;
969		}
970	}
971
972	//pattern - testValue matches the pattern so far
973	if (!mustRevert && this.pattern) {
974		var currentRegExp = this.patternToRegExp(testValue.length);
975		if (!currentRegExp.test(testValue)) {
976			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
977			mustRevert = true;
978		} else if (this.patternLength != testValue.length) {
979			//testValue matches pattern so far, but it's not ok if it does not have the proper length
980			//do not revert, but should show the error
981			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
982		}
983	}
984
985	if (fixedValue == '') {
986		errors = errors | Spry.Widget.ValidationTextField.ERROR_REQUIRED;
987	}
988
989	if (!mustRevert && this.pattern && this.useCharacterMasking) {
990		var n = this.getAutoComplete(testValue.length);
991		if (n) {
992			fixedValue += n;
993		}
994	}
995
996	if(!mustRevert && this.minChars !== null  && continueValidations) {
997		if (testValue.length < this.minChars) {
998			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MIN;
999			continueValidations = false;
1000		}
1001	}
1002
1003	if(!mustRevert && this.maxChars !== null && continueValidations) {
1004		if (testValue.length > this.maxChars) {
1005			errors = errors | Spry.Widget.ValidationTextField.ERROR_CHARS_MAX;
1006			continueValidations = false;
1007		}
1008	}
1009
1010	//validation - testValue passes widget validation function
1011	if (!mustRevert && this.validation && continueValidations) {
1012		var value = this.validation(fixedValue, this.options);
1013		if (false === value) {
1014			errors = errors | Spry.Widget.ValidationTextField.ERROR_FORMAT;
1015			continueValidations = false;
1016		} else {
1017			this.typedValue = value;
1018		}
1019	}
1020
1021	if(!mustRevert && this.validation && this.minValue !== null && continueValidations) {
1022		var minValue = this.validation(this.minValue.toString(), this.options);
1023		if (minValue !== false) {
1024			if (this.typedValue < minValue) {
1025				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MIN;
1026				continueValidations = false;
1027			}
1028		}
1029	}
1030
1031	if(!mustRevert && this.validation && this.maxValue !== null && continueValidations) {
1032		var maxValue = this.validation(this.maxValue.toString(), this.options);
1033		if (maxValue !== false) {
1034			if( this.typedValue > maxValue) {
1035				errors = errors | Spry.Widget.ValidationTextField.ERROR_RANGE_MAX;
1036				continueValidations = false;
1037			}
1038		}
1039	}
1040
1041	//an invalid value was tested; must make sure it does not get inside the input
1042	if (this.useCharacterMasking && mustRevert) {
1043		this.revertState(revertValue);
1044	}
1045
1046	this.errors = errors;
1047	this.fixedValue = fixedValue;
1048
1049	this.flags.locked = false;
1050
1051	return mustRevert;
1052};
1053
1054Spry.Widget.ValidationTextField.prototype.onChange = function(e)
1055{
1056	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
1057		return true;
1058	}
1059	if (Spry.is.ie && e && e.propertyName != 'value') {
1060		return true;
1061	}
1062
1063	if (this.flags.drop) {
1064		//delay this if it's a drop operation
1065		var self = this;
1066		setTimeout(function() {
1067			self.flags.drop = false;
1068			self.onChange(null);
1069		}, 0);
1070		return;
1071	}
1072
1073	if (this.flags.hintOn) {
1074		return true;
1075	}
1076
1077	if (this.keyCode == 8 || this.keyCode == 46 ) {
1078		var mustRevert = this.doValidations(this.input.value, this.input.value);
1079		this.oldValue = this.input.value;
1080		if ((mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
1081			var self = this;
1082			setTimeout(function() {self.validate();}, 0);
1083			return true;
1084		}
1085	}
1086
1087	var mustRevert = this.doValidations(this.input.value, this.oldValue);
1088	if ((!mustRevert || this.errors) && this.validateOn & Spry.Widget.ValidationTextField.ONCHANGE) {
1089		var self = this;
1090		setTimeout(function() {self.validate();}, 0);
1091	}
1092	return true;
1093};
1094
1095Spry.Widget.ValidationTextField.prototype.onKeyUp = function(e) {
1096	if (this.flags.operaRevertOnKeyUp) {
1097		this.setValue(this.oldValue);
1098		Spry.Widget.Utils.stopEvent(e);
1099		this.selection.moveTo(this.selection.start, this.selection.start);
1100		this.flags.operaRevertOnKeyUp = false;
1101		return false;
1102	}
1103	if (this.flags.operaPasteOperation) {
1104		window.clearInterval(this.flags.operaPasteOperation);
1105		this.flags.operaPasteOperation = null;
1106	}
1107};
1108
1109Spry.Widget.ValidationTextField.prototype.operaPasteMonitor = function() {
1110	if (this.input.value != this.oldValue) {
1111		var mustRevert = this.doValidations(this.input.value, this.input.value);
1112		if (mustRevert) {
1113			this.setValue(this.oldValue);
1114			this.selection.moveTo(this.selection.start, this.selection.start);
1115		} else {
1116			this.onChange();
1117		}
1118	}
1119};
1120
1121
1122Spry.Widget.ValidationTextField.prototype.compileDatePattern = function () 
1123{
1124	var dateValidationPatternString = "";
1125	var groupPatterns = [];
1126	var fullGroupPatterns = [];
1127	var autocompleteCharacters = [];
1128	
1129	
1130	var formatRegExp = /^([mdy]+)([\.\-\/\\\s]+)([mdy]+)([\.\-\/\\\s]+)([mdy]+)$/i;
1131	var formatGroups = this.options.format.match(formatRegExp);
1132	if (formatGroups !== null) {
1133		for (var i=1; i<formatGroups.length; i++) {
1134			switch (formatGroups[i].toLowerCase()) {
1135				case "dd":
1136					groupPatterns[i-1] = "\\d{1,2}";
1137					fullGroupPatterns[i-1] = "\\d\\d";
1138					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
1139					autocompleteCharacters[i-1] = null;
1140					break;
1141				case "mm":
1142					groupPatterns[i-1] = "\\d{1,2}";
1143					fullGroupPatterns[i-1] = "\\d\\d";
1144					dateValidationPatternString += "(" + groupPatterns[i-1] + ")";
1145					autocompleteCharacters[i-1] = null;
1146					break;
1147				case "yy":
1148					groupPatterns[i-1] = "\\d{1,2}";
1149					fullGroupPatterns[i-1] = "\\d\\d";
1150					dateValidationPatternString += "(\\d\\d)";
1151					autocompleteCharacters[i-1] = null;
1152					break;
1153				case "yyyy":
1154					groupPatterns[i-1] = "\\d{1,4}";
1155					fullGroupPatterns[i-1] = "\\d\\d\\d\\d";
1156					dateValidationPatternString += "(\\d\\d\\d\\d)";
1157					autocompleteCharacters[i-1] = null;
1158					break;
1159				default:
1160					groupPatterns[i-1] = fullGroupPatterns[i-1] = Spry.Widget.ValidationTextField.regExpFromChars(formatGroups[i]);
1161					dateValidationPatternString += "["+ groupPatterns[i-1] + "]";
1162					autocompleteCharacters[i-1] = formatGroups[i];
1163			}
1164		}
1165	}
1166	this.dateValidationPattern = new RegExp("^" + dateValidationPatternString + "$" , "");
1167	this.dateAutocompleteCharacters = autocompleteCharacters;
1168	this.dateGroupPatterns = groupPatterns;
1169	this.dateFullGroupPatterns = fullGroupPatterns;
1170	this.lastDateGroup = formatGroups.length-2;
1171};
1172
1173Spry.Widget.ValidationTextField.prototype.getRegExpForGroup = function (group) 
1174{
1175	var ret = '^';
1176	for (var j = 0; j <= group; j++) ret += this.dateGroupPatterns[j];
1177	ret += '$';
1178	return new RegExp(ret, "");	
1179};
1180
1181Spry.Widget.ValidationTextField.prototype.getRegExpForFullGroup = function (group) 
1182{
1183	var ret = '^';
1184	for (var j = 0; j < group; j++) ret += this.dateGroupPatterns[j];
1185	ret += this.dateFullGroupPatterns[group];
1186	return new RegExp(ret, "");	
1187};
1188
1189Spry.Widget.ValidationTextField.prototype.getDateGroup = function(value, pos) 
1190{
1191	if (pos == 0) return 0;
1192	var test_value = value.substring(0, pos);
1193	for (var i=0; i <= this.lastDateGroup; i++) 
1194		if (this.getRegExpForGroup(i).test(test_value)) return i;
1195	return -1;
1196};
1197
1198
1199Spry.Widget.ValidationTextField.prototype.isDateGroupFull = function(value, group) 
1200{
1201	return this.getRegExpForFullGroup(group).test(value);
1202};
1203
1204Spry.Widget.ValidationTextField.prototype.isValueValid = function(value, pos, group) 
1205{
1206	var test_value = value.substring(0, pos);
1207	return this.getRegExpForGroup(group).test(test_value);
1208};
1209
1210
1211Spry.Widget.ValidationTextField.prototype.isPositionAtEndOfGroup = function (value, pos, group)
1212{
1213	var test_value = value.substring(0, pos);
1214	return this.getRegExpForFullGroup(group).test(test_value);
1215};
1216
1217Spry.Widget.ValidationTextField.prototype.nextDateDelimiterExists = function (value, pos, group)
1218{
1219	var autocomplete = this.dateAutocompleteCharacters[group+1];
1220	if (value.length < pos  + autocomplete.length) 
1221		return false;
1222	else 
1223	{
1224		var test_value = value.substring(pos, pos+autocomplete.length);
1225		if (test_value == autocomplete) 
1226			return true;
1227	}
1228	return false;
1229};
1230
1231
1232
1233Spry.Widget.ValidationTextField.prototype.onKeyPress = function(e)
1234{
1235	if (this.flags.skp) {
1236		this.flags.skp = false;
1237		Spry.Widget.Utils.stopEvent(e);
1238		return false;
1239	}
1240
1241	if (e.ctrlKey || e.metaKey || !this.useCharacterMasking) {
1242		return true;
1243	}
1244/*
1245	if (Spry.is.safari) {
1246		if ( (e.timeStamp - this.flags.lastKeyPressedTimeStamp)<10 ) {
1247			return true;
1248		}
1249		this.flags.lastKeyPressedTimeStamp = e.timeStamp;
1250	}
1251*/
1252	if (Spry.is.opera && this.flags.operaRevertOnKeyUp) {
1253		Spry.Widget.Utils.stopEvent(e);
1254		return false;
1255	}
1256
1257	if (this.keyCode == 8 || this.keyCode == 46) {
1258		var mr = this.doValidations(this.input.value, this.input.value);
1259		if (mr) {
1260			return true;
1261		}
1262	}
1263
1264	var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);
1265
1266	if (pressed && this.characterMasking) {
1267		if (!this.characterMasking.test(pressed)) {
1268			Spry.Widget.Utils.stopEvent(e);
1269			this.redTextFlash();
1270			return false;
1271		}
1272	}
1273
1274	if(pressed && this.pattern) {
1275		var currentPatternChar = this.patternCharacters[this.selection.start];
1276		if (/[ax]/i.test(currentPatternChar)) {
1277			//convert the entered character to the pattern character case
1278			if (currentPatternChar.toLowerCase() == currentPatternChar) {
1279				pressed = pressed.toLowerCase();
1280			} else {
1281				pressed = pressed.toUpperCase();
1282			}
1283		}
1284
1285		var autocomplete = this.getAutoComplete(this.selection.start);
1286		if (this.selection.start == this.oldValue.length) {
1287			if (this.oldValue.length < this.patternLength) {
1288				if (autocomplete) {
1289					Spry.Widget.Utils.stopEvent(e);
1290					var futureValue = this.oldValue.substring(0, this.selection.start) + autocomplete + pressed;
1291					var mustRevert = this.doValidations(futureValue, this.oldValue);
1292					if (!mustRevert) {
1293						this.setValue(this.fixedValue);
1294						this.selection.moveTo(this.fixedValue.length, this.fixedValue.length);
1295					} else {
1296						this.setValue(this.oldValue.substring(0, this.selection.start) + autocomplete);
1297						this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1298					}
1299					return false;
1300				}
1301			} else {
1302				Spry.Widget.Utils.stopEvent(e);
1303				this.setValue(this.input.value);
1304				return false;
1305			}
1306		} else if (autocomplete) {
1307			Spry.Widget.Utils.stopEvent(e);
1308			this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1309			return false;
1310		}
1311
1312		Spry.Widget.Utils.stopEvent(e);
1313
1314		var futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1315		var mustRevert = this.doValidations(futureValue, this.oldValue);
1316
1317		if (!mustRevert) {
1318			autocomplete = this.getAutoComplete(this.selection.start + 1);
1319			this.setValue(this.fixedValue);
1320			this.selection.moveTo(this.selection.start + 1 + autocomplete.length, this.selection.start + 1 + autocomplete.length);
1321		} else {
1322			this.selection.moveTo(this.selection.start, this.selection.start);
1323		}
1324
1325		return false;
1326	}
1327	
1328	
1329	if (pressed && this.type == 'date' && this.useCharacterMasking) 
1330	{
1331		var group = this.getDateGroup(this.oldValue, this.selection.start);
1332		if (group != -1) {
1333			Spry.Widget.Utils.stopEvent(e);
1334			if ( (group % 2) !=0 ) 
1335				group ++;
1336			
1337			if (this.isDateGroupFull(this.oldValue, group)) 
1338			{
1339				if(this.isPositionAtEndOfGroup(this.oldValue, this.selection.start, group))
1340				{
1341					if(group == this.lastDateGroup) 
1342					{
1343						this.redTextFlash(); return false;
1344					}
1345					else 
1346					{
1347						// add or jump over autocomplete delimiter
1348						var autocomplete = this.dateAutocompleteCharacters[group+1];
1349						
1350						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
1351						{
1352							var autocomplete = this.dateAutocompleteCharacters[group+1];
1353							
1354							this.selection.moveTo(this.selection.start + autocomplete.length, this.selection.start + autocomplete.length);
1355							if (pressed == autocomplete) 
1356								return false;
1357							
1358							if (this.isDateGroupFull(this.oldValue, group+2)) 
1359								// need to overwrite first char in the next digit group
1360								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1361							else
1362								futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
1363								
1364							if (!this.isValueValid(futureValue, this.selection.start + 1, group +2 )) 
1365							{
1366								this.redTextFlash(); return false;						
1367							}
1368							else
1369							{
1370								this.setValue (futureValue);
1371								this.selection.moveTo(this.selection.start + 1, this.selection.start + 1);									
1372							}
1373							return false;					
1374						}
1375						else 
1376						{
1377							var autocomplete = this.dateAutocompleteCharacters[group+1];
1378							
1379							var insertedValue = autocomplete + pressed;
1380							futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
1381							if (!this.isValueValid(futureValue, this.selection.start + insertedValue.length, group +2 )) 
1382							{
1383								// block this type
1384								insertedValue = autocomplete;
1385								futureValue = this.oldValue.substring(0, this.selection.start) + insertedValue + this.oldValue.substring(this.selection.start);
1386								this.setValue (futureValue);
1387								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
1388								this.redTextFlash(); return false;
1389							}
1390							else 
1391							{
1392								this.setValue (futureValue);
1393								this.selection.moveTo(this.selection.start + insertedValue.length, this.selection.start + insertedValue.length);									
1394								return false;
1395							}
1396						}
1397						
1398					}
1399				}
1400				else
1401				{
1402					// it's not the end of the full digits group
1403					
1404					// overwrite
1405					var movePosition = 1;
1406					futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start + 1);
1407					if (!this.isValueValid(futureValue, this.selection.start + 1, group)) 
1408					{
1409						this.redTextFlash(); return false;
1410					}
1411					else 
1412					{
1413						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
1414						{
1415							if (group != this.lastDateGroup)
1416							{
1417								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
1418								{
1419									var autocomplete = this.dateAutocompleteCharacters[group+1];
1420									movePosition = 1 + autocomplete.length;
1421								}
1422								else
1423								{
1424									var autocomplete = this.dateAutocompleteCharacters[group+1];
1425									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
1426									movePosition = 1 + autocomplete.length;
1427								}
1428							}
1429						}
1430						this.setValue (futureValue);
1431						this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
1432						return false;							
1433					}			
1434				}
1435			}
1436			else
1437			{
1438				// date group is not full
1439				// insert
1440				futureValue = this.oldValue.substring(0, this.selection.start) + pressed + this.oldValue.substring(this.selection.start);
1441				var movePosition = 1;
1442				if (!this.isValueValid(futureValue, this.selection.start + 1, group) && !this.isValueValid(futureValue, this.selection.start + 1, group+1)) 
1443				{
1444					this.redTextFlash(); return false;
1445				}
1446				else 
1447				{
1448					var autocomplete = this.dateAutocompleteCharacters[group+1];
1449					if (pressed == autocomplete) 
1450					{
1451						if (this.nextDateDelimiterExists(this.oldValue, this.selection.start, group))
1452						{
1453							futureValue = this.oldValue;
1454							movePosition = 1;
1455						}
1456					}
1457					else
1458					{
1459						if(this.isPositionAtEndOfGroup(futureValue, this.selection.start+1, group)) 
1460						{
1461							if (group != this.lastDateGroup)
1462							{
1463								if (this.nextDateDelimiterExists(futureValue, this.selection.start + 1, group))
1464								{
1465									var autocomplete = this.dateAutocompleteCharacters[group+1];
1466									movePosition = 1 + autocomplete.length;
1467								}
1468								else
1469								{
1470									var autocomplete = this.dateAutocompleteCharacters[group+1];
1471									futureValue = this.oldValue.substring(0, this.selection.start) + pressed + autocomplete + this.oldValue.substring(this.selection.start + 1);
1472									movePosition = 1 + autocomplete.length;
1473								}
1474							}
1475						}
1476					}
1477					this.setValue (futureValue);
1478					this.selection.moveTo(this.selection.start + movePosition, this.selection.start + movePosition);									
1479					return false;						
1480				}	
1481			}
1482		}
1483		return false;
1484	}
1485	
1486};
1487
1488Spry.Widget.ValidationTextField.prototype.onKeyDown = function(e)
1489{
1490	this.saveState();
1491	this.keyCode = e.keyCode;
1492
1493	if (Spry.is.opera) {
1494		if (this.flags.operaPasteOperation) {
1495			window.clearInterval(this.flags.operaPasteOperation);
1496			this.flags.operaPasteOperation = null;
1497		}
1498		if (e.ctrlKey) {
1499			var pressed = Spry.Widget.Utils.getCharacterFromEvent(e);
1500			if (pressed && 'vx'.indexOf(pressed.toLowerCase()) != -1) {
1501				var self = this;
1502				this.flags.operaPasteOperation = window.setInterval(function() { self.operaPasteMonitor();}, 1);
1503				return true;
1504			}
1505		}
1506	}
1507
1508	if (this.keyCode != 8 && this.keyCode != 46 && Spry.Widget.Utils.isSpecialKey(e)) {
1509		return true;
1510	}
1511	if (this.keyCode == 8 || this.keyCode == 46 ) {
1512		var mr = this.doValidations(this.input.value, this.input.value);
1513		if (mr) {
1514			return true;
1515		}
1516	}
1517
1518	//DELETE
1519	if (this.useCharacterMasking && this.pattern && this.keyCode == 46) {
1520		if (e.ctrlKey) {
1521			//delete from selection until end
1522			this.setValue(this.input.value.substring(0, this.selection.start));
1523		} else if (this.selection.end == this.input.value.length || this.selection.start == this.input.value.length-1){
1524			//allow key if selection is at end (will delete selection)
1525			return true;
1526		} else {
1527			this.flags.operaRevertOnKeyUp = true;
1528		}
1529		if (Spry.is.mozilla && Spry.is.mac) {
1530			this.flags.skp = true;
1531		}
1532		Spry.Widget.Utils.stopEvent(e);
1533		return false;
1534	}
1535
1536	//BACKSPACE
1537	if (this.useCharacterMasking && this.pattern && !e.ctrlKey && this.keyCode == 8) {
1538		if (this.selection.start == this.input.value.length) {
1539			//delete with BACKSPACE from the end of the input value only
1540			var n = this.getAutoComplete(this.selection.start, -1);
1541			this.setValue(this.input.value.substring(0, this.input.value.length - (Spry.is.opera?0:1) - n.length));
1542			if (Spry.is.opera) {
1543				//cant stop the event on Opera, we'll just preserve the selection so delete will act on it
1544				this.selection.start = this.selection.start - 1 - n.length;
1545				this.selection.end = this.selection.end - 1 - n.length;
1546			}
1547		} else if (this.selection.end == this.input.value.length){
1548			//allow BACKSPACE if selection is at end (will delete selection)
1549			return true;
1550		} else {
1551			this.flags.operaRevertOnKeyUp = true;
1552		}
1553		if (Spry.is.mozilla && Spry.is.mac) {
1554			this.flags.skp = true;
1555		} 
1556		Spry.Widget.Utils.stopEvent(e);
1557		return false;
1558	}
1559
1560	return true;
1561};
1562
1563Spry.Widget.ValidationTextField.prototype.onMouseDown = function(e)
1564{
1565	if (this.flags.active) {
1566		//mousedown fires before focus
1567		//avoid double saveState on first focus by mousedown by checking if the control has focus
1568		//do nothing if it's not focused because saveState will be called onfocus
1569		this.saveState();
1570	}
1571};
1572
1573Spry.Widget.ValidationTextField.prototype.onDrop = function(e)
1574{
1575	//mark that a drop operation is in progress to avoid race conditions with event handlers for other events
1576	//especially onchange and onfocus
1577	this.flags.drop = true;
1578	this.removeHint();
1579	this.saveState();
1580	this.flags.active = true;
1581	this.addClassName(this.element, this.focusClass);
1582	this.addClassName(this.additionalError, this.focusClass);
1583};
1584
1585Spry.Widget.ValidationTextField.prototype.onFocus = function(e)
1586{
1587	if (this.flags.drop) {
1588		return;
1589	}
1590	this.removeHint();
1591
1592	if (this.pattern && this.useCharacterMasking) {
1593		var autocomplete = this.getAutoComplete(this.selection.start);
1594		this.setValue(this.input.value + autocomplete);
1595		this.selection.moveTo(this.input.value.length, this.input.value.length);
1596	}
1597	
1598	this.saveState();
1599	this.flags.active = true;
1600	this.addClassName(this.element, this.focusClass);
1601	this.addClassName(this.additionalError, this.focusClass);
1602};
1603	
1604Spry.Widget.ValidationTextField.prototype.onBlur = function(e)
1605{
1606	this.flags.active = false;
1607	this.removeClassName(this.element, this.focusClass);
1608	this.removeClassName(this.additionalError, this.focusClass);
1609	this.flags.restoreSelection = false;
1610	var mustRevert = this.doValidations(this.input.value, this.input.value);
1611	this.flags.restoreSelection = true;
1612
1613	if (this.validateOn & Spry.Widget.ValidationTextField.ONBLUR) {
1614		this.validate();
1615	}
1616	var self = this;
1617	setTimeout(function() {self.putHint();}, 10);
1618	return true;
1619};
1620
1621Spry.Widget.ValidationTextField.prototype.compilePattern = function() {
1622	if (!this.pattern) {
1623		return;
1624	}
1625	var compiled = [];
1626	var regexps = [];
1627	var patternCharacters = [];
1628	var idx = 0;
1629	var c = '', p = '';
1630	for (var i=0; i<this.pattern.length; i++) {
1631		c = this.pattern.charAt(i);
1632		if (p == '\\') {
1633			if (/[0ABXY\?]/i.test(c)) {
1634				regexps[idx - 1] = c;
1635			} else {
1636				regexps[idx - 1] = Spry.Widget.ValidationTextField.regExpFromChars(c);
1637			}
1638			compiled[idx - 1] = c;
1639			patternCharacters[idx - 1] = null;
1640			p = '';
1641			continue;
1642		}
1643		regexps[idx] = Spry.Widget.ValidationTextField.regExpFromChars(c);
1644		if (/[0ABXY\?]/i.test(c)) {
1645			compiled[idx] = null;
1646			patternCharacters[idx] = c;
1647		} else if (c == '\\') {
1648			compiled[idx] = c;
1649			patternCharacters[idx] = '\\';
1650		} else {
1651			compiled[idx] = c;
1652			patternCharacters[idx] = null;
1653		}
1654		idx++;
1655		p = c;
1656	}
1657
1658	this.autoCompleteCharacters = compiled;
1659	this.compiledPattern = regexps;
1660	this.patternCharacters = patternCharacters;
1661	this.patternLength = compiled.length;
1662};
1663
1664Spry.Widget.ValidationTextField.prototype.getAutoComplete = function(from, direction) {
1665	if (direction == -1) {
1666		var n = '', m = '';
1667		while(from && (n = this.getAutoComplete(--from) )) {
1668			m = n;
1669		}
1670		return m;
1671	}
1672	var ret = '', c = '';
1673	for (var i=from; i<this.autoCompleteCharacters.length; i++) {
1674		c = this.autoCompleteCharacters[i];
1675		if (c) {
1676			ret += c;
1677		} else {
1678			break;
1679		}
1680	}
1681	return ret;
1682};
1683
1684Spry.Widget.ValidationTextField.regExpFromChars = function (string) {
1685	//string contains pattern characters
1686	var ret = '', character = '';
1687	for (var i = 0; i<string.length; i++) {
1688		character = string.charAt(i);
1689		switch (character) {
1690			case '0': ret += '\\d';break;
1691			case 'A': ret += '[A-Z]';break;
1692//			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;
1693			case 'a': ret += '[a-z]';break;
1694//			case 'a': ret += '[\u0080-\u00FF]';break;
1695			case 'B': case 'b': ret += '[a-zA-Z]';break;
1696			case 'x': ret += '[0-9a-z]';break;
1697			case 'X': ret += '[0-9A-Z]';break;
1698			case 'Y': case 'y': ret += '[0-9a-zA-Z]';break;
1699			case '?': ret += '.';break;
1700			case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
1701				ret += character;
1702				break;
1703			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':
1704				ret += character;
1705				break;
1706			default: ret += '\\' + character;
1707		}
1708	}
1709	return ret;
1710};
1711
1712Spry.Widget.ValidationTextField.prototype.patternToRegExp = function(len) {
1713	var ret = '^';
1714	var end = Math.min(this.compiledPattern.length, len);
1715	for (var i=0; i < end; i++) {
1716		ret += this.compiledPattern[i];
1717	}
1718	ret += '$';
1719	ret = new RegExp(ret, "");
1720	return ret;
1721};
1722
1723Spry.Widget.ValidationTextField.prototype.resetClasses = function() {
1724	var classes = [this.requiredClass, this.invalidFormatClass, this.invalidRangeMinClass, this.invalidRangeMaxClass, this.invalidCharsMinClass, this.invalidCharsMaxClass, this.validClass];
1725	for (var i=0; i < classes.length; i++)
1726	{
1727		this.removeClassName(this.element, classes[i]);
1728		this.removeClassName(this.additionalError, classes[i]);
1729	}
1730};
1731
1732Spry.Widget.ValidationTextField.prototype.reset = function() {
1733	this.removeHint();
1734	this.oldValue = this.input.defaultValue;
1735	
1736	this.resetClasses();
1737	if (Spry.is.ie) {
1738		//this will fire the onpropertychange event right after the className changed on the container element
1739		//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
1740		//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
1741		this.input.forceFireFirstOnPropertyChange = true;
1742		this.input.removeAttribute("forceFireFirstOnPropertyChange");
1743	}
1744	var self = this;
1745	setTimeout(function() {self.putHint();}, 10);
1746};
1747
1748Spry.Widget.ValidationTextField.prototype.validate = function() {
1749
1750	this.resetClasses();
1751	//possible states: required, format, rangeMin, rangeMax, charsMin, charsMax
1752	if (this.validateOn & Spry.Widget.ValidationTextField.ONSUBMIT) {
1753
1754		this.removeHint();
1755		this.doValidations(this.input.value, this.input.value);
1756
1757		if(!this.flags.active) {
1758			var self = this;
1759			setTimeout(function() {self.putHint();}, 10);
1760		}
1761	}
1762
1763	if (this.isRequired && this.errors & Spry.Widget.ValidationTextField.ERROR_REQUIRED) {
1764		this.addClassName(this.element, this.requiredClass);
1765		this.addClassName(this.additionalError, this.requiredClass);
1766		return false;
1767	}
1768
1769	if (this.errors & Spry.Widget.ValidationTextField.ERROR_FORMAT) {
1770		this.addClassName(this.element, this.invalidFormatClass);
1771		this.addClassName(this.additionalError, this.invalidFormatClass);
1772		return false;
1773	}
1774
1775	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MIN) {
1776		this.addClassName(this.element, this.invalidRangeMinClass);
1777		this.addClassName(this.additionalError, this.invalidRangeMinClass);
1778		return false;
1779	}
1780
1781	if (this.errors & Spry.Widget.ValidationTextField.ERROR_RANGE_MAX) {
1782		this.addClassName(this.element, this.invalidRangeMaxClass);
1783		this.addClassName(this.additionalError, this.invalidRangeMaxClass);
1784		return false;
1785	}
1786
1787	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MIN) {
1788		this.addClassName(this.element, this.invalidCharsMinClass);
1789		this.addClassName(this.additionalError, this.invalidCharsMinClass);
1790		return false;
1791	}
1792
1793	if (this.errors & Spry.Widget.ValidationTextField.ERROR_CHARS_MAX) {
1794		this.addClassName(this.element, this.invalidCharsMaxClass);
1795		this.addClassName(this.additionalError, this.invalidCharsMaxClass);
1796		return false;
1797	}
1798
1799	this.addClassName(this.element, this.validClass);
1800	this.addClassName(this.additionalError, this.validClass);
1801	return true;
1802};
1803
1804Spry.Widget.ValidationTextField.prototype.addClassName = function(ele, className)
1805{
1806	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
1807		return;
1808	ele.className += (ele.className ? " " : "") + className;
1809};
1810
1811Spry.Widget.ValidationTextField.prototype.removeClassName = function(ele, className)
1812{
1813	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))
1814		return;
1815	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
1816};
1817Spry.Widget.ValidationTextField.prototype.showError = function(msg)
1818{
1819	alert('Spry.Widget.TextField ERR: ' + msg);
1820};
1821/**
1822 * SelectionDescriptor is a wrapper for input type text selection methods and properties 
1823 * as implemented by various  browsers
1824 */
1825Spry.Widget.SelectionDescriptor = function (element)
1826{
1827	this.element = element;
1828	this.update();
1829};
1830
1831Spry.Widget.SelectionDescriptor.prototype.update = function()
1832{
1833	if (Spry.is.ie && Spry.is.windows) {
1834		var sel = this.element.ownerDocument.selection;
1835		if (this.element.nodeName == "TEXTAREA") {
1836			if (sel.type != 'None') {
1837				try{var range = sel.createRange();}catch(err){return;}
1838				if (range.parentElement() == this.element){
1839					var range_all = this.element.ownerDocument.body.createTextRange();
1840					range_all.moveToElementText(this.element);
1841					for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++){
1842						range_all.moveStart('character', 1);
1843					}
1844					this.start = sel_start;
1845					// create a selection of the whole this.element
1846					range_all = this.element.ownerDocument.body.createTextRange();
1847					range_all.moveToElementText(this.element);
1848					for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; sel_end++){
1849						range_all.moveStart('character', 1);
1850					}
1851					this.end = sel_end;
1852					this.length = this.end - this.start;
1853					// get selected and surrounding text
1854					this.text = range.text;
1855		 		}
1856			}        
1857		} else if (this.element.nodeName == "INPUT"){
1858			try{this.range = sel.createRange();}catch(err){return;}
1859			this.length = this.range.text.length;
1860			var clone = this.range.duplicate();
1861			this.start = -clone.moveStart("character", -10000);
1862			clone = this.range.duplicate();
1863			clone.collapse(false);
1864			this.end = -clone.moveStart("character", -10000);
1865			this.text = this.range.text;
1866		}
1867	} else {
1868		var tmp = this.element;
1869		var selectionStart = 0;
1870		var selectionEnd = 0;
1871        
1872		try { selectionStart = tmp.selectionStart;} catch(err) {}
1873		try { selectionEnd = tmp.selectionEnd;} catch(err) {}
1874
1875		if (Spry.is.safari) {
1876			if (selectionStart == 2147483647) {
1877				selectionStart = 0;
1878			}
1879			if (selectionEnd == 2147483647) {
1880				selectionEnd = 0;
1881			}
1882		}
1883		this.start = selectionStart;
1884		this.end = selectionEnd;
1885		this.length = selectionEnd - selectionStart;
1886		this.text = this.element.value.substring(selectionStart, selectionEnd);
1887	}
1888};
1889
1890Spry.Widget.SelectionDescriptor.prototype.destroy = function() {
1891	try { delete this.range} catch(err) {}
1892	try { delete this.element} catch(err) {}
1893};
1894
1895Spry.Widget.SelectionDescriptor.prototype.move = function(amount)
1896{
1897	if (Spry.is.ie && Spry.is.windows) {
1898		this.range.move("character", amount);
1899		this.range.select();
1900	} else {
1901		try { this.element.selectionStart++;}catch(err) {}
1902	}
1903	this.update();
1904};
1905
1906Spry.Widget.SelectionDescriptor.prototype.moveTo = function(start, end)
1907{
1908	if (Spry.is.ie && Spry.is.windows) {
1909		if (this.element.nodeName == "TEXTAREA") {
1910			var ta_range = this.element.createTextRange();
1911			this.range = this.element.createTextRange();
1912			this.range.move("character", start);
1913			this.range.moveEnd("character", end - start);
1914			
1915			var c1 = this.range.compareEndPoints("StartToStart", ta_range);
1916			if (c1 < 0) {
1917				this.range.setEndPoint("StartToStart", ta_range);
1918			}
1919
1920			var c2 = this.range.compareEndPoints("EndToEnd", ta_range);
1921			if (c2 > 0) {
1922				this.range.setEndPoint("EndToEnd", ta_range);
1923			}
1924		} else if (this.element.nodeName == "INPUT"){
1925			this.range = this.element.ownerDocument.selection.createRange();
1926			this.range.move("character", -10000);
1927			this.start = this.range.moveStart("character", start);
1928			this.end = this.start + this.range.moveEnd("character", end - start);
1929		}
1930		this.range.select();
1931	} else {
1932		this.start = start;
1933		try { this.element.selectionStart = start;} catch(err) {}
1934		this.end = end;
1935		try { this.element.selectionEnd = end;} catch(err) {}
1936	}
1937	this.ignore = true;
1938	this.update();
1939};
1940
1941Spry.Widget.SelectionDescriptor.prototype.moveEnd = function(amount)
1942{
1943	if (Spry.is.ie && Spry.is.windows) {
1944		this.range.moveEnd("character", amount);
1945		this.range.select();
1946	} else {
1947		try { this.element.selectionEnd++;} catch(err) {}
1948	}
1949	this.update();
1950};
1951
1952Spry.Widget.SelectionDescriptor.prototype.collapse = function(begin)
1953{
1954	if (Spry.is.ie && Spry.is.windows) {
1955		this.range = this.element.ownerDocument.selection.createRange();
1956		this.range.collapse(begin);
1957		this.range.select();
1958	} else {
1959		if (begin) {
1960			try { this.element.selectionEnd = this.element.selectionStart;} catch(err) {}
1961		} else {
1962			try { this.element.selectionStart = this.element.selectionEnd;} catch(err) {}
1963		}
1964	}
1965
1966	this.update();
1967};
1968
1969//////////////////////////////////////////////////////////////////////
1970//
1971// Spry.Widget.Form - common for all widgets
1972//
1973//////////////////////////////////////////////////////////////////////
1974
1975if (!Spry.Widget.Form) Spry.Widget.Form = {};
1976if (!Spry.Widget.Form.onSubmitWidgetQueue) Spry.Widget.Form.onSubmitWidgetQueue = [];
1977
1978if (!Spry.Widget.Form.validate) {
1979	Spry.Widget.Form.validate = function(vform) {
1980		var isValid = true;
1981		var isElementValid = true;
1982		var q = Spry.Widget.Form.onSubmitWidgetQueue;
1983		var qlen = q.length;
1984		for (var i = 0; i < qlen; i++) {
1985			if (!q[i].isDisabled() && q[i].form == vform) {
1986				isElementValid = q[i].validate();
1987				isValid = isElementValid && isValid;
1988			}
1989		}
1990		return isValid;
1991	}
1992};
1993
1994if (!Spry.Widget.Form.onSubmit) {
1995	Spry.Widget.Form.onSubmit = function(e, form)
1996	{
1997		if (Spry.Widget.Form.validate(form) == false) {
1998			return false;
1999		}
2000		return true;
2001	};
2002};
2003
2004if (!Spry.Widget.Form.onReset) {
2005	Spry.Widget.Form.onReset = function(e, vform)
2006	{
2007		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2008		var qlen = q.length;
2009		for (var i = 0; i < qlen; i++) {
2010			if (!q[i].isDisabled() && q[i].form == vform && typeof(q[i].reset) == 'function') {
2011				q[i].reset();
2012			}
2013		}
2014		return true;
2015	};
2016};
2017
2018if (!Spry.Widget.Form.destroy) {
2019	Spry.Widget.Form.destroy = function(form)
2020	{
2021		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2022		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2023			if (q[i].form == form && typeof(q[i].destroy) == 'function') {
2024				q[i].destroy();
2025				i--;
2026			}
2027		}
2028	}
2029};
2030
2031if (!Spry.Widget.Form.destroyAll) {
2032	Spry.Widget.Form.destroyAll = function()
2033	{
2034		var q = Spry.Widget.Form.onSubmitWidgetQueue;
2035		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2036			if (typeof(q[i].destroy) == 'function') {
2037				q[i].destroy();
2038				i--;
2039			}
2040		}
2041	}
2042};
2043
2044//////////////////////////////////////////////////////////////////////
2045//
2046// Spry.Widget.Utils
2047//
2048//////////////////////////////////////////////////////////////////////
2049
2050if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};
2051
2052Spry.Widget.Utils.punycode_constants = {
2053	base : 36, tmin : 1, tmax : 26, skew : 38, damp : 700,
2054  initial_bias : 72, initial_n : 0x80, delimiter : 0x2D,
2055  maxint : 2<<26-1
2056};
2057
2058Spry.Widget.Utils.punycode_encode_digit = function (d) {
2059  return String.fromCharCode(d + 22 + 75 * (d < 26));
2060};
2061
2062Spry.Widget.Utils.punycode_adapt = function (delta, numpoints, firsttime) {
2063	delta = firsttime ? delta / this.punycode_constants.damp : delta >> 1;
2064	delta += delta / numpoints;
2065	
2066	for (var k = 0; delta > ((this.punycode_constants.base - this.punycode_constants.tmin) * this.punycode_constants.tmax) / 2; k += this.punycode_constants.base) {
2067		delta /= this.punycode_constants.base - this.punycode_constants.tmin;
2068	}
2069	return k + (this.punycode_constants.base - this.punycode_constants.tmin + 1) * delta / (delta + this.punycode_constants.skew);
2070};
2071
2072/**
2073 * returns a 	Punicode representation of a UTF-8 string
2074 * adapted from http://tools.ietf.org/html/rfc3492
2075 */
2076Spry.Widget.Utils.punycode_encode = function (input, max_out) {
2077	var inputc = input.split("");
2078	input = [];
2079	for(var i=0; i<inputc.length; i++) {
2080		input.push(inputc[i].charCodeAt(0));
2081	}
2082	var output = '';
2083
2084  var h, b, j, m, q, k, t;
2085	var input_len = input.length;
2086  var n = this.punycode_constants.initial_n;
2087  var delta = 0;
2088  var bias = this.punycode_constants.initial_bias;
2089  var out = 0;
2090
2091  for (j = 0; j < input_len; j++) {
2092		if (input[j] < 128) {
2093			if (max_out - out < 2) {
2094				return false;
2095			}
2096			output += String.fromCharCode(input[j]);
2097			out++;
2098		}
2099	}
2100
2101	h = b = out;
2102	if (b > 0) {
2103		output += String.fromCharCode(this.punycode_constants.delimiter);
2104		out++;
2105	}
2106
2107  while (h < input_len)	{
2108		for (m = this.punycode_constants.maxint, j = 0; j < input_len; j++) {
2109			if (input[j] >= n && input[j] < m) {
2110				m = input[j];
2111			}
2112		}
2113		if (m - n > (this.punycode_constants.maxint - delta) / (h + 1)) {
2114			return false;
2115		}
2116		
2117		delta += (m - n) * (h + 1);
2118		n = m;
2119
2120		for (j = 0; j < input_len; j++) {
2121			if (input[j] < n ) {
2122				if (++delta == 0) {
2123					return false;
2124				}
2125			}
2126
2127			if (input[j] == n) {
2128				for (q = delta, k = this.punycode_constants.base; true; k += this.punycode_constants.base) {
2129					if (out >= max_out) {
2130						return false;
2131					}
2132
2133					t = k <= bias ? this.punycode_constants.tmin : k >= bias + this.punycode_constants.tmax ? this.punycode_constants.tmax : k - bias;
2134					if (q < t) {
2135						break;
2136					}
2137
2138					output += this.punycode_encode_digit(t + (q - t) % (this.punycode_constants.base - t));
2139					out++;
2140					q = (q - t) / (this.punycode_constants.base - t);
2141				}
2142
2143				output += this.punycode_encode_digit(q);
2144				out++;
2145				bias = this.punycode_adapt(delta, h + 1, h == b);
2146				delta = 0;
2147				h++;
2148			}
2149		}
2150		delta++, n++;
2151	}
2152
2153  return output;
2154};
2155
2156Spry.Widget.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
2157{
2158	if (!optionsObj)
2159		return;
2160	for (var optionName in optionsObj)
2161	{
2162		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
2163			continue;
2164		obj[optionName] = optionsObj[optionName];
2165	}
2166};
2167
2168Spry.Widget.Utils.firstValid = function() {
2169	var ret = null;
2170	for(var i=0; i<Spry.Widget.Utils.firstValid.arguments.length; i++) {
2171		if (typeof(Spry.Widget.Utils.firstValid.arguments[i]) != 'undefined') {
2172			ret = Spry.Widget.Utils.firstValid.arguments[i];
2173			break;
2174		}
2175	}
2176	return ret;
2177};
2178
2179
2180Spry.Widget.Utils.specialCharacters = ",8,9,16,17,18,20,27,33,34,35,36,37,38,40,45,144,192,63232,";
2181Spry.Widget.Utils.specialSafariNavKeys = "63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,";
2182Spry.Widget.Utils.specialNotSafariCharacters = "39,46,91,92,93,";
2183
2184Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys;
2185
2186if (!Spry.is.safari) {
2187	Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialNotSafariCharacters;
2188}
2189
2190Spry.Widget.Utils.isSpecialKey = function (ev) {
2191	return Spry.Widget.Utils.specialCharacters.indexOf("," + ev.keyCode + ",") != -1;
2192};
2193
2194Spry.Widget.Utils.getCharacterFromEvent = function(e){
2195	var keyDown = e.type == "keydown";
2196
2197	var code = null;
2198	var character = null;
2199	if(Spry.is.mozilla && !keyDown){
2200		if(e.charCode){
2201			character = String.fromCharCode(e.charCode);
2202		} else {
2203			code = e.keyCode;
2204		}
2205	} else {
2206		code = e.keyCode || e.which;
2207		if (code != 13) {
2208			character = String.fromCharCode(code);
2209		}
2210	}
2211
2212	if (Spry.is.safari) {
2213		if (keyDown) {
2214			code = e.keyCode || e.which;
2215			character = String.fromCharCode(code);
2216		} else {
2217			code = e.keyCode || e.which;
2218			if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
2219				character = null;
2220			} else {
2221				character = String.fromCharCode(code);
2222			}
2223		}
2224	}
2225
2226	if(Spry.is.opera) {
2227		if (Spry.Widget.Utils.specialCharacters.indexOf("," + code + ",") != -1) {
2228			character = null;
2229		} else {
2230			character = String.fromCharCode(code);
2231		}
2232	}
2233
2234	return character;
2235};
2236
2237Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName)
2238{
2239	var elements  = node.getElementsByTagName(nodeName);
2240	if (elements) {
2241		return elements[0];
2242	}
2243	return null;
2244};
2245
2246Spry.Widget.Utils.getFirstParentWithNodeName = function(node, nodeName)
2247{
2248	while (node.parentNode
2249			&& node.parentNode.nodeName.toLowerCase() != nodeName.toLowerCase()
2250			&& node.parentNode.nodeName != 'BODY') {
2251		node = node.parentNode;
2252	}
2253
2254	if (node.parentNode && node.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {
2255		return node.parentNode;
2256	} else {
2257		return null;
2258	}
2259};
2260
2261Spry.Widget.Utils.destroyWidgets = function (container)
2262{
2263	if (typeof container == 'string') {
2264		container = document.getElementById(container);
2265	}
2266
2267	var q = Spry.Widget.Form.onSubmitWidgetQueue;
2268	for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
2269		if (typeof(q[i].destroy) == 'function' && Spry.Widget.Utils.contains(container, q[i].element)) {
2270			q[i].destroy();
2271			i--;
2272		}
2273	}
2274};
2275
2276Spry.Widget.Utils.contains = function (who, what)
2277{
2278	if (typeof who.contains == 'object') {
2279		return what && who && (who == what || who.contains(what));
2280	} else {
2281		var el = what;
2282		while(el) {
2283			if (el == who) {
2284				return true;
2285			}
2286			el = el.parentNode;
2287		}
2288		return false;
2289	}
2290};
2291
2292Spry.Widget.Utils.addEventListener = function(element, eventType, handler, capture)
2293{
2294	try
2295	{
2296		if (element.addEventListener)
2297			element.addEventListener(eventType, handler, capture);
2298		else if (element.attachEvent)
2299			element.attachEvent("on" + eventType, handler, capture);
2300	}
2301	catch (e) {}
2302};
2303
2304Spry.Widget.Utils.removeEventListener = function(element, eventType, handler, capture)
2305{
2306	try
2307	{
2308		if (element.removeEventListener)
2309			element.removeEventListener(eventType, handler, capture);
2310		else if (element.detachEvent)
2311			element.detachEvent("on" + eventType, handler, capture);
2312	}
2313	catch (e) {}
2314};
2315
2316Spry.Widget.Utils.stopEvent = function(ev)
2317{
2318	try
2319	{
2320		this.stopPropagation(ev);
2321		this.preventDefault(ev);
2322	}
2323	catch (e) {}
2324};
2325
2326/**
2327 * Stops event propagation
2328 * @param {Event} ev the event
2329 */
2330Spry.Widget.Utils.stopPropagation = function(ev)
2331{
2332	if (ev.stopPropagation)
2333	{
2334		ev.stopPropagation();
2335	}
2336	else
2337	{
2338		ev.cancelBubble = true;
2339	}
2340};
2341
2342/**
2343 * Prevents the default behavior of the event
2344 * @param {Event} ev the event
2345 */
2346Spry.Widget.Utils.preventDefault = function(ev)
2347{
2348	if (ev.preventDefault)
2349	{
2350		ev.preventDefault();
2351	}
2352	else
2353	{
2354		ev.returnValue = false;
2355	}
2356};
2357
2358})(); // EndSpryComponent

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.