1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 
 25 /**
 26  * Creates an input field.
 27  * @constructor
 28  * @class
 29  * This class represents an input field..
 30  * <p>
 31  * <h3>CSS Elements</h3>
 32  * <ul>
 33  * <li><code><className></code> input 			specifies the look of the input field during normal editing
 34  * <li><code><className>-error</code> input		specifies the look of the input field in an error state
 35  * </ul>
 36  *
 37  * @author Ross Dargahi
 38  *
 39  * @param {hash}	params			a hash of parameters
 40  * @param {DwtComposite}      params.parent			the parent widget
 41  * @param {constant}      params.type				the data type of the input field
 42  * @param {string}      params.initialValue		the initial value of the field
 43  * @param {number}      params.size				size of the input field (in characters)
 44  * @param {number}      params.rows				the number of rows (more than 1 means textarea)
 45  * @param {boolean}      params.forceMultiRow		if <code>true</code>, forces use of textarea even if rows == 1
 46  * @param {number}      params.maxLen			the maximum length (in characters) of the input
 47  * @param {constant}      params.errorIconStyle		the error icon style
 48  * @param {constant}      params.validationStyle	the validation type
 49  * @param  {function}     params.validator			the custom validation function
 50  * @param {Object}      params.validatorCtxtObj		the object context for validation function
 51  * @param {string}      params.className			the CSS class
 52  * @param {constant}      params.posStyle			the positioning style (see {@link DwtControl})
 53  * @param {boolean}      params.required          if <code>true</code>, mark as required.
 54  * @param {string}      params.hint				a hint to display in the input field when the value is empty.
 55  * @param {string}      params.id				an explicit ID to use for the control's DIV element
 56  * @param {string}      params.inputId			an explicit ID to use for the control's INPUT element
 57  *
 58  * @extends		DwtComposite
 59  * 
 60  */
 61 DwtInputField = function(params) {
 62 
 63 	if (arguments.length == 0) return;
 64 	params.className = params.className  || "DwtInputField";
 65 	this._origClassName = params.className;
 66 	this._errorClassName = this._origClassName + "-error";
 67 	this._hintClassName = this._origClassName + "-hint";
 68 	this._disabledClassName = this._origClassName + "-disabled";
 69 	this._focusedClassName = this._origClassName + "-focused";
 70 	this._errorHintClassName = this._origClassName + "-errorhint";
 71 	this._requiredClassName = this._origClassName + "-required";
 72 	DwtComposite.call(this, params);
 73 
 74     this._inputEventHandlers = {};
 75 
 76     this._type = params.type ? params.type : DwtInputField.STRING;
 77     this._rows = params.rows ? params.rows : 1;
 78     this._size = params.size;
 79 
 80 	this._errorIconStyle = params.errorIconStyle ? params.errorIconStyle :
 81 							params.validator ? DwtInputField.ERROR_ICON_RIGHT : DwtInputField.ERROR_ICON_NONE;
 82 	this._validationStyle = params.validationStyle ? params.validationStyle : DwtInputField.ONEXIT_VALIDATION;
 83 
 84 	this._hasError = false;
 85 	this._hintIsVisible = false;
 86 	this._hint = params.hint;
 87 	this._label = params.label;
 88 
 89 	this.addListener(DwtEvent.ONFOCUS, this._focusHdlr.bind(this));
 90 	this.addListener(DwtEvent.ONBLUR, this._blurHdlr.bind(this));
 91 	this.addListener(DwtEvent.STATE_CHANGE, this._stateChanged.bind(this));
 92 
 93 	var inputFieldId = params.inputId || Dwt.getNextId();
 94 	var errorIconId = Dwt.getNextId();
 95 	var htmlEl = this.getHtmlElement();
 96 	if (this._errorIconStyle == DwtInputField.ERROR_ICON_NONE) {
 97 		if (params.forceMultiRow || (params.rows && params.rows > 1)) {
 98 			var htmlArr = ["<textarea id='", inputFieldId, "' rows=", params.rows];
 99 			var i = htmlArr.length;
100 			if (params.forceMultiRow || params.size) {
101 				htmlArr[i++] = " cols=";
102 				htmlArr[i++] = params.size || 1;
103 			}
104 			if (params.wrap) {
105 				htmlArr[i++] = " wrap=";
106 				htmlArr[i++] = params.wrap;
107 			}
108 			htmlArr[i++] = "></textarea>"
109 			htmlEl.innerHTML = htmlArr.join("");
110 		} else {
111 			htmlEl.innerHTML = ["<input id='",inputFieldId,"'>"].join("");
112 		}
113 
114 	} else {
115 		var htmlArr = ["<table cellspacing='0' cellpadding='0'><tr>"];
116 		var i = 1;
117 		if (this._errorIconStyle == DwtInputField.ERROR_ICON_LEFT)
118 			htmlArr[i++] = ["<td style='padding-right:2px;'id='", errorIconId, "'></td>"].join("");
119 
120 		htmlArr[i++] = ["<td>", "<input id='", inputFieldId, "'>", "</td>"].join("");
121 
122 		if (this._errorIconStyle == DwtInputField.ERROR_ICON_RIGHT)
123 			htmlArr[i++] = ["<td style='padding-left:2px;' id='", errorIconId, "'></td>"].join("");
124 
125 		htmlArr[i++] = "</tr></table>";
126 		htmlEl.innerHTML = htmlArr.join("");
127 
128 		if (this._errorIconStyle != DwtInputField.ERROR_ICON_NONE) {
129 			this._errorIconTd = document.getElementById(errorIconId);
130 			this._errorIconTd.vAlign = "middle";
131 			this._errorIconTd.innerHTML = DwtInputField._NOERROR_ICON_HTML;
132 		}
133 	}
134 
135 	if (params.forceMultiRow || this._rows > 1) {
136         this._inputField = document.getElementById(inputFieldId);
137         this._inputField.onkeyup = DwtInputField._keyUpHdlr;
138         this._inputField.onkeydown = DwtInputField._keyDownHdlr;
139 
140         if (params.size)
141             this._inputField.size = params.size;
142         if (params.maxLen)
143             this._inputField.maxLength = this._maxLen = params.maxLen;
144 
145         //MOW:  this.setCursor("default");
146 
147         this._inputField.value = params.initialValue || "";
148 	}
149     else {
150         var oinput = document.getElementById(inputFieldId);
151         var ninput = this.__createInputEl(params);
152 		// bug fix #
153 		if (AjxEnv.isCamino) {
154 			oinput.parentNode.style.overflow = "hidden";
155 		}
156 		oinput.parentNode.replaceChild(ninput, oinput);
157 	}
158 
159     this.setFocusElement(); // now that INPUT has been created
160     this.setValidatorFunction(params.validatorCtxtObj, params.validator);
161 	this._setMouseEventHdlrs(false);
162 	this._setKeyPressEventHdlr(false);
163 	
164     if (params.required != null) {
165         this.setRequired(params.required);
166     }
167 
168     if (params.hint != null) {
169         this.setHint(params.hint);
170     }
171 };
172 
173 DwtInputField.prototype = new DwtComposite;
174 DwtInputField.prototype.constructor = DwtInputField;
175 
176 DwtInputField.prototype.isDwtInputField = true;
177 DwtInputField.prototype.isInputControl = true;
178 DwtInputField.prototype.toString = function() { return "DwtInputField"; };
179 
180 //
181 // Constants
182 //
183 
184 // Error Icon Style
185 /**
186  * Defines the "left" error icon style.
187  */
188 DwtInputField.ERROR_ICON_LEFT = 1;
189 /**
190  * Defines the "right" error icon style.
191  */
192 DwtInputField.ERROR_ICON_RIGHT = 2;
193 /**
194  * Defines the "none" error icon style.
195  */
196 DwtInputField.ERROR_ICON_NONE = 3;
197 
198 // Validation Style
199 /**
200  * Validate field after each character is typed.
201  */
202 DwtInputField.CONTINUAL_VALIDATION = 1;
203 /**
204  * Validate the field (i.e. after TAB or CR).
205  */
206 DwtInputField.ONEXIT_VALIDATION    = 2;
207 /**
208  * Validate the field  manually.
209  */
210 DwtInputField.MANUAL_VALIDATION    = 3;
211 
212 // types
213 /**
214  * Defines the "Integer or float input field" data type.
215  */
216 DwtInputField.NUMBER 	= 1;
217 /**
218  * Defines the "Integer input field (no floating point numbers)" data type.
219  */
220 DwtInputField.INTEGER	= 2;
221 /**
222  * Defines the "Numeric input field" data type.
223  */
224 DwtInputField.FLOAT		= 3;
225 /**
226  * Defines the "String input field" data type.
227  */
228 DwtInputField.STRING	= 4;
229 /**
230  * Defines the "Password input field" data type.
231  */
232 DwtInputField.PASSWORD	= 5;
233 /**
234  * Defines the "Date input field" data type.
235  */
236 DwtInputField.DATE 		= 6;
237 
238 DwtInputField._ERROR_ICON_HTML = AjxImg.getImageHtml("Critical");
239 DwtInputField._NOERROR_ICON_HTML = AjxImg.getImageHtml("Blank_9");
240 
241 //
242 // Public methods
243 //
244 
245 DwtInputField.prototype.dispose =
246 function() {
247 	this._errorIconTd = null;
248 	this._inputField = null;
249 	DwtComposite.prototype.dispose.call(this);
250 };
251 
252 DwtInputField.prototype.setHandler =
253 function(eventType, hdlrFunc) {
254 	if (!this._checkState()) return;
255     this._inputEventHandlers[eventType] = hdlrFunc;
256 	Dwt.setHandler(this.getInputElement(), eventType, hdlrFunc);
257 };
258 
259 /**
260  * Sets the input type.
261  * 
262  * @param	{constant}	type		the input type
263  */
264 DwtInputField.prototype.setInputType = function(type) {
265 
266     if (type != this._type && this._rows == 1) {
267         this._type = type;
268         if (AjxEnv.isIE) {
269             var oinput = this._inputField;
270             var ninput = this.__createInputEl();
271             oinput.parentNode.replaceChild(ninput, oinput);
272         }
273         else {
274             this._inputField.type = this._type != DwtInputField.PASSWORD ? "text" : "password";
275         }
276     }
277 };
278 
279 /**
280  * Applies a regular expression to the contents of this input field, retaining
281  * selection and carent location if supported by the browser.
282  *
283  * @param	{RegExp}	regex		the regular expression to search for
284  * @param	{String}	replacement	the replacement string
285  */
286 DwtInputField.prototype.applySubstitution = function(regex, replacement) {
287 	if (!this._inputField.setRangeText) {
288 		// IE8 doesn't support setRangeText() - so we replace the value
289 		// directly. This moves the caret, if any, to the end of the text.
290 		this.setValue(this.getValue().replace(regex, replacement));
291 	} else {
292 		var match;
293 
294 		while ((match = regex.exec(this.getValue()))) {
295 			this._inputField.setRangeText(replacement, match.index,
296 		    	                          match.index + match[0].length);
297 
298 			if (!regex.global)
299 				return;
300 		}
301 	}
302 };
303 
304 /**
305 * Sets the validator function. This function is executed during validation.
306 *
307 * @param {Object}	obj 		if present, the validator function is executed within
308 *		the context of this object
309 * @param {function}	validator 	the validator function
310 */
311 DwtInputField.prototype.setValidatorFunction =
312 function(obj, validator) {
313 	if (validator) {
314 		this._validator = validator;
315 		this._validatorObj = obj;
316 	} else {
317 		switch (this._type) {
318 			case DwtInputField.NUMBER:	this._validator = DwtInputField.validateNumber; break;
319 		    case DwtInputField.INTEGER:	this._validator = DwtInputField.validateInteger; break;
320 		    case DwtInputField.FLOAT:	this._validator = DwtInputField.validateFloat; break;
321 		    case DwtInputField.STRING:
322 		    case DwtInputField.PASSWORD:this._validator = DwtInputField.validateString;	break;
323 		    case DwtInputField.DATE: 	this._validator = DwtInputField.validateDate; break;
324 		    default: 					this._validator = DwtInputField.validateAny;
325 		}
326 	}
327 };
328 
329 /**
330 * Sets the validator to be a regular expression instead of a function.
331 *
332 * @param {string}	regExp 	the regular expression
333 * @param {string}	errorString 		the error string to set for tooltip if the user enters invalid data
334 */
335 DwtInputField.prototype.setValidatorRegExp =
336 function(regExp, errorString) {
337 	this._validator = regExp;
338 	this._validatorObj = null;
339 	this._errorString = errorString || "";
340 };
341 
342 /**
343 * Sets a validation callback. This callback is invoked any time
344 * the input field is validated. The callback is invoked with two
345 * parameters. The first <code>params[0]</code> is the value of the input field.
346 * The second <code>params[1]</code> is a {Boolean} that if <code>true</code> indicates if the value is valid.
347 *
348 * @param {AjxCallback}	callback the callback
349 */
350 DwtInputField.prototype.setValidationCallback =
351 function(callback) {
352 	this._validationCallback = callback;
353 };
354 
355 /**
356 * Gets the internal native input element
357 *
358 * @return {Element}	the input element
359 */
360 DwtInputField.prototype.getInputElement =
361 function() {
362 	return this._inputField;
363 };
364 
365 /**
366 * Gets the input field current value.
367 *
368 * @return {string}	 the value
369 */
370 DwtInputField.prototype.getValue =
371 function() {
372 	return this._hintIsVisible ? '' : AjxStringUtil.trim(this._inputField.value);
373 };
374 
375 /**
376  * Sets the value for the input field.
377  *
378  * @param	{string}	value	the value
379  * @param	{boolean}	noValidate		if <code>true</code>, do not validate
380  */
381 DwtInputField.prototype.setValue =
382 function(value, noValidate) {
383 	// XXX: if we're disabled, the validation step messes up the style
384 	value = value || "";
385 	this._inputField.value = value;
386 	if(!noValidate) {
387 		value = this._validateInput(value);
388 		if (value != null) {
389 			this._inputField.value = value;
390 		}
391 	}
392 	if (this._hintIsVisible && value) {
393 		this._hideHint(value);
394 	} else if (!value) {
395 		this._showHint();
396 	}
397 };
398 
399 DwtInputField.prototype.clear =
400 function() {
401 	this.setValue("");
402 };
403 
404 /**
405  * Sets the hint for the input field.
406  *
407  * @param {string}	hint 	the hint
408  */
409 DwtInputField.prototype.setHint =
410 function(hint) {
411 	this._hint = hint;
412 	var inputElement = this.getInputElement();
413 	if (AjxEnv.supportsPlaceholder) {
414 		inputElement.placeholder = hint || "";
415 		return;
416 	}
417 
418 	if (this._hintIsVisible) {
419 		inputElement.value = hint;
420 		if (!hint) {
421 			this._hintIsVisible = false;
422 			this._updateClassName();
423 		}
424 	}
425 	else if (inputElement.value === '') {
426 		this._showHint();
427 	}
428 };
429 
430 /**
431  * Sets the ARIA label for the input field.
432  *
433  * @param {string}	label 	the label
434  */
435 DwtInputField.prototype.setLabel =
436 function(label) {
437 	this._label = label;
438 	var inputElement = this.getInputElement();
439 	if (label) {
440 		inputElement.setAttribute('aria-label', label);
441 	} else {
442 		inputElement.removeAttribute('aria-label', label);
443 	}
444 };
445 
446 /**
447  * Sets a valid number range. This method is only applicable for numeric input fields. It sets
448  * the valid range (inclusive) of numeric values for the field
449  *
450  * @param {number}		min 		the minimum permitted value or <code>null</code> for no minimum
451  * @param {number}	max 		the maximum permitted value or <code>null</code> for no maximum
452  */
453 DwtInputField.prototype.setValidNumberRange =
454 function(min, max) {
455 	this._minNumVal = min;
456 	this._maxNumVal = max;
457 	var value = this._validateInput(this.getValue());
458 	if (value != null)
459 		this.setValue(value);
460 };
461 
462 /**
463  * Sets a valid string length.
464  *
465  * @param {number}	min 		the minimum length or <code>null</code> for no minimum
466  * @param {number}	max 		the maximum length or <code>null</code> for no maximum
467  */
468 DwtInputField.prototype.setValidStringLengths =
469 function(minLen, maxLen) {
470 	this._minLen = minLen || 0;
471 	if (maxLen != null) {
472 		this._inputField.maxLength = maxLen;
473 		this._maxLen = maxLen;
474 	}
475 };
476 
477 /**
478  * Sets the number precision.
479  * 
480  * @param	{number}	decimals	the decimals
481  */
482 DwtInputField.prototype.setNumberPrecision =
483 function(decimals) {
484 	this._decimals = decimals;
485 };
486 
487 /**
488  * Sets the read only flag.
489  * 
490  * @param	{boolean}	readonly		if <code>true</code>, make field read only
491  */
492 DwtInputField.prototype.setReadOnly =
493 function(readonly) {
494 	this._inputField.setAttribute("readonly", (readonly == null ? true : readonly));
495 };
496 
497 /**
498  * Gets the required flag.
499  * 
500  * @return	{boolean}	<code>true</code> if the field is required
501  */
502 DwtInputField.prototype.getRequired =
503 function() {
504 	var val = this.getInputElement().getAttribute('aria-required');
505 
506 	//the attribute is always a String, and returns "true" or "false"
507 	return val && val.toLowerCase() === "true";
508 };
509 
510 /**
511  * Sets the required flag.
512  * 
513  * @param	{boolean}	required		if <code>true</code>, make field required
514  */
515 DwtInputField.prototype.setRequired =
516 function(required) {
517 	//must set String as setAttribute only sets Strings... (no point in trying to set a boolean)
518 	this.getInputElement().setAttribute('aria-required', required ? "true" : "false");
519 };
520 
521 /**
522  * Sets the visibility flag.
523  * 
524  * @param	{boolean}	visible		if <code>true</code>, the field is visible
525  */
526 DwtInputField.prototype.setVisible = function(visible) {
527 	DwtComposite.prototype.setVisible.apply(this, arguments);
528 	Dwt.setVisible(this.getInputElement(), visible);
529 };
530 
531 /**
532  * Checks the validity of the input field value.
533  *
534  * @return {string}	a canonical value if valid or <code>null</code> if the field value is not valid
535  */
536 DwtInputField.prototype.isValid =
537 function() {
538 	if (!this.getEnabled()) {
539 		return this.getValue();
540 	}
541 	try {
542 		if (typeof this._validator == "function") {
543 			return this._validatorObj
544 				? this._validator.call(this._validatorObj, this.getValue(), this)
545 				: this._validator(this.getValue());
546 		} else {
547 			return this._validator.test(this._inputField.value);
548 		}
549 	} catch(ex) {
550 		if (typeof ex == "string")
551 			return null;
552 		else
553 			throw ex;
554 	}
555 };
556 
557 /**
558  * Checks the validity of the input field value; returns the error message, if any.
559  */
560 DwtInputField.prototype.getValidationError =
561 function() {
562 	this.validate();
563 
564 	return this._validationError;
565 };
566 
567 /**
568  * Validates the current input in the field. This method should be called
569  * if the validation style has been set to DwtInputField.MANUAL_VALIDATION
570  * and it is time for the field to be validated
571  *
572  * @return {boolean}	<code>true</code> if the field is valid
573  */
574 DwtInputField.prototype.validate =
575 function() {
576 	var value = this._validateInput(this.getValue());
577 	if (value != null) {
578 		this.setValue(value);
579 		return true;
580 	} else {
581 		return false;
582 	}
583 };
584 
585 /* Built-in validators */
586 
587 /**
588  * Validates a number.
589  * 
590  * @param	{string}	value		the value
591  * @return	{boolean}	<code>true</code> if valid
592  */
593 DwtInputField.validateNumber =
594 function(value) {
595 	var n = new Number(value);
596 	if (isNaN(n) || (Math.round(n) != n))
597 		throw AjxMsg.notAnInteger;
598 	return DwtInputField.validateFloat.call(this, value);
599 };
600 
601 /**
602  * Validates an integer.
603  * 
604  * @param	{string}	value		the value
605  * @return	{boolean}	<code>true</code> if valid
606  */
607 DwtInputField.validateInteger =
608 function(value) {
609 	var n = new Number(value);
610 	if (isNaN(n) || (Math.round(n) != n) || (n.toString() != value))
611 		throw AjxMsg.notAnInteger;
612 	if (this._minNumVal && value < this._minNumVal)
613 		throw AjxMessageFormat.format(AjxMsg.numberLessThanMin, this._minNumVal);
614 	if (this._maxNumVal && value > this._maxNumVal)
615 		throw AjxMessageFormat.format(AjxMsg.numberMoreThanMax, this._maxNumVal);
616 	return value;
617 };
618 
619 /**
620  * Validates a float.
621  * 
622  * @param	{string}	value		the value
623  * @return	{boolean}	<code>true</code> if valid
624  */
625 DwtInputField.validateFloat =
626 function(value) {
627 	var n = new Number(value);
628 	if (isNaN(n))
629 		throw AjxMsg.notANumber;
630 	if (this._minNumVal && value < this._minNumVal)
631 		throw AjxMessageFormat.format(AjxMsg.numberLessThanMin, this._minNumVal);
632 	if (this._maxNumVal && value > this._maxNumVal)
633 		throw AjxMessageFormat.format(AjxMsg.numberMoreThanMax, this._maxNumVal);
634 
635 	// make canonical value
636 	if (this._decimals != null) {
637 		var str = n.toString();
638 		var pos = str.indexOf(".");
639 		if (pos == -1)
640 			pos = str.length;
641 		value = n.toPrecision(pos + this._decimals);
642 	} else {
643 		value = n.toString();
644 	}
645 
646 	return value;
647 };
648 
649 /**
650  * Validates a string.
651  * 
652  * @param	{string}	value		the value
653  * @return	{boolean}	<code>true</code> if valid
654  */
655 DwtInputField.validateString =
656 function(value) {
657 	if (this._minLen != null && value.length < this._minLen)
658 		throw AjxMessageFormat.format(AjxMsg.stringTooShort, this._minLen);
659 	if (this._maxLen != null && value.length > this._maxLen)
660 		throw AjxMessageFormat.format(AjxMsg.stringTooLong, this._maxLen);
661 	return value;
662 };
663 
664 /**
665  * Validates a date.
666  * 
667  * @param	{string}	value		the value
668  * @return	{boolean}	<code>true</code> if valid
669  */
670 DwtInputField.validateDate = 
671 function(value) {
672 	if (AjxDateUtil.simpleParseDateStr(value) == null) {
673 		throw AjxMsg.invalidDatetimeString;
674 	}
675 
676 	return value;
677 };
678 
679 /**
680  * Validates an email.
681  * 
682  * @param	{string}	value		the value
683  * @return	{boolean}	<code>true</code> if valid
684  */
685 DwtInputField.validateEmail = function(value) {
686 	if (!AjxEmailAddress.isValid(value))
687 		throw AjxMsg.invalidEmailAddr;
688 	return value;
689 };
690 
691 DwtInputField.validateAny =
692 function(value) {
693 	// note that null will always be regarded as invalid. :-) I guess this
694 	// is OK.  An input field never has a null value.
695 	return value;
696 };
697 
698 //
699 // Protected methods
700 //
701 
702 DwtInputField.prototype._validateRegExp =
703 function(value) {
704 	if (this._regExp && !this._regExp.test(value)) {
705 		throw this._errorString;
706 	}
707 	return value;
708 };
709 
710 DwtInputField._keyUpHdlr =
711 function(ev) {
712 	var keyEv = DwtShell.keyEvent;
713 	keyEv.setFromDhtmlEvent(ev, true);
714 
715 	var obj = keyEv.dwtObj;
716 	var keyCode = keyEv.keyCode;
717     if (obj.notifyListeners(DwtEvent.ONKEYUP, keyEv)) {
718         return true;
719     }
720 
721 	// ENTER || TAB
722 	var val = null;
723 	if ((keyCode == 0x0D || keyCode == 0x09)
724 	    && obj._validationStyle == DwtInputField.ONEXIT_VALIDATION)
725 		val = obj._validateInput(obj.getValue());
726 	else if (obj._validationStyle == DwtInputField.CONTINUAL_VALIDATION)
727 		val = obj._validateInput(obj.getValue());
728 
729 	if (val != null && val != obj.getValue())
730 		obj.setValue(val);
731 
732 	return true;
733 };
734 
735 DwtInputField.prototype._blurHdlr = function(ev) {
736 
737     DBG.println(AjxDebug.FOCUS, "DwtInputField ONBLUR: " + this);
738 
739     if (this.isDisposed()) {
740         return;
741     }
742     this._updateClassName();
743     if (this._validationStyle == DwtInputField.ONEXIT_VALIDATION) {
744         var val = this._validateInput(this.getValue());
745         if (val != null) {
746             this.setValue(val);
747         }
748     }
749     if (!this._hintIsVisible && this._hint) {
750         this._showHint();
751     }
752 };
753 
754 DwtInputField.prototype._focusHdlr = function(ev) {
755 
756     DBG.println(AjxDebug.FOCUS, "DwtInputField ONFOCUS: " + this);
757 	appCtxt.getKeyboardMgr().updateFocus(this);
758 	this._updateClassName();
759 	if (this._hintIsVisible) {
760 		this._hideHint('');
761 	}
762 };
763 
764 DwtInputField._keyDownHdlr =
765 function(ev) {
766     var obj = DwtControl.getTargetControl(ev);
767     if (obj) {
768         if (obj._hintIsVisible) {
769             obj._hideHint('');
770         }
771     }
772 };
773 
774 DwtInputField.prototype._hideHint = 
775 function(value) {
776 	if (!AjxEnv.supportsPlaceholder) {
777 		var element = this.getInputElement();
778 		element.value = value;
779 		element.title = this._hint || "";
780 		this._hintIsVisible = false;
781 		this._updateClassName();
782 	}
783 };
784 
785 DwtInputField.prototype._showHint = 
786 function() {
787 	if (!AjxEnv.supportsPlaceholder && this._hint) {
788 		var element = this.getInputElement();
789 		if (!element.value) {
790 			this._hintIsVisible = true;
791 			this._updateClassName();
792 			element.title = "";
793 			element.value = this._hint;
794 		}
795 	}
796 };
797 
798 DwtInputField.prototype._updateClassName = 
799 function() {
800 	var classList = [];
801 	if (this._hasFocus) {
802 		classList.push(this._focusedClassName);
803 	}
804 	if (!this.getEnabled()) {
805 		classList.push(this._disabledClassName);
806 	} else if (this._hasError) {
807 		if (this._validationError === AjxMsg.valueIsRequired) {
808 			classList.push(this._requiredClassName);
809 		} else if (this._hintIsVisible && !this._hasFocus) {
810 			classList.push(this._errorHintClassName);
811 		} else {
812 			classList.push(this._errorClassName);
813 		}
814 	} else if (this._hintIsVisible && !this._hasFocus) {
815 		classList.push(this._hintClassName);
816 	}
817 	classList.push(this._origClassName);
818 	this.getHtmlElement().className = classList.join(' ');
819 };
820 
821 DwtInputField.prototype._validateInput =
822 function(value) {
823 	var isValid = true;
824 	var retVal;
825 	this._validationError = null;
826 
827 	if (!this.getEnabled()) {
828 		retVal = this.getValue();
829 	} else if (this.getRequired() && value == "") {
830 		this._validationError = AjxMsg.valueIsRequired;
831 	} else {
832 		try {
833 			if (typeof this._validator == "function") {
834 				retVal = value = this._validatorObj
835 					? this._validator.call(this._validatorObj, value, this)
836 					: this._validator(value);
837 			} else if (!this._validator.test(value)) {
838 				this._validationError = this._errorString;
839 			}
840 		} catch(ex) {
841 			if (typeof ex == "string")
842 				this._validationError = ex;
843 			else
844 				throw ex;
845 		}
846 	}
847 	
848 	if (this._validationError) {
849 		this._hasError = true;
850 		if (this._errorIconTd)
851 			this._errorIconTd.innerHTML = DwtInputField._ERROR_ICON_HTML;
852 		this.setToolTipContent(this._validationError);
853 		isValid = false;
854 		retVal = null;
855 	} else {
856 		this._hasError = false;
857 		if (this._errorIconTd)
858 			this._errorIconTd.innerHTML = DwtInputField._NOERROR_ICON_HTML;
859 		this.setToolTipContent(null);
860 		isValid = true;
861 	}
862 	this._updateClassName();
863 
864 	if (this._validationCallback)
865 		this._validationCallback.run(this, isValid, value);
866 
867 	return retVal;
868 };
869 
870 /** 
871  * Overriding default implementation in {@link DwtControl}.
872  * 
873  * @private
874  */
875 DwtInputField.prototype._focusByMouseUpEvent =
876 function()  {
877 	if (this.getEnabled()) {
878 		this._hasFocus = true;
879 	}
880 };
881 
882 /**
883  * The input field inherits the id for accessibility purposes.
884  *
885  * @private
886  */
887 DwtInputField.prototype._replaceElementHook =
888 function(oel, nel, inheritClass, inheritStyle) {
889     nel = this.getInputElement();
890     DwtControl.prototype._replaceElementHook.call(this, oel, nel, inheritClass, inheritStyle);
891     if (oel.id) {
892         nel.id = oel.id;
893     }
894 	if (oel.size) {
895 		nel.size = oel.size;
896 	}
897 	if (oel.title) {
898 		this.setHint(oel.title);
899 	}
900 };
901 
902 //
903 // Private methods
904 //
905 
906 
907 DwtInputField.prototype.__createInputEl =
908 function(params) {
909 	// clean up old input field if present
910 	var oinput = this._inputField;
911 	if (oinput) {
912 		for (var eventType in this._inputEventHandlers) {
913 			oinput.removeAttribute(eventType);
914 		}
915 	}
916 
917 	// create new input field
918 	var ninput;
919 	var type = this._type != DwtInputField.PASSWORD ? "text" : "password";
920 	ninput = document.createElement("INPUT");
921 	ninput.type = type;
922 	this._inputField = ninput;
923 
924 	// set common values
925 	var size = params ? params.size : oinput.size;
926 	var maxLen = params ? params.maxLen : oinput.maxLength;
927 
928 	ninput.autocomplete = "off";
929 	if (size) {
930 		ninput.size = size;
931 	}
932 	if (maxLen) {
933 		ninput.maxLength = maxLen;
934 	}
935 	ninput.value = (params ? params.initialValue : oinput.value) || "";
936 	ninput.readonly = oinput ? oinput.readonly : false;
937     if (params && params.inputId) {
938         ninput.id = params.inputId;
939     }
940 
941 	if (AjxEnv.supportsPlaceholder && this._hint) {
942 		ninput.placeholder = this._hint;
943 	}
944 
945 	if (this._label) {
946 		ninput.setAttribute('aria-label', this._label);
947 	}
948 
949 	// add event handlers
950 	ninput.onkeyup = DwtInputField._keyUpHdlr;
951     ninput.onkeydown = DwtInputField._keyDownHdlr;
952 	this._makeFocusable(ninput);
953 
954 	for (var eventType in this._inputEventHandlers) {
955 		ninput[eventType] = this._inputEventHandlers[eventType];
956 	}
957 
958 	return ninput;
959 };
960 
961 DwtInputField.prototype._stateChanged = function(ev) {
962 	this.getInputElement().disabled = !this.getEnabled();
963 	this._validateInput(this.getValue());
964 }
965 
966 /*
967  * clears the onFocus handler
968  */
969 DwtInputField.prototype.disableFocusHdlr =
970 function() {
971     this._inputField.onfocus = null;
972 };
973 
974 /*
975  * enables the onFocus handler
976  */
977 DwtInputField.prototype.enableFocusHdlr =
978 function(){
979     this._inputField.onfocus = DwtInputField._focusHdlr;
980 };
981 
982 /*
983  * enables the onKeyDown handler
984  * bug fix # 80423 - Firefox loses the handler
985  */
986 DwtInputField.prototype.enableKeyDownHdlr =
987 function() {
988     this._inputField.onkeydown = DwtInputField._keyDownHdlr;
989 };
990 
991 DwtInputField.prototype.moveCursorToEnd =
992 function() {
993 	Dwt.moveCursorToEnd(this._inputField);
994 };
995