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