1/** 2 * NetteForms - simple form validation. 3 * 4 * This file is part of the Nette Framework (https://nette.org) 5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com) 6 */ 7 8(function(global, factory) { 9 if (!global.JSON) { 10 return; 11 } 12 13 if (typeof define === 'function' && define.amd) { 14 define(function() { 15 return factory(global); 16 }); 17 } else if (typeof module === 'object' && typeof module.exports === 'object') { 18 module.exports = factory(global); 19 } else { 20 var init = !global.Nette || !global.Nette.noInit; 21 global.Nette = factory(global); 22 if (init) { 23 global.Nette.initOnLoad(); 24 } 25 } 26 27}(typeof window !== 'undefined' ? window : this, function(window) { 28 29 'use strict'; 30 31 var Nette = {}; 32 var preventFiltering = {}; 33 var formToggles = {}; 34 var toggleListeners = new window.WeakMap(); 35 36 Nette.formErrors = []; 37 Nette.version = '3.0'; 38 Nette.invalidNumberMessage = 'Please enter a valid value.'; 39 40 41 /** 42 * Function to execute when the DOM is fully loaded. 43 * @private 44 */ 45 Nette.onDocumentReady = function(callback) { 46 if (document.readyState !== 'loading') { 47 callback.call(this); 48 } else { 49 document.addEventListener('DOMContentLoaded', callback); 50 } 51 }; 52 53 54 /** 55 * Returns the value of form element. 56 */ 57 Nette.getValue = function(elem) { 58 var i; 59 if (!elem) { 60 return null; 61 62 } else if (!elem.tagName) { // RadioNodeList, HTMLCollection, array 63 return elem[0] ? Nette.getValue(elem[0]) : null; 64 65 } else if (elem.type === 'radio') { 66 var elements = elem.form.elements; // prevents problem with name 'item' or 'namedItem' 67 for (i = 0; i < elements.length; i++) { 68 if (elements[i].name === elem.name && elements[i].checked) { 69 return elements[i].value; 70 } 71 } 72 return null; 73 74 } else if (elem.type === 'file') { 75 return elem.files || elem.value; 76 77 } else if (elem.tagName.toLowerCase() === 'select') { 78 var index = elem.selectedIndex, 79 options = elem.options, 80 values = []; 81 82 if (elem.type === 'select-one') { 83 return index < 0 ? null : options[index].value; 84 } 85 86 for (i = 0; i < options.length; i++) { 87 if (options[i].selected) { 88 values.push(options[i].value); 89 } 90 } 91 return values; 92 93 } else if (elem.name && elem.name.substr(-2) === '[]') { // multiple elements [] 94 elements = elem.form.elements[elem.name].tagName ? [elem] : elem.form.elements[elem.name]; 95 values = []; 96 97 for (i = 0; i < elements.length; i++) { 98 if (elements[i].type !== 'checkbox' || elements[i].checked) { 99 values.push(elements[i].value); 100 } 101 } 102 return values; 103 104 } else if (elem.type === 'checkbox') { 105 return elem.checked; 106 107 } else if (elem.tagName.toLowerCase() === 'textarea') { 108 return elem.value.replace('\r', ''); 109 110 } else { 111 return elem.value.replace('\r', '').replace(/^\s+|\s+$/g, ''); 112 } 113 }; 114 115 116 /** 117 * Returns the effective value of form element. 118 */ 119 Nette.getEffectiveValue = function(elem, filter) { 120 var val = Nette.getValue(elem); 121 if (elem.getAttribute) { 122 if (val === elem.getAttribute('data-nette-empty-value')) { 123 val = ''; 124 } 125 } 126 if (filter && preventFiltering[elem.name] === undefined) { 127 preventFiltering[elem.name] = true; 128 var ref = {value: val}; 129 Nette.validateControl(elem, null, true, ref); 130 val = ref.value; 131 delete preventFiltering[elem.name]; 132 } 133 return val; 134 }; 135 136 137 /** 138 * Validates form element against given rules. 139 */ 140 Nette.validateControl = function(elem, rules, onlyCheck, value, emptyOptional) { 141 var top = !rules; 142 elem = elem.tagName ? elem : elem[0]; // RadioNodeList 143 rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); 144 value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; 145 emptyOptional = emptyOptional === undefined ? !Nette.validateRule(elem, ':filled', null, value) : emptyOptional; 146 147 for (var id = 0, len = rules.length; id < len; id++) { 148 var rule = rules[id], 149 op = rule.op.match(/(~)?([^?]+)/), 150 curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; 151 152 rule.neg = op[1]; 153 rule.op = op[2]; 154 rule.condition = !!rule.rules; 155 156 if (!curElem) { 157 continue; 158 } else if (emptyOptional && !rule.condition && rule.op !== ':filled') { 159 continue; 160 } 161 162 curElem = curElem.tagName ? curElem : curElem[0]; // RadioNodeList 163 var success = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); 164 165 if (success === null) { 166 continue; 167 } else if (rule.neg) { 168 success = !success; 169 } 170 171 if (rule.condition && success) { 172 if (!Nette.validateControl(elem, rule.rules, onlyCheck, value, rule.op === ':blank' ? false : emptyOptional)) { 173 return false; 174 } 175 } else if (!rule.condition && !success) { 176 if (Nette.isDisabled(curElem)) { 177 continue; 178 } 179 if (!onlyCheck) { 180 var arr = Array.isArray(rule.arg) ? rule.arg : [rule.arg], 181 message = rule.msg.replace(/%(value|\d+)/g, function(foo, m) { 182 return Nette.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control)); 183 });
184 Nette.addError(curElem, message); 185 } 186 return false; 187 } 188 } 189 190 if (elem.type === 'number' && !elem.validity.valid) { 191 if (top && !onlyCheck) { 192 Nette.addError(elem, Nette.invalidNumberMessage); 193 } 194 return false; 195 } 196 197 return true; 198 }; 199 200 201 /** 202 * Validates whole form. 203 */ 204 Nette.validateForm = function(sender, onlyCheck) { 205 var form = sender.form || sender, 206 scope = false; 207 208 Nette.formErrors = []; 209 210 if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) { 211 var scopeArr = JSON.parse(form['nette-submittedBy'].getAttribute('data-nette-validation-scope') || '[]'); 212 if (scopeArr.length) { 213 scope = new RegExp('^(' + scopeArr.join('-|') + '-)'); 214 } else { 215 Nette.showFormErrors(form, []); 216 return true; 217 } 218 } 219 220 var radios = {}, i, elem; 221 222 for (i = 0; i < form.elements.length; i++) { 223 elem = form.elements[i]; 224 225 if (elem.tagName && !(elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1})) { 226 continue; 227 228 } else if (elem.type === 'radio') { 229 if (radios[elem.name]) { 230 continue; 231 } 232 radios[elem.name] = true; 233 } 234 235 if ((scope && !elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) || Nette.isDisabled(elem)) { 236 continue; 237 } 238 239 if (!Nette.validateControl(elem, null, onlyCheck) && !Nette.formErrors.length) { 240 return false; 241 } 242 } 243 var success = !Nette.formErrors.length; 244 Nette.showFormErrors(form, Nette.formErrors); 245 return success; 246 }; 247 248 249 /** 250 * Check if input is disabled. 251 */ 252 Nette.isDisabled = function(elem) { 253 if (elem.type === 'radio') { 254 for (var i = 0, elements = elem.form.elements; i < elements.length; i++) { 255 if (elements[i].name === elem.name && !elements[i].disabled) { 256 return false; 257 } 258 } 259 return true; 260 } 261 return elem.disabled; 262 }; 263 264 265 /** 266 * Adds error message to the queue. 267 */ 268 Nette.addError = function(elem, message) { 269 Nette.formErrors.push({ 270 element: elem, 271 message: message 272 }); 273 }; 274 275 276 /** 277 * Display error messages. 278 */ 279 Nette.showFormErrors = function(form, errors) { 280 var messages = [], 281 focusElem; 282 283 for (var i = 0; i < errors.length; i++) { 284 var elem = errors[i].element, 285 message = errors[i].message; 286 287 if (messages.indexOf(message) < 0) { 288 messages.push(message); 289 290 if (!focusElem && elem.focus) { 291 focusElem = elem; 292 } 293 } 294 } 295 296 if (messages.length) { 297 alert(messages.join('\n')); 298 299 if (focusElem) { 300 focusElem.focus(); 301 } 302 } 303 }; 304 305 306 /** 307 * Validates single rule. 308 */ 309 Nette.validateRule = function(elem, op, arg, value) { 310 value = value === undefined ? {value: Nette.getEffectiveValue(elem, true)} : value; 311 312 if (op.charAt(0) === ':') { 313 op = op.substr(1); 314 } 315 op = op.replace('::', '_'); 316 op = op.replace(/\\/g, ''); 317 318 var arr = Array.isArray(arg) ? arg.slice(0) : [arg]; 319 for (var i = 0, len = arr.length; i < len; i++) { 320 if (arr[i] && arr[i].control) { 321 var control = elem.form.elements.namedItem(arr[i].control); 322 arr[i] = control === elem ? value.value : Nette.getEffectiveValue(control, true); 323 } 324 } 325 326 return Nette.validators[op] 327 ? Nette.validators[op](elem, Array.isArray(arg) ? arr : arr[0], value.value, value) 328 : null; 329 }; 330 331 332 Nette.validators = { 333 filled: function(elem, arg, val) { 334 if (elem.type === 'number' && elem.validity.badInput) { 335 return true; 336 } 337 return val !== '' && val !== false && val !== null 338 && (!Array.isArray(val) || !!val.length) 339 && (!window.FileList || !(val instanceof window.FileList) || val.length); 340 }, 341 342 blank: function(elem, arg, val) { 343 return !Nette.validators.filled(elem, arg, val); 344 }, 345 346 valid: function(elem) { 347 return Nette.validateControl(elem, null, true); 348 }, 349 350 equal: function(elem, arg, val) { 351 if (arg === undefined) { 352 return null; 353 } 354 355 function toString(val) { 356 if (typeof val === 'number' || typeof val === 'string') { 357 return '' + val; 358 } else { 359 return val === true ? '1' : ''; 360 } 361 } 362 363 val = Array.isArray(val) ? val : [val]; 364 arg = Array.isArray(arg) ? arg : [arg]; 365 loop: 366 for (var i1 = 0, len1 = val.length; i1 < len1; i1++) { 367 for (var i2 = 0, len2 = arg.length; i2 < len2; i2++) { 368 if (toString(val[i1]) === toString(arg[i2])) { 369 continue loop; 370 } 371 } 372 return false; 373 } 374 return true; 375 }, 376 377 notEqual: function(elem, arg, val) { 378 return arg === undefined ? null : !Nette.validators.equal(elem, arg, val); 379 }, 380 381 minLength: function(elem, arg, val) { 382 if (elem.type === 'number') { 383 if (elem.validity.tooShort) { 384 return false; 385 } else if (elem.validity.badInput) { 386 return null; 387 } 388 } 389 return val.length >= arg; 390 }, 391 392 maxLength: function(elem, arg, val) { 393 if (elem.type === 'number') { 394 if (elem.validity.tooLong) { 395 return false; 396 } else if (elem.validity.badInput) { 397 return null; 398 } 399 } 400 return val.length <= arg; 401 }, 402 403 length: function(elem, arg, val) { 404 if (elem.type === 'number') { 405 if (elem.validity.tooShort || elem.validity.tooLong) { 406 return false; 407 } else if (elem.validity.badInput) { 408 return null; 409 } 410 } 411 arg = Array.isArray(arg) ? arg : [arg, arg]; 412 return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]); 413 }, 414 415 email: function(elem, arg, val) { 416 return (/^("([ !#-[\]-~]|\\[ -~])+"|[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*)@([0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)+[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?$/i).test(val); 417 }, 418 419 url: function(elem, arg, val, value) { 420 if (!(/^[a-z\d+.-]+:/).test(val)) { 421 val = 'http://' + val; 422 } 423 if ((/^https?:\/\/((([-_0-9a-z\u00C0-\u02FF\u0370-\u1EFF]+\.)*[0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)?[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]{3,39}\])(:\d{1,5})?(\/\S*)?$/i).test(val)) { 424 value.value = val; 425 return true; 426 } 427 return false; 428 }, 429 430 regexp: function(elem, arg, val) { 431 var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false; 432 try { 433 return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val); 434 } catch (e) {} // eslint-disable-line no-empty 435 }, 436 437 pattern: function(elem, arg, val, value, caseInsensitive) { 438 if (typeof arg !== 'string') { 439 return null; 440 } 441 442 try { 443 try { 444 var regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'ui' : 'u'); 445 } catch (e) { 446 regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : ''); 447 } 448 449 if (window.FileList && val instanceof FileList) { 450 for (var i = 0; i < val.length; i++) { 451 if (!regExp.test(val[i].name)) { 452 return false; 453 } 454 } 455 456 return true; 457 } 458 459 return regExp.test(val); 460 } catch (e) {} // eslint-disable-line no-empty 461 }, 462 463 patternCaseInsensitive: function(elem, arg, val) { 464 return Nette.validators.pattern(elem, arg, val, null, true); 465 }, 466 467 numeric: function(elem, arg, val) { 468 if (elem.type === 'number' && elem.validity.badInput) { 469 return false; 470 } 471 return (/^[0-9]+$/).test(val); 472 }, 473 474 integer: function(elem, arg, val) { 475 if (elem.type === 'number' && elem.validity.badInput) { 476 return false; 477 } 478 return (/^-?[0-9]+$/).test(val); 479 }, 480 481 'float': function(elem, arg, val, value) { 482 if (elem.type === 'number' && elem.validity.badInput) { 483 return false; 484 } 485 val = val.replace(/ +/g, '').replace(/,/g, '.'); 486 if ((/^-?[0-9]*\.?[0-9]+$/).test(val)) { 487 value.value = val; 488 return true; 489 } 490 return false; 491 }, 492 493 min: function(elem, arg, val) { 494 if (elem.type === 'number') { 495 if (elem.validity.rangeUnderflow) { 496 return false; 497 } else if (elem.validity.badInput) { 498 return null; 499 } 500 } 501 return arg === null || parseFloat(val) >= arg; 502 }, 503 504 max: function(elem, arg, val) { 505 if (elem.type === 'number') { 506 if (elem.validity.rangeOverflow) { 507 return false; 508 } else if (elem.validity.badInput) { 509 return null; 510 } 511 } 512 return arg === null || parseFloat(val) <= arg; 513 }, 514 515 range: function(elem, arg, val) { 516 if (elem.type === 'number') { 517 if (elem.validity.rangeUnderflow || elem.validity.rangeOverflow) { 518 return false; 519 } else if (elem.validity.badInput) { 520 return null; 521 } 522 } 523 return Array.isArray(arg) ? 524 ((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null; 525 }, 526 527 submitted: function(elem) { 528 return elem.form['nette-submittedBy'] === elem; 529 }, 530 531 fileSize: function(elem, arg, val) { 532 if (window.FileList) { 533 for (var i = 0; i < val.length; i++) { 534 if (val[i].size > arg) { 535 return false; 536 } 537 } 538 } 539 return true; 540 }, 541 542 mimeType: function (elem, arg, val) { 543 arg = Array.isArray(arg) ? arg : [arg]; 544 for (var i = 0, len = arg.length, re = []; i < len; i++) { 545 re.push('^' + arg[i].replace(/([^\w])/g, '\\$1').replace('\\*', '.*') + '$'); 546 } 547 re = new RegExp(re.join('|')); 548 549 if (window.FileList && val instanceof FileList) { 550 for (i = 0; i < val.length; i++) { 551 if (val[i].type && !re.test(val[i].type)) { 552 return false; 553 } else if (elem.validity.badInput) { 554 return null; 555 } 556 } 557 } 558 return true; 559 }, 560 561 image: function (elem, arg, val) { 562 return Nette.validators.mimeType(elem, ['image/gif', 'image/png', 'image/jpeg', 'image/webp'], val); 563 }, 564 565 'static': function (elem, arg) { 566 return arg; 567 } 568 }; 569 570 571 /** 572 * Process all toggles in form. 573 */ 574 Nette.toggleForm = function(form, event) { 575 var i; 576 formToggles = {}; 577 for (i = 0; i < form.elements.length; i++) { 578 if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) { 579 Nette.toggleControl(form.elements[i], null, null, !event); 580 } 581 } 582 583 for (i in formToggles) { 584 Nette.toggle(i, formToggles[i].state, formToggles[i].elem, event); 585 } 586 }; 587 588 589 /** 590 * Process toggles on form element. 591 */ 592 Nette.toggleControl = function(elem, rules, success, firsttime, value, emptyOptional) { 593 rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); 594 value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; 595 emptyOptional = emptyOptional === undefined ? !Nette.validateRule(elem, ':filled', null, value) : emptyOptional; 596 597 var has = false, 598 handler = function (e) { 599 Nette.toggleForm(elem.form, e); 600 }, 601 curSuccess; 602 603 for (var id = 0, len = rules.length; id < len; id++) { 604 var rule = rules[id], 605 op = rule.op.match(/(~)?([^?]+)/), 606 curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; 607 608 rule.neg = op[1]; 609 rule.op = op[2]; 610 rule.condition = !!rule.rules; 611 612 if (!curElem) { 613 continue; 614 } else if (emptyOptional && !rule.condition && rule.op !== ':filled') { 615 continue; 616 } 617 618 curSuccess = success; 619 if (success !== false) { 620 curSuccess = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); 621 if (curSuccess === null) { 622 continue; 623 624 } else if (rule.neg) { 625 curSuccess = !curSuccess; 626 } 627 if (!rule.condition) { 628 success = curSuccess; 629 } 630 } 631 632 if ((rule.condition && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value, rule.op === ':blank' ? false : emptyOptional)) || rule.toggle) { 633 has = true; 634 if (firsttime) { 635 var name = curElem.tagName ? curElem.name : curElem[0].name, 636 els = curElem.tagName ? curElem.form.elements : curElem; 637 638 for (var i = 0; i < els.length; i++) { 639 if (els[i].name === name && !toggleListeners.has(els[i])) { 640 els[i].addEventListener('change', handler); 641 toggleListeners.set(els[i], null); 642 } 643 } 644 } 645 for (var toggleId in rule.toggle || []) { 646 formToggles[toggleId] = formToggles[toggleId] || {elem: elem}; 647 formToggles[toggleId].state = formToggles[toggleId].state || (rule.toggle[toggleId] ? curSuccess : !curSuccess); 648 } 649 } 650 } 651 return has; 652 }; 653 654 655 /** 656 * Displays or hides HTML element. 657 */ 658 Nette.toggle = function(selector, visible, srcElement, event) { // eslint-disable-line no-unused-vars 659 if (/^\w[\w.:-]*$/.test(selector)) { // id 660 selector = '#' + selector; 661 } 662 var elems = document.querySelectorAll(selector); 663 for (var i = 0; i < elems.length; i++) { 664 elems[i].hidden = !visible; 665 } 666 }; 667 668 669 /** 670 * Compact checkboxes 671 */ 672 Nette.compactCheckboxes = function(form) { 673 var name, i, elem, values = {}; 674 675 for (i = 0; i < form.elements.length; i++) { 676 elem = form.elements[i]; 677 if (elem.tagName 678 && elem.tagName.toLowerCase() === 'input' 679 && elem.type === 'checkbox' 680 ) { 681 if (elem.name 682 && elem.name.substr(-2) === '[]' 683 ) { 684 name = elem.name.substr(0, elem.name.length - 2); 685 elem.removeAttribute('name'); 686 elem.setAttribute('data-nette-name', name); 687 } 688 689 if (name = elem.getAttribute('data-nette-name')) { // eslint-disable-line no-cond-assign 690 values[name] = values[name] || []; 691 if (elem.checked && !elem.disabled) { 692 values[name].push(elem.value); 693 } 694 } 695 } 696 } 697 698 for (name in values) { 699 if (form.elements[name] === undefined) { 700 elem = document.createElement('input'); 701 elem.setAttribute('name', name); 702 elem.setAttribute('type', 'hidden'); 703 form.appendChild(elem); 704 } 705 form.elements[name].value = values[name].join(','); 706 form.elements[name].disabled = values[name].length === 0; 707 } 708 }; 709 710 711 /** 712 * Setup handlers. 713 */ 714 Nette.initForm = function(form) { 715 if (form.method === 'get' && form.hasAttribute('data-nette-compact')) { 716 form.addEventListener('submit', function() { 717 Nette.compactCheckboxes(form); 718 }); 719 } 720 721 check: { 722 for (var i = 0; i < form.elements.length; i++) { 723 if (form.elements[i].getAttribute('data-nette-rules')) { 724 break check; 725 } 726 } 727 return; 728 } 729 730 Nette.toggleForm(form); 731 732 if (form.noValidate) { 733 return; 734 } 735 form.noValidate = true; 736 737 form.addEventListener('submit', function(e) { 738 if (!Nette.validateForm(form)) { 739 e.stopPropagation(); 740 e.preventDefault(); 741 } 742 }); 743 }; 744 745 746 /** 747 * @private 748 */ 749 Nette.initOnLoad = function() { 750 Nette.onDocumentReady(function() { 751 for (var i = 0; i < document.forms.length; i++) { 752 Nette.initForm(document.forms[i]); 753 } 754 755 document.body.addEventListener('click', function(e) { 756 var target = e.target; 757 while (target) { 758 if (target.form && target.type in {submit: 1, image: 1}) { 759 target.form['nette-submittedBy'] = target; 760 break; 761 } 762 target = target.parentNode; 763 } 764 }); 765 }); 766 }; 767 768 769 /** 770 * Converts string to web safe characters [a-z0-9-] text. 771 */ 772 Nette.webalize = function(s) { 773 s = s.toLowerCase(); 774 var res = '', i, ch; 775 for (i = 0; i < s.length; i++) { 776 ch = Nette.webalizeTable[s.charAt(i)];
777 res += ch ? ch : s.charAt(i); 778 } 779 return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); 780 }; 781 782 Nette.webalizeTable = {\u00e1: 'a', \u00e4: 'a', \u010d: 'c', \u010f: 'd', \u00e9: 'e', \u011b: 'e', \u00ed: 'i', \u013e: 'l', \u0148: 'n', \u00f3: 'o', \u00f4: 'o', \u0159: 'r', \u0161: 's', \u0165: 't', \u00fa: 'u', \u016f: 'u', \u00fd: 'y', \u017e: 'z'}; 783 784 return Nette; 785}));
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.