| 1 |
/** |
| 2 |
* Live Form Validation for Nette Forms 3.0 |
| 3 |
* |
| 4 |
* @author Robert Pösel, zakrava, Radek JeždÃk, MartyIX, David Grudl |
| 5 |
* @version 2.0-dev |
| 6 |
* @url https://github.com/Robyer/nette-live-form-validation/ |
| 7 |
*/ |
| 8 |
|
| 9 |
(function (global, factoryLiveValidation, factoryNetteForm) { |
| 10 |
|
| 11 |
if (typeof define === 'function' && define.amd) { |
| 12 |
// AMD |
| 13 |
define(function () { |
| 14 |
return { |
| 15 |
LiveForm: factoryLiveValidation(global), |
| 16 |
Nette: factoryNetteForm(global) |
| 17 |
} |
| 18 |
}) |
| 19 |
} else if (typeof exports === 'object') { |
| 20 |
// Node, CommonJS-like |
| 21 |
module.exports = { |
| 22 |
LiveForm: factoryLiveValidation(global), |
| 23 |
Nette: factoryNetteForm(global) |
| 24 |
} |
| 25 |
} else { |
| 26 |
global.LiveForm = factoryLiveValidation(global); |
| 27 |
// Browser globals (root is window) |
| 28 |
var init = !global.Nette || !global.Nette.noInit; |
| 29 |
global.Nette = factoryNetteForm(global); |
| 30 |
if (init) { |
| 31 |
global.Nette.initOnLoad(); |
| 32 |
} |
| 33 |
} |
| 34 |
|
| 35 |
|
| 36 |
}(typeof window !== 'undefined' ? window : this, function (window) { |
| 37 |
'use strict' |
| 38 |
|
| 39 |
|
| 40 |
var LiveForm = { |
| 41 |
options: { |
| 42 |
// CSS class of control's parent where error/valid class should be added; or "false" to use control directly |
| 43 |
showMessageClassOnParent: 'form-group', |
| 44 |
|
| 45 |
// CSS class of control's parent where error/valid message should be added (fallback to direct parent if not found); or "false" to use control's direct parent |
| 46 |
messageParentClass: false, |
| 47 |
|
| 48 |
// CSS class for an invalid control |
| 49 |
controlErrorClass: 'has-error', |
| 50 |
|
| 51 |
// CSS class for a valid control |
| 52 |
controlValidClass: 'has-success', |
| 53 |
|
| 54 |
// CSS class for an error message |
| 55 |
messageErrorClass: 'help-block text-danger', |
| 56 |
|
| 57 |
// 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) |
| 58 |
enableHiddenMessageClass: 'show-hidden-error', |
| 59 |
|
| 60 |
// control with this CSS class will have disabled live validation |
| 61 |
disableLiveValidationClass: 'no-live-validation', |
| 62 |
|
| 63 |
// control with this CSS class will not show valid message |
| 64 |
disableShowValidClass: 'no-show-valid', |
| 65 |
|
| 66 |
// tag that will hold the error/valid message |
| 67 |
messageTag: 'span', |
| 68 |
|
| 69 |
// message element id = control id + this postfix |
| 70 |
messageIdPostfix: '_message', |
| 71 |
|
| 72 |
// show this html before error message itself |
| 73 |
messageErrorPrefix: ' <i class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></i> ', |
| 74 |
|
| 75 |
// show all errors when submitting form; or use "false" to show only first error |
| 76 |
showAllErrors: true, |
| 77 |
|
| 78 |
// show message when valid |
| 79 |
showValid: false, |
| 80 |
|
| 81 |
// delay in ms before validating on keyup/keydown; or use "false" to disable it |
| 82 |
wait: false, |
| 83 |
|
| 84 |
// vertical screen offset in px to scroll after focusing element with error (useful when using fixed navbar menu which may otherwise obscure the element in focus); or use "false" for default behavior |
| 85 |
focusScreenOffsetY: false |
| 86 |
}, |
| 87 |
|
| 88 |
forms: {} |
| 89 |
}; |
| 90 |
|
| 91 |
LiveForm.setOptions = function (userOptions) { |
| 92 |
for (var prop in userOptions) { |
| 93 |
if (Object.prototype.hasOwnProperty.call(this.options, prop)) { |
| 94 |
this.options[prop] = userOptions[prop]; |
| 95 |
} |
| 96 |
} |
| 97 |
} |
| 98 |
|
| 99 |
// Allow setting options before loading the script just by creating global PacketeryLiveFormOptions object with options. |
| 100 |
if (typeof window.LiveFormOptions !== 'undefined') { |
| 101 |
LiveForm.setOptions(window.LiveFormOptions); |
| 102 |
} |
| 103 |
|
| 104 |
LiveForm.isSpecialKey = function (k) { |
| 105 |
// http://stackoverflow.com/questions/7770561/jquery-javascript-reject-control-keys-on-keydown-event |
| 106 |
return (k == 20 /* Caps lock */ |
| 107 |
|| k == 16 /* Shift */ |
| 108 |
|| k == 9 /* Tab */ |
| 109 |
|| k == 27 /* Escape Key */ |
| 110 |
|| k == 17 /* Control Key */ |
| 111 |
|| k == 91 /* Windows Command Key */ |
| 112 |
|| k == 19 /* Pause Break */ |
| 113 |
|| k == 18 /* Alt Key */ |
| 114 |
|| k == 93 /* Right Click Point Key */ |
| 115 |
|| (k >= 35 && k <= 40) /* Home, End, Arrow Keys */ |
| 116 |
|| k == 45 /* Insert Key */ |
| 117 |
|| (k >= 33 && k <= 34) /*Page Down, Page Up */ |
| 118 |
|| (k >= 112 && k <= 123) /* F1 - F12 */ |
| 119 |
|| (k >= 144 && k <= 145)); /* Num Lock, Scroll Lock */ |
| 120 |
} |
| 121 |
|
| 122 |
/** |
| 123 |
* Handlers for all the events that trigger validation |
| 124 |
* YOU CAN CHANGE these handlers (ie. to use jQuery events instead) |
| 125 |
*/ |
| 126 |
LiveForm.setupHandlers = function (el) { |
| 127 |
if (this.hasClass(el, this.options.disableLiveValidationClass)) |
| 128 |
return; |
| 129 |
|
| 130 |
// Check if element was already initialized |
| 131 |
if (el.getAttribute("data-lfv-initialized")) |
| 132 |
return; |
| 133 |
|
| 134 |
// Remember we initialized this element so we won't do it again |
| 135 |
el.setAttribute('data-lfv-initialized', 'true'); |
| 136 |
|
| 137 |
var handler = function (event) { |
| 138 |
event = event || window.event; |
| 139 |
Nette.validateControl(event.target ? event.target : event.srcElement); |
| 140 |
}; |
| 141 |
|
| 142 |
var self = this; |
| 143 |
|
| 144 |
el.addEventListener('change', handler); |
| 145 |
el.addEventListener('blur', handler); |
| 146 |
el.addEventListener('keydown', function (event) { |
| 147 |
if (!self.isSpecialKey(event.which) && (self.options.wait === false || self.options.wait >= 200)) { |
| 148 |
// Hide validation span tag. |
| 149 |
self.removeClass(self.getGroupElement(this), self.options.controlErrorClass); |
| 150 |
self.removeClass(self.getGroupElement(this), self.options.controlValidClass); |
| 151 |
|
| 152 |
var messageEl = self.getMessageElement(this); |
| 153 |
messageEl.innerHTML = ''; |
| 154 |
messageEl.className = ''; |
| 155 |
|
| 156 |
// Cancel timeout to run validation handler |
| 157 |
if (self.timeout) { |
| 158 |
clearTimeout(self.timeout); |
| 159 |
} |
| 160 |
} |
| 161 |
}); |
| 162 |
el.addEventListener('keyup', function (event) { |
| 163 |
if (self.options.wait !== false) { |
| 164 |
event = event || window.event; |
| 165 |
if (event.keyCode !== 9) { |
| 166 |
if (self.timeout) clearTimeout(self.timeout); |
| 167 |
self.timeout = setTimeout(function () { |
| 168 |
handler(event); |
| 169 |
}, self.options.wait); |
| 170 |
} |
| 171 |
} |
| 172 |
}); |
| 173 |
}; |
| 174 |
|
| 175 |
LiveForm.processServerErrors = function (el) { |
| 176 |
var messageEl = this.getMessageElement(el); |
| 177 |
var parentEl = this.getMessageParent(el); // This is parent element which contain the error elements |
| 178 |
|
| 179 |
var errors = []; |
| 180 |
|
| 181 |
// Find existing error elements by class (from server-validation) |
| 182 |
var errorEls = parentEl.getElementsByClassName(this.options.messageErrorClass); |
| 183 |
for (var i = errorEls.length - 1; i > -1; i--) { |
| 184 |
// Don't touch our main message element |
| 185 |
if (errorEls[i] == messageEl) |
| 186 |
continue; |
| 187 |
|
| 188 |
// Remove only direct children |
| 189 |
var errorParent = errorEls[i].parentNode; |
| 190 |
if (errorParent == parentEl) { |
| 191 |
errors.push(errorEls[i].outerHTML); |
| 192 |
errorParent.removeChild(errorEls[i]); |
| 193 |
} |
| 194 |
} |
| 195 |
|
| 196 |
// Wrap all server errors into one element |
| 197 |
if (errors.length > 0) { |
| 198 |
messageEl.innerHTML = errors.join(""); |
| 199 |
} |
| 200 |
}; |
| 201 |
|
| 202 |
LiveForm.addError = function (el, message) { |
| 203 |
// Ignore elements with disabled live validation |
| 204 |
if (this.hasClass(el, this.options.disableLiveValidationClass)) |
| 205 |
return; |
| 206 |
|
| 207 |
var groupEl = this.getGroupElement(el); |
| 208 |
this.setFormProperty(el.form, "hasError", true); |
| 209 |
this.addClass(groupEl, this.options.controlErrorClass); |
| 210 |
|
| 211 |
if (this.options.showValid) { |
| 212 |
this.removeClass(groupEl, this.options.controlValidClass); |
| 213 |
} |
| 214 |
|
| 215 |
if (!message) { |
| 216 |
message = ' '; |
| 217 |
} else { |
| 218 |
message = this.options.messageErrorPrefix + message; |
| 219 |
} |
| 220 |
|
| 221 |
var messageEl = this.getMessageElement(el); |
| 222 |
messageEl.innerHTML = message; |
| 223 |
messageEl.className = this.options.messageErrorClass; |
| 224 |
}; |
| 225 |
|
| 226 |
LiveForm.removeError = function (el) { |
| 227 |
// We don't want to remove any errors during onLoadValidation |
| 228 |
if (this.getFormProperty(el.form, "onLoadValidation")) |
| 229 |
return; |
| 230 |
|
| 231 |
var groupEl = this.getGroupElement(el); |
| 232 |
this.removeClass(groupEl, this.options.controlErrorClass); |
| 233 |
|
| 234 |
var id = el.getAttribute('data-lfv-message-id'); |
| 235 |
if (id) { |
| 236 |
var messageEl = this.getMessageElement(el); |
| 237 |
messageEl.innerHTML = ''; |
| 238 |
messageEl.className = ''; |
| 239 |
} |
| 240 |
|
| 241 |
if (this.options.showValid) { |
| 242 |
if (this.showValid(el)) |
| 243 |
this.addClass(groupEl, this.options.controlValidClass); |
| 244 |
else |
| 245 |
this.removeClass(groupEl, this.options.controlValidClass); |
| 246 |
} |
| 247 |
}; |
| 248 |
|
| 249 |
LiveForm.showValid = function (el) { |
| 250 |
if (el.type) { |
| 251 |
var type = el.type.toLowerCase(); |
| 252 |
if (type == 'checkbox' || type == 'radio') { |
| 253 |
return false; |
| 254 |
} |
| 255 |
} |
| 256 |
|
| 257 |
var rules = JSON.parse(el.getAttribute('data-nette-rules')); |
| 258 |
if (rules === null || rules.length == 0) { |
| 259 |
return false; |
| 260 |
} |
| 261 |
|
| 262 |
if (Nette.getEffectiveValue(el) == '') { |
| 263 |
return false; |
| 264 |
} |
| 265 |
|
| 266 |
if (this.hasClass(el, this.options.disableShowValidClass)) { |
| 267 |
return false; |
| 268 |
} |
| 269 |
|
| 270 |
return true; |
| 271 |
}; |
| 272 |
|
| 273 |
LiveForm.getGroupElement = function (el) { |
| 274 |
if (this.options.showMessageClassOnParent === false) |
| 275 |
return el; |
| 276 |
|
| 277 |
var groupEl = el; |
| 278 |
|
| 279 |
while (!this.hasClass(groupEl, this.options.showMessageClassOnParent)) { |
| 280 |
groupEl = groupEl.parentNode; |
| 281 |
|
| 282 |
if (groupEl === null) { |
| 283 |
return el; |
| 284 |
} |
| 285 |
} |
| 286 |
|
| 287 |
return groupEl; |
| 288 |
} |
| 289 |
|
| 290 |
LiveForm.getMessageId = function (el) { |
| 291 |
var tmp = el.id + this.options.messageIdPostfix; |
| 292 |
|
| 293 |
// For elements without ID, or multi elements (with same name), we must generate whole ID ourselves |
| 294 |
if (el.name && (!el.id || !el.form.elements[el.name].tagName)) { |
| 295 |
// Strip possible [] from name |
| 296 |
var name = el.name.match(/\[\]$/) ? el.name.match(/(.*)\[\]$/)[1] : el.name; |
| 297 |
// Generate new ID based on form ID, element name and messageIdPostfix from options |
| 298 |
tmp = (el.form.id ? el.form.id : 'frm') + '-' + name + this.options.messageIdPostfix; |
| 299 |
} |
| 300 |
|
| 301 |
// We want unique ID which doesn't exist yet |
| 302 |
var id = tmp, |
| 303 |
i = 0; |
| 304 |
while (document.getElementById(id)) { |
| 305 |
id = id + '_' + ++i; |
| 306 |
} |
| 307 |
|
| 308 |
return id; |
| 309 |
} |
| 310 |
|
| 311 |
LiveForm.getMessageElement = function (el) { |
| 312 |
// For multi elements (with same name) work only with first element attributes |
| 313 |
if (el.name && el.name.match(/\[\]$/)) { |
| 314 |
el = el.form.elements[el.name].tagName ? el : el.form.elements[el.name][0]; |
| 315 |
} |
| 316 |
|
| 317 |
var id = el.getAttribute('data-lfv-message-id'); |
| 318 |
if (!id) { |
| 319 |
// ID is not specified yet, let's create a new one |
| 320 |
id = this.getMessageId(el); |
| 321 |
|
| 322 |
// Remember this id for next use |
| 323 |
el.setAttribute('data-lfv-message-id', id); |
| 324 |
} |
| 325 |
|
| 326 |
var messageEl = document.getElementById(id); |
| 327 |
if (!messageEl) { |
| 328 |
// Message element doesn't exist, lets create a new one |
| 329 |
messageEl = document.createElement(this.options.messageTag); |
| 330 |
messageEl.id = id; |
| 331 |
if (el.style.display == 'none' && !this.hasClass(el, this.options.enableHiddenMessageClass)) { |
| 332 |
messageEl.style.display = 'none'; |
| 333 |
} |
| 334 |
|
| 335 |
var parentEl = this.getMessageParent(el); |
| 336 |
if (parentEl === el.parentNode) { |
| 337 |
parentEl.insertBefore(messageEl, el.nextSibling); |
| 338 |
} else if (parentEl) { |
| 339 |
typeof parentEl.append === 'function' ? parentEl.append(messageEl) : parentEl.appendChild(messageEl); |
| 340 |
} |
| 341 |
} |
| 342 |
|
| 343 |
return messageEl; |
| 344 |
}; |
| 345 |
|
| 346 |
LiveForm.getMessageParent = function (el) { |
| 347 |
var parentEl = el.parentNode; |
| 348 |
var parentFound = false; |
| 349 |
|
| 350 |
if (this.options.messageParentClass !== false) { |
| 351 |
parentFound = true; |
| 352 |
while (!this.hasClass(parentEl, this.options.messageParentClass)) { |
| 353 |
parentEl = parentEl.parentNode; |
| 354 |
|
| 355 |
if (parentEl === null) { |
| 356 |
// We didn't found wanted parent, so use element's direct parent |
| 357 |
parentEl = el.parentNode; |
| 358 |
parentFound = false; |
| 359 |
break; |
| 360 |
} |
| 361 |
} |
| 362 |
} |
| 363 |
|
| 364 |
// Don't append error message to radio/checkbox input's label, but along label |
| 365 |
if (el.type) { |
| 366 |
var type = el.type.toLowerCase(); |
| 367 |
if ((type == 'checkbox' || type == 'radio') && parentEl.tagName == 'LABEL') { |
| 368 |
parentEl = parentEl.parentNode; |
| 369 |
} |
| 370 |
} |
| 371 |
|
| 372 |
// For multi elements (with same name) use parent's parent as parent (if wanted one is not found) |
| 373 |
if (!parentFound && el.name && !el.form.elements[el.name].tagName) { |
| 374 |
parentEl = parentEl.parentNode; |
| 375 |
} |
| 376 |
|
| 377 |
return parentEl; |
| 378 |
} |
| 379 |
|
| 380 |
LiveForm.addClass = function (el, className) { |
| 381 |
if (!el.className) { |
| 382 |
el.className = className; |
| 383 |
} else if (!this.hasClass(el, className)) { |
| 384 |
el.className += ' ' + className; |
| 385 |
} |
| 386 |
}; |
| 387 |
|
| 388 |
LiveForm.hasClass = function (el, className) { |
| 389 |
if (el.className) |
| 390 |
return el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')); |
| 391 |
return false; |
| 392 |
}; |
| 393 |
|
| 394 |
LiveForm.removeClass = function (el, className) { |
| 395 |
if (this.hasClass(el, className)) { |
| 396 |
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)'); |
| 397 |
var m = el.className.match(reg); |
| 398 |
el.className = el.className.replace(reg, (m[1] == ' ' && m[2] == ' ') ? ' ' : ''); |
| 399 |
} |
| 400 |
}; |
| 401 |
|
| 402 |
LiveForm.getFormProperty = function (form, propertyName) { |
| 403 |
if (form == null || this.forms[form.id] == null) |
| 404 |
return false; |
| 405 |
|
| 406 |
return this.forms[form.id][propertyName]; |
| 407 |
}; |
| 408 |
|
| 409 |
LiveForm.setFormProperty = function (form, propertyName, value) { |
| 410 |
if (form == null) |
| 411 |
return; |
| 412 |
|
| 413 |
if (this.forms[form.id] == null) |
| 414 |
this.forms[form.id] = {}; |
| 415 |
|
| 416 |
this.forms[form.id][propertyName] = value; |
| 417 |
}; |
| 418 |
|
| 419 |
return LiveForm; |
| 420 |
|
| 421 |
//////////////////////////// modified netteForms.js /////////////////////////////////// |
| 422 |
|
| 423 |
/** |
| 424 |
* NetteForms - simple form validation. |
| 425 |
* |
| 426 |
* This file is part of the Nette Framework (https://nette.org) |
| 427 |
* Copyright (c) 2004 David Grudl (https://davidgrudl.com) |
| 428 |
*/ |
| 429 |
/* |
| 430 |
(function(global, factory) { |
| 431 |
if (!global.JSON) { |
| 432 |
return; |
| 433 |
} |
| 434 |
if (typeof define === 'function' && define.amd) { |
| 435 |
define(function() { |
| 436 |
return factory(global); |
| 437 |
}); |
| 438 |
} else if (typeof module === 'object' && typeof module.exports === 'object') { |
| 439 |
module.exports = factory(global); |
| 440 |
} else { |
| 441 |
var init = !global.Nette || !global.Nette.noInit; |
| 442 |
global.Nette = factory(global); |
| 443 |
if (init) { |
| 444 |
global.Nette.initOnLoad(); |
| 445 |
} |
| 446 |
} |
| 447 |
}(typeof window !== 'undefined' ? window : this, function(window) { |
| 448 |
*/ |
| 449 |
|
| 450 |
}, function (window) { |
| 451 |
'use strict'; |
| 452 |
|
| 453 |
var Nette = {}; |
| 454 |
var preventFiltering = {}; |
| 455 |
var formToggles = {}; |
| 456 |
|
| 457 |
// LiveForm: original netteForms.js code |
| 458 |
// Nette.formErrors = []; |
| 459 |
Nette.version = '3.0'; |
| 460 |
|
| 461 |
|
| 462 |
/** |
| 463 |
* Function to execute when the DOM is fully loaded. |
| 464 |
* @private |
| 465 |
*/ |
| 466 |
Nette.onDocumentReady = function (callback) { |
| 467 |
if (document.readyState !== 'loading') { |
| 468 |
callback.call(this); |
| 469 |
} else { |
| 470 |
document.addEventListener('DOMContentLoaded', callback); |
| 471 |
} |
| 472 |
}; |
| 473 |
|
| 474 |
|
| 475 |
/** |
| 476 |
* Attaches a handler to an event for the element. |
| 477 |
*/ |
| 478 |
Nette.addEvent = function (element, on, callback) { |
| 479 |
if (element.addEventListener) { |
| 480 |
element.addEventListener(on, callback); |
| 481 |
} else if (on === 'DOMContentLoaded') { |
| 482 |
element.attachEvent('onreadystatechange', function () { |
| 483 |
if (element.readyState === 'complete') { |
| 484 |
callback.call(this); |
| 485 |
} |
| 486 |
}); |
| 487 |
} else { |
| 488 |
element.attachEvent('on' + on, getHandler(callback)); |
| 489 |
} |
| 490 |
}; |
| 491 |
|
| 492 |
/** |
| 493 |
* Returns the value of form element. |
| 494 |
*/ |
| 495 |
Nette.getValue = function (elem) { |
| 496 |
var i; |
| 497 |
if (!elem) { |
| 498 |
return null; |
| 499 |
|
| 500 |
} else if (!elem.tagName) { // RadioNodeList, HTMLCollection, array |
| 501 |
return elem[0] ? Nette.getValue(elem[0]) : null; |
| 502 |
|
| 503 |
} else if (elem.type === 'radio') { |
| 504 |
var elements = elem.form.elements; // prevents problem with name 'item' or 'namedItem' |
| 505 |
for (i = 0; i < elements.length; i++) { |
| 506 |
if (elements[i].name === elem.name && elements[i].checked) { |
| 507 |
return elements[i].value; |
| 508 |
} |
| 509 |
} |
| 510 |
return null; |
| 511 |
|
| 512 |
} else if (elem.type === 'file') { |
| 513 |
return elem.files || elem.value; |
| 514 |
|
| 515 |
} else if (elem.tagName.toLowerCase() === 'select') { |
| 516 |
var index = elem.selectedIndex, |
| 517 |
options = elem.options, |
| 518 |
values = []; |
| 519 |
|
| 520 |
if (elem.type === 'select-one') { |
| 521 |
return index < 0 ? null : options[index].value; |
| 522 |
} |
| 523 |
|
| 524 |
for (i = 0; i < options.length; i++) { |
| 525 |
if (options[i].selected) { |
| 526 |
values.push(options[i].value); |
| 527 |
} |
| 528 |
} |
| 529 |
return values; |
| 530 |
|
| 531 |
} else if (elem.name && elem.name.match(/\[\]$/)) { // multiple elements [] |
| 532 |
elements = elem.form.elements[elem.name].tagName ? [elem] : elem.form.elements[elem.name]; |
| 533 |
values = []; |
| 534 |
|
| 535 |
for (i = 0; i < elements.length; i++) { |
| 536 |
// LiveForm: original netteForms.js code |
| 537 |
/*if (elements[i].type !== 'checkbox' || elements[i].checked) { |
| 538 |
values.push(elements[i].value); |
| 539 |
}*/ |
| 540 |
// LiveForm: addition |
| 541 |
var value = elements[i].value; |
| 542 |
if (elements[i].type === 'checkbox' && elements[i].checked) { |
| 543 |
values.push(value); |
| 544 |
} else if (elements[i].type !== 'checkbox' && value !== '') { |
| 545 |
values.push(value); |
| 546 |
} |
| 547 |
} |
| 548 |
return values; |
| 549 |
|
| 550 |
} else if (elem.type === 'checkbox') { |
| 551 |
return elem.checked; |
| 552 |
|
| 553 |
} else if (elem.tagName.toLowerCase() === 'textarea') { |
| 554 |
return elem.value.replace('\r', ''); |
| 555 |
|
| 556 |
} else { |
| 557 |
return elem.value.replace('\r', '').replace(/^\s+|\s+$/g, ''); |
| 558 |
} |
| 559 |
}; |
| 560 |
|
| 561 |
|
| 562 |
/** |
| 563 |
* Returns the effective value of form element. |
| 564 |
*/ |
| 565 |
Nette.getEffectiveValue = function (elem, filter) { |
| 566 |
var val = Nette.getValue(elem); |
| 567 |
if (elem.getAttribute) { |
| 568 |
if (val === elem.getAttribute('data-nette-empty-value')) { |
| 569 |
val = ''; |
| 570 |
} |
| 571 |
} |
| 572 |
if (filter && preventFiltering[elem.name] === undefined) { |
| 573 |
preventFiltering[elem.name] = true; |
| 574 |
var ref = {value: val}; |
| 575 |
Nette.validateControl(elem, null, true, ref); |
| 576 |
val = ref.value; |
| 577 |
delete preventFiltering[elem.name]; |
| 578 |
} |
| 579 |
return val; |
| 580 |
}; |
| 581 |
|
| 582 |
|
| 583 |
/** |
| 584 |
* Validates form element against given rules. |
| 585 |
*/ |
| 586 |
Nette.validateControl = function (elem, rules, onlyCheck, value, emptyOptional) { |
| 587 |
// LiveForm: addition |
| 588 |
// Fix for CheckboxList - validation rules are present always only on first input |
| 589 |
if (elem.name && elem.name.match(/\[\]$/) && elem.type.toLowerCase() == 'checkbox') { |
| 590 |
elem = elem.form.elements[elem.name].tagName ? elem : elem.form.elements[elem.name][0]; |
| 591 |
} |
| 592 |
|
| 593 |
elem = elem.tagName ? elem : elem[0]; // RadioNodeList |
| 594 |
rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); |
| 595 |
value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; |
| 596 |
emptyOptional = emptyOptional || !Nette.validateRule(elem, ':filled', null, value); |
| 597 |
|
| 598 |
for (var id = 0, len = rules.length; id < len; id++) { |
| 599 |
var rule = rules[id], |
| 600 |
op = rule.op.match(/(~)?([^?]+)/), |
| 601 |
curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; |
| 602 |
|
| 603 |
rule.neg = op[1]; |
| 604 |
rule.op = op[2]; |
| 605 |
rule.condition = !!rule.rules; |
| 606 |
|
| 607 |
if (!curElem) { |
| 608 |
continue; |
| 609 |
} else if (emptyOptional && !rule.condition && rule.op !== ':filled') { |
| 610 |
continue; |
| 611 |
} |
| 612 |
|
| 613 |
curElem = curElem.tagName ? curElem : curElem[0]; // RadioNodeList |
| 614 |
var success = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); |
| 615 |
|
| 616 |
if (success === null) { |
| 617 |
continue; |
| 618 |
} else if (rule.neg) { |
| 619 |
success = !success; |
| 620 |
} |
| 621 |
|
| 622 |
if (rule.condition && success) { |
| 623 |
if (!Nette.validateControl(elem, rule.rules, onlyCheck, value, rule.op === ':blank' ? false : emptyOptional)) { |
| 624 |
return false; |
| 625 |
} |
| 626 |
} else if (!rule.condition && !success) { |
| 627 |
if (Nette.isDisabled(curElem)) { |
| 628 |
continue; |
| 629 |
} |
| 630 |
if (!onlyCheck) { |
| 631 |
var arr = Array.isArray(rule.arg) ? rule.arg : [rule.arg], |
| 632 |
message = rule.msg.replace(/%(value|\d+)/g, function (foo, m) { |
| 633 |
return Nette.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control)); |
| 634 |
}); |
| 635 |
Nette.addError(curElem, message); |
| 636 |
} |
| 637 |
return false; |
| 638 |
} |
| 639 |
} |
| 640 |
|
| 641 |
if (elem.type === 'number' && !elem.validity.valid) { |
| 642 |
if (!onlyCheck) { |
| 643 |
Nette.addError(elem, 'Please enter a valid value.'); |
| 644 |
} |
| 645 |
return false; |
| 646 |
} |
| 647 |
|
| 648 |
// LiveForm: addition |
| 649 |
if (!onlyCheck) { |
| 650 |
LiveForm.removeError(elem); |
| 651 |
} |
| 652 |
|
| 653 |
return true; |
| 654 |
}; |
| 655 |
|
| 656 |
|
| 657 |
/** |
| 658 |
* Validates whole form. |
| 659 |
*/ |
| 660 |
Nette.validateForm = function (sender, onlyCheck) { |
| 661 |
var form = sender.form || sender, |
| 662 |
scope = false; |
| 663 |
|
| 664 |
// LiveForm: addition |
| 665 |
LiveForm.setFormProperty(form, "hasError", false); |
| 666 |
|
| 667 |
// LiveForm: original netteForms.js code |
| 668 |
// Nette.formErrors = []; |
| 669 |
|
| 670 |
if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) { |
| 671 |
var scopeArr = JSON.parse(form['nette-submittedBy'].getAttribute('data-nette-validation-scope') || '[]'); |
| 672 |
if (scopeArr.length) { |
| 673 |
scope = new RegExp('^(' + scopeArr.join('-|') + '-)'); |
| 674 |
} else { |
| 675 |
// LiveForm: original netteForms.js code |
| 676 |
// Nette.showFormErrors(form, []); |
| 677 |
return true; |
| 678 |
} |
| 679 |
} |
| 680 |
|
| 681 |
var radios = {}, i, elem; |
| 682 |
// LiveForm: addition |
| 683 |
var success = true; |
| 684 |
|
| 685 |
for (i = 0; i < form.elements.length; i++) { |
| 686 |
elem = form.elements[i]; |
| 687 |
|
| 688 |
if (elem.tagName && !(elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1})) { |
| 689 |
continue; |
| 690 |
|
| 691 |
} else if (elem.type === 'radio') { |
| 692 |
if (radios[elem.name]) { |
| 693 |
continue; |
| 694 |
} |
| 695 |
radios[elem.name] = true; |
| 696 |
} |
| 697 |
|
| 698 |
if ((scope && !elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) || Nette.isDisabled(elem)) { |
| 699 |
continue; |
| 700 |
} |
| 701 |
|
| 702 |
// LiveForm: addition |
| 703 |
success = Nette.validateControl(elem) && success; |
| 704 |
if (!success && !LiveForm.options.showAllErrors) { |
| 705 |
break; |
| 706 |
} |
| 707 |
// LiveForm: original netteForms.js code |
| 708 |
/*if (!Nette.validateControl(elem, null, onlyCheck) && !Nette.formErrors.length) { |
| 709 |
return false; |
| 710 |
}*/ |
| 711 |
} |
| 712 |
|
| 713 |
// LiveForm: change |
| 714 |
return success; |
| 715 |
|
| 716 |
// LiveForm: original netteForms.js code |
| 717 |
/*var success = !Nette.formErrors.length; |
| 718 |
Nette.showFormErrors(form, Nette.formErrors); |
| 719 |
return success;*/ |
| 720 |
}; |
| 721 |
|
| 722 |
|
| 723 |
/** |
| 724 |
* Check if input is disabled. |
| 725 |
*/ |
| 726 |
Nette.isDisabled = function (elem) { |
| 727 |
if (elem.type === 'radio') { |
| 728 |
for (var i = 0, elements = elem.form.elements; i < elements.length; i++) { |
| 729 |
if (elements[i].name === elem.name && !elements[i].disabled) { |
| 730 |
return false; |
| 731 |
} |
| 732 |
} |
| 733 |
return true; |
| 734 |
} |
| 735 |
return elem.disabled; |
| 736 |
}; |
| 737 |
|
| 738 |
|
| 739 |
// LiveForm: change |
| 740 |
/** |
| 741 |
* Display error message. |
| 742 |
*/ |
| 743 |
Nette.addError = function (elem, message) { |
| 744 |
// LiveForm: addition |
| 745 |
var noLiveValidation = LiveForm.hasClass(elem, LiveForm.options.disableLiveValidationClass); |
| 746 |
// User explicitly disabled live-validation so we want to show simple alerts |
| 747 |
if (noLiveValidation) { |
| 748 |
// notify errors for elements with disabled live validation (but only errors and not during onLoadValidation) |
| 749 |
if (message && !LiveForm.getFormProperty(elem.form, "hasError") && !LiveForm.getFormProperty(elem.form, "onLoadValidation")) { |
| 750 |
alert(message); |
| 751 |
} |
| 752 |
} |
| 753 |
if (elem.focus && !LiveForm.getFormProperty(elem.form, "hasError")) { |
| 754 |
if (!LiveForm.focusing) { |
| 755 |
LiveForm.focusing = true; |
| 756 |
elem.focus(); |
| 757 |
setTimeout(function () { |
| 758 |
LiveForm.focusing = false; |
| 759 |
|
| 760 |
// Scroll by defined offset (if enabled) |
| 761 |
// NOTE: We use it with setTimetout because IE9 doesn't always catch instant scrollTo request |
| 762 |
var focusOffsetY = LiveForm.options.focusScreenOffsetY; |
| 763 |
if (focusOffsetY !== false && elem.getBoundingClientRect().top < focusOffsetY) { |
| 764 |
window.scrollBy(0, elem.getBoundingClientRect().top - focusOffsetY); |
| 765 |
} |
| 766 |
}, 10); |
| 767 |
} |
| 768 |
} |
| 769 |
if (!noLiveValidation) { |
| 770 |
LiveForm.addError(elem, message); |
| 771 |
} |
| 772 |
}; |
| 773 |
|
| 774 |
|
| 775 |
// LiveForm: original netteForms.js code |
| 776 |
/*/!** |
| 777 |
* Adds error message to the queue. |
| 778 |
*!/ |
| 779 |
Nette.addError = function(elem, message) { |
| 780 |
Nette.formErrors.push({ |
| 781 |
element: elem, |
| 782 |
message: message |
| 783 |
}); |
| 784 |
};*/ |
| 785 |
|
| 786 |
|
| 787 |
// LiveForm: original netteForms.js code |
| 788 |
/*/!** |
| 789 |
* Display error messages. |
| 790 |
*!/ |
| 791 |
Nette.showFormErrors = function(form, errors) { |
| 792 |
var messages = [], |
| 793 |
focusElem; |
| 794 |
for (var i = 0; i < errors.length; i++) { |
| 795 |
var elem = errors[i].element, |
| 796 |
message = errors[i].message; |
| 797 |
if (messages.indexOf(message) < 0) { |
| 798 |
messages.push(message); |
| 799 |
if (!focusElem && elem.focus) { |
| 800 |
focusElem = elem; |
| 801 |
} |
| 802 |
} |
| 803 |
} |
| 804 |
if (messages.length) { |
| 805 |
alert(messages.join('\n')); |
| 806 |
if (focusElem) { |
| 807 |
focusElem.focus(); |
| 808 |
} |
| 809 |
} |
| 810 |
};*/ |
| 811 |
|
| 812 |
|
| 813 |
/** |
| 814 |
* Validates single rule. |
| 815 |
*/ |
| 816 |
Nette.validateRule = function (elem, op, arg, value) { |
| 817 |
value = value === undefined ? {value: Nette.getEffectiveValue(elem, true)} : value; |
| 818 |
|
| 819 |
if (op.charAt(0) === ':') { |
| 820 |
op = op.substr(1); |
| 821 |
} |
| 822 |
op = op.replace('::', '_'); |
| 823 |
op = op.replace(/\\/g, ''); |
| 824 |
|
| 825 |
var arr = Array.isArray(arg) ? arg.slice(0) : [arg]; |
| 826 |
for (var i = 0, len = arr.length; i < len; i++) { |
| 827 |
if (arr[i] && arr[i].control) { |
| 828 |
var control = elem.form.elements.namedItem(arr[i].control); |
| 829 |
arr[i] = control === elem ? value.value : Nette.getEffectiveValue(control, true); |
| 830 |
} |
| 831 |
} |
| 832 |
|
| 833 |
return Nette.validators[op] |
| 834 |
? Nette.validators[op](elem, Array.isArray(arg) ? arr : arr[0], value.value, value) |
| 835 |
: null; |
| 836 |
}; |
| 837 |
|
| 838 |
|
| 839 |
Nette.validators = { |
| 840 |
filled: function (elem, arg, val) { |
| 841 |
if (elem.type === 'number' && elem.validity.badInput) { |
| 842 |
return true; |
| 843 |
} |
| 844 |
return val !== '' && val !== false && val !== null |
| 845 |
&& (!Array.isArray(val) || !!val.length) |
| 846 |
&& (!window.FileList || !(val instanceof window.FileList) || val.length); |
| 847 |
}, |
| 848 |
|
| 849 |
blank: function (elem, arg, val) { |
| 850 |
return !Nette.validators.filled(elem, arg, val); |
| 851 |
}, |
| 852 |
|
| 853 |
valid: function (elem) { |
| 854 |
return Nette.validateControl(elem, null, true); |
| 855 |
}, |
| 856 |
|
| 857 |
equal: function (elem, arg, val) { |
| 858 |
if (arg === undefined) { |
| 859 |
return null; |
| 860 |
} |
| 861 |
|
| 862 |
function toString(val) { |
| 863 |
if (typeof val === 'number' || typeof val === 'string') { |
| 864 |
return '' + val; |
| 865 |
} else { |
| 866 |
return val === true ? '1' : ''; |
| 867 |
} |
| 868 |
} |
| 869 |
|
| 870 |
val = Array.isArray(val) ? val : [val]; |
| 871 |
arg = Array.isArray(arg) ? arg : [arg]; |
| 872 |
loop: |
| 873 |
for (var i1 = 0, len1 = val.length; i1 < len1; i1++) { |
| 874 |
for (var i2 = 0, len2 = arg.length; i2 < len2; i2++) { |
| 875 |
if (toString(val[i1]) === toString(arg[i2])) { |
| 876 |
continue loop; |
| 877 |
} |
| 878 |
} |
| 879 |
return false; |
| 880 |
} |
| 881 |
return true; |
| 882 |
}, |
| 883 |
|
| 884 |
notEqual: function (elem, arg, val) { |
| 885 |
return arg === undefined ? null : !Nette.validators.equal(elem, arg, val); |
| 886 |
}, |
| 887 |
|
| 888 |
minLength: function (elem, arg, val) { |
| 889 |
if (elem.type === 'number') { |
| 890 |
if (elem.validity.tooShort) { |
| 891 |
return false; |
| 892 |
} else if (elem.validity.badInput) { |
| 893 |
return null; |
| 894 |
} |
| 895 |
} |
| 896 |
return val.length >= arg; |
| 897 |
}, |
| 898 |
|
| 899 |
maxLength: function (elem, arg, val) { |
| 900 |
if (elem.type === 'number') { |
| 901 |
if (elem.validity.tooLong) { |
| 902 |
return false; |
| 903 |
} else if (elem.validity.badInput) { |
| 904 |
return null; |
| 905 |
} |
| 906 |
} |
| 907 |
return val.length <= arg; |
| 908 |
}, |
| 909 |
|
| 910 |
length: function (elem, arg, val) { |
| 911 |
if (elem.type === 'number') { |
| 912 |
if (elem.validity.tooShort || elem.validity.tooLong) { |
| 913 |
return false; |
| 914 |
} else if (elem.validity.badInput) { |
| 915 |
return null; |
| 916 |
} |
| 917 |
} |
| 918 |
arg = Array.isArray(arg) ? arg : [arg, arg]; |
| 919 |
return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]); |
| 920 |
}, |
| 921 |
|
| 922 |
email: function (elem, arg, val) { |
| 923 |
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); |
| 924 |
}, |
| 925 |
|
| 926 |
url: function (elem, arg, val, value) { |
| 927 |
if (!(/^[a-z\d+.-]+:/).test(val)) { |
| 928 |
val = 'http://' + val; |
| 929 |
} |
| 930 |
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)) { |
| 931 |
value.value = val; |
| 932 |
return true; |
| 933 |
} |
| 934 |
return false; |
| 935 |
}, |
| 936 |
|
| 937 |
regexp: function (elem, arg, val) { |
| 938 |
var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false; |
| 939 |
try { |
| 940 |
return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val); |
| 941 |
} catch (e) { |
| 942 |
} // eslint-disable-line no-empty |
| 943 |
}, |
| 944 |
|
| 945 |
pattern: function (elem, arg, val, value, caseInsensitive) { |
| 946 |
if (typeof arg !== 'string') { |
| 947 |
return null; |
| 948 |
} |
| 949 |
|
| 950 |
try { |
| 951 |
try { |
| 952 |
var regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'ui' : 'u'); |
| 953 |
} catch (e) { |
| 954 |
regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : ''); |
| 955 |
} |
| 956 |
|
| 957 |
if (window.FileList && val instanceof FileList) { |
| 958 |
for (var i = 0; i < val.length; i++) { |
| 959 |
if (!regExp.test(val[i].name)) { |
| 960 |
return false; |
| 961 |
} |
| 962 |
} |
| 963 |
|
| 964 |
return true; |
| 965 |
} |
| 966 |
|
| 967 |
return regExp.test(val); |
| 968 |
} catch (e) { |
| 969 |
} // eslint-disable-line no-empty |
| 970 |
}, |
| 971 |
|
| 972 |
patternCaseInsensitive: function (elem, arg, val) { |
| 973 |
return Nette.validators.pattern(elem, arg, val, null, true); |
| 974 |
}, |
| 975 |
|
| 976 |
numeric: function (elem, arg, val) { |
| 977 |
if (elem.type === 'number' && elem.validity.badInput) { |
| 978 |
return false; |
| 979 |
} |
| 980 |
return (/^[0-9]+$/).test(val); |
| 981 |
}, |
| 982 |
|
| 983 |
integer: function (elem, arg, val) { |
| 984 |
if (elem.type === 'number' && elem.validity.badInput) { |
| 985 |
return false; |
| 986 |
} |
| 987 |
return (/^-?[0-9]+$/).test(val); |
| 988 |
}, |
| 989 |
|
| 990 |
'float': function (elem, arg, val, value) { |
| 991 |
if (elem.type === 'number' && elem.validity.badInput) { |
| 992 |
return false; |
| 993 |
} |
| 994 |
val = val.replace(/ +/g, '').replace(/,/g, '.'); |
| 995 |
if ((/^-?[0-9]*\.?[0-9]+$/).test(val)) { |
| 996 |
value.value = val; |
| 997 |
return true; |
| 998 |
} |
| 999 |
return false; |
| 1000 |
}, |
| 1001 |
|
| 1002 |
min: function (elem, arg, val) { |
| 1003 |
if (elem.type === 'number') { |
| 1004 |
if (elem.validity.rangeUnderflow) { |
| 1005 |
return false; |
| 1006 |
} else if (elem.validity.badInput) { |
| 1007 |
return null; |
| 1008 |
} |
| 1009 |
} |
| 1010 |
return arg === null || parseFloat(val) >= arg; |
| 1011 |
}, |
| 1012 |
|
| 1013 |
max: function (elem, arg, val) { |
| 1014 |
if (elem.type === 'number') { |
| 1015 |
if (elem.validity.rangeOverflow) { |
| 1016 |
return false; |
| 1017 |
} else if (elem.validity.badInput) { |
| 1018 |
return null; |
| 1019 |
} |
| 1020 |
} |
| 1021 |
return arg === null || parseFloat(val) <= arg; |
| 1022 |
}, |
| 1023 |
|
| 1024 |
range: function (elem, arg, val) { |
| 1025 |
if (elem.type === 'number') { |
| 1026 |
if (elem.validity.rangeUnderflow || elem.validity.rangeOverflow) { |
| 1027 |
return false; |
| 1028 |
} else if (elem.validity.badInput) { |
| 1029 |
return null; |
| 1030 |
} |
| 1031 |
} |
| 1032 |
return Array.isArray(arg) ? |
| 1033 |
((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null; |
| 1034 |
}, |
| 1035 |
|
| 1036 |
submitted: function (elem) { |
| 1037 |
return elem.form['nette-submittedBy'] === elem; |
| 1038 |
}, |
| 1039 |
|
| 1040 |
fileSize: function (elem, arg, val) { |
| 1041 |
if (window.FileList) { |
| 1042 |
for (var i = 0; i < val.length; i++) { |
| 1043 |
if (val[i].size > arg) { |
| 1044 |
return false; |
| 1045 |
} |
| 1046 |
} |
| 1047 |
} |
| 1048 |
return true; |
| 1049 |
}, |
| 1050 |
|
| 1051 |
image: function (elem, arg, val) { |
| 1052 |
if (window.FileList && val instanceof window.FileList) { |
| 1053 |
for (var i = 0; i < val.length; i++) { |
| 1054 |
var type = val[i].type; |
| 1055 |
if (type && type !== 'image/gif' && type !== 'image/png' && type !== 'image/jpeg') { |
| 1056 |
return false; |
| 1057 |
} |
| 1058 |
} |
| 1059 |
} |
| 1060 |
return true; |
| 1061 |
}, |
| 1062 |
|
| 1063 |
'static': function (elem, arg) { |
| 1064 |
return arg; |
| 1065 |
} |
| 1066 |
}; |
| 1067 |
|
| 1068 |
|
| 1069 |
/** |
| 1070 |
* Process all toggles in form. |
| 1071 |
*/ |
| 1072 |
Nette.toggleForm = function (form, elem) { |
| 1073 |
var i; |
| 1074 |
formToggles = {}; |
| 1075 |
for (i = 0; i < form.elements.length; i++) { |
| 1076 |
if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) { |
| 1077 |
Nette.toggleControl(form.elements[i], null, null, !elem); |
| 1078 |
} |
| 1079 |
} |
| 1080 |
|
| 1081 |
for (i in formToggles) { |
| 1082 |
Nette.toggle(i, formToggles[i], elem); |
| 1083 |
} |
| 1084 |
}; |
| 1085 |
|
| 1086 |
|
| 1087 |
/** |
| 1088 |
* Process toggles on form element. |
| 1089 |
*/ |
| 1090 |
Nette.toggleControl = function (elem, rules, success, firsttime, value) { |
| 1091 |
rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); |
| 1092 |
value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; |
| 1093 |
|
| 1094 |
var has = false, |
| 1095 |
handled = [], |
| 1096 |
handler = function () { |
| 1097 |
Nette.toggleForm(elem.form, elem); |
| 1098 |
}, |
| 1099 |
curSuccess; |
| 1100 |
|
| 1101 |
for (var id = 0, len = rules.length; id < len; id++) { |
| 1102 |
var rule = rules[id], |
| 1103 |
op = rule.op.match(/(~)?([^?]+)/), |
| 1104 |
curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem; |
| 1105 |
|
| 1106 |
if (!curElem) { |
| 1107 |
continue; |
| 1108 |
} |
| 1109 |
|
| 1110 |
curSuccess = success; |
| 1111 |
if (success !== false) { |
| 1112 |
rule.neg = op[1]; |
| 1113 |
rule.op = op[2]; |
| 1114 |
curSuccess = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); |
| 1115 |
if (curSuccess === null) { |
| 1116 |
continue; |
| 1117 |
|
| 1118 |
} else if (rule.neg) { |
| 1119 |
curSuccess = !curSuccess; |
| 1120 |
} |
| 1121 |
if (!rule.rules) { |
| 1122 |
success = curSuccess; |
| 1123 |
} |
| 1124 |
} |
| 1125 |
|
| 1126 |
if ((rule.rules && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value)) || rule.toggle) { |
| 1127 |
has = true; |
| 1128 |
if (firsttime) { |
| 1129 |
var name = curElem.tagName ? curElem.name : curElem[0].name, |
| 1130 |
els = curElem.tagName ? curElem.form.elements : curElem; |
| 1131 |
|
| 1132 |
for (var i = 0; i < els.length; i++) { |
| 1133 |
if (els[i].name === name && handled.indexOf(els[i]) < 0) { |
| 1134 |
els[i].addEventListener('change', handler); |
| 1135 |
handled.push(els[i]); |
| 1136 |
} |
| 1137 |
} |
| 1138 |
} |
| 1139 |
for (var id2 in rule.toggle || []) { |
| 1140 |
if (Object.prototype.hasOwnProperty.call(rule.toggle, id2)) { |
| 1141 |
formToggles[id2] = formToggles[id2] || (rule.toggle[id2] ? curSuccess : !curSuccess); |
| 1142 |
} |
| 1143 |
} |
| 1144 |
} |
| 1145 |
} |
| 1146 |
return has; |
| 1147 |
}; |
| 1148 |
|
| 1149 |
|
| 1150 |
/** |
| 1151 |
* Displays or hides HTML element. |
| 1152 |
*/ |
| 1153 |
Nette.toggle = function (selector, visible, srcElement) { // eslint-disable-line no-unused-vars |
| 1154 |
if (/^\w[\w.:-]*$/.test(selector)) { // id |
| 1155 |
selector = '#' + selector; |
| 1156 |
} |
| 1157 |
var elems = document.querySelectorAll(selector); |
| 1158 |
for (var i = 0; i < elems.length; i++) { |
| 1159 |
elems[i].hidden = !visible; |
| 1160 |
} |
| 1161 |
}; |
| 1162 |
|
| 1163 |
|
| 1164 |
/** |
| 1165 |
* Setup handlers. |
| 1166 |
*/ |
| 1167 |
Nette.initForm = function (form) { |
| 1168 |
Nette.toggleForm(form); |
| 1169 |
|
| 1170 |
if (form.noValidate) { |
| 1171 |
return; |
| 1172 |
} |
| 1173 |
|
| 1174 |
form.noValidate = true; |
| 1175 |
|
| 1176 |
// LiveForm: addition |
| 1177 |
LiveForm.forms[form.id] = { |
| 1178 |
hasError: false, |
| 1179 |
onLoadValidation: false |
| 1180 |
}; |
| 1181 |
|
| 1182 |
form.addEventListener('submit', function (e) { |
| 1183 |
if (!Nette.validateForm(form)) { |
| 1184 |
e.stopPropagation(); |
| 1185 |
e.preventDefault(); |
| 1186 |
} |
| 1187 |
}); |
| 1188 |
|
| 1189 |
// LiveForm: addition |
| 1190 |
for (var i = 0; i < form.elements.length; i++) { |
| 1191 |
LiveForm.setupHandlers(form.elements[i]); |
| 1192 |
LiveForm.processServerErrors(form.elements[i]); |
| 1193 |
} |
| 1194 |
}; |
| 1195 |
|
| 1196 |
/** |
| 1197 |
* @private |
| 1198 |
*/ |
| 1199 |
Nette.initOnLoad = function () { |
| 1200 |
Nette.addEvent(document, 'DOMContentLoaded', function () { |
| 1201 |
// LiveForm: original netteForms.js code |
| 1202 |
/* |
| 1203 |
for (var i = 0; i < document.forms.length; i++) { |
| 1204 |
var form = document.forms[i]; |
| 1205 |
for (var j = 0; j < form.elements.length; j++) { |
| 1206 |
if (form.elements[j].getAttribute('data-nette-rules')) { |
| 1207 |
Nette.initForm(form); |
| 1208 |
break; |
| 1209 |
} |
| 1210 |
} |
| 1211 |
} |
| 1212 |
Nette.addEvent(document.body, 'click', function(e) { |
| 1213 |
var target = e.target || e.srcElement; |
| 1214 |
if (target.form && target.type in {submit: 1, image: 1}) { |
| 1215 |
target.form['nette-submittedBy'] = target; |
| 1216 |
} |
| 1217 |
}); |
| 1218 |
*/ |
| 1219 |
// LiveForm: addition |
| 1220 |
Nette.init(); |
| 1221 |
}); |
| 1222 |
}; |
| 1223 |
|
| 1224 |
// LiveForm: addition |
| 1225 |
/** |
| 1226 |
* Init function to be called in case usage as module |
| 1227 |
* |
| 1228 |
* @public |
| 1229 |
*/ |
| 1230 |
Nette.init = function () { |
| 1231 |
for (var i = 0; i < document.forms.length; i++) { |
| 1232 |
var form = document.forms[i]; |
| 1233 |
for (var j = 0; j < form.elements.length; j++) { |
| 1234 |
if (form.elements[j].getAttribute('data-nette-rules')) { |
| 1235 |
Nette.initForm(form); |
| 1236 |
|
| 1237 |
if (LiveForm.hasClass(form, 'validate-on-load')) { |
| 1238 |
// This is not so nice way, but I don't want to spoil validateForm, validateControl and other methods with another parameter |
| 1239 |
LiveForm.setFormProperty(form, "onLoadValidation", true); |
| 1240 |
Nette.validateForm(form); |
| 1241 |
LiveForm.setFormProperty(form, "onLoadValidation", false); |
| 1242 |
} |
| 1243 |
|
| 1244 |
break; |
| 1245 |
} |
| 1246 |
} |
| 1247 |
} |
| 1248 |
|
| 1249 |
Nette.addEvent(document.body, 'click', function (e) { |
| 1250 |
var target = e.target || e.srcElement; |
| 1251 |
if (target.form && target.type in {submit: 1, image: 1}) { |
| 1252 |
target.form['nette-submittedBy'] = target; |
| 1253 |
} |
| 1254 |
}); |
| 1255 |
}; |
| 1256 |
|
| 1257 |
|
| 1258 |
/** |
| 1259 |
* Determines whether the argument is an array. |
| 1260 |
*/ |
| 1261 |
Nette.isArray = function (arg) { |
| 1262 |
return Object.prototype.toString.call(arg) === '[object Array]'; |
| 1263 |
}; |
| 1264 |
|
| 1265 |
|
| 1266 |
/** |
| 1267 |
* Search for a specified value within an array. |
| 1268 |
*/ |
| 1269 |
Nette.inArray = function (arr, val) { |
| 1270 |
if ([].indexOf) { |
| 1271 |
return arr.indexOf(val) > -1; |
| 1272 |
} else { |
| 1273 |
for (var i = 0; i < arr.length; i++) { |
| 1274 |
if (arr[i] === val) { |
| 1275 |
return true; |
| 1276 |
} |
| 1277 |
} |
| 1278 |
return false; |
| 1279 |
} |
| 1280 |
}; |
| 1281 |
|
| 1282 |
|
| 1283 |
/** |
| 1284 |
* Converts string to web safe characters [a-z0-9-] text. |
| 1285 |
*/ |
| 1286 |
Nette.webalize = function (s) { |
| 1287 |
s = s.toLowerCase(); |
| 1288 |
var res = '', i, ch; |
| 1289 |
for (i = 0; i < s.length; i++) { |
| 1290 |
ch = Nette.webalizeTable[s.charAt(i)]; |
| 1291 |
res += ch ? ch : s.charAt(i); |
| 1292 |
} |
| 1293 |
return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); |
| 1294 |
}; |
| 1295 |
|
| 1296 |
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'}; |
| 1297 |
|
| 1298 |
return Nette; |
| 1299 |
})); |
| 1300 |
|