PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / forms / src / assets / netteForms.js

netteForms.js in Packeta 1.6.1, at deps/nette/forms/src/assets/netteForms.js

786 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 val.length > 0;
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 = 'https://' + 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 }));
786