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