1/** 2 * Live Form Validation for Nette Forms 2.3 3 * 4 * @author Robert Pösel, zakrava, Radek JeždÃk, MartyIX, David Grudl 5 * @version 1.6.0 6 * @url https://github.com/Robyer/nette-live-form-validation/ 7 */ 8 9var LiveForm = { 10 options: { 11 // CSS class of control's parent where error/valid class should be added; or "false" to use control directly 12 showMessageClassOnParent: 'form-group', 13 14 // CSS class for an invalid control 15 controlErrorClass: 'has-error', 16 17 // CSS class for a valid control 18 controlValidClass: 'has-success', 19 20 // CSS class for an error message 21 messageErrorClass: 'help-block text-danger', 22 23 // control with this CSS class will show error/valid message even when control itself is hidden (useful for controls which are hidden and wrapped into special component) 24 enableHiddenMessageClass: 'show-hidden-error', 25 26 // control with this CSS class will have disabled live validation 27 disableLiveValidationClass: 'no-live-validation', 28 29 // control with this CSS class will not show valid message 30 disableShowValidClass: 'no-show-valid', 31 32 // tag that will hold the error/valid message 33 messageTag: 'span', 34 35 // message element id = control id + this postfix 36 messageIdPostfix: '_message', 37 38 // show this html before error message itself 39 messageErrorPrefix: ' <i class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></i> ', 40 41 // show all errors when submitting form; or use "false" to show only first error 42 showAllErrors: true, 43 44 // show message when valid 45 showValid: false, 46 47 // delay in ms before validating on keyup/keydown; or use "false" to disable it 48 wait: false 49 }, 50 51 forms: { } 52}; 53 54LiveForm.isSpecialKey = function(k) { 55 // http://stackoverflow.com/questions/7770561/jquery-javascript-reject-control-keys-on-keydown-event 56 return (k == 20 /* Caps lock */ 57 || k == 16 /* Shift */ 58 || k == 9 /* Tab */ 59 || k == 27 /* Escape Key */ 60 || k == 17 /* Control Key */ 61 || k == 91 /* Windows Command Key */ 62 || k == 19 /* Pause Break */ 63 || k == 18 /* Alt Key */ 64 || k == 93 /* Right Click Point Key */ 65 || (k >= 35 && k <= 40) /* Home, End, Arrow Keys */ 66 || k == 45 /* Insert Key */ 67 || (k >= 33 && k <= 34) /*Page Down, Page Up */ 68 || (k >= 112 && k <= 123) /* F1 - F12 */ 69 || (k >= 144 && k <= 145)); /* Num Lock, Scroll Lock */ 70} 71 72/** 73 * Handlers for all the events that trigger validation 74 * YOU CAN CHANGE these handlers (ie. to use jQuery events instead) 75 */ 76LiveForm.setupHandlers = function(el) { 77 if (this.hasClass(el, this.options.disableLiveValidationClass)) 78 return; 79 80 // Check if element was already initialized 81 if (el.getAttribute("data-lfv-initialized")) 82 return; 83 84 // Remember we initialized this element so we won't do it again 85 el.setAttribute('data-lfv-initialized', 'true'); 86 87 var handler = function(event) { 88 event = event || window.event; 89 Nette.validateControl(event.target ? event.target : event.srcElement); 90 }; 91 92 var self = this; 93 94 Nette.addEvent(el, "change", handler); 95 Nette.addEvent(el, "blur", handler); 96 Nette.addEvent(el, "keydown", function (event) { 97 if (!self.isSpecialKey(event.which) && (self.options.wait === false || self.options.wait >= 200)) { 98 // Hide validation span tag. 99 self.removeClass(self.getGroupElement(this), self.options.controlErrorClass); 100 self.removeClass(self.getGroupElement(this), self.options.controlValidClass); 101 102 var messageEl = self.getMessageElement(this); 103 messageEl.innerHTML = ''; 104 messageEl.className = ''; 105 106 // Cancel timeout to run validation handler 107 if (self.timeout) { 108 clearTimeout(self.timeout); 109 } 110 } 111 }); 112 Nette.addEvent(el, "keyup", function(event) { 113 if (self.options.wait !== false) { 114 event = event || window.event; 115 if (event.keyCode !== 9) { 116 if (self.timeout) clearTimeout(self.timeout); 117 self.timeout = setTimeout(function() { 118 handler(event); 119 }, self.options.wait); 120 } 121 } 122 }); 123}; 124 125LiveForm.processServerErrors = function(el) { 126 var messageEl = this.getMessageElement(el); 127 var parentEl = this.getMessageParent(el); // This is parent element which contain the error elements 128 129 var errors = []; 130 131 // Find existing error elements by class (from server-validation) 132 var errorEls = parentEl.getElementsByClassName(this.options.messageErrorClass); 133 for (var i = errorEls.length - 1; i > -1; i--) { 134 // Don't touch our main message element 135 if (errorEls[i] == messageEl) 136 continue; 137 138 // Remove only direct children 139 var errorParent = errorEls[i].parentNode; 140 if (errorParent == parentEl) { 141 errors.push(errorEls[i].outerHTML); 142 errorParent.removeChild(errorEls[i]); 143 } 144 } 145 146 // Wrap all server errors into one element 147 if (errors.length > 0) { 148 messageEl.innerHTML = errors.join(""); 149 } 150}; 151 152LiveForm.addError = function(el, message) { 153 // Ignore elements with disabled live validation 154 if (this.hasClass(el, this.options.disableLiveValidationClass)) 155 return; 156 157 this.setFormProperty(el.form, "hasError", true); 158 this.addClass(this.getGroupElement(el), this.options.controlErrorClass); 159 160 if (this.options.showValid && this.showValid(el)) { 161 this.removeClass(this.getGroupElement(el), this.options.controlValidClass); 162 } 163 164 if (!message) { 165 message = ' '; 166 } else { 167 message = this.options.messageErrorPrefix + message; 168 } 169 170 var messageEl = this.getMessageElement(el); 171 messageEl.innerHTML = message; 172 messageEl.className = this.options.messageErrorClass; 173}; 174 175LiveForm.removeError = function(el) { 176 // We don't want to remove any errors during onLoadValidation 177 if (this.getFormProperty(el.form, "onLoadValidation")) 178 return; 179 180 var groupEl = this.getGroupElement(el); 181 this.removeClass(groupEl, this.options.controlErrorClass); 182 183 var id = el.getAttribute('data-lfv-message-id'); 184 if (id) { 185 var messageEl = this.getMessageElement(el); 186 messageEl.innerHTML = ''; 187 messageEl.className = ''; 188 } 189 190 if (this.options.showValid && this.showValid(el)) { 191 this.addClass(groupEl, this.options.controlValidClass); 192 return; 193 } 194}; 195 196LiveForm.showValid = function(el) { 197 if (el.type) { 198 var type = el.type.toLowerCase(); 199 if (type == 'checkbox' || type == 'radio') { 200 return false;
201 } 202 } 203 204 var rules = Nette.parseJSON(el.getAttribute('data-nette-rules')); 205 if (rules.length == 0) { 206 return false; 207 } 208 209 if (this.hasClass(el, this.options.disableShowValidClass)) { 210 return false; 211 } 212 213 return true; 214}; 215 216LiveForm.getGroupElement = function(el) { 217 if (this.options.showMessageClassOnParent === false) 218 return el; 219 220 var groupEl = el; 221 222 while (!this.hasClass(groupEl, this.options.showMessageClassOnParent)) { 223 groupEl = groupEl.parentNode; 224 225 if (groupEl === null) { 226 return el; 227 } 228 } 229 230 return groupEl; 231} 232 233LiveForm.getMessageElement = function(el) { 234 var id = el.getAttribute('data-lfv-message-id'); 235 if (!id) { 236 // Id is not specified yet, let's create a new one 237 id = el.id + this.options.messageIdPostfix; 238 239 var i = 0; 240 while (document.getElementById(id)) { 241 // We want unique ID which doesn't exist yet 242 id = el.id + this.options.messageIdPostfix + '_' + ++i; 243 } 244 245 // Remember this id for next use 246 el.setAttribute('data-lfv-message-id', id); 247 } 248 249 var messageEl = document.getElementById(id); 250 if (!messageEl) { 251 // Message element doesn't exist, lets create a new one 252 messageEl = document.createElement(this.options.messageTag); 253 messageEl.id = id; 254 if (el.style.display == 'none' && !this.hasClass(el, this.options.enableHiddenMessageClass)) { 255 messageEl.style.display = 'none'; 256 } 257 258 var parentEl = this.getMessageParent(el); 259 if (parentEl) { 260 parentEl.appendChild(messageEl); 261 } 262 } 263 264 return messageEl; 265}; 266 267LiveForm.getMessageParent = function(el) { 268 var parentEl = el.parentNode; 269 270 // Don't append error message to radio/checkbox input's label, but along label 271 if (el.type) { 272 var type = el.type.toLowerCase(); 273 if ((type == 'checkbox' || type == 'radio') && parentEl.tagName == 'LABEL') { 274 parentEl = parentEl.parentNode; 275 } 276 } 277 278 return parentEl; 279} 280 281LiveForm.addClass = function(el, className) { 282 if (!el.className) { 283 el.className = className; 284 } else if (!this.hasClass(el, className)) { 285 el.className += ' ' + className; 286 } 287}; 288 289LiveForm.hasClass = function(el, className) { 290 if (el.className) 291 return el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')); 292 return false; 293}; 294 295LiveForm.removeClass = function(el, className) { 296 if (this.hasClass(el, className)) { 297 var reg = new RegExp('(\\s|^)'+ className + '(\\s|$)'); 298 var m = el.className.match(reg); 299 el.className = el.className.replace(reg, (m[1] == ' ' && m[2] == ' ') ? ' ' : ''); 300 } 301}; 302 303LiveForm.getFormProperty = function(form, propertyName) { 304 if (form == null || this.forms[form.id] == null) 305 return false; 306 307 return this.forms[form.id][propertyName]; 308}; 309 310LiveForm.setFormProperty = function(form, propertyName, value) { 311 if (form == null) 312 return; 313 314 if (this.forms[form.id] == null) 315 this.forms[form.id] = {}; 316 317 this.forms[form.id][propertyName] = value; 318}; 319 320//////////////////////////// modified netteForms.js /////////////////////////////////// 321 322/** 323 * NetteForms - simple form validation. 324 * 325 * This file is part of the Nette Framework (http://nette.org) 326 * Copyright (c) 2004 David Grudl (http://davidgrudl.com) 327 */ 328 329(function(global, factory) { 330 if (typeof define === 'function' && define.amd) { 331 define(function() { 332 return factory(global); 333 }); 334 } else if (typeof module === 'object' && typeof module.exports === 'object') { 335 module.exports = factory(global); 336 } else { 337 global.Nette = factory(global); 338 global.Nette.initOnLoad(); 339 } 340 341}(typeof window !== 'undefined' ? window : this, function(window) { 342 343'use strict'; 344 345var Nette = {}; 346 347/** 348 * Attaches a handler to an event for the element. 349 */ 350Nette.addEvent = function(element, on, callback) { 351 var original = element['on' + on]; 352 element['on' + on] = function() { 353 if (typeof original === 'function' && original.apply(element, arguments) === false) { 354 return false; 355 } 356 return callback.apply(element, arguments); 357 }; 358}; 359 360 361/** 362 * Returns the value of form element. 363 */ 364Nette.getValue = function(elem) { 365 var i; 366 if (!elem) { 367 return null; 368 369 } else if (!elem.tagName) { // RadioNodeList, HTMLCollection, array 370 return elem[0] ? Nette.getValue(elem[0]) : null; 371 372 } else if (elem.type === 'radio') { 373 var elements = elem.form.elements; // prevents problem with name 'item' or 'namedItem' 374 for (i = 0; i < elements.length; i++) { 375 if (elements[i].name === elem.name && elements[i].checked) { 376 return elements[i].value; 377 } 378 } 379 return null; 380 381 } else if (elem.type === 'file') { 382 return elem.files || elem.value; 383 384 } else if (elem.tagName.toLowerCase() === 'select') { 385 var index = elem.selectedIndex, 386 options = elem.options, 387 values = []; 388 389 if (elem.type === 'select-one') { 390 return index < 0 ? null : options[index].value; 391 } 392 393 for (i = 0; i < options.length; i++) { 394 if (options[i].selected) { 395 values.push(options[i].value); 396 } 397 } 398 return values; 399 400 } else if (elem.name && elem.name.match(/\[\]$/)) { // multiple elements [] 401 var elements = elem.form.elements[elem.name].tagName ? [elem] : elem.form.elements[elem.name], 402 values = []; 403 404 for (i = 0; i < elements.length; i++) { 405 if (elements[i].type !== 'checkbox' || elements[i].checked) { 406 values.push(elements[i].value); 407 } 408 } 409 return values; 410 411 } else if (elem.type === 'checkbox') { 412 return elem.checked; 413 414 } else if (elem.tagName.toLowerCase() === 'textarea') { 415 return elem.value.replace("\r", ''); 416 417 } else { 418 return elem.value.replace("\r", '').replace(/^\s+|\s+$/g, ''); 419 } 420}; 421 422 423/** 424 * Returns the effective value of form element. 425 */ 426Nette.getEffectiveValue = function(elem) { 427 var val = Nette.getValue(elem); 428 if (elem.getAttribute) { 429 if (val === elem.getAttribute('data-nette-empty-value')) { 430 val = ''; 431 } 432 } 433 return val; 434}; 435 436 437/** 438 * Validates form element against given rules. 439 */ 440Nette.validateControl = function(elem, rules, onlyCheck, value) { 441 elem = elem.tagName ? elem : elem[0]; // RadioNodeList 442 rules = rules || Nette.parseJSON(elem.getAttribute('data-nette-rules')); 443 value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; 444 445 for (var id = 0, len = rules.length; id < len; id++) { 446 var rule = rules[id], 447 op = rule.op.match(/(~)?([^?]+)/), 448 curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; 449 450 if (!curElem) { 451 continue; 452 } 453 454 rule.neg = op[1]; 455 rule.op = op[2]; 456 rule.condition = !!rule.rules; 457 curElem = curElem.tagName ? curElem : curElem[0]; // RadioNodeList 458 459 var curValue = elem === curElem ? value : {value: Nette.getEffectiveValue(curElem)}, 460 success = Nette.validateRule(curElem, rule.op, rule.arg, curValue); 461 462 if (success === null) { 463 continue; 464 } else if (rule.neg) { 465 success = !success; 466 } 467 468 if (rule.condition && success) { 469 if (!Nette.validateControl(elem, rule.rules, onlyCheck, value)) { 470 return false;
471 } 472 } else if (!rule.condition && !success) { 473 if (Nette.isDisabled(curElem)) { 474 continue; 475 } 476 if (!onlyCheck) { 477 var arr = Nette.isArray(rule.arg) ? rule.arg : [rule.arg], 478 message = rule.msg.replace(/%(value|\d+)/g, function(foo, m) { 479 return Nette.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control)); 480 }); 481 Nette.addError(curElem, message); 482 } 483 return false; 484 } 485 } 486 487 // LiveForm: addition 488 if (!onlyCheck) { 489 LiveForm.removeError(elem); 490 } 491 492 return true; 493}; 494 495 496/** 497 * Validates whole form. 498 */ 499Nette.validateForm = function(sender) { 500 var form = sender.form || sender, 501 scope = false; 502 503 // LiveForm: addition 504 LiveForm.setFormProperty(form, "hasError", false); 505 506 if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) { 507 var scopeArr = Nette.parseJSON(form['nette-submittedBy'].getAttribute('data-nette-validation-scope')); 508 if (scopeArr.length) { 509 scope = new RegExp('^(' + scopeArr.join('-|') + '-)'); 510 } else { 511 return true; 512 } 513 } 514 515 // LiveForm: addition 516 var success = true; 517 518 var radios = {}, i, elem; 519 520 for (i = 0; i < form.elements.length; i++) { 521 elem = form.elements[i]; 522 523 if (elem.tagName && !(elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1})) { 524 continue; 525 526 } else if (elem.type === 'radio') { 527 if (radios[elem.name]) { 528 continue; 529 } 530 radios[elem.name] = true; 531 } 532 533 if ((scope && !elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) || Nette.isDisabled(elem)) { 534 continue; 535 } 536 537 if (!Nette.validateControl(elem)) { 538 // LiveForm: change 539 success = false; 540 541 // LiveForm: addition 542 if (!LiveForm.options.showAllErrors) 543 break; 544 } 545 } 546 547 // LiveForm: change 548 return success; 549}; 550 551 552/** 553 * Check if input is disabled. 554 */ 555Nette.isDisabled = function(elem) { 556 if (elem.type === 'radio') { 557 for (var i = 0, elements = elem.form.elements; i < elements.length; i++) { 558 if (elements[i].name === elem.name && !elements[i].disabled) { 559 return false; 560 } 561 } 562 return true; 563 } 564 return elem.disabled; 565}; 566 567 568/** 569 * Display error message. 570 */ 571Nette.addError = function(elem, message) { 572 // LiveForm: addition 573 var noLiveValidation = LiveForm.hasClass(elem, LiveForm.options.disableLiveValidationClass); 574 // User explicitly disabled live-validation so we want to show simple alerts 575 if (noLiveValidation) { 576 // notify errors for elements with disabled live validation (but only errors and not during onLoadValidation) 577 if (message && !LiveForm.getFormProperty(elem.form, "hasError") && !LiveForm.getFormProperty(elem.form, "onLoadValidation")) { 578 alert(message); 579 } 580 } 581 if (elem.focus && !LiveForm.getFormProperty(elem.form, "hasError")) { 582 if (!LiveForm.focusing) { 583 LiveForm.focusing = true; 584 elem.focus(); 585 setTimeout(function() { LiveForm.focusing = false; }, 10); 586 } 587 } 588 if (!noLiveValidation) { 589 LiveForm.addError(elem, message); 590 } 591 592 // LiveForm: original netteForms.js code 593 /* 594 if (message) { 595 alert(message); 596 } 597 if (elem.focus) { 598 elem.focus(); 599 } 600 */ 601}; 602 603 604/** 605 * Expand rule argument. 606 */ 607Nette.expandRuleArgument = function(form, arg) { 608 if (arg && arg.control) { 609 arg = Nette.getEffectiveValue(form.elements.namedItem(arg.control)); 610 } 611 return arg; 612}; 613 614 615/** 616 * Validates single rule. 617 */ 618Nette.validateRule = function(elem, op, arg, value) { 619 value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; 620 621 if (op.charAt(0) === ':') { 622 op = op.substr(1); 623 } 624 op = op.replace('::', '_'); 625 op = op.replace(/\\/g, ''); 626 627 var arr = Nette.isArray(arg) ? arg.slice(0) : [arg]; 628 for (var i = 0, len = arr.length; i < len; i++) { 629 arr[i] = Nette.expandRuleArgument(elem.form, arr[i]); 630 } 631 return Nette.validators[op] 632 ? Nette.validators[op](elem, Nette.isArray(arg) ? arr : arr[0], value.value, value) 633 : null; 634}; 635 636 637Nette.validators = { 638 filled: function(elem, arg, val) { 639 return val !== '' && val !== false && val !== null 640 && (!Nette.isArray(val) || !!val.length) 641 && (!window.FileList || !(val instanceof window.FileList) || val.length); 642 }, 643 644 blank: function(elem, arg, val) { 645 return !Nette.validators.filled(elem, arg, val); 646 }, 647 648 valid: function(elem, arg, val) { 649 return Nette.validateControl(elem, null, true); 650 }, 651 652 equal: function(elem, arg, val) { 653 if (arg === undefined) { 654 return null; 655 } 656 val = Nette.isArray(val) ? val : [val]; 657 arg = Nette.isArray(arg) ? arg : [arg]; 658 loop: 659 for (var i1 = 0, len1 = val.length; i1 < len1; i1++) { 660 for (var i2 = 0, len2 = arg.length; i2 < len2; i2++) { 661 /* jshint eqeqeq: false */ 662 if (val[i1] == arg[i2]) { 663 continue loop; 664 } 665 } 666 return false;
667 } 668 return true; 669 }, 670 671 notEqual: function(elem, arg, val) { 672 return arg === undefined ? null : !Nette.validators.equal(elem, arg, val); 673 }, 674 675 minLength: function(elem, arg, val) { 676 return val.length >= arg; 677 }, 678 679 maxLength: function(elem, arg, val) { 680 return val.length <= arg; 681 }, 682 683 length: function(elem, arg, val) { 684 arg = Nette.isArray(arg) ? arg : [arg, arg]; 685 return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]); 686 }, 687 688 email: function(elem, arg, val) { 689 return (/^("([ !\x23-\x5B\x5D-\x7E]*|\\[ -~])+"|[-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); 690 }, 691 692 url: function(elem, arg, val, value) { 693 if (!(/^[a-z\d+.-]+:/).test(val)) { 694 val = 'http://' + val; 695 } 696 if ((/^https?:\/\/([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)) { 697 value.value = val; 698 return true; 699 } 700 return false; 701 }, 702 703 regexp: function(elem, arg, val) { 704 var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false; 705 try { 706 return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val); 707 } catch (e) {} 708 }, 709 710 pattern: function(elem, arg, val) { 711 try { 712 return typeof arg === 'string' ? (new RegExp('^(' + arg + ')$')).test(val) : null; 713 } catch (e) {} 714 }, 715 716 integer: function(elem, arg, val) { 717 return (/^-?[0-9]+$/).test(val); 718 }, 719 720 'float': function(elem, arg, val, value) { 721 val = val.replace(' ', '').replace(',', '.'); 722 if ((/^-?[0-9]*[.,]?[0-9]+$/).test(val)) { 723 value.value = val; 724 return true; 725 } 726 return false; 727 }, 728 729 min: function(elem, arg, val) { 730 return Nette.validators.range(elem, [arg, null], val); 731 }, 732 733 max: function(elem, arg, val) { 734 return Nette.validators.range(elem, [null, arg], val); 735 }, 736 737 range: function(elem, arg, val) { 738 return Nette.isArray(arg) ? 739 ((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null; 740 }, 741 742 submitted: function(elem, arg, val) { 743 return elem.form['nette-submittedBy'] === elem; 744 }, 745 746 fileSize: function(elem, arg, val) { 747 if (window.FileList) { 748 for (var i = 0; i < val.length; i++) { 749 if (val[i].size > arg) { 750 return false; 751 } 752 } 753 } 754 return true; 755 }, 756 757 image: function (elem, arg, val) { 758 if (window.FileList && val instanceof window.FileList) { 759 for (var i = 0; i < val.length; i++) { 760 var type = val[i].type; 761 if (type && type !== 'image/gif' && type !== 'image/png' && type !== 'image/jpeg') { 762 return false; 763 } 764 } 765 } 766 return true; 767 } 768}; 769 770 771/** 772 * Process all toggles in form. 773 */ 774Nette.toggleForm = function(form, elem) { 775 var i; 776 Nette.toggles = {}; 777 for (i = 0; i < form.elements.length; i++) { 778 if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) { 779 Nette.toggleControl(form.elements[i], null, null, !elem); 780 } 781 } 782 783 for (i in Nette.toggles) { 784 Nette.toggle(i, Nette.toggles[i], elem); 785 } 786}; 787 788 789/** 790 * Process toggles on form element. 791 */ 792Nette.toggleControl = function(elem, rules, success, firsttime, value) { 793 rules = rules || Nette.parseJSON(elem.getAttribute('data-nette-rules')); 794 value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; 795 796 var has = false, 797 handled = [], 798 handler = function () { 799 Nette.toggleForm(elem.form, elem); 800 }, 801 curSuccess; 802 803 for (var id = 0, len = rules.length; id < len; id++) { 804 var rule = rules[id], 805 op = rule.op.match(/(~)?([^?]+)/), 806 curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; 807 808 if (!curElem) { 809 continue; 810 } 811 812 curSuccess = success; 813 if (success !== false) { 814 rule.neg = op[1]; 815 rule.op = op[2]; 816 var curValue = elem === curElem ? value : {value: Nette.getEffectiveValue(curElem)}; 817 curSuccess = Nette.validateRule(curElem, rule.op, rule.arg, curValue); 818 if (curSuccess === null) { 819 continue; 820 821 } else if (rule.neg) { 822 curSuccess = !curSuccess; 823 } 824 if (!rule.rules) { 825 success = curSuccess; 826 } 827 } 828 829 if ((rule.rules && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value)) || rule.toggle) { 830 has = true; 831 if (firsttime) { 832 var oldIE = !document.addEventListener, // IE < 9 833 name = curElem.tagName ? curElem.name : curElem[0].name, 834 els = curElem.tagName ? curElem.form.elements : curElem; 835 836 for (var i = 0; i < els.length; i++) { 837 if (els[i].name === name && !Nette.inArray(handled, els[i])) { 838 Nette.addEvent(els[i], oldIE && els[i].type in {checkbox: 1, radio: 1} ? 'click' : 'change', handler); 839 handled.push(els[i]); 840 } 841 } 842 } 843 for (var id2 in rule.toggle || []) { 844 if (Object.prototype.hasOwnProperty.call(rule.toggle, id2)) { 845 Nette.toggles[id2] = Nette.toggles[id2] || (rule.toggle[id2] ? curSuccess : !curSuccess); 846 } 847 } 848 } 849 } 850 return has; 851}; 852 853 854Nette.parseJSON = function(s) { 855 s = s || '[]'; 856 if (s.substr(0, 3) === '{op') { 857 return eval('[' + s + ']'); // backward compatibility 858 } 859 return window.JSON && window.JSON.parse ? JSON.parse(s) : eval(s); 860}; 861 862 863/** 864 * Displays or hides HTML element. 865 */ 866Nette.toggle = function(id, visible, srcElement) { 867 var elem = document.getElementById(id); 868 if (elem) { 869 elem.style.display = visible ? '' : 'none'; 870 } 871}; 872 873 874/** 875 * Setup handlers. 876 */ 877Nette.initForm = function(form) { 878 879 880 // LiveForm: addition 881 LiveForm.forms[form.id] = { 882 hasError: false, 883 onLoadValidation: false 884 }; 885 886 Nette.addEvent(form, 'submit', function(e) { 887 if (!Nette.validateForm(form)) { 888 if (e && e.stopPropagation) { 889 e.stopPropagation(); 890 } else if (window.event) { 891 event.cancelBubble = true; 892 } 893 return false;
894 } 895 }); 896 897 Nette.addEvent(form, 'click', function(e) { 898 e = e || event; 899 var target = e.target || e.srcElement; 900 form['nette-submittedBy'] = (target.type in {submit: 1, image: 1}) ? target : null; 901 }); 902 903 Nette.toggleForm(form); 904 905 // LiveForm: addition 906 for (var i = 0; i < form.elements.length; i++) { 907 LiveForm.setupHandlers(form.elements[i]); 908 LiveForm.processServerErrors(form.elements[i]); 909 } 910}; 911 912 913/** 914 * @private 915 */ 916Nette.initOnLoad = function() { 917 Nette.addEvent(window, 'load', function() { 918 for (var i = 0; i < document.forms.length; i++) { 919 var form = document.forms[i]; 920 for (var j = 0; j < form.elements.length; j++) { 921 if (form.elements[j].getAttribute('data-nette-rules')) { 922 Nette.initForm(form); 923 924 // LiveForm: addition 925 if (LiveForm.hasClass(form, 'validate-on-load')) { 926 // This is not so nice way, but I don't want to spoil validateForm, validateControl and other methods with another parameter 927 LiveForm.setFormProperty(form, "onLoadValidation", true); 928 Nette.validateForm(form); 929 LiveForm.setFormProperty(form, "onLoadValidation", false); 930 } 931 932 break; 933 } 934 } 935 } 936 }); 937}; 938 939 940/** 941 * Determines whether the argument is an array. 942 */ 943Nette.isArray = function(arg) { 944 return Object.prototype.toString.call(arg) === '[object Array]'; 945}; 946 947 948/** 949 * Search for a specified value within an array. 950 */ 951Nette.inArray = function(arr, val) { 952 if ([].indexOf) { 953 return arr.indexOf(val) > -1; 954 } else { 955 for (var i = 0; i < arr.length; i++) { 956 if (arr[i] === val) { 957 return true; 958 } 959 } 960 return false; 961 } 962}; 963 964 965/** 966 * Converts string to web safe characters [a-z0-9-] text. 967 */ 968Nette.webalize = function(s) { 969 s = s.toLowerCase(); 970 var res = '', i, ch; 971 for (i = 0; i < s.length; i++) { 972 ch = Nette.webalizeTable[s.charAt(i)]; 973 res += ch ? ch : s.charAt(i); 974 } 975 return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); 976}; 977 978Nette.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'}; 979 980return Nette; 981}));
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.