PageSourceSearch

https://drjeffreymarks.com/SpryAssets/SpryValidationTextarea.js

js drjeffreymarks.com collected 2026-10-07 06:22:24 UTC 37,254 bytes, 1,152 lines download raw bytes

1// SpryValidationTextarea.js - version 0.18 - 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
78
79Spry.Widget.ValidationTextarea = function(element, options){
80	
81	options = Spry.Widget.Utils.firstValid(options, {});
82	this.flags = {locked: false};
83	this.options = {};
84	this.element = element;
85	this.init(element);
86
87	if (!this.isBrowserSupported()){
88		return;	
89	}
90
91	options.useCharacterMasking = Spry.Widget.Utils.firstValid(options.useCharacterMasking, true);
92	options.hint = Spry.Widget.Utils.firstValid(options.hint, '');
93	options.isRequired = Spry.Widget.Utils.firstValid(options.isRequired, true);
94	options.additionalError = Spry.Widget.Utils.firstValid(options.additionalError, false);
95
96	Spry.Widget.Utils.setOptions(this, options);
97	Spry.Widget.Utils.setOptions(this.options, options);
98
99	if (options.additionalError)
100		this.additionalError = this.getElement(options.additionalError);
101
102	//make sure we validate at least on submit
103	var validateOn = ['submit'].concat(Spry.Widget.Utils.firstValid(this.options.validateOn, []));
104	validateOn = validateOn.join(",");
105	this.validateOn = 0;
106	this.validateOn = this.validateOn | (validateOn.indexOf('submit') != -1 ? Spry.Widget.ValidationTextarea.ONSUBMIT : 0);
107	this.validateOn = this.validateOn | (validateOn.indexOf('blur') != -1 ? Spry.Widget.ValidationTextarea.ONBLUR : 0);
108	this.validateOn = this.validateOn | (validateOn.indexOf('change') != -1 ? Spry.Widget.ValidationTextarea.ONCHANGE : 0);
109
110	if (Spry.Widget.ValidationTextarea.onloadDidFire){
111		this.attachBehaviors();
112	}else{
113		Spry.Widget.ValidationTextarea.loadQueue.push(this);
114	}
115};
116
117Spry.Widget.ValidationTextarea.ONCHANGE = 1;
118Spry.Widget.ValidationTextarea.ONBLUR = 2;
119Spry.Widget.ValidationTextarea.ONSUBMIT = 4;
120
121Spry.Widget.ValidationTextarea.INITIAL = 'Initial';
122Spry.Widget.ValidationTextarea.REQUIRED = 'Required';
123Spry.Widget.ValidationTextarea.INVALID = 'Invalid Format';
124Spry.Widget.ValidationTextarea.MINIMUM = 'Minimum Number of Chars Not Met';
125Spry.Widget.ValidationTextarea.MAXIMUM = 'Maximum Number of Chars Exceeded';
126Spry.Widget.ValidationTextarea.VALID = 'Valid';
127
128Spry.Widget.ValidationTextarea.prototype.init = function(element)
129{
130	this.element = this.getElement(element);
131	this.event_handlers = [];
132
133	this.requiredClass = "textareaRequiredState";
134	this.invalidCharsMaxClass = "textareaMaxCharsState";
135	this.invalidCharsMinClass = "textareaMinCharsState";
136	this.validClass = "textareaValidState";
137	this.focusClass = "textareaFocusState";
138	this.hintClass = "textareaHintState";
139	this.textareaFlashClass = "textareaFlashState";
140
141	this.isMaxInvalid = false;
142	this.isMinInvalid = false;
143	this.isRequireInvalid = false;
144	
145	this.safariClicked = false;
146	this.state = Spry.Widget.ValidationTextarea.INITIAL;
147};
148
149Spry.Widget.ValidationTextarea.prototype.destroy = function() {
150	if (this.event_handlers)
151		for (var i=0; i<this.event_handlers.length; i++) {
152			Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
153		}
154	try { delete this.element; } catch(err) {}
155	try { delete this.input; } catch(err) {}
156	try { delete this.counterEl; } catch(err) {}
157	try { delete this.form; } catch(err) {}
158	try { delete this.event_handlers; } catch(err) {}
159	try { this.cursorPosition.destroy(); } catch(err) {}
160	try { delete this.cursorPosition; } catch(err) {}
161	try { this.initialCursor.destroy(); } catch(err) {}
162	try { delete this.initialCursor; } catch(err) {}
163
164	var q = Spry.Widget.Form.onSubmitWidgetQueue;
165	var qlen = q.length;
166	for (var i = 0; i < qlen; i++) {
167		if (q[i] == this) {
168			q.splice(i, 1);
169			break;
170		}
171	}
172};
173
174Spry.Widget.ValidationTextarea.prototype.isDisabled = function() {
175	return this.input && (this.input.disabled || this.input.readOnly) || !this.input;
176};
177
178Spry.Widget.ValidationTextarea.prototype.getElement = function(ele)
179{
180	if (ele && typeof ele == "string")
181		return document.getElementById(ele);
182	return ele;
183};
184
185
186Spry.Widget.ValidationTextarea.addLoadListener = function(handler){
187	if (typeof window.addEventListener != 'undefined'){
188		window.addEventListener('load', handler, false);
189	}else if (typeof document.addEventListener != 'undefined'){
190		document.addEventListener('load', handler, false);
191	}else if (typeof window.attachEvent != 'undefined'){
192		window.attachEvent('onload', handler);
193	}
194};
195
196Spry.Widget.ValidationTextarea.processLoadQueue = function(handler){
197	Spry.Widget.ValidationTextarea.onloadDidFire = true;
198	var q = Spry.Widget.ValidationTextarea.loadQueue;
199	var qlen = q.length;
200	for (var i = 0; i < qlen; i++){
201		q[i].attachBehaviors();
202	}
203};
204
205Spry.Widget.ValidationTextarea.onloadDidFire = false;
206Spry.Widget.ValidationTextarea.loadQueue = [];
207Spry.Widget.ValidationTextarea.addLoadListener(Spry.Widget.ValidationTextarea.processLoadQueue);
208Spry.Widget.ValidationTextarea.addLoadListener(function(){
209	Spry.Widget.Utils.addEventListener(window, "unload", Spry.Widget.Form.destroyAll, false);
210});
211
212Spry.Widget.ValidationTextarea.prototype.isBrowserSupported = function()
213{
214	return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows
215		||
216	Spry.is.mozilla && Spry.is.v >= 1.4
217		||
218	Spry.is.safari
219		||
220	Spry.is.opera && Spry.is.v >= 9;
221};
222
223/* 
224 * register our input to different event notifiers 
225 *
226 */
227Spry.Widget.ValidationTextarea.prototype.attachBehaviors = function()
228{
229	if (this.element){
230		if (this.element.nodeName == "TEXTAREA") {
231			this.input = this.element;
232		} else {
233			this.input = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.element, "TEXTAREA");
234		}
235	}
236	if (this.options && this.options.counterType && (this.options.counterType == 'chars_count' || this.options.counterType == 'chars_remaining')){
237			this.counterEl = document.getElementById(this.options.counterId);
238			this.counterChar();
239	}
240
241	if (this.input) {
242		this.input.setAttribute("AutoComplete", "off");
243		this.putHint();
244		this.cursorPosition = new Spry.Widget.SelectionDescriptor(this.input);
245
246		var self = this;
247		this.event_handlers = [];
248
249		//attach the pattern related event handlers (to stop invalid keys) 
250		if (this.useCharacterMasking) {
251			if (Spry.is.ie){
252				this.event_handlers.push([this.input, "propertychange", function(e) { return self.onKeyEvent(e || event); }]);
253				this.event_handlers.push([this.input, "drop", function(e) { return self.onDrop (e || event); }]);
254				this.event_handlers.push([this.input, "keypress", function(e) { return self.onKeyPress(e || event); }]);
255			} else{
256				this.event_handlers.push([this.input, "keydown", function(e) { return self.onKeyDown(e); }]);
257				this.event_handlers.push([this.input, "keypress", function(e) { return self.safariKeyPress(e); }]);
258				this.event_handlers.push([this.input, "keyup", function(e) { return self.safariValidate(e); }]);
259				if (Spry.is.safari){
260					this.event_handlers.push([this.input, "mouseup", function(e) { return self.safariMouseUp(e); }]);
261					this.event_handlers.push([this.input, "mousedown", function(e) { return self.safariMouseDown(e); }]);
262				} else {
263					//Firefox bug: 355219
264					//this.event_handlers.push([this.input, "input", function(e) { self.onKeyEvent(e); return true;}]);
265					this.event_handlers.push([this.input, "dragdrop", function(e) { return self.onKeyEvent(e); }]);
266					this.event_handlers.push([this.input, "dragenter", function(e) { self.removeHint(); return self.onKeyDown(e); }]);
267					this.event_handlers.push([this.input, "dragexit", function(e) { return self.putHint(); }]);
268				}
269			}
270			// we need to save an initial state in case of invalid input
271			this.event_handlers.push([this.input, "keydown", function(e) {return self.onKeyDown(e || event); }]);
272		}
273
274		this.event_handlers.push([this.input, "focus", function(e) { return self.onFocus(e || event); }]);
275		this.event_handlers.push([this.input, "mousedown", function(e) { return self.onMouseDown(e || event); }]);
276		this.event_handlers.push([this.input, "blur", function(e) { return self.onBlur(e || event); }]);
277
278		if (this.validateOn & Spry.Widget.ValidationTextarea.ONCHANGE){
279				if (Spry.is.ie){
280						this.event_handlers.push([this.input, "propertychange", function(e) { return self.onChange(e || event); }]);
281						this.event_handlers.push([this.input, "drop", function(e) { return self.onChange(e || event); }]);
282				} else{
283						this.event_handlers.push([this.input, "keydown", function(e) { return self.onKeyDown(e); }]);
284						this.event_handlers.push([this.input, "keypress", function(e) { return self.safariChangeKeyPress(e); }]);
285						this.event_handlers.push([this.input, "keyup", function(e) { return self.safariChangeValidate(e); }]);
286						if (Spry.is.safari){
287							this.event_handlers.push([this.input, "mouseup", function(e) { return self.safariChangeMouseUp(e); }]);
288							this.event_handlers.push([this.input, "mousedown", function(e) { return self.safariMouseDown(e); }]);
289						} else {
290							// Firefox bug: 355219
291							//this.event_handlers.push([this.input, "input", function(e) { return self.onChange(e); }]);
292							this.event_handlers.push([this.input, "dragdrop", function(e) {return self.onChange(e); }]);
293							this.event_handlers.push([this.input, "dragenter", function(e) { self.removeHint(); return self.onKeyDown(e); }]);
294							this.event_handlers.push([this.input, "dragexit", function(e) { return self.putHint(); }]);
295						}
296				}
297		}
298		// The counter should be called directly when no enforcement or change restrictions exists
299		if (! (this.validateOn & Spry.Widget.ValidationTextarea.ONCHANGE) && !this.useCharacterMasking){
300				if (Spry.is.ie){
301						this.event_handlers.push([this.input, "propertychange", function(e) { return self.counterChar(); }]);
302						this.event_handlers.push([this.input, "drop", function(e) { return self.counterChar(); }]);
303				} else{
304						this.event_handlers.push([this.input, "keypress", function(e) { return self.counterChar(); }]);
305						this.event_handlers.push([this.input, "keyup", function(e) { return self.counterChar(); }]);
306						if (Spry.is.safari){
307							this.event_handlers.push([this.input, "mouseup", function(e) { return self.counterChar(); }]);
308						} else {
309							// Firefox bug: 355219
310							//this.event_handlers.push([this.input, "input", function(e) { return self.onChange(e); }]);
311							this.event_handlers.push([this.input, "dragdrop", function(e) {return self.counterChar(); }]);
312						}
313				}
314		}
315
316		for (var i=0; i<this.event_handlers.length; i++) {
317			Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false);
318		}
319
320		this.form = Spry.Widget.Utils.getFirstParentWithNodeName(this.input, "FORM");
321		if (this.form) {
322			if (!this.form.attachedSubmitHandler && !this.form.onsubmit) {
323				this.form.onsubmit = function(e) { e = e || event; return Spry.Widget.Form.onSubmit(e, e.srcElement || e.currentTarget) };
324				this.form.attachedSubmitHandler = true;                 
325			}
326			if (!this.form.attachedResetHandler) {
327				Spry.Widget.Utils.addEventListener(this.form, "reset", function(e) { e = e || event; return Spry.Widget.Form.onReset(e, e.srcElement || e.currentTarget) }, false);
328				this.form.attachedResetHandler = true;                 
329			}
330			// add the currrent widget to the "onSubmit" check queue;
331			Spry.Widget.Form.onSubmitWidgetQueue.push(this);
332		}
333	}
334	this.saveState();
335};
336
337Spry.Widget.ValidationTextarea.prototype.onTyping = function(e){
338	if (this.input.disabled == true || this.input.readOnly == true){
339			return;	
340	}
341
342	if (!this.initialCursor){
343		this.initialCursor = this.cursorPosition;	
344	}
345	// on IE a stack overflow appears
346	if (this.flags.locked){
347			return true;
348	}
349
350	var val = this.input.value;
351
352	var ret = true;
353	
354	if (this.flags.hintOn){
355		return true;
356	}
357 	if (e && this.input && this.options && this.options.maxChars > 0 && ret){
358		if ( val.length > this.options.maxChars  && 
359							((!Spry.Widget.Utils.isSpecialKey(e) && this.cursorPosition.start == this.cursorPosition.end) ||
360				 			 (Spry.Widget.Utils.isSpecialKey(e) && val != this.initialValue) ||
361				 				this.cursorPosition.start != this.cursorPosition.end)
362			 ){
363					// cut the extra chars and display error
364					this.flags.locked = true;
365					var initial = this.initialValue;
366					var start = this.initialCursor.start;
367					var end = this.initialCursor.end;
368					if (initial.length && this.initialCursor.end < initial.length) {
369							// we try to behave more like maxlength textfield
370							var tmp = end - start + this.options.maxChars - initial.length;
371							var newValue = initial.substring(0, start) + val.substring(start, start+tmp) + initial.substring(end, initial.length < this.options.maxChars ? initial.length:this.options.maxChars);
372							end = start + tmp;
373					}else{
374							var newValue = val.substring(0, this.options.maxChars);
375							end = start = this.options.maxChars;
376					}
377					if (Spry.is.ie) {
378						this.input.innerText = newValue;
379					} else {
380						this.input.value = newValue;
381					}
382					this.redTextFlash();
383					this.cursorPosition.moveTo(end, end);
384					this.flags.locked = false;
385					ret = false;
386			} else{
387					this.setState(Spry.Widget.ValidationTextarea.VALID);
388					this.isMaxInvalid = false;
389			}
390	}
391	this.counterChar();
392	return ret;
393};
394
395Spry.Widget.ValidationTextarea.prototype.validateMinRequired = function(val){
396	var oldInvalid = false;
397	if (typeof this.notFireMinYet == 'undefined'){
398		this.notFireMinYet = false;
399	}else{
400		oldInvalid = true;
401		this.notFireMinYet = true;
402	}
403	if (this.onBlurOn){
404		this.notFireMinYet = true;
405	}else if (!this.onKeyEventOn){
406		this.notFireMinYet = true;
407	}
408
409	if (this.input && this.options && this.options.isRequired){
410			if (val.length > 0 && this.isRequireInvalid && (!this.hint || (this.hint && !this.flags.hintOn) || (this.hint && val != this.hint))){
411						this.switchClassName(this.validClass);
412						this.setState(Spry.Widget.ValidationTextarea.VALID);
413						this.isRequireInvalid = false;
414			}else if ((val.length == 0 || !(!this.hint || (this.hint && !this.flags.hintOn) || (this.hint && val != this.hint))) && (!this.isRequireInvalid || oldInvalid)){
415						if (this.notFireMinYet || Spry.is.ie){
416							this.switchClassName(this.requiredClass);
417							this.setState(Spry.Widget.ValidationTextarea.REQUIRED);
418						}
419						this.isRequireInvalid = true;
420						this.isMinInvalid = false;
421			}
422	}
423	if (this.input && this.options && this.options.minChars > 0 && !this.isRequireInvalid){
424			if (val.length >= this.options.minChars && (!this.hint || (this.hint && !this.flags.hintOn) || (this.hint && val != this.hint)) && this.isMinInvalid){
425						this.switchClassName(this.validClass);
426						this.setState(Spry.Widget.ValidationTextarea.VALID);
427						this.isMinInvalid = false;
428			}else if ( (val.length < this.options.minChars || (this.hint && val == this.hint && this.flags.hintOn)) && !this.isMinInvalid){
429						this.switchClassName(this.invalidCharsMinClass);
430						this.setState(Spry.Widget.ValidationTextarea.MINIMUM);
431						this.isMinInvalid = true;
432			}
433	}
434};
435Spry.Widget.ValidationTextarea.prototype.counterChar = function(){
436	if (!this.counterEl || !this.options || !this.options.counterType || (this.options.counterType != 'chars_remaining' && this.options.counterType != 'chars_count')){
437		return;	
438	}
439
440	if (this.options.counterType == 'chars_remaining') {
441		if (this.options.maxChars > 0){
442			if (this.flags.hintOn){
443				this.setCounterElementValue(this.options.maxChars);
444			} else {
445				if (this.options.maxChars > this.input.value.length){
446					this.setCounterElementValue(this.options.maxChars - this.input.value.length);
447				}else{
448					this.setCounterElementValue(0);
449				}
450			}
451		}
452	} else {
453		if (this.flags.hintOn){
454			this.setCounterElementValue(0);
455		} else {
456			if (this.useCharacterMasking && typeof this.options.maxChars != 'undefined' && this.options.maxChars < this.input.value.length){
457				this.setCounterElementValue(this.options.maxChars);
458			} else {
459				this.setCounterElementValue(this.input.value.length);
460			}
461		}
462	}
463};
464
465Spry.Widget.ValidationTextarea.prototype.setCounterElementValue = function(val){
466		if ( this.counterEl.nodeName.toLowerCase() != 'input' && 
467			this.counterEl.nodeName.toLowerCase() != 'textarea' &&
468			this.counterEl.nodeName.toLowerCase() != 'select' &&
469			this.counterEl.nodeName.toLowerCase() != 'img'){
470			this.counterEl.innerHTML = val;
471		}
472};
473Spry.Widget.ValidationTextarea.prototype.reset = function() {
474	this.removeHint();
475	this.removeClassName(this.requiredClass);
476	this.removeClassName(this.invalidCharsMinClass);
477	this.removeClassName(this.invalidCharsMaxClass);
478	this.removeClassName(this.validClass);
479	this.setState(Spry.Widget.ValidationTextarea.INITIAL);
480	var self = this;
481	setTimeout(function() {self.putHint();self.counterChar();}, 10);
482};
483
484Spry.Widget.ValidationTextarea.prototype.validate = function(){
485	if (this.input.disabled == true || this.input.readOnly == true){
486			return true;	
487	}
488
489  if (this.validateOn & Spry.Widget.ValidationTextarea.ONSUBMIT) {
490    this.removeHint();
491  }
492  
493	var val = this.input.value;
494	this.validateMinRequired(val);
495
496	var ret = !this.isMinInvalid && !this.isRequireInvalid;
497
498	if (ret && this.options.maxChars > 0 && !this.useCharacterMasking){
499			if (val.length <= this.options.maxChars || (this.hint && this.hint == val && this.flags.hintOn))	{
500					this.switchClassName(this.validClass);
501					this.setState(Spry.Widget.ValidationTextarea.VALID);
502				  this.isMaxInvalid = false;
503			}else{
504					this.switchClassName(this.invalidCharsMaxClass);
505					this.setState(Spry.Widget.ValidationTextarea.MAXIMUM);
506					this.isMaxInvalid = true;	
507			}
508	}
509	ret = ret && !this.isMaxInvalid;
510	if (ret) {
511		this.switchClassName(this.validClass);
512	}
513	this.counterChar();	
514	return ret;
515};
516
517Spry.Widget.ValidationTextarea.prototype.setState = function(newstate){
518	this.state = newstate;
519};
520
521Spry.Widget.ValidationTextarea.prototype.getState = function(){
522	return this.state;
523};
524
525Spry.Widget.ValidationTextarea.prototype.removeHint = function()
526{
527	if (this.flags.hintOn) 
528	{
529		this.flags.locked = true;
530		this.input.value = "";
531		this.flags.locked = false;
532		this.flags.hintOn = false;
533		this.removeClassName(this.hintClass);
534	}
535};
536
537Spry.Widget.ValidationTextarea.prototype.putHint = function()
538{
539	if(this.hint && this.input.value == "") {
540		this.flags.hintOn = true;
541		this.input.value = this.hint;
542		this.addClassName(this.hintClass);
543	}
544};
545
546Spry.Widget.ValidationTextarea.prototype.redTextFlash = function()
547{
548	var self = this;
549	this.addClassName(this.textareaFlashClass);
550	setTimeout(function() {
551		self.removeClassName(self.textareaFlashClass)
552	}, 200);
553};
554
555
556Spry.Widget.ValidationTextarea.prototype.onKeyPress = function(e)
557{
558	//ENTER has length 2 on IE Windows, so will exceed maxLength on proximity
559	if (Spry.is.ie && Spry.is.windows && e.keyCode == 13) {
560		if ( (this.initialCursor.length + this.options.maxChars - this.input.value.length) < 2) {
561			Spry.Widget.Utils.stopEvent(e);
562			return false;
563		}
564	}
565};
566
567Spry.Widget.ValidationTextarea.prototype.onKeyDown = function(e)
568{ 
569	this.saveState();
570	this.keyCode = e.keyCode;
571	return true;
572};
573
574/*
575 * hadle for the max chars restrictions
576 * if key pressed or the input text is invalid it returns false
577 * 
578 */
579Spry.Widget.ValidationTextarea.prototype.onKeyEvent = function(e){
580	// on IE we look only for this input value changes
581	if (e.type == 'propertychange' && e.propertyName != 'value'){
582			return true;
583	}
584
585	var allow = this.onTyping(e);
586
587	if (!allow){
588		Spry.Widget.Utils.stopEvent(e);
589	}
590	//return allow;
591};
592
593/*
594 * handle for the min or required value
595 * if the input text is invalid it returns false
596 * 
597 */
598Spry.Widget.ValidationTextarea.prototype.onChange = function(e){
599	if (Spry.is.ie && e && e.type == 'propertychange' && e.propertyName != 'value') {
600		return true;
601	}
602
603	if (this.flags.drop) {
604		//delay this if it's a drop operation
605		var self = this;
606		setTimeout(function() {
607			self.flags.drop = false;
608			self.onChange(null);
609		}, 0);
610		return true;
611	}
612	if (this.flags.hintOn) {
613		return true;
614	}
615	this.onKeyEventOn = true;
616	var answer = this.validate();
617	this.onKeyEventOn = false;
618	return answer;
619};
620
621Spry.Widget.ValidationTextarea.prototype.onMouseDown = function(e)
622{
623	if (this.flags.active) {
624		//mousedown fires before focus
625		//avoid double saveState on first focus by mousedown by checking if the control has focus
626		//do nothing if it's not focused because saveState will be called onfocus
627		this.saveState();
628	}
629};
630
631Spry.Widget.ValidationTextarea.prototype.onDrop = function(e)
632{
633	//mark that a drop operation is in progress to avoid race conditions with event handlers for other events
634	//especially onchange and onfocus
635	this.flags.drop = true;
636	this.removeHint();
637
638	if (Spry.is.ie) {
639		var rng = document.body.createTextRange();
640		rng.moveToPoint(e.x, e.y);
641		rng.select();
642	}
643
644	this.saveState();
645	this.flags.active = true;
646	this.addClassName(this.focusClass);
647};
648
649Spry.Widget.ValidationTextarea.prototype.onFocus = function(e)
650{
651	if (this.flags.drop) {
652		return;
653	}
654	this.removeHint();
655	this.saveState();
656	this.flags.active = true;
657	this.addClassName(this.focusClass);
658};
659
660Spry.Widget.ValidationTextarea.prototype.onBlur = function(e){
661	this.removeClassName(this.focusClass);
662
663	if (this.validateOn & Spry.Widget.ValidationTextarea.ONBLUR) {
664		this.onBlurOn = true;
665		this.validate();
666		this.onBlurOn = false;
667	}
668
669	this.flags.active = false;
670	var self = this;
671	setTimeout(function() {self.putHint();}, 10);
672};
673
674Spry.Widget.ValidationTextarea.prototype.safariMouseDown = function(e){
675	this.safariClicked = true;
676};
677Spry.Widget.ValidationTextarea.prototype.safariChangeMouseUp = function(e){
678		if (!this.safariClicked){
679			this.onKeyDown(e); 
680			return this.safariChangeValidate(e, false);
681		}else{
682			this.safariClicked = false;
683			return true;
684		}
685};
686
687Spry.Widget.ValidationTextarea.prototype.safariMouseUp = function(e){
688		if (!this.safariClicked){
689			this.onKeyDown(e);
690			return this.safariValidate(e, false);
691		}else{
692			this.safariClicked = false;
693			return true;
694		}
695};
696
697Spry.Widget.ValidationTextarea.prototype.safariKeyPress = function(e){
698	this.safariFlag = new Date();
699	return this.safariValidate(e, true);
700};
701
702Spry.Widget.ValidationTextarea.prototype.safariValidate = function(e, recall)
703{
704	if (e.keyCode && Spry.Widget.Utils.isSpecialKey(e) && e.keyCode != 8 && e.keyCode != 46){
705		return true;
706	}
707	var answer = this.onTyping(e);
708
709	// the answer to this is not yet final - we schedule another closing check
710	if (new Date() - this.safariFlag < 1000 && recall){
711		var self = this;
712		setTimeout(function(){self.safariValidate(e, false);}, 1000);
713	}
714	return answer;
715};
716
717Spry.Widget.ValidationTextarea.prototype.safariChangeKeyPress = function(e){
718	this.safariChangeFlag = new Date();
719	return this.safariChangeValidate(e, true);
720};
721
722Spry.Widget.ValidationTextarea.prototype.safariChangeValidate = function(e, recall){
723	
724	if(e.keyCode && Spry.Widget.Utils.isSpecialKey(e) && e.keyCode != 8 && e.keyCode != 46){
725		return true;	
726	}
727	var answer = this.onChange(e);
728
729	// the answer to this is not yet final - we schedule another closing check
730	if (new Date() - this.safariChangeFlag < 1000 && recall){
731		var self = this;
732		setTimeout(function(){ self.safariChangeValidate(e, false);}, 1000 - new Date() + this.safariChangeFlag);
733	}
734	return answer;
735};
736
737/*
738 * save an initial state of the input to restore if the value is invalid
739 * 
740 */
741Spry.Widget.ValidationTextarea.prototype.saveState = function(e){
742	
743	// we don't need this initial value that is already invalid
744	if (this.options.maxChars > 0 && this.input.value.length > this.options.maxChars){
745		return;
746	}
747	this.cursorPosition.update();
748	if (!this.flags.hintOn){
749		this.initialValue = this.input.value;
750	}else{
751		this.initialValue = '';
752	}
753	this.initialCursor = this.cursorPosition; 
754	return true;
755};
756
757Spry.Widget.ValidationTextarea.prototype.checkClassName = function(ele, className){
758	if (!ele || !className){
759		return false;
760	}
761	if (typeof ele == 'string' ) {
762		ele = document.getElementById(ele);
763		if (!ele){
764			return false;	
765		}
766	}
767	if (!ele.className){
768		ele.className = ' ';
769	}
770	return ele;
771};
772
773Spry.Widget.ValidationTextarea.prototype.switchClassName = function (className){
774	var classes = [this.invalidCharsMaxClass, this.validClass, this.requiredClass, this.invalidCharsMinClass];
775
776	for (var k = 0; k < classes.length; k++){
777		if (classes[k] != className){
778				this.removeClassName(classes[k]);			
779		}
780	}
781
782	this.addClassName(className);
783};
784
785Spry.Widget.ValidationTextarea.prototype.addClassName = function(clssName){
786	var ele = this.checkClassName(this.element, clssName);
787	var add = this.checkClassName(this.additionalError, clssName);
788
789	if (!ele || ele.className.search(new RegExp("\\b" + clssName + "\\b")) != -1){
790	  return;
791	}
792	this.element.className += ' ' + clssName;
793	if (add)
794		add.className += ' ' + clssName;
795};
796
797Spry.Widget.ValidationTextarea.prototype.removeClassName = function(className){
798	var ele = this.checkClassName(this.element, className);
799	var add = this.checkClassName(this.additionalError, className);
800	if (!ele){
801	  return;	
802	}
803	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), '');
804	if (add){
805		add.className = add.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), '');
806	}
807}; 
808
809/**
810 * SelectionDescriptor is a wrapper for input type text selection methods and properties 
811 * as implemented by various  browsers
812 */
813Spry.Widget.SelectionDescriptor = function (element)
814{
815	this.element = element;
816	this.update();
817};
818
819Spry.Widget.SelectionDescriptor.prototype.update = function()
820{
821	if (Spry.is.ie && Spry.is.windows) {
822		var sel = this.element.ownerDocument.selection;
823		if (this.element.nodeName == "TEXTAREA") {
824			if (sel.type != 'None') {
825				try{var range = sel.createRange();}catch(err){return;}
826				if (range.parentElement() == this.element){
827					var range_all = this.element.ownerDocument.body.createTextRange();
828					range_all.moveToElementText(this.element);
829					for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++){
830						range_all.moveStart('character', 1);
831					}
832					this.start = sel_start;
833					// create a selection of the whole this.element
834					range_all = this.element.ownerDocument.body.createTextRange();
835					range_all.moveToElementText(this.element);
836					for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; sel_end++){
837						range_all.moveStart('character', 1);
838					}
839					this.end = sel_end;
840					this.length = this.end - this.start;
841					// get selected and surrounding text
842					this.text = range.text;
843		 		}
844			}        
845		} else if (this.element.nodeName == "INPUT"){
846			try{this.range = sel.createRange();}catch(err){return;}
847			this.length = this.range.text.length;
848			var clone = this.range.duplicate();
849			this.start = -clone.moveStart("character", -10000);
850			clone = this.range.duplicate();
851			clone.collapse(false);
852			this.end = -clone.moveStart("character", -10000);
853			this.text = this.range.text;
854		}
855	} else {
856		var tmp = this.element;
857		var selectionStart = 0;
858		var selectionEnd = 0;
859        
860		try { selectionStart = tmp.selectionStart;} catch(err) {}
861		try { selectionEnd = tmp.selectionEnd;} catch(err) {}
862
863		if (Spry.is.safari) {
864			if (selectionStart == 2147483647) {
865				selectionStart = 0;
866			}
867			if (selectionEnd == 2147483647) {
868				selectionEnd = 0;
869			}
870		}
871		this.start = selectionStart;
872		this.end = selectionEnd;
873		this.length = selectionEnd - selectionStart;
874		this.text = this.element.value.substring(selectionStart, selectionEnd);
875	}
876};
877Spry.Widget.SelectionDescriptor.prototype.destroy = function() {
878	try { delete this.range} catch(err) {}
879	try { delete this.element} catch(err) {}
880};
881
882Spry.Widget.SelectionDescriptor.prototype.moveTo = function(start, end)
883{
884	if (Spry.is.ie && Spry.is.windows) {
885		if (this.element.nodeName == "TEXTAREA") {
886			var ta_range = this.element.createTextRange();
887			this.range = this.element.createTextRange();
888			this.range.move("character", start);
889			this.range.moveEnd("character", end - start);
890			
891			var c1 = this.range.compareEndPoints("StartToStart", ta_range);
892			if (c1 < 0) {
893				this.range.setEndPoint("StartToStart", ta_range);
894			}
895
896			var c2 = this.range.compareEndPoints("EndToEnd", ta_range);
897			if (c2 > 0) {
898				this.range.setEndPoint("EndToEnd", ta_range);
899			}
900		} else if (this.element.nodeName == "INPUT"){
901			this.range = this.element.ownerDocument.selection.createRange();
902			this.range.move("character", -10000);
903			this.start = this.range.moveStart("character", start);
904			this.end = this.start + this.range.moveEnd("character", end - start);
905		}
906		this.range.select();
907	} else {
908		this.start = start;
909		try { this.element.selectionStart = start; } catch(err) {}
910		this.end = end;
911		try { this.element.selectionEnd = end; } catch(err) {}
912	}
913	this.ignore = true;
914	this.update();
915};
916
917//////////////////////////////////////////////////////////////////////
918//
919// Spry.Widget.Form - common for all widgets
920//
921//////////////////////////////////////////////////////////////////////
922
923if (!Spry.Widget.Form) Spry.Widget.Form = {};
924if (!Spry.Widget.Form.onSubmitWidgetQueue) Spry.Widget.Form.onSubmitWidgetQueue = [];
925
926if (!Spry.Widget.Form.validate) {
927	Spry.Widget.Form.validate = function(vform) {
928		var isValid = true;
929		var isElementValid = true;
930		var q = Spry.Widget.Form.onSubmitWidgetQueue;
931		var qlen = q.length;
932		for (var i = 0; i < qlen; i++) {
933			if (!q[i].isDisabled() && q[i].form == vform) {
934				isElementValid = q[i].validate();
935				isValid = isElementValid && isValid;
936			}
937		}
938		return isValid;
939	}
940};
941
942if (!Spry.Widget.Form.onSubmit) {
943	Spry.Widget.Form.onSubmit = function(e, form)
944	{
945		if (Spry.Widget.Form.validate(form) == false) {
946			return false;
947		}
948		return true;
949	};
950};
951
952if (!Spry.Widget.Form.onReset) {
953	Spry.Widget.Form.onReset = function(e, vform)
954	{
955		var q = Spry.Widget.Form.onSubmitWidgetQueue;
956		var qlen = q.length;
957		for (var i = 0; i < qlen; i++) {
958			if (!q[i].isDisabled() && q[i].form == vform && typeof(q[i].reset) == 'function') {
959				q[i].reset();
960			}
961		}
962		return true;
963	};
964};
965
966if (!Spry.Widget.Form.destroy) {
967	Spry.Widget.Form.destroy = function(form)
968	{
969		var q = Spry.Widget.Form.onSubmitWidgetQueue;
970		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
971			if (q[i].form == form && typeof(q[i].destroy) == 'function') {
972				q[i].destroy();
973				i--;
974			}
975		}
976	}
977};
978
979if (!Spry.Widget.Form.destroyAll) {
980	Spry.Widget.Form.destroyAll = function()
981	{
982		var q = Spry.Widget.Form.onSubmitWidgetQueue;
983		for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
984			if (typeof(q[i].destroy) == 'function') {
985				q[i].destroy();
986				i--;
987			}
988		}
989	}
990};
991
992//////////////////////////////////////////////////////////////////////
993//
994// Spry.Widget.Utils
995//
996//////////////////////////////////////////////////////////////////////
997
998if (!Spry.Widget.Utils)	Spry.Widget.Utils = {};
999
1000Spry.Widget.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
1001{
1002	if (!optionsObj)
1003		return;
1004	for (var optionName in optionsObj)
1005	{
1006		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
1007			continue;
1008		obj[optionName] = optionsObj[optionName];
1009	}
1010};
1011
1012Spry.Widget.Utils.firstValid = function() {
1013	var ret = null;
1014	for(var i=0; i<Spry.Widget.Utils.firstValid.arguments.length; i++) {
1015		if (typeof(Spry.Widget.Utils.firstValid.arguments[i]) != 'undefined') {
1016			ret = Spry.Widget.Utils.firstValid.arguments[i];
1017			break;
1018		}
1019	}
1020	return ret;
1021};
1022
1023Spry.Widget.Utils.specialSafariNavKeys = ",63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,";
1024
1025Spry.Widget.Utils.specialCharacters = ",8,9,16,17,18,20,27,33,34,35,36,37,38,39,40,45,46,91,92,93,144,192,63232,";
1026Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys;
1027
1028Spry.Widget.Utils.isSpecialKey = function (ev) {
1029	return Spry.Widget.Utils.specialCharacters.indexOf("," + ev.keyCode + ",") != -1;
1030};
1031
1032Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName) {
1033	var elements  = node.getElementsByTagName(nodeName);
1034	if (elements) {
1035		return elements[0];
1036	}
1037	return null;
1038};
1039
1040Spry.Widget.Utils.getFirstParentWithNodeName = function(node, nodeName)
1041{
1042	while (node.parentNode
1043			&& node.parentNode.nodeName.toLowerCase() != nodeName.toLowerCase()
1044			&& node.parentNode.nodeName != 'BODY') {
1045		node = node.parentNode;
1046	}
1047
1048	if (node.parentNode && node.parentNode.nodeName.toLowerCase() == nodeName.toLowerCase()) {
1049		return node.parentNode;
1050	} else {
1051		return null;
1052	}
1053};
1054
1055Spry.Widget.Utils.destroyWidgets = function (container)
1056{
1057	if (typeof container == 'string') {
1058		container = document.getElementById(container);
1059	}
1060
1061	var q = Spry.Widget.Form.onSubmitWidgetQueue;
1062	for (var i = 0; i < Spry.Widget.Form.onSubmitWidgetQueue.length; i++) {
1063		if (typeof(q[i].destroy) == 'function' && Spry.Widget.Utils.contains(container, q[i].element)) {
1064			q[i].destroy();
1065			i--;
1066		}
1067	}
1068};
1069
1070Spry.Widget.Utils.contains = function (who, what)
1071{
1072	if (typeof who.contains == 'object') {
1073		return what && who && (who == what || who.contains(what));
1074	} else {
1075		var el = what;
1076		while(el) {
1077			if (el == who) {
1078				return true;
1079			}
1080			el = el.parentNode;
1081		}
1082		return false;
1083	}
1084};
1085
1086Spry.Widget.Utils.addEventListener = function(element, eventType, handler, capture)
1087{
1088	try
1089	{
1090		if (element.addEventListener)
1091			element.addEventListener(eventType, handler, capture);
1092		else if (element.attachEvent)
1093			element.attachEvent("on" + eventType, handler, capture);
1094	}
1095	catch (e) {}
1096};
1097
1098Spry.Widget.Utils.removeEventListener = function(element, eventType, handler, capture)
1099{
1100	try
1101	{
1102		if (element.removeEventListener)
1103			element.removeEventListener(eventType, handler, capture);
1104		else if (element.detachEvent)
1105			element.detachEvent("on" + eventType, handler, capture);
1106	}
1107	catch (e) {}
1108};
1109
1110Spry.Widget.Utils.stopEvent = function(ev)
1111{
1112	try
1113	{
1114		this.stopPropagation(ev);
1115		this.preventDefault(ev);
1116	}
1117	catch (e) {}
1118};
1119
1120/**
1121 * Stops event propagation
1122 * @param {Event} ev the event
1123 */
1124Spry.Widget.Utils.stopPropagation = function(ev)
1125{
1126	if (ev.stopPropagation)
1127	{
1128		ev.stopPropagation();
1129	}
1130	else
1131	{
1132		ev.cancelBubble = true;
1133	}
1134};
1135
1136/**
1137 * Prevents the default behavior of the event
1138 * @param {Event} ev the event
1139 */
1140Spry.Widget.Utils.preventDefault = function(ev)
1141{
1142	if (ev.preventDefault)
1143	{
1144		ev.preventDefault();
1145	}
1146	else
1147	{
1148		ev.returnValue = false;
1149	}
1150};
1151
1152})(); // 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.