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