1/******************************************************************** 2* Limit the characters that may be entered in a text field 3* Common options: alphanumeric, alphabetic or numeric 4* Kevin Sheedy, 2012 5* http://github.com/KevinSheedy/jquery.alphanum 6*********************************************************************/ 7(function( $ ){ 8 9 // API /////////////////////////////////////////////////////////////////// 10 $.fn.alphanum = function(settings) { 11 12 var combinedSettings = getCombinedSettingsAlphaNum(settings); 13 14 var $collection = this; 15 16 setupEventHandlers($collection, trimAlphaNum, combinedSettings); 17 18 return this; 19 }; 20 21 $.fn.alpha = function(settings) { 22 23 var defaultAlphaSettings = getCombinedSettingsAlphaNum("alpha"); 24 var combinedSettings = getCombinedSettingsAlphaNum(settings, defaultAlphaSettings); 25 26 var $collection = this; 27 28 setupEventHandlers($collection, trimAlphaNum, combinedSettings); 29 30 return this; 31 }; 32 33 $.fn.numeric = function(settings) { 34 35 var combinedSettings = getCombinedSettingsNum(settings); 36 var $collection = this; 37 38 setupEventHandlers($collection, trimNum, combinedSettings); 39 40 $collection.blur(function(){ 41 numericField_Blur(this, settings); 42 }); 43 44 return this; 45 }; 46 47 // End of API ///////////////////////////////////////////////////////////// 48 49 50 // Start Settings //////////////////////////////////////////////////////// 51 52 var DEFAULT_SETTINGS_ALPHANUM = { 53 allow : '', // Allow extra characters 54 disallow : '', // Disallow extra characters 55 allowSpace : true, // Allow the space character 56 allowNumeric : true, // Allow digits 0-9 57 allowUpper : true, // Allow upper case characters 58 allowLower : true, // Allow lower case characters 59 allowCaseless : true, // Allow characters that don't have both upper & lower variants - eg Arabic or Chinese 60 allowLatin : true, // a-z A-Z 61 allowOtherCharSets : true, // eg é, Ã, Arabic, Chinese etc 62 maxLength : '' // eg Max Length 63 } 64 65 var DEFAULT_SETTINGS_NUM = { 66 allowPlus : false, // Allow the + sign 67 allowMinus : true, // Allow the - sign 68 allowThouSep : true, // Allow the thousands separator, default is the comma eg 12,000 69 allowDecSep : true, // Allow the decimal separator, default is the fullstop eg 3.141 70 allowLeadingSpaces : false, 71 maxDigits : '', // The max number of digits or '' for no max 72 maxDecimalPlaces : '', // The max number of decimal places or '' for no max 73 maxPreDecimalPlaces : '', // The max number digits before the decimal point or '' for no max 74 max : '', // The max numeric value allowed 75 min : '' // The min numeric value allowed 76 } 77 78 // Some pre-defined groups of settings for convenience 79 var CONVENIENCE_SETTINGS_ALPHANUM = { 80 "alpha" : { 81 allowNumeric : false 82 }, 83 "upper" : { 84 allowNumeric : false, 85 allowUpper : true, 86 allowLower : false, 87 allowCaseless : true 88 }, 89 "lower" : { 90 allowNumeric : false, 91 allowUpper : false, 92 allowLower : true, 93 allowCaseless : true 94 } 95 }; 96 97 // Some pre-defined groups of settings for convenience 98 var CONVENIENCE_SETTINGS_NUMERIC = { 99 "integer" : { 100 allowPlus : false, 101 allowMinus : true, 102 allowThouSep : false, 103 allowDecSep : false 104 }, 105 "positiveInteger" : { 106 allowPlus : false, 107 allowMinus : false, 108 allowThouSep : false, 109 allowDecSep : false 110 } 111 }; 112 113 114 var BLACKLIST = getBlacklistAscii() + getBlacklistNonAscii(); 115 var THOU_SEP = ","; 116 var DEC_SEP = "."; 117 var DIGITS = getDigitsMap(); 118 var LATIN_CHARS = getLatinCharsSet(); 119 120 // Return the blacklisted special chars that are encodable using 7-bit ascii 121 function getBlacklistAscii(){ 122 var blacklist = '!@#$%^&*()+=[]\\\';,/{}|":<>?~`.-_';
123 blacklist += " "; // 'Space' is on the blacklist but can be enabled using the 'allowSpace' config entry 124 return blacklist; 125 } 126 127 // Return the blacklisted special chars that are NOT encodable using 7-bit ascii 128 // We want this .js file to be encoded using 7-bit ascii so it can reach the widest possible audience 129 // Higher order chars must be escaped eg "\xAC" 130 // Not too worried about comments containing higher order characters for now (let's wait and see if it becomes a problem) 131 function getBlacklistNonAscii(){ 132 var blacklist = 133 "\xAC" // ¬ 134 + "\u20AC" //  135 + "\xA3" // £ 136 + "\xA6" // Š137 ; 138 return blacklist; 139 } 140 141 // End Settings //////////////////////////////////////////////////////// 142 143 144 // Implementation details go here //////////////////////////////////////////////////////// 145 146 function setupEventHandlers($textboxes, trimFunction, settings) { 147 148 $textboxes.each(function(){ 149 150 var $textbox = $(this); 151 152 $textbox.bind("keyup change paste", function(e){ 153 154 var pastedText = ""; 155 156 if(e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) 157 pastedText = e.originalEvent.clipboardData.getData("text/plain") 158 159 // setTimeout is necessary for handling the 'paste' event 160 setTimeout(function(){ 161 trimTextbox($textbox, trimFunction, settings, pastedText); 162 }, 0); 163 }); 164 165 $textbox.bind("keypress", function(e){ 166 167 // Determine which key is pressed. 168 // If it's a control key, then allow the event's default action to occur eg backspace, tab 169 var charCode = !e.charCode ? e.which : e.charCode; 170 if(isControlKey(charCode) 171 || e.ctrlKey 172 || e.metaKey ) // cmd on MacOS 173 return; 174 175 var newChar = String.fromCharCode(charCode); 176 177 // Determine if some text was selected / highlighted when the key was pressed 178 var selectionObject = $textbox.selection(); 179 var start = selectionObject.start; 180 var end = selectionObject.end; 181 182 var textBeforeKeypress = $textbox.val(); 183 184 // The new char may be inserted: 185 // 1) At the start 186 // 2) In the middle 187 // 3) At the end 188 // 4) User highlights some text and then presses a key which would replace the highlighted text 189 // 190 // Here we build the string that would result after the keypress. 191 // If the resulting string is invalid, we cancel the event. 192 // Unfortunately, it isn't enough to just check if the new char is valid because some chars 193 // are position sensitive eg the decimal point '.'' or the minus sign '-'' are only valid in certain positions. 194 var potentialTextAfterKeypress = textBeforeKeypress.substring(0, start) + newChar + textBeforeKeypress.substring(end); 195 var validatedText = trimFunction(potentialTextAfterKeypress, settings); 196 197 // If the keypress would cause the textbox to contain invalid characters, then cancel the keypress event 198 if(validatedText != potentialTextAfterKeypress) 199 e.preventDefault(); 200 }); 201 }); 202 203 } 204 205 // Ensure the text is a valid number when focus leaves the textbox 206 // This catches the case where a user enters '-' or '.' without entering any digits 207 function numericField_Blur(inputBox, settings) { 208 var fieldValueNumeric = parseFloat($(inputBox).val()); 209 var $inputBox = $(inputBox); 210 211 if(isNaN(fieldValueNumeric)) { 212 $inputBox.val(""); 213 return; 214 } 215 216 if(isNumeric(settings.min) && fieldValueNumeric < settings.min) 217 $inputBox.val(""); 218 219 if(isNumeric(settings.max) && fieldValueNumeric > settings.max) 220 $inputBox.val(""); 221 } 222 223 function isNumeric(value) { 224 return !isNaN(value); 225 } 226 227 function isControlKey(charCode) { 228 229 if(charCode >= 32) 230 return false; 231 if(charCode == 10) 232 return false; 233 if(charCode == 13) 234 return false; 235 236 return true; 237 } 238 239 // One way to prevent a character being entered is to cancel the keypress event. 240 // However, this gets messy when you have to deal with things like copy paste which isn't a keypress. 241 // Which event gets fired first, keypress or keyup? What about IE6 etc etc? 242 // Instead, it's easier to allow the 'bad' character to be entered and then to delete it immediately after. 243 244 function trimTextbox($textBox, trimFunction, settings, pastedText){ 245 246 var inputString = $textBox.val(); 247 248 if(inputString == "" && pastedText.length > 0) 249 inputString = pastedText; 250 251 var outputString = trimFunction(inputString, settings); 252 253 if(inputString == outputString) 254 return; 255 256 var caretPos = $textBox.alphanum_caret(); 257 258 $textBox.val(outputString); 259 260 //Reset the caret position 261 if(inputString.length ==(outputString.length + 1)) 262 $textBox.alphanum_caret(caretPos - 1); 263 else 264 $textBox.alphanum_caret(caretPos); 265 } 266 267 function getCombinedSettingsAlphaNum(settings, defaultSettings){ 268 if(typeof defaultSettings == "undefined") 269 defaultSettings = DEFAULT_SETTINGS_ALPHANUM; 270 var userSettings, combinedSettings = {}; 271 if(typeof settings === "string") 272 userSettings = CONVENIENCE_SETTINGS_ALPHANUM[settings]; 273 else if(typeof settings == "undefined") 274 userSettings = {}; 275 else 276 userSettings = settings; 277 278 $.extend(combinedSettings, defaultSettings, userSettings); 279 280 if(typeof combinedSettings.blacklist == 'undefined') 281 combinedSettings.blacklistSet = getBlacklistSet(combinedSettings.allow, combinedSettings.disallow); 282 283 return combinedSettings; 284 } 285 286 function getCombinedSettingsNum(settings){ 287 var userSettings, combinedSettings = {}; 288 if(typeof settings === "string") 289 userSettings = CONVENIENCE_SETTINGS_NUMERIC[settings]; 290 else if(typeof settings == "undefined") 291 userSettings = {}; 292 else 293 userSettings = settings; 294 295 $.extend(combinedSettings, DEFAULT_SETTINGS_NUM, userSettings); 296 297 return combinedSettings; 298 } 299 300 301 // This is the heart of the algorithm 302 function alphanum_allowChar(validatedStringFragment, Char, settings){ 303 304 if(settings.allow.indexOf(Char) >=0 ) 305 return true; 306 307 if(settings.allowSpace && (Char == " ")) 308 return true; 309 310 if(settings.blacklistSet.contains(Char)) 311 return false;
312 313 if(!settings.allowNumeric && DIGITS[Char]) 314 return false; 315 316 if(!settings.allowUpper && isUpper(Char)) 317 return false; 318 319 if(!settings.allowLower && isLower(Char)) 320 return false; 321 322 if(!settings.allowCaseless && isCaseless(Char)) 323 return false; 324 325 if(!settings.allowLatin && LATIN_CHARS.contains(Char)) 326 return false; 327 328 if(settings.maxLength && validatedStringFragment.length >= settings.maxLength) 329 return false; 330 331 if(!settings.allowOtherCharSets){ 332 if(DIGITS[Char] || LATIN_CHARS.contains(Char)) 333 return true; 334 else 335 return false; 336 } 337 338 return true; 339 } 340 341 function numeric_allowChar(validatedStringFragment, Char, settings){ 342 343 if(DIGITS[Char]) { 344 345 if(isMaxDigitsReached(validatedStringFragment, settings)) 346 return false; 347 348 if(isMaxPreDecimalsReached(validatedStringFragment, settings)) 349 return false; 350 351 if(isMaxDecimalsReached(validatedStringFragment, settings)) 352 return false; 353 354 if(isGreaterThanMax(validatedStringFragment + Char, settings)) 355 return false; 356 357 if(isLessThanMin(validatedStringFragment + Char, settings)) 358 return false; 359 360 return true; 361 } 362 363 if(settings.allowPlus && Char == '+' && validatedStringFragment == '') 364 return true; 365 366 if(settings.allowMinus && Char == '-' && validatedStringFragment == '') 367 return true; 368 369 if(Char == THOU_SEP && settings.allowThouSep && allowThouSep(validatedStringFragment, Char)) 370 return true; 371 372 if(Char == DEC_SEP) { 373 // Only one decimal separator allowed 374 if(validatedStringFragment.indexOf(DEC_SEP) >= 0) 375 return false; 376 if(settings.allowDecSep) 377 return true; 378 } 379 380 return false; 381 } 382 383 function countDigits(string) { 384 385 // Error handling, nulls etc 386 string = string + ""; 387 388 // Count the digits 389 return string.replace(/[^0-9]/g,"").length; 390 } 391 392 function isMaxDigitsReached(string, settings) { 393 394 var maxDigits = settings.maxDigits; 395 396 if(maxDigits == "" || isNaN(maxDigits)) 397 return false; // In this case, there is no maximum 398 399 var numDigits = countDigits(string); 400 401 if(numDigits >= maxDigits) 402 return true; 403 404 return false; 405 } 406 407 function isMaxDecimalsReached(string, settings) { 408 409 var maxDecimalPlaces = settings.maxDecimalPlaces; 410 411 if(maxDecimalPlaces == "" || isNaN(maxDecimalPlaces)) 412 return false; // In this case, there is no maximum 413 414 var indexOfDecimalPoint = string.indexOf(DEC_SEP); 415 416 if(indexOfDecimalPoint == -1) 417 return false; 418 419 var decimalSubstring = string.substring(indexOfDecimalPoint); 420 var numDecimals = countDigits(decimalSubstring); 421 422 if(numDecimals >= maxDecimalPlaces) 423 return true; 424 425 return false; 426 } 427 428 function isMaxPreDecimalsReached(string, settings) { 429 430 var maxPreDecimalPlaces = settings.maxPreDecimalPlaces; 431 432 if(maxPreDecimalPlaces == "" || isNaN(maxPreDecimalPlaces)) 433 return false; // In this case, there is no maximum 434 435 var indexOfDecimalPoint = string.indexOf(DEC_SEP); 436 437 if(indexOfDecimalPoint >= 0) 438 return false; 439 440 var numPreDecimalDigits = countDigits(string); 441 442 if(numPreDecimalDigits >= maxPreDecimalPlaces) 443 return true; 444 445 return false; 446 } 447 448 function isGreaterThanMax(numericString, settings) { 449 450 if(!settings.max || settings.max < 0) 451 return false; 452 453 var outputNumber = parseFloat(numericString); 454 if(outputNumber > settings.max) 455 return true; 456 457 return false; 458 } 459 460 function isLessThanMin(numericString, settings) { 461 462 if(!settings.min || settings.min > 0) 463 return false; 464 465 var outputNumber = parseFloat(numericString); 466 if(outputNumber < settings.min) 467 return true; 468 469 return false; 470 } 471 472 /******************************** 473 * Trims a string according to the settings provided 474 ********************************/ 475 function trimAlphaNum(inputString, settings){ 476 477 if(typeof inputString != "string") 478 return inputString; 479 480 var inChars = inputString.split(""); 481 var outChars = []; 482 var i = 0; 483 var Char; 484 485 for(i=0; i<inChars.length; i++){ 486 Char = inChars[i]; 487 var validatedStringFragment = outChars.join(""); 488 if(alphanum_allowChar(validatedStringFragment, Char, settings)) 489 outChars.push(Char); 490 } 491 492 return outChars.join(""); 493 } 494 495 function trimNum(inputString, settings){ 496 if(typeof inputString != "string") 497 return inputString; 498 499 var inChars = inputString.split(""); 500 var outChars = []; 501 var i = 0; 502 var Char; 503 504 for(i=0; i<inChars.length; i++){ 505 Char = inChars[i];
506 var validatedStringFragment = outChars.join(""); 507 if(numeric_allowChar(validatedStringFragment, Char, settings)) 508 outChars.push(Char); 509 } 510 511 return outChars.join(""); 512 } 513 514 function removeUpperCase(inputString){ 515 var charArray = inputString.split(''); 516 var i = 0; 517 var outputArray = []; 518 var Char; 519 520 for(i=0; i<charArray.length; i++){ 521 Char = charArray[i]; 522 } 523 } 524 525 function removeLowerCase(inputString){ 526 527 } 528 529 function isUpper(Char){ 530 var upper = Char.toUpperCase(); 531 var lower = Char.toLowerCase(); 532 533 if( (Char == upper) && (upper != lower)) 534 return true; 535 else 536 return false; 537 } 538 539 function isLower(Char){ 540 var upper = Char.toUpperCase(); 541 var lower = Char.toLowerCase(); 542 543 if( (Char == lower) && (upper != lower)) 544 return true; 545 else 546 return false; 547 } 548 549 function isCaseless(Char){ 550 if(Char.toUpperCase() == Char.toLowerCase()) 551 return true; 552 else 553 return false; 554 } 555 556 function getBlacklistSet(allow, disallow){ 557 558 var setOfBadChars = new Set(BLACKLIST + disallow); 559 var setOfGoodChars = new Set(allow); 560 561 var blacklistSet = setOfBadChars.subtract(setOfGoodChars); 562 563 return blacklistSet; 564 } 565 566 function getDigitsMap(){ 567 var array = "0123456789".split(""); 568 var map = {}; 569 var i = 0; 570 var digit; 571 572 for(i=0; i<array.length; i++){ 573 digit = array[i]; 574 map[digit] = true; 575 } 576 577 return map; 578 } 579 580 function getLatinCharsSet(){ 581 var lower = "abcdefghijklmnopqrstuvwxyz"; 582 var upper = lower.toUpperCase(); 583 var azAZ = new Set(lower + upper); 584 585 return azAZ; 586 } 587 588 function allowThouSep(currentString, Char) { 589 590 // Can't start with a THOU_SEP 591 if(currentString.length == 0) 592 return false; 593 594 // Can't have a THOU_SEP anywhere after a DEC_SEP 595 var posOfDecSep = currentString.indexOf(DEC_SEP); 596 if(posOfDecSep >= 0) 597 return false; 598 599 var posOfFirstThouSep = currentString.indexOf(THOU_SEP); 600 601 // Check if this is the first occurrence of a THOU_SEP 602 if(posOfFirstThouSep < 0) 603 return true; 604 605 var posOfLastThouSep = currentString.lastIndexOf(THOU_SEP); 606 var charsSinceLastThouSep = currentString.length - posOfLastThouSep - 1; 607 608 // Check if there has been 3 digits since the last THOU_SEP 609 if(charsSinceLastThouSep < 3) 610 return false; 611 612 var digitsSinceFirstThouSep = countDigits(currentString.substring(posOfFirstThouSep)); 613 614 // Check if there has been a multiple of 3 digits since the first THOU_SEP 615 if((digitsSinceFirstThouSep % 3) > 0) 616 return false; 617 618 return true; 619 } 620 621 //////////////////////////////////////////////////////////////////////////////////// 622 // Implementation of a Set 623 //////////////////////////////////////////////////////////////////////////////////// 624 function Set(elems){ 625 if(typeof elems == "string") 626 this.map = stringToMap(elems); 627 else 628 this.map = {}; 629 } 630 631 Set.prototype.add = function(set){ 632 633 var newSet = this.clone(); 634 635 for(var key in set.map) 636 newSet.map[key] = true; 637 638 return newSet; 639 } 640 641 Set.prototype.subtract = function(set){ 642 643 var newSet = this.clone(); 644 645 for(var key in set.map) 646 delete newSet.map[key]; 647 648 return newSet; 649 } 650 651 Set.prototype.contains = function(key){ 652 if(this.map[key]) 653 return true; 654 else 655 return false; 656 } 657 658 Set.prototype.clone = function(){ 659 var newSet = new Set(); 660 661 for(var key in this.map) 662 newSet.map[key] = true; 663 664 return newSet; 665 } 666 //////////////////////////////////////////////////////////////////////////////////// 667 668 function stringToMap(string){ 669 var map = {}; 670 var array = string.split(""); 671 var i=0; 672 var Char; 673 674 for(i=0; i<array.length; i++){ 675 Char = array[i]; 676 map[Char] = true; 677 } 678 679 return map; 680 } 681 682 // Backdoor for testing 683 $.fn.alphanum.backdoorAlphaNum = function(inputString, settings){ 684 var combinedSettings = getCombinedSettingsAlphaNum(settings); 685 686 return trimAlphaNum(inputString, combinedSettings); 687 }; 688 689 $.fn.alphanum.backdoorNumeric = function(inputString, settings){ 690 var combinedSettings = getCombinedSettingsNum(settings); 691 692 return trimNum(inputString, combinedSettings); 693 }; 694 695 $.fn.alphanum.setNumericSeparators = function(settings) { 696 697 if(settings.thousandsSeparator.length != 1) 698 return; 699 700 if(settings.decimalSeparator.length != 1) 701 return; 702 703 THOU_SEP = thousandsSeparator; 704 DEC_SEP = decimalSeparator; 705 } 706 707})( jQuery ); 708 709 710//Include the 3rd party lib: jquery.caret.js 711 712 713// Set caret position easily in jQuery 714// Written by and Copyright of Luke Morton, 2011 715// Licensed under MIT 716(function ($) { 717 // Behind the scenes method deals with browser 718 // idiosyncrasies and such 719 function caretTo(el, index) { 720 if (el.createTextRange) { 721 var range = el.createTextRange(); 722 range.move("character", index); 723 range.select();
724 } else if (el.selectionStart != null) { 725 el.focus(); 726 el.setSelectionRange(index, index); 727 } 728 }; 729 730 // Another behind the scenes that collects the 731 // current caret position for an element 732 733 // TODO: Get working with Opera 734 function caretPos(el) { 735 if ("selection" in document) { 736 var range = el.createTextRange(); 737 try { 738 range.setEndPoint("EndToStart", document.selection.createRange()); 739 } catch (e) { 740 // Catch IE failure here, return 0 like 741 // other browsers 742 return 0; 743 } 744 return range.text.length; 745 } else if (el.selectionStart != null) { 746 return el.selectionStart; 747 } 748 }; 749 750 // The following methods are queued under fx for more 751 // flexibility when combining with $.fn.delay() and 752 // jQuery effects. 753 754 // Set caret to a particular index 755 $.fn.alphanum_caret = function (index, offset) { 756 if (typeof(index) === "undefined") { 757 return caretPos(this.get(0)); 758 } 759 760 return this.queue(function (next) { 761 if (isNaN(index)) { 762 var i = $(this).val().indexOf(index); 763 764 if (offset === true) { 765 i += index.length; 766 } else if (typeof(offset) !== "undefined") { 767 i += offset; 768 } 769 770 caretTo(this, i); 771 } else { 772 caretTo(this, index); 773 } 774 775 next(); 776 }); 777 }; 778}(jQuery)); 779 780/********************************************************** 781* Selection Library 782* Used to determine what text is highlighted in the textbox before a key is pressed. 783* http://donejs.com/docs.html#!jQuery.fn.selection 784* https://github.com/jupiterjs/jquerymx/blob/master/dom/selection/selection.js 785***********************************************************/ 786(function(e){var t=function(e){return e.replace(/([a-z])([a-z]+)/gi,function(e,t,n){return t+n.toLowerCase()}).replace(/_/g,"")},n=function(e){return e.replace(/^([a-z]+)_TO_([a-z]+)/i,function(e,t,n){return n+"_TO_"+t})},r=function(e){return e?e.ownerDocument.defaultView||e.ownerDocument.parentWindow:window},i=function(t,n){var r=e.Range.current(t).clone(),i=e.Range(t).select(t);if(!r.overlaps(i)){return null}if(r.compare("START_TO_START",i)<1){startPos=0;r.move("START_TO_START",i)}else{fromElementToCurrent=i.clone();fromElementToCurrent.move("END_TO_START",r);startPos=fromElementToCurrent.toString().length}if(r.compare("END_TO_END",i)>=0){endPos=i.toString().length}else{endPos=startPos+r.toString().length}return{start:startPos,end:endPos}},s=function(t){var n=r(t);if(t.selectionStart!==undefined){if(document.activeElement&&document.activeElement!=t&&t.selectionStart==t.selectionEnd&&t.selectionStart==0){return{start:t.value.length,end:t.value.length}}return{start:t.selectionStart,end:t.selectionEnd}}else if(n.getSelection){return i(t,n)}else{try{if(t.nodeName.toLowerCase()=="input"){var s=r(t).document.selection.createRange(),o=t.createTextRange();o.setEndPoint("EndToStart",s);var u=o.text.length;return{start:u,end:u+s.text.length}}else{var a=i(t,n);if(!a){return a}var f=e.Range.current().clone(),l=f.clone().collapse().range,c=f.clone().collapse(false).range;l.moveStart("character",-1);c.moveStart("character",-1);if(a.startPos!=0&&l.text==""){a.startPos+=2}if(a.endPos!=0&&c.text==""){a.endPos+=2}return a}}catch(h){return{start:t.value.length,end:t.value.length}}}},o=function(e,t,n){var i=r(e);if(e.setSelectionRange){if(n===undefined){e.focus();e.setSelectionRange(t,t)}else{e.select();e.selectionStart=t;e.selectionEnd=n}}else if(e.createTextRange){var s=e.createTextRange();s.moveStart("character",t);n=n||t;s.moveEnd("character",n-e.value.length);s.select()}else if(i.getSelection){var o=i.document,u=i.getSelection(),f=o.createRange(),l=[t,n!==undefined?n:t];a([e],l);f.setStart(l[0].el,l[0].count);f.setEnd(l[1].el,l[1].count);u.removeAllRanges();u.addRange(f)}else if(i.document.body.createTextRange){var f=document.body.createTextRange();f.moveToElementText(e);f.collapse();f.moveStart("character",t);f.moveEnd("character",n!==undefined?n:t);f.select()}},u=function(e,t,n,r){if(typeof n[0]==="number"&&n[0]<t){n[0]={el:r,count:n[0]-e}}if(typeof n[1]==="number"&&n[1]<=t){n[1]={el:r,count:n[1]-e};}},a=function(e,t,n){var r,i;n=n||0;for(var s=0;e[s];s++){r=e[s];
786if(r.nodeType===3||r.nodeType===4){i=n;n+=r.nodeValue.length;u(i,n,t,r)}else if(r.nodeType!==8){n=a(r.childNodes,t,n)}}return n};jQuery.fn.selection=function(e,t){if(e!==undefined){return this.each(function(){o(this,e,t)})}else{return s(this[0])}};e.fn.selection.getCharElement=a})(jQuery)
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.