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