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