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