1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | ;(function ($, window, document, undefined) {
|
12 |
|
13 | 'use strict';
|
14 |
|
15 | $.isFunction = $.isFunction || function(obj) {
|
16 | return typeof obj === "function" && typeof obj.nodeType !== "number";
|
17 | };
|
18 |
|
19 | window = (typeof window != 'undefined' && window.Math == Math)
|
20 | ? window
|
21 | : (typeof self != 'undefined' && self.Math == Math)
|
22 | ? self
|
23 | : Function('return this')()
|
24 | ;
|
25 |
|
26 | $.fn.form = function(parameters) {
|
27 | var
|
28 | $allModules = $(this),
|
29 | moduleSelector = $allModules.selector || '',
|
30 |
|
31 | time = new Date().getTime(),
|
32 | performance = [],
|
33 |
|
34 | query = arguments[0],
|
35 | legacyParameters = arguments[1],
|
36 | methodInvoked = (typeof query == 'string'),
|
37 | queryArguments = [].slice.call(arguments, 1),
|
38 | returnedValue
|
39 | ;
|
40 | $allModules
|
41 | .each(function() {
|
42 | var
|
43 | $module = $(this),
|
44 | element = this,
|
45 |
|
46 | formErrors = [],
|
47 | keyHeldDown = false,
|
48 |
|
49 |
|
50 | $field,
|
51 | $group,
|
52 | $message,
|
53 | $prompt,
|
54 | $submit,
|
55 | $clear,
|
56 | $reset,
|
57 |
|
58 | settings,
|
59 | validation,
|
60 |
|
61 | metadata,
|
62 | selector,
|
63 | className,
|
64 | regExp,
|
65 | error,
|
66 |
|
67 | namespace,
|
68 | moduleNamespace,
|
69 | eventNamespace,
|
70 |
|
71 | submitting = false,
|
72 | dirty = false,
|
73 | history = ['clean', 'clean'],
|
74 |
|
75 | instance,
|
76 | module
|
77 | ;
|
78 |
|
79 | module = {
|
80 |
|
81 | initialize: function() {
|
82 |
|
83 |
|
84 | module.get.settings();
|
85 | if(methodInvoked) {
|
86 | if(instance === undefined) {
|
87 | module.instantiate();
|
88 | }
|
89 | module.invoke(query);
|
90 | }
|
91 | else {
|
92 | if(instance !== undefined) {
|
93 | instance.invoke('destroy');
|
94 | }
|
95 | module.verbose('Initializing form validation', $module, settings);
|
96 | module.bindEvents();
|
97 | module.set.defaults();
|
98 | if (settings.autoCheckRequired) {
|
99 | module.set.autoCheck();
|
100 | }
|
101 | module.instantiate();
|
102 | }
|
103 | },
|
104 |
|
105 | instantiate: function() {
|
106 | module.verbose('Storing instance of module', module);
|
107 | instance = module;
|
108 | $module
|
109 | .data(moduleNamespace, module)
|
110 | ;
|
111 | },
|
112 |
|
113 | destroy: function() {
|
114 | module.verbose('Destroying previous module', instance);
|
115 | module.removeEvents();
|
116 | $module
|
117 | .removeData(moduleNamespace)
|
118 | ;
|
119 | },
|
120 |
|
121 | refresh: function() {
|
122 | module.verbose('Refreshing selector cache');
|
123 | $field = $module.find(selector.field);
|
124 | $group = $module.find(selector.group);
|
125 | $message = $module.find(selector.message);
|
126 | $prompt = $module.find(selector.prompt);
|
127 |
|
128 | $submit = $module.find(selector.submit);
|
129 | $clear = $module.find(selector.clear);
|
130 | $reset = $module.find(selector.reset);
|
131 | },
|
132 |
|
133 | submit: function() {
|
134 | module.verbose('Submitting form', $module);
|
135 | submitting = true;
|
136 | $module.submit();
|
137 | },
|
138 |
|
139 | attachEvents: function(selector, action) {
|
140 | action = action || 'submit';
|
141 | $(selector).on('click' + eventNamespace, function(event) {
|
142 | module[action]();
|
143 | event.preventDefault();
|
144 | });
|
145 | },
|
146 |
|
147 | bindEvents: function() {
|
148 | module.verbose('Attaching form events');
|
149 | $module
|
150 | .on('submit' + eventNamespace, module.validate.form)
|
151 | .on('blur' + eventNamespace, selector.field, module.event.field.blur)
|
152 | .on('click' + eventNamespace, selector.submit, module.submit)
|
153 | .on('click' + eventNamespace, selector.reset, module.reset)
|
154 | .on('click' + eventNamespace, selector.clear, module.clear)
|
155 | ;
|
156 | if(settings.keyboardShortcuts) {
|
157 | $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
|
158 | }
|
159 | $field.each(function(index, el) {
|
160 | var
|
161 | $input = $(el),
|
162 | type = $input.prop('type'),
|
163 | inputEvent = module.get.changeEvent(type, $input)
|
164 | ;
|
165 | $input.on(inputEvent + eventNamespace, module.event.field.change);
|
166 | });
|
167 |
|
168 |
|
169 | if (settings.preventLeaving) {
|
170 | $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
|
171 | }
|
172 |
|
173 | $field.on('change click keyup keydown blur', function(e) {
|
174 | $(this).triggerHandler(e.type + ".dirty");
|
175 | });
|
176 |
|
177 | $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
|
178 |
|
179 | $module.on('dirty' + eventNamespace, function(e) {
|
180 | settings.onDirty.call();
|
181 | });
|
182 |
|
183 | $module.on('clean' + eventNamespace, function(e) {
|
184 | settings.onClean.call();
|
185 | })
|
186 | },
|
187 |
|
188 | clear: function() {
|
189 | $field.each(function (index, el) {
|
190 | var
|
191 | $field = $(el),
|
192 | $element = $field.parent(),
|
193 | $fieldGroup = $field.closest($group),
|
194 | $prompt = $fieldGroup.find(selector.prompt),
|
195 | $calendar = $field.closest(selector.uiCalendar),
|
196 | defaultValue = $field.data(metadata.defaultValue) || '',
|
197 | isCheckbox = $element.is(selector.uiCheckbox),
|
198 | isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
|
199 | isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
|
200 | isErrored = $fieldGroup.hasClass(className.error)
|
201 | ;
|
202 | if(isErrored) {
|
203 | module.verbose('Resetting error on field', $fieldGroup);
|
204 | $fieldGroup.removeClass(className.error);
|
205 | $prompt.remove();
|
206 | }
|
207 | if(isDropdown) {
|
208 | module.verbose('Resetting dropdown value', $element, defaultValue);
|
209 | $element.dropdown('clear', true);
|
210 | }
|
211 | else if(isCheckbox) {
|
212 | $field.prop('checked', false);
|
213 | }
|
214 | else if (isCalendar) {
|
215 | $calendar.calendar('clear');
|
216 | }
|
217 | else {
|
218 | module.verbose('Resetting field value', $field, defaultValue);
|
219 | $field.val('');
|
220 | }
|
221 | });
|
222 | },
|
223 |
|
224 | reset: function() {
|
225 | $field.each(function (index, el) {
|
226 | var
|
227 | $field = $(el),
|
228 | $element = $field.parent(),
|
229 | $fieldGroup = $field.closest($group),
|
230 | $calendar = $field.closest(selector.uiCalendar),
|
231 | $prompt = $fieldGroup.find(selector.prompt),
|
232 | defaultValue = $field.data(metadata.defaultValue),
|
233 | isCheckbox = $element.is(selector.uiCheckbox),
|
234 | isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
|
235 | isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
|
236 | isErrored = $fieldGroup.hasClass(className.error)
|
237 | ;
|
238 | if(defaultValue === undefined) {
|
239 | return;
|
240 | }
|
241 | if(isErrored) {
|
242 | module.verbose('Resetting error on field', $fieldGroup);
|
243 | $fieldGroup.removeClass(className.error);
|
244 | $prompt.remove();
|
245 | }
|
246 | if(isDropdown) {
|
247 | module.verbose('Resetting dropdown value', $element, defaultValue);
|
248 | $element.dropdown('restore defaults', true);
|
249 | }
|
250 | else if(isCheckbox) {
|
251 | module.verbose('Resetting checkbox value', $element, defaultValue);
|
252 | $field.prop('checked', defaultValue);
|
253 | }
|
254 | else if (isCalendar) {
|
255 | $calendar.calendar('set date', defaultValue);
|
256 | }
|
257 | else {
|
258 | module.verbose('Resetting field value', $field, defaultValue);
|
259 | $field.val(defaultValue);
|
260 | }
|
261 | });
|
262 |
|
263 | module.determine.isDirty();
|
264 | },
|
265 |
|
266 | determine: {
|
267 | isValid: function() {
|
268 | var
|
269 | allValid = true
|
270 | ;
|
271 | $.each(validation, function(fieldName, field) {
|
272 | if( !( module.validate.field(field, fieldName, true) ) ) {
|
273 | allValid = false;
|
274 | }
|
275 | });
|
276 | return allValid;
|
277 | },
|
278 | isDirty: function(e) {
|
279 | var formIsDirty = false;
|
280 |
|
281 | $field.each(function(index, el) {
|
282 | var
|
283 | $el = $(el),
|
284 | isCheckbox = ($el.filter(selector.checkbox).length > 0),
|
285 | isDirty
|
286 | ;
|
287 |
|
288 | if (isCheckbox) {
|
289 | isDirty = module.is.checkboxDirty($el);
|
290 | } else {
|
291 | isDirty = module.is.fieldDirty($el);
|
292 | }
|
293 |
|
294 | $el.data(settings.metadata.isDirty, isDirty);
|
295 |
|
296 | formIsDirty |= isDirty;
|
297 | });
|
298 |
|
299 | if (formIsDirty) {
|
300 | module.set.dirty();
|
301 | } else {
|
302 | module.set.clean();
|
303 | }
|
304 |
|
305 | if (e && e.namespace === 'dirty') {
|
306 | e.stopImmediatePropagation();
|
307 | e.preventDefault();
|
308 | }
|
309 | }
|
310 | },
|
311 |
|
312 | is: {
|
313 | bracketedRule: function(rule) {
|
314 | return (rule.type && rule.type.match(settings.regExp.bracket));
|
315 | },
|
316 | shorthandFields: function(fields) {
|
317 | var
|
318 | fieldKeys = Object.keys(fields),
|
319 | firstRule = fields[fieldKeys[0]]
|
320 | ;
|
321 | return module.is.shorthandRules(firstRule);
|
322 | },
|
323 |
|
324 | shorthandRules: function(rules) {
|
325 | return (typeof rules == 'string' || Array.isArray(rules));
|
326 | },
|
327 | empty: function($field) {
|
328 | if(!$field || $field.length === 0) {
|
329 | return true;
|
330 | }
|
331 | else if($field.is(selector.checkbox)) {
|
332 | return !$field.is(':checked');
|
333 | }
|
334 | else {
|
335 | return module.is.blank($field);
|
336 | }
|
337 | },
|
338 | blank: function($field) {
|
339 | return $.trim($field.val()) === '';
|
340 | },
|
341 | valid: function(field) {
|
342 | var
|
343 | allValid = true
|
344 | ;
|
345 | if(field) {
|
346 | module.verbose('Checking if field is valid', field);
|
347 | return module.validate.field(validation[field], field, false);
|
348 | }
|
349 | else {
|
350 | module.verbose('Checking if form is valid');
|
351 | $.each(validation, function(fieldName, field) {
|
352 | if( !module.is.valid(fieldName) ) {
|
353 | allValid = false;
|
354 | }
|
355 | });
|
356 | return allValid;
|
357 | }
|
358 | },
|
359 | dirty: function() {
|
360 | return dirty;
|
361 | },
|
362 | clean: function() {
|
363 | return !dirty;
|
364 | },
|
365 | fieldDirty: function($el) {
|
366 | var initialValue = $el.data(metadata.defaultValue);
|
367 |
|
368 | if (initialValue == null) { initialValue = ''; }
|
369 | var currentValue = $el.val();
|
370 | if (currentValue == null) { currentValue = ''; }
|
371 |
|
372 |
|
373 | var boolRegex = /^(true|false)$/i;
|
374 | var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
|
375 | if (isBoolValue) {
|
376 | var regex = new RegExp("^" + initialValue + "$", "i");
|
377 | return !regex.test(currentValue);
|
378 | }
|
379 |
|
380 | return currentValue !== initialValue;
|
381 | },
|
382 | checkboxDirty: function($el) {
|
383 | var initialValue = $el.data(metadata.defaultValue);
|
384 | var currentValue = $el.is(":checked");
|
385 |
|
386 | return initialValue !== currentValue;
|
387 | },
|
388 | justDirty: function() {
|
389 | return (history[0] === 'dirty');
|
390 | },
|
391 | justClean: function() {
|
392 | return (history[0] === 'clean');
|
393 | }
|
394 | },
|
395 |
|
396 | removeEvents: function() {
|
397 | $module.off(eventNamespace);
|
398 | $field.off(eventNamespace);
|
399 | $submit.off(eventNamespace);
|
400 | $field.off(eventNamespace);
|
401 | },
|
402 |
|
403 | event: {
|
404 | field: {
|
405 | keydown: function(event) {
|
406 | var
|
407 | $field = $(this),
|
408 | key = event.which,
|
409 | isInput = $field.is(selector.input),
|
410 | isCheckbox = $field.is(selector.checkbox),
|
411 | isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
|
412 | keyCode = {
|
413 | enter : 13,
|
414 | escape : 27
|
415 | }
|
416 | ;
|
417 | if( key == keyCode.escape) {
|
418 | module.verbose('Escape key pressed blurring field');
|
419 | $field
|
420 | .blur()
|
421 | ;
|
422 | }
|
423 | if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
|
424 | if(!keyHeldDown) {
|
425 | $field.one('keyup' + eventNamespace, module.event.field.keyup);
|
426 | module.submit();
|
427 | module.debug('Enter pressed on input submitting form');
|
428 | }
|
429 | keyHeldDown = true;
|
430 | }
|
431 | },
|
432 | keyup: function() {
|
433 | keyHeldDown = false;
|
434 | },
|
435 | blur: function(event) {
|
436 | var
|
437 | $field = $(this),
|
438 | $fieldGroup = $field.closest($group),
|
439 | validationRules = module.get.validation($field)
|
440 | ;
|
441 | if( $fieldGroup.hasClass(className.error) ) {
|
442 | module.debug('Revalidating field', $field, validationRules);
|
443 | if(validationRules) {
|
444 | module.validate.field( validationRules );
|
445 | }
|
446 | }
|
447 | else if(settings.on == 'blur') {
|
448 | if(validationRules) {
|
449 | module.validate.field( validationRules );
|
450 | }
|
451 | }
|
452 | },
|
453 | change: function(event) {
|
454 | var
|
455 | $field = $(this),
|
456 | $fieldGroup = $field.closest($group),
|
457 | validationRules = module.get.validation($field)
|
458 | ;
|
459 | if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
|
460 | clearTimeout(module.timer);
|
461 | module.timer = setTimeout(function() {
|
462 | module.debug('Revalidating field', $field, module.get.validation($field));
|
463 | module.validate.field( validationRules );
|
464 | }, settings.delay);
|
465 | }
|
466 | }
|
467 | },
|
468 | beforeUnload: function(event) {
|
469 | if (module.is.dirty() && !submitting) {
|
470 | var event = event || window.event;
|
471 |
|
472 |
|
473 | if (event) {
|
474 | event.returnValue = settings.text.leavingMessage;
|
475 | }
|
476 |
|
477 |
|
478 | return settings.text.leavingMessage;
|
479 | }
|
480 | }
|
481 |
|
482 | },
|
483 |
|
484 | get: {
|
485 | ancillaryValue: function(rule) {
|
486 | if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
|
487 | return false;
|
488 | }
|
489 | return (rule.value !== undefined)
|
490 | ? rule.value
|
491 | : rule.type.match(settings.regExp.bracket)[1] + ''
|
492 | ;
|
493 | },
|
494 | ruleName: function(rule) {
|
495 | if( module.is.bracketedRule(rule) ) {
|
496 | return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
|
497 | }
|
498 | return rule.type;
|
499 | },
|
500 | changeEvent: function(type, $input) {
|
501 | if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
|
502 | return 'change';
|
503 | }
|
504 | else {
|
505 | return module.get.inputEvent();
|
506 | }
|
507 | },
|
508 | inputEvent: function() {
|
509 | return (document.createElement('input').oninput !== undefined)
|
510 | ? 'input'
|
511 | : (document.createElement('input').onpropertychange !== undefined)
|
512 | ? 'propertychange'
|
513 | : 'keyup'
|
514 | ;
|
515 | },
|
516 | fieldsFromShorthand: function(fields) {
|
517 | var
|
518 | fullFields = {}
|
519 | ;
|
520 | $.each(fields, function(name, rules) {
|
521 | if(typeof rules == 'string') {
|
522 | rules = [rules];
|
523 | }
|
524 | fullFields[name] = {
|
525 | rules: []
|
526 | };
|
527 | $.each(rules, function(index, rule) {
|
528 | fullFields[name].rules.push({ type: rule });
|
529 | });
|
530 | });
|
531 | return fullFields;
|
532 | },
|
533 | prompt: function(rule, field) {
|
534 | var
|
535 | ruleName = module.get.ruleName(rule),
|
536 | ancillary = module.get.ancillaryValue(rule),
|
537 | $field = module.get.field(field.identifier),
|
538 | value = $field.val(),
|
539 | prompt = $.isFunction(rule.prompt)
|
540 | ? rule.prompt(value)
|
541 | : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
|
542 | requiresValue = (prompt.search('{value}') !== -1),
|
543 | requiresName = (prompt.search('{name}') !== -1),
|
544 | $label,
|
545 | name
|
546 | ;
|
547 | if(requiresValue) {
|
548 | prompt = prompt.replace(/\{value\}/g, $field.val());
|
549 | }
|
550 | if(requiresName) {
|
551 | $label = $field.closest(selector.group).find('label').eq(0);
|
552 | name = ($label.length == 1)
|
553 | ? $label.text()
|
554 | : $field.prop('placeholder') || settings.text.unspecifiedField
|
555 | ;
|
556 | prompt = prompt.replace(/\{name\}/g, name);
|
557 | }
|
558 | prompt = prompt.replace(/\{identifier\}/g, field.identifier);
|
559 | prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
|
560 | if(!rule.prompt) {
|
561 | module.verbose('Using default validation prompt for type', prompt, ruleName);
|
562 | }
|
563 | return prompt;
|
564 | },
|
565 | settings: function() {
|
566 | if($.isPlainObject(parameters)) {
|
567 | var
|
568 | keys = Object.keys(parameters),
|
569 | isLegacySettings = (keys.length > 0)
|
570 | ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
|
571 | : false
|
572 | ;
|
573 | if(isLegacySettings) {
|
574 |
|
575 | settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
|
576 | validation = $.extend({}, $.fn.form.settings.defaults, parameters);
|
577 | module.error(settings.error.oldSyntax, element);
|
578 | module.verbose('Extending settings from legacy parameters', validation, settings);
|
579 | }
|
580 | else {
|
581 |
|
582 | if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
|
583 | parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
|
584 | }
|
585 | settings = $.extend(true, {}, $.fn.form.settings, parameters);
|
586 | validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
|
587 | module.verbose('Extending settings', validation, settings);
|
588 | }
|
589 | }
|
590 | else {
|
591 | settings = $.fn.form.settings;
|
592 | validation = $.fn.form.settings.defaults;
|
593 | module.verbose('Using default form validation', validation, settings);
|
594 | }
|
595 |
|
596 |
|
597 | namespace = settings.namespace;
|
598 | metadata = settings.metadata;
|
599 | selector = settings.selector;
|
600 | className = settings.className;
|
601 | regExp = settings.regExp;
|
602 | error = settings.error;
|
603 | moduleNamespace = 'module-' + namespace;
|
604 | eventNamespace = '.' + namespace;
|
605 |
|
606 |
|
607 | instance = $module.data(moduleNamespace);
|
608 |
|
609 |
|
610 | module.refresh();
|
611 | },
|
612 | field: function(identifier) {
|
613 | module.verbose('Finding field with identifier', identifier);
|
614 | identifier = module.escape.string(identifier);
|
615 | var t;
|
616 | if((t=$field.filter('#' + identifier)).length > 0 ) {
|
617 | return t;
|
618 | }
|
619 | if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
|
620 | return t;
|
621 | }
|
622 | if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
|
623 | return t;
|
624 | }
|
625 | if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
|
626 | return t;
|
627 | }
|
628 | return $('<input/>');
|
629 | },
|
630 | fields: function(fields) {
|
631 | var
|
632 | $fields = $()
|
633 | ;
|
634 | $.each(fields, function(index, name) {
|
635 | $fields = $fields.add( module.get.field(name) );
|
636 | });
|
637 | return $fields;
|
638 | },
|
639 | validation: function($field) {
|
640 | var
|
641 | fieldValidation,
|
642 | identifier
|
643 | ;
|
644 | if(!validation) {
|
645 | return false;
|
646 | }
|
647 | $.each(validation, function(fieldName, field) {
|
648 | identifier = field.identifier || fieldName;
|
649 | $.each(module.get.field(identifier), function(index, groupField) {
|
650 | if(groupField == $field[0]) {
|
651 | field.identifier = identifier;
|
652 | fieldValidation = field;
|
653 | return false;
|
654 | }
|
655 | });
|
656 | });
|
657 | return fieldValidation || false;
|
658 | },
|
659 | value: function (field) {
|
660 | var
|
661 | fields = [],
|
662 | results
|
663 | ;
|
664 | fields.push(field);
|
665 | results = module.get.values.call(element, fields);
|
666 | return results[field];
|
667 | },
|
668 | values: function (fields) {
|
669 | var
|
670 | $fields = Array.isArray(fields)
|
671 | ? module.get.fields(fields)
|
672 | : $field,
|
673 | values = {}
|
674 | ;
|
675 | $fields.each(function(index, field) {
|
676 | var
|
677 | $field = $(field),
|
678 | $calendar = $field.closest(selector.uiCalendar),
|
679 | name = $field.prop('name'),
|
680 | value = $field.val(),
|
681 | isCheckbox = $field.is(selector.checkbox),
|
682 | isRadio = $field.is(selector.radio),
|
683 | isMultiple = (name.indexOf('[]') !== -1),
|
684 | isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
|
685 | isChecked = (isCheckbox)
|
686 | ? $field.is(':checked')
|
687 | : false
|
688 | ;
|
689 | if(name) {
|
690 | if(isMultiple) {
|
691 | name = name.replace('[]', '');
|
692 | if(!values[name]) {
|
693 | values[name] = [];
|
694 | }
|
695 | if(isCheckbox) {
|
696 | if(isChecked) {
|
697 | values[name].push(value || true);
|
698 | }
|
699 | else {
|
700 | values[name].push(false);
|
701 | }
|
702 | }
|
703 | else {
|
704 | values[name].push(value);
|
705 | }
|
706 | }
|
707 | else {
|
708 | if(isRadio) {
|
709 | if(values[name] === undefined || values[name] === false) {
|
710 | values[name] = (isChecked)
|
711 | ? value || true
|
712 | : false
|
713 | ;
|
714 | }
|
715 | }
|
716 | else if(isCheckbox) {
|
717 | if(isChecked) {
|
718 | values[name] = value || true;
|
719 | }
|
720 | else {
|
721 | values[name] = false;
|
722 | }
|
723 | }
|
724 | else if(isCalendar) {
|
725 | var date = $calendar.calendar('get date');
|
726 |
|
727 | if (date !== null) {
|
728 | if (settings.dateHandling == 'date') {
|
729 | values[name] = date;
|
730 | } else if(settings.dateHandling == 'input') {
|
731 | values[name] = $calendar.calendar('get input date')
|
732 | } else if (settings.dateHandling == 'formatter') {
|
733 | var type = $calendar.calendar('setting', 'type');
|
734 |
|
735 | switch(type) {
|
736 | case 'date':
|
737 | values[name] = settings.formatter.date(date);
|
738 | break;
|
739 |
|
740 | case 'datetime':
|
741 | values[name] = settings.formatter.datetime(date);
|
742 | break;
|
743 |
|
744 | case 'time':
|
745 | values[name] = settings.formatter.time(date);
|
746 | break;
|
747 |
|
748 | case 'month':
|
749 | values[name] = settings.formatter.month(date);
|
750 | break;
|
751 |
|
752 | case 'year':
|
753 | values[name] = settings.formatter.year(date);
|
754 | break;
|
755 |
|
756 | default:
|
757 | module.debug('Wrong calendar mode', $calendar, type);
|
758 | values[name] = '';
|
759 | }
|
760 | }
|
761 | } else {
|
762 | values[name] = '';
|
763 | }
|
764 | } else {
|
765 | values[name] = value;
|
766 | }
|
767 | }
|
768 | }
|
769 | });
|
770 | return values;
|
771 | },
|
772 | dirtyFields: function() {
|
773 | return $field.filter(function(index, e) {
|
774 | return $(e).data(metadata.isDirty);
|
775 | });
|
776 | }
|
777 | },
|
778 |
|
779 | has: {
|
780 |
|
781 | field: function(identifier) {
|
782 | module.verbose('Checking for existence of a field with identifier', identifier);
|
783 | identifier = module.escape.string(identifier);
|
784 | if(typeof identifier !== 'string') {
|
785 | module.error(error.identifier, identifier);
|
786 | }
|
787 | if($field.filter('#' + identifier).length > 0 ) {
|
788 | return true;
|
789 | }
|
790 | else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
|
791 | return true;
|
792 | }
|
793 | else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
|
794 | return true;
|
795 | }
|
796 | return false;
|
797 | }
|
798 |
|
799 | },
|
800 |
|
801 | can: {
|
802 | useElement: function(element){
|
803 | if ($.fn[element] !== undefined) {
|
804 | return true;
|
805 | }
|
806 | module.error(error.noElement.replace('{element}',element));
|
807 | return false;
|
808 | }
|
809 | },
|
810 |
|
811 | escape: {
|
812 | string: function(text) {
|
813 | text = String(text);
|
814 | return text.replace(regExp.escape, '\\$&');
|
815 | }
|
816 | },
|
817 |
|
818 | add: {
|
819 |
|
820 | rule: function(name, rules) {
|
821 | module.add.field(name, rules);
|
822 | },
|
823 | field: function(name, rules) {
|
824 |
|
825 | if(validation[name] === undefined || validation[name].rules === undefined) {
|
826 | validation[name] = {
|
827 | rules: []
|
828 | };
|
829 | }
|
830 | var
|
831 | newValidation = {
|
832 | rules: []
|
833 | }
|
834 | ;
|
835 | if(module.is.shorthandRules(rules)) {
|
836 | rules = Array.isArray(rules)
|
837 | ? rules
|
838 | : [rules]
|
839 | ;
|
840 | $.each(rules, function(_index, rule) {
|
841 | newValidation.rules.push({ type: rule });
|
842 | });
|
843 | }
|
844 | else {
|
845 | newValidation.rules = rules.rules;
|
846 | }
|
847 |
|
848 | $.each(newValidation.rules, function (_index, rule) {
|
849 | if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
|
850 | validation[name].rules.push(rule);
|
851 | }
|
852 | });
|
853 | module.debug('Adding rules', newValidation.rules, validation);
|
854 | },
|
855 | fields: function(fields) {
|
856 | var
|
857 | newValidation
|
858 | ;
|
859 | if(fields && module.is.shorthandFields(fields)) {
|
860 | newValidation = module.get.fieldsFromShorthand(fields);
|
861 | }
|
862 | else {
|
863 | newValidation = fields;
|
864 | }
|
865 | validation = $.extend({}, validation, newValidation);
|
866 | },
|
867 | prompt: function(identifier, errors, internal) {
|
868 | var
|
869 | $field = module.get.field(identifier),
|
870 | $fieldGroup = $field.closest($group),
|
871 | $prompt = $fieldGroup.children(selector.prompt),
|
872 | promptExists = ($prompt.length !== 0)
|
873 | ;
|
874 | errors = (typeof errors == 'string')
|
875 | ? [errors]
|
876 | : errors
|
877 | ;
|
878 | module.verbose('Adding field error state', identifier);
|
879 | if(!internal) {
|
880 | $fieldGroup
|
881 | .addClass(className.error)
|
882 | ;
|
883 | }
|
884 | if(settings.inline) {
|
885 | if(!promptExists) {
|
886 | $prompt = settings.templates.prompt(errors, className.label);
|
887 | $prompt
|
888 | .appendTo($fieldGroup)
|
889 | ;
|
890 | }
|
891 | $prompt
|
892 | .html(errors[0])
|
893 | ;
|
894 | if(!promptExists) {
|
895 | if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
|
896 | module.verbose('Displaying error with css transition', settings.transition);
|
897 | $prompt.transition(settings.transition + ' in', settings.duration);
|
898 | }
|
899 | else {
|
900 | module.verbose('Displaying error with fallback javascript animation');
|
901 | $prompt
|
902 | .fadeIn(settings.duration)
|
903 | ;
|
904 | }
|
905 | }
|
906 | else {
|
907 | module.verbose('Inline errors are disabled, no inline error added', identifier);
|
908 | }
|
909 | }
|
910 | },
|
911 | errors: function(errors) {
|
912 | module.debug('Adding form error messages', errors);
|
913 | module.set.error();
|
914 | $message
|
915 | .html( settings.templates.error(errors) )
|
916 | ;
|
917 | }
|
918 | },
|
919 |
|
920 | remove: {
|
921 | rule: function(field, rule) {
|
922 | var
|
923 | rules = Array.isArray(rule)
|
924 | ? rule
|
925 | : [rule]
|
926 | ;
|
927 | if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
|
928 | return;
|
929 | }
|
930 | if(rule === undefined) {
|
931 | module.debug('Removed all rules');
|
932 | validation[field].rules = [];
|
933 | return;
|
934 | }
|
935 | $.each(validation[field].rules, function(index, rule) {
|
936 | if(rule && rules.indexOf(rule.type) !== -1) {
|
937 | module.debug('Removed rule', rule.type);
|
938 | validation[field].rules.splice(index, 1);
|
939 | }
|
940 | });
|
941 | },
|
942 | field: function(field) {
|
943 | var
|
944 | fields = Array.isArray(field)
|
945 | ? field
|
946 | : [field]
|
947 | ;
|
948 | $.each(fields, function(index, field) {
|
949 | module.remove.rule(field);
|
950 | });
|
951 | },
|
952 |
|
953 | rules: function(field, rules) {
|
954 | if(Array.isArray(field)) {
|
955 | $.each(field, function(index, field) {
|
956 | module.remove.rule(field, rules);
|
957 | });
|
958 | }
|
959 | else {
|
960 | module.remove.rule(field, rules);
|
961 | }
|
962 | },
|
963 | fields: function(fields) {
|
964 | module.remove.field(fields);
|
965 | },
|
966 | prompt: function(identifier) {
|
967 | var
|
968 | $field = module.get.field(identifier),
|
969 | $fieldGroup = $field.closest($group),
|
970 | $prompt = $fieldGroup.children(selector.prompt)
|
971 | ;
|
972 | $fieldGroup
|
973 | .removeClass(className.error)
|
974 | ;
|
975 | if(settings.inline && $prompt.is(':visible')) {
|
976 | module.verbose('Removing prompt for field', identifier);
|
977 | if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
|
978 | $prompt.transition(settings.transition + ' out', settings.duration, function() {
|
979 | $prompt.remove();
|
980 | });
|
981 | }
|
982 | else {
|
983 | $prompt
|
984 | .fadeOut(settings.duration, function(){
|
985 | $prompt.remove();
|
986 | })
|
987 | ;
|
988 | }
|
989 | }
|
990 | }
|
991 | },
|
992 |
|
993 | set: {
|
994 | success: function() {
|
995 | $module
|
996 | .removeClass(className.error)
|
997 | .addClass(className.success)
|
998 | ;
|
999 | },
|
1000 | defaults: function () {
|
1001 | $field.each(function (index, el) {
|
1002 | var
|
1003 | $el = $(el),
|
1004 | $parent = $el.parent(),
|
1005 | isCheckbox = ($el.filter(selector.checkbox).length > 0),
|
1006 | isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
|
1007 | $calendar = $el.closest(selector.uiCalendar),
|
1008 | isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
|
1009 | value = (isCheckbox)
|
1010 | ? $el.is(':checked')
|
1011 | : $el.val()
|
1012 | ;
|
1013 | if (isDropdown) {
|
1014 | $parent.dropdown('save defaults');
|
1015 | }
|
1016 | else if (isCalendar) {
|
1017 | $calendar.calendar('refresh');
|
1018 | }
|
1019 | $el.data(metadata.defaultValue, value);
|
1020 | $el.data(metadata.isDirty, false);
|
1021 | });
|
1022 | },
|
1023 | error: function() {
|
1024 | $module
|
1025 | .removeClass(className.success)
|
1026 | .addClass(className.error)
|
1027 | ;
|
1028 | },
|
1029 | value: function (field, value) {
|
1030 | var
|
1031 | fields = {}
|
1032 | ;
|
1033 | fields[field] = value;
|
1034 | return module.set.values.call(element, fields);
|
1035 | },
|
1036 | values: function (fields) {
|
1037 | if($.isEmptyObject(fields)) {
|
1038 | return;
|
1039 | }
|
1040 | $.each(fields, function(key, value) {
|
1041 | var
|
1042 | $field = module.get.field(key),
|
1043 | $element = $field.parent(),
|
1044 | $calendar = $field.closest(selector.uiCalendar),
|
1045 | isMultiple = Array.isArray(value),
|
1046 | isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
|
1047 | isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
|
1048 | isRadio = ($field.is(selector.radio) && isCheckbox),
|
1049 | isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
|
1050 | fieldExists = ($field.length > 0),
|
1051 | $multipleField
|
1052 | ;
|
1053 | if(fieldExists) {
|
1054 | if(isMultiple && isCheckbox) {
|
1055 | module.verbose('Selecting multiple', value, $field);
|
1056 | $element.checkbox('uncheck');
|
1057 | $.each(value, function(index, value) {
|
1058 | $multipleField = $field.filter('[value="' + value + '"]');
|
1059 | $element = $multipleField.parent();
|
1060 | if($multipleField.length > 0) {
|
1061 | $element.checkbox('check');
|
1062 | }
|
1063 | });
|
1064 | }
|
1065 | else if(isRadio) {
|
1066 | module.verbose('Selecting radio value', value, $field);
|
1067 | $field.filter('[value="' + value + '"]')
|
1068 | .parent(selector.uiCheckbox)
|
1069 | .checkbox('check')
|
1070 | ;
|
1071 | }
|
1072 | else if(isCheckbox) {
|
1073 | module.verbose('Setting checkbox value', value, $element);
|
1074 | if(value === true || value === 1) {
|
1075 | $element.checkbox('check');
|
1076 | }
|
1077 | else {
|
1078 | $element.checkbox('uncheck');
|
1079 | }
|
1080 | }
|
1081 | else if(isDropdown) {
|
1082 | module.verbose('Setting dropdown value', value, $element);
|
1083 | $element.dropdown('set selected', value);
|
1084 | }
|
1085 | else if (isCalendar) {
|
1086 | $calendar.calendar('set date',value);
|
1087 | }
|
1088 | else {
|
1089 | module.verbose('Setting field value', value, $field);
|
1090 | $field.val(value);
|
1091 | }
|
1092 | }
|
1093 | });
|
1094 | },
|
1095 | dirty: function() {
|
1096 | module.verbose('Setting state dirty');
|
1097 | dirty = true;
|
1098 | history[0] = history[1];
|
1099 | history[1] = 'dirty';
|
1100 |
|
1101 | if (module.is.justClean()) {
|
1102 | $module.trigger('dirty');
|
1103 | }
|
1104 | },
|
1105 | clean: function() {
|
1106 | module.verbose('Setting state clean');
|
1107 | dirty = false;
|
1108 | history[0] = history[1];
|
1109 | history[1] = 'clean';
|
1110 |
|
1111 | if (module.is.justDirty()) {
|
1112 | $module.trigger('clean');
|
1113 | }
|
1114 | },
|
1115 | asClean: function() {
|
1116 | module.set.defaults();
|
1117 | module.set.clean();
|
1118 | },
|
1119 | asDirty: function() {
|
1120 | module.set.defaults();
|
1121 | module.set.dirty();
|
1122 | },
|
1123 | autoCheck: function() {
|
1124 | module.debug('Enabling auto check on required fields');
|
1125 | $field.each(function (_index, el) {
|
1126 | var
|
1127 | $el = $(el),
|
1128 | $elGroup = $(el).closest($group),
|
1129 | isCheckbox = ($el.filter(selector.checkbox).length > 0),
|
1130 | isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
|
1131 | isDisabled = $el.prop('disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
|
1132 | validation = module.get.validation($el),
|
1133 | hasEmptyRule = validation
|
1134 | ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
|
1135 | : false,
|
1136 | identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
|
1137 | ;
|
1138 | if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
|
1139 | if (isCheckbox) {
|
1140 | module.verbose("Adding 'checked' rule on field", identifier);
|
1141 | module.add.rule(identifier, "checked");
|
1142 | } else {
|
1143 | module.verbose("Adding 'empty' rule on field", identifier);
|
1144 | module.add.rule(identifier, "empty");
|
1145 | }
|
1146 | }
|
1147 | });
|
1148 | }
|
1149 | },
|
1150 |
|
1151 | validate: {
|
1152 |
|
1153 | form: function(event, ignoreCallbacks) {
|
1154 | var values = module.get.values();
|
1155 |
|
1156 |
|
1157 | if(keyHeldDown) {
|
1158 | return false;
|
1159 | }
|
1160 |
|
1161 |
|
1162 | formErrors = [];
|
1163 | if( module.determine.isValid() ) {
|
1164 | module.debug('Form has no validation errors, submitting');
|
1165 | module.set.success();
|
1166 | if(ignoreCallbacks !== true) {
|
1167 | return settings.onSuccess.call(element, event, values);
|
1168 | }
|
1169 | }
|
1170 | else {
|
1171 | module.debug('Form has errors');
|
1172 | module.set.error();
|
1173 | if(!settings.inline) {
|
1174 | module.add.errors(formErrors);
|
1175 | }
|
1176 |
|
1177 | if(event && $module.data('moduleApi') !== undefined) {
|
1178 | event.stopImmediatePropagation();
|
1179 | }
|
1180 | if(ignoreCallbacks !== true) {
|
1181 | return settings.onFailure.call(element, formErrors, values);
|
1182 | }
|
1183 | }
|
1184 | },
|
1185 |
|
1186 |
|
1187 | field: function(field, fieldName, showErrors) {
|
1188 | showErrors = (showErrors !== undefined)
|
1189 | ? showErrors
|
1190 | : true
|
1191 | ;
|
1192 | if(typeof field == 'string') {
|
1193 | module.verbose('Validating field', field);
|
1194 | fieldName = field;
|
1195 | field = validation[field];
|
1196 | }
|
1197 | var
|
1198 | identifier = field.identifier || fieldName,
|
1199 | $field = module.get.field(identifier),
|
1200 | $dependsField = (field.depends)
|
1201 | ? module.get.field(field.depends)
|
1202 | : false,
|
1203 | fieldValid = true,
|
1204 | fieldErrors = []
|
1205 | ;
|
1206 | if(!field.identifier) {
|
1207 | module.debug('Using field name as identifier', identifier);
|
1208 | field.identifier = identifier;
|
1209 | }
|
1210 | var isDisabled = true;
|
1211 | $.each($field, function(){
|
1212 | if(!$(this).prop('disabled')) {
|
1213 | isDisabled = false;
|
1214 | return false;
|
1215 | }
|
1216 | });
|
1217 | if(isDisabled) {
|
1218 | module.debug('Field is disabled. Skipping', identifier);
|
1219 | }
|
1220 | else if(field.optional && module.is.blank($field)){
|
1221 | module.debug('Field is optional and blank. Skipping', identifier);
|
1222 | }
|
1223 | else if(field.depends && module.is.empty($dependsField)) {
|
1224 | module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
|
1225 | }
|
1226 | else if(field.rules !== undefined) {
|
1227 | $field.closest($group).removeClass(className.error);
|
1228 | $.each(field.rules, function(index, rule) {
|
1229 | if( module.has.field(identifier)) {
|
1230 | var invalidFields = module.validate.rule(field, rule,true) || [];
|
1231 | if (invalidFields.length>0){
|
1232 | module.debug('Field is invalid', identifier, rule.type);
|
1233 | fieldErrors.push(module.get.prompt(rule, field));
|
1234 | fieldValid = false;
|
1235 | if(showErrors){
|
1236 | $(invalidFields).closest($group).addClass(className.error);
|
1237 | }
|
1238 | }
|
1239 | }
|
1240 | });
|
1241 | }
|
1242 | if(fieldValid) {
|
1243 | if(showErrors) {
|
1244 | module.remove.prompt(identifier, fieldErrors);
|
1245 | settings.onValid.call($field);
|
1246 | }
|
1247 | }
|
1248 | else {
|
1249 | if(showErrors) {
|
1250 | formErrors = formErrors.concat(fieldErrors);
|
1251 | module.add.prompt(identifier, fieldErrors, true);
|
1252 | settings.onInvalid.call($field, fieldErrors);
|
1253 | }
|
1254 | return false;
|
1255 | }
|
1256 | return true;
|
1257 | },
|
1258 |
|
1259 |
|
1260 | rule: function(field, rule, internal) {
|
1261 | var
|
1262 | $field = module.get.field(field.identifier),
|
1263 | ancillary = module.get.ancillaryValue(rule),
|
1264 | ruleName = module.get.ruleName(rule),
|
1265 | ruleFunction = settings.rules[ruleName],
|
1266 | invalidFields = [],
|
1267 | isCheckbox = $field.is(selector.checkbox),
|
1268 | isValid = function(field){
|
1269 | var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
|
1270 |
|
1271 | value = (value === undefined || value === '' || value === null)
|
1272 | ? ''
|
1273 | : (settings.shouldTrim) ? $.trim(value + '') : String(value + '')
|
1274 | ;
|
1275 | return ruleFunction.call(field, value, ancillary, $module);
|
1276 | }
|
1277 | ;
|
1278 | if( !$.isFunction(ruleFunction) ) {
|
1279 | module.error(error.noRule, ruleName);
|
1280 | return;
|
1281 | }
|
1282 | if(isCheckbox) {
|
1283 | if (!isValid($field)) {
|
1284 | invalidFields = $field;
|
1285 | }
|
1286 | } else {
|
1287 | $.each($field, function (index, field) {
|
1288 | if (!isValid(field)) {
|
1289 | invalidFields.push(field);
|
1290 | }
|
1291 | });
|
1292 | }
|
1293 | return internal ? invalidFields : !(invalidFields.length>0);
|
1294 | }
|
1295 | },
|
1296 |
|
1297 | setting: function(name, value) {
|
1298 | if( $.isPlainObject(name) ) {
|
1299 | $.extend(true, settings, name);
|
1300 | }
|
1301 | else if(value !== undefined) {
|
1302 | settings[name] = value;
|
1303 | }
|
1304 | else {
|
1305 | return settings[name];
|
1306 | }
|
1307 | },
|
1308 | internal: function(name, value) {
|
1309 | if( $.isPlainObject(name) ) {
|
1310 | $.extend(true, module, name);
|
1311 | }
|
1312 | else if(value !== undefined) {
|
1313 | module[name] = value;
|
1314 | }
|
1315 | else {
|
1316 | return module[name];
|
1317 | }
|
1318 | },
|
1319 | debug: function() {
|
1320 | if(!settings.silent && settings.debug) {
|
1321 | if(settings.performance) {
|
1322 | module.performance.log(arguments);
|
1323 | }
|
1324 | else {
|
1325 | module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
1326 | module.debug.apply(console, arguments);
|
1327 | }
|
1328 | }
|
1329 | },
|
1330 | verbose: function() {
|
1331 | if(!settings.silent && settings.verbose && settings.debug) {
|
1332 | if(settings.performance) {
|
1333 | module.performance.log(arguments);
|
1334 | }
|
1335 | else {
|
1336 | module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
1337 | module.verbose.apply(console, arguments);
|
1338 | }
|
1339 | }
|
1340 | },
|
1341 | error: function() {
|
1342 | if(!settings.silent) {
|
1343 | module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
|
1344 | module.error.apply(console, arguments);
|
1345 | }
|
1346 | },
|
1347 | performance: {
|
1348 | log: function(message) {
|
1349 | var
|
1350 | currentTime,
|
1351 | executionTime,
|
1352 | previousTime
|
1353 | ;
|
1354 | if(settings.performance) {
|
1355 | currentTime = new Date().getTime();
|
1356 | previousTime = time || currentTime;
|
1357 | executionTime = currentTime - previousTime;
|
1358 | time = currentTime;
|
1359 | performance.push({
|
1360 | 'Name' : message[0],
|
1361 | 'Arguments' : [].slice.call(message, 1) || '',
|
1362 | 'Element' : element,
|
1363 | 'Execution Time' : executionTime
|
1364 | });
|
1365 | }
|
1366 | clearTimeout(module.performance.timer);
|
1367 | module.performance.timer = setTimeout(module.performance.display, 500);
|
1368 | },
|
1369 | display: function() {
|
1370 | var
|
1371 | title = settings.name + ':',
|
1372 | totalTime = 0
|
1373 | ;
|
1374 | time = false;
|
1375 | clearTimeout(module.performance.timer);
|
1376 | $.each(performance, function(index, data) {
|
1377 | totalTime += data['Execution Time'];
|
1378 | });
|
1379 | title += ' ' + totalTime + 'ms';
|
1380 | if(moduleSelector) {
|
1381 | title += ' \'' + moduleSelector + '\'';
|
1382 | }
|
1383 | if($allModules.length > 1) {
|
1384 | title += ' ' + '(' + $allModules.length + ')';
|
1385 | }
|
1386 | if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
|
1387 | console.groupCollapsed(title);
|
1388 | if(console.table) {
|
1389 | console.table(performance);
|
1390 | }
|
1391 | else {
|
1392 | $.each(performance, function(index, data) {
|
1393 | console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
|
1394 | });
|
1395 | }
|
1396 | console.groupEnd();
|
1397 | }
|
1398 | performance = [];
|
1399 | }
|
1400 | },
|
1401 | invoke: function(query, passedArguments, context) {
|
1402 | var
|
1403 | object = instance,
|
1404 | maxDepth,
|
1405 | found,
|
1406 | response
|
1407 | ;
|
1408 | passedArguments = passedArguments || queryArguments;
|
1409 | context = element || context;
|
1410 | if(typeof query == 'string' && object !== undefined) {
|
1411 | query = query.split(/[\. ]/);
|
1412 | maxDepth = query.length - 1;
|
1413 | $.each(query, function(depth, value) {
|
1414 | var camelCaseValue = (depth != maxDepth)
|
1415 | ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
|
1416 | : query
|
1417 | ;
|
1418 | if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
|
1419 | object = object[camelCaseValue];
|
1420 | }
|
1421 | else if( object[camelCaseValue] !== undefined ) {
|
1422 | found = object[camelCaseValue];
|
1423 | return false;
|
1424 | }
|
1425 | else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
|
1426 | object = object[value];
|
1427 | }
|
1428 | else if( object[value] !== undefined ) {
|
1429 | found = object[value];
|
1430 | return false;
|
1431 | }
|
1432 | else {
|
1433 | return false;
|
1434 | }
|
1435 | });
|
1436 | }
|
1437 | if( $.isFunction( found ) ) {
|
1438 | response = found.apply(context, passedArguments);
|
1439 | }
|
1440 | else if(found !== undefined) {
|
1441 | response = found;
|
1442 | }
|
1443 | if(Array.isArray(returnedValue)) {
|
1444 | returnedValue.push(response);
|
1445 | }
|
1446 | else if(returnedValue !== undefined) {
|
1447 | returnedValue = [returnedValue, response];
|
1448 | }
|
1449 | else if(response !== undefined) {
|
1450 | returnedValue = response;
|
1451 | }
|
1452 | return found;
|
1453 | }
|
1454 | };
|
1455 | module.initialize();
|
1456 | })
|
1457 | ;
|
1458 |
|
1459 | return (returnedValue !== undefined)
|
1460 | ? returnedValue
|
1461 | : this
|
1462 | ;
|
1463 | };
|
1464 |
|
1465 | $.fn.form.settings = {
|
1466 |
|
1467 | name : 'Form',
|
1468 | namespace : 'form',
|
1469 |
|
1470 | debug : false,
|
1471 | verbose : false,
|
1472 | performance : true,
|
1473 |
|
1474 | fields : false,
|
1475 |
|
1476 | keyboardShortcuts : true,
|
1477 | on : 'submit',
|
1478 | inline : false,
|
1479 |
|
1480 | delay : 200,
|
1481 | revalidate : true,
|
1482 | shouldTrim : true,
|
1483 |
|
1484 | transition : 'scale',
|
1485 | duration : 200,
|
1486 |
|
1487 | autoCheckRequired : false,
|
1488 | preventLeaving : false,
|
1489 | dateHandling : 'date',
|
1490 |
|
1491 | onValid : function() {},
|
1492 | onInvalid : function() {},
|
1493 | onSuccess : function() { return true; },
|
1494 | onFailure : function() { return false; },
|
1495 | onDirty : function() {},
|
1496 | onClean : function() {},
|
1497 |
|
1498 | metadata : {
|
1499 | defaultValue : 'default',
|
1500 | validate : 'validate',
|
1501 | isDirty : 'isDirty'
|
1502 | },
|
1503 |
|
1504 | regExp: {
|
1505 | htmlID : /^[a-zA-Z][\w:.-]*$/g,
|
1506 | bracket : /\[(.*)\]/i,
|
1507 | decimal : /^\d+\.?\d*$/,
|
1508 | email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
|
1509 | escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
|
1510 | flags : /^\/(.*)\/(.*)?/,
|
1511 | integer : /^\-?\d+$/,
|
1512 | number : /^\-?\d*(\.\d+)?$/,
|
1513 | url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
|
1514 | },
|
1515 |
|
1516 | text: {
|
1517 | unspecifiedRule : 'Please enter a valid value',
|
1518 | unspecifiedField : 'This field',
|
1519 | leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
|
1520 | },
|
1521 |
|
1522 | prompt: {
|
1523 | empty : '{name} must have a value',
|
1524 | checked : '{name} must be checked',
|
1525 | email : '{name} must be a valid e-mail',
|
1526 | url : '{name} must be a valid url',
|
1527 | regExp : '{name} is not formatted correctly',
|
1528 | integer : '{name} must be an integer',
|
1529 | decimal : '{name} must be a decimal number',
|
1530 | number : '{name} must be set to a number',
|
1531 | is : '{name} must be "{ruleValue}"',
|
1532 | isExactly : '{name} must be exactly "{ruleValue}"',
|
1533 | not : '{name} cannot be set to "{ruleValue}"',
|
1534 | notExactly : '{name} cannot be set to exactly "{ruleValue}"',
|
1535 | contain : '{name} must contain "{ruleValue}"',
|
1536 | containExactly : '{name} must contain exactly "{ruleValue}"',
|
1537 | doesntContain : '{name} cannot contain "{ruleValue}"',
|
1538 | doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
|
1539 | minLength : '{name} must be at least {ruleValue} characters',
|
1540 | length : '{name} must be at least {ruleValue} characters',
|
1541 | exactLength : '{name} must be exactly {ruleValue} characters',
|
1542 | maxLength : '{name} cannot be longer than {ruleValue} characters',
|
1543 | match : '{name} must match {ruleValue} field',
|
1544 | different : '{name} must have a different value than {ruleValue} field',
|
1545 | creditCard : '{name} must be a valid credit card number',
|
1546 | minCount : '{name} must have at least {ruleValue} choices',
|
1547 | exactCount : '{name} must have exactly {ruleValue} choices',
|
1548 | maxCount : '{name} must have {ruleValue} or less choices'
|
1549 | },
|
1550 |
|
1551 | selector : {
|
1552 | checkbox : 'input[type="checkbox"], input[type="radio"]',
|
1553 | clear : '.clear',
|
1554 | field : 'input, textarea, select',
|
1555 | group : '.field',
|
1556 | input : 'input',
|
1557 | message : '.error.message',
|
1558 | prompt : '.prompt.label',
|
1559 | radio : 'input[type="radio"]',
|
1560 | reset : '.reset:not([type="reset"])',
|
1561 | submit : '.submit:not([type="submit"])',
|
1562 | uiCheckbox : '.ui.checkbox',
|
1563 | uiDropdown : '.ui.dropdown',
|
1564 | uiCalendar : '.ui.calendar'
|
1565 | },
|
1566 |
|
1567 | className : {
|
1568 | error : 'error',
|
1569 | label : 'ui basic red pointing prompt label',
|
1570 | pressed : 'down',
|
1571 | success : 'success',
|
1572 | required : 'required',
|
1573 | disabled : 'disabled'
|
1574 | },
|
1575 |
|
1576 | error: {
|
1577 | identifier : 'You must specify a string identifier for each field',
|
1578 | method : 'The method you called is not defined.',
|
1579 | noRule : 'There is no rule matching the one you specified',
|
1580 | oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
|
1581 | noElement : 'This module requires ui {element}'
|
1582 | },
|
1583 |
|
1584 | templates: {
|
1585 |
|
1586 |
|
1587 | error: function(errors) {
|
1588 | var
|
1589 | html = '<ul class="list">'
|
1590 | ;
|
1591 | $.each(errors, function(index, value) {
|
1592 | html += '<li>' + value + '</li>';
|
1593 | });
|
1594 | html += '</ul>';
|
1595 | return $(html);
|
1596 | },
|
1597 |
|
1598 |
|
1599 | prompt: function(errors, labelClasses) {
|
1600 | return $('<div/>')
|
1601 | .addClass(labelClasses)
|
1602 | .html(errors[0])
|
1603 | ;
|
1604 | }
|
1605 | },
|
1606 |
|
1607 | formatter: {
|
1608 | date: function(date) {
|
1609 | return Intl.DateTimeFormat('en-GB').format(date);
|
1610 | },
|
1611 | datetime: function(date) {
|
1612 | return Intl.DateTimeFormat('en-GB', {
|
1613 | year: "numeric",
|
1614 | month: "2-digit",
|
1615 | day: "2-digit",
|
1616 | hour: '2-digit',
|
1617 | minute: '2-digit',
|
1618 | second: '2-digit'
|
1619 | }).format(date);
|
1620 | },
|
1621 | time: function(date) {
|
1622 | return Intl.DateTimeFormat('en-GB', {
|
1623 | hour: '2-digit',
|
1624 | minute: '2-digit',
|
1625 | second: '2-digit'
|
1626 | }).format(date);
|
1627 | },
|
1628 | month: function(date) {
|
1629 | return Intl.DateTimeFormat('en-GB', {
|
1630 | month: '2-digit',
|
1631 | year: 'numeric'
|
1632 | }).format(date);
|
1633 | },
|
1634 | year: function(date) {
|
1635 | return Intl.DateTimeFormat('en-GB', {
|
1636 | year: 'numeric'
|
1637 | }).format(date);
|
1638 | }
|
1639 | },
|
1640 |
|
1641 | rules: {
|
1642 |
|
1643 |
|
1644 | empty: function(value) {
|
1645 | return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
|
1646 | },
|
1647 |
|
1648 |
|
1649 | checked: function() {
|
1650 | return ($(this).filter(':checked').length > 0);
|
1651 | },
|
1652 |
|
1653 |
|
1654 | email: function(value){
|
1655 | return $.fn.form.settings.regExp.email.test(value);
|
1656 | },
|
1657 |
|
1658 |
|
1659 | url: function(value) {
|
1660 | return $.fn.form.settings.regExp.url.test(value);
|
1661 | },
|
1662 |
|
1663 |
|
1664 | regExp: function(value, regExp) {
|
1665 | if(regExp instanceof RegExp) {
|
1666 | return value.match(regExp);
|
1667 | }
|
1668 | var
|
1669 | regExpParts = regExp.match($.fn.form.settings.regExp.flags),
|
1670 | flags
|
1671 | ;
|
1672 |
|
1673 | if(regExpParts) {
|
1674 | regExp = (regExpParts.length >= 2)
|
1675 | ? regExpParts[1]
|
1676 | : regExp
|
1677 | ;
|
1678 | flags = (regExpParts.length >= 3)
|
1679 | ? regExpParts[2]
|
1680 | : ''
|
1681 | ;
|
1682 | }
|
1683 | return value.match( new RegExp(regExp, flags) );
|
1684 | },
|
1685 |
|
1686 |
|
1687 | integer: function(value, range) {
|
1688 | var
|
1689 | intRegExp = $.fn.form.settings.regExp.integer,
|
1690 | min,
|
1691 | max,
|
1692 | parts
|
1693 | ;
|
1694 | if( !range || ['', '..'].indexOf(range) !== -1) {
|
1695 |
|
1696 | }
|
1697 | else if(range.indexOf('..') == -1) {
|
1698 | if(intRegExp.test(range)) {
|
1699 | min = max = range - 0;
|
1700 | }
|
1701 | }
|
1702 | else {
|
1703 | parts = range.split('..', 2);
|
1704 | if(intRegExp.test(parts[0])) {
|
1705 | min = parts[0] - 0;
|
1706 | }
|
1707 | if(intRegExp.test(parts[1])) {
|
1708 | max = parts[1] - 0;
|
1709 | }
|
1710 | }
|
1711 | return (
|
1712 | intRegExp.test(value) &&
|
1713 | (min === undefined || value >= min) &&
|
1714 | (max === undefined || value <= max)
|
1715 | );
|
1716 | },
|
1717 |
|
1718 |
|
1719 | decimal: function(value) {
|
1720 | return $.fn.form.settings.regExp.decimal.test(value);
|
1721 | },
|
1722 |
|
1723 |
|
1724 | number: function(value) {
|
1725 | return $.fn.form.settings.regExp.number.test(value);
|
1726 | },
|
1727 |
|
1728 |
|
1729 | is: function(value, text) {
|
1730 | text = (typeof text == 'string')
|
1731 | ? text.toLowerCase()
|
1732 | : text
|
1733 | ;
|
1734 | value = (typeof value == 'string')
|
1735 | ? value.toLowerCase()
|
1736 | : value
|
1737 | ;
|
1738 | return (value == text);
|
1739 | },
|
1740 |
|
1741 |
|
1742 | isExactly: function(value, text) {
|
1743 | return (value == text);
|
1744 | },
|
1745 |
|
1746 |
|
1747 | not: function(value, notValue) {
|
1748 | value = (typeof value == 'string')
|
1749 | ? value.toLowerCase()
|
1750 | : value
|
1751 | ;
|
1752 | notValue = (typeof notValue == 'string')
|
1753 | ? notValue.toLowerCase()
|
1754 | : notValue
|
1755 | ;
|
1756 | return (value != notValue);
|
1757 | },
|
1758 |
|
1759 |
|
1760 | notExactly: function(value, notValue) {
|
1761 | return (value != notValue);
|
1762 | },
|
1763 |
|
1764 |
|
1765 | contains: function(value, text) {
|
1766 |
|
1767 | text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
|
1768 | return (value.search( new RegExp(text, 'i') ) !== -1);
|
1769 | },
|
1770 |
|
1771 |
|
1772 | containsExactly: function(value, text) {
|
1773 |
|
1774 | text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
|
1775 | return (value.search( new RegExp(text) ) !== -1);
|
1776 | },
|
1777 |
|
1778 |
|
1779 | doesntContain: function(value, text) {
|
1780 |
|
1781 | text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
|
1782 | return (value.search( new RegExp(text, 'i') ) === -1);
|
1783 | },
|
1784 |
|
1785 |
|
1786 | doesntContainExactly: function(value, text) {
|
1787 |
|
1788 | text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
|
1789 | return (value.search( new RegExp(text) ) === -1);
|
1790 | },
|
1791 |
|
1792 |
|
1793 | minLength: function(value, requiredLength) {
|
1794 | return (value !== undefined)
|
1795 | ? (value.length >= requiredLength)
|
1796 | : false
|
1797 | ;
|
1798 | },
|
1799 |
|
1800 |
|
1801 | length: function(value, requiredLength) {
|
1802 | return (value !== undefined)
|
1803 | ? (value.length >= requiredLength)
|
1804 | : false
|
1805 | ;
|
1806 | },
|
1807 |
|
1808 |
|
1809 | exactLength: function(value, requiredLength) {
|
1810 | return (value !== undefined)
|
1811 | ? (value.length == requiredLength)
|
1812 | : false
|
1813 | ;
|
1814 | },
|
1815 |
|
1816 |
|
1817 | maxLength: function(value, maxLength) {
|
1818 | return (value !== undefined)
|
1819 | ? (value.length <= maxLength)
|
1820 | : false
|
1821 | ;
|
1822 | },
|
1823 |
|
1824 |
|
1825 | match: function(value, identifier, $module) {
|
1826 | var
|
1827 | matchingValue,
|
1828 | matchingElement
|
1829 | ;
|
1830 | if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
|
1831 | matchingValue = matchingElement.val();
|
1832 | }
|
1833 | else if((matchingElement = $module.find('#' + identifier)).length > 0) {
|
1834 | matchingValue = matchingElement.val();
|
1835 | }
|
1836 | else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
|
1837 | matchingValue = matchingElement.val();
|
1838 | }
|
1839 | else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
|
1840 | matchingValue = matchingElement;
|
1841 | }
|
1842 | return (matchingValue !== undefined)
|
1843 | ? ( value.toString() == matchingValue.toString() )
|
1844 | : false
|
1845 | ;
|
1846 | },
|
1847 |
|
1848 |
|
1849 | different: function(value, identifier, $module) {
|
1850 |
|
1851 | var
|
1852 | matchingValue,
|
1853 | matchingElement
|
1854 | ;
|
1855 | if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
|
1856 | matchingValue = matchingElement.val();
|
1857 | }
|
1858 | else if((matchingElement = $module.find('#' + identifier)).length > 0) {
|
1859 | matchingValue = matchingElement.val();
|
1860 | }
|
1861 | else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
|
1862 | matchingValue = matchingElement.val();
|
1863 | }
|
1864 | else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
|
1865 | matchingValue = matchingElement;
|
1866 | }
|
1867 | return (matchingValue !== undefined)
|
1868 | ? ( value.toString() !== matchingValue.toString() )
|
1869 | : false
|
1870 | ;
|
1871 | },
|
1872 |
|
1873 | creditCard: function(cardNumber, cardTypes) {
|
1874 | var
|
1875 | cards = {
|
1876 | visa: {
|
1877 | pattern : /^4/,
|
1878 | length : [16]
|
1879 | },
|
1880 | amex: {
|
1881 | pattern : /^3[47]/,
|
1882 | length : [15]
|
1883 | },
|
1884 | mastercard: {
|
1885 | pattern : /^5[1-5]/,
|
1886 | length : [16]
|
1887 | },
|
1888 | discover: {
|
1889 | pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
|
1890 | length : [16]
|
1891 | },
|
1892 | unionPay: {
|
1893 | pattern : /^(62|88)/,
|
1894 | length : [16, 17, 18, 19]
|
1895 | },
|
1896 | jcb: {
|
1897 | pattern : /^35(2[89]|[3-8][0-9])/,
|
1898 | length : [16]
|
1899 | },
|
1900 | maestro: {
|
1901 | pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
|
1902 | length : [12, 13, 14, 15, 16, 17, 18, 19]
|
1903 | },
|
1904 | dinersClub: {
|
1905 | pattern : /^(30[0-5]|^36)/,
|
1906 | length : [14]
|
1907 | },
|
1908 | laser: {
|
1909 | pattern : /^(6304|670[69]|6771)/,
|
1910 | length : [16, 17, 18, 19]
|
1911 | },
|
1912 | visaElectron: {
|
1913 | pattern : /^(4026|417500|4508|4844|491(3|7))/,
|
1914 | length : [16]
|
1915 | }
|
1916 | },
|
1917 | valid = {},
|
1918 | validCard = false,
|
1919 | requiredTypes = (typeof cardTypes == 'string')
|
1920 | ? cardTypes.split(',')
|
1921 | : false,
|
1922 | unionPay,
|
1923 | validation
|
1924 | ;
|
1925 |
|
1926 | if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
|
1927 | return;
|
1928 | }
|
1929 |
|
1930 |
|
1931 | cardNumber = cardNumber.replace(/[\-]/g, '');
|
1932 |
|
1933 |
|
1934 | if(requiredTypes) {
|
1935 | $.each(requiredTypes, function(index, type){
|
1936 |
|
1937 | validation = cards[type];
|
1938 | if(validation) {
|
1939 | valid = {
|
1940 | length : ($.inArray(cardNumber.length, validation.length) !== -1),
|
1941 | pattern : (cardNumber.search(validation.pattern) !== -1)
|
1942 | };
|
1943 | if(valid.length && valid.pattern) {
|
1944 | validCard = true;
|
1945 | }
|
1946 | }
|
1947 | });
|
1948 |
|
1949 | if(!validCard) {
|
1950 | return false;
|
1951 | }
|
1952 | }
|
1953 |
|
1954 |
|
1955 | unionPay = {
|
1956 | number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
|
1957 | pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
|
1958 | };
|
1959 | if(unionPay.number && unionPay.pattern) {
|
1960 | return true;
|
1961 | }
|
1962 |
|
1963 |
|
1964 | var
|
1965 | length = cardNumber.length,
|
1966 | multiple = 0,
|
1967 | producedValue = [
|
1968 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
1969 | [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
|
1970 | ],
|
1971 | sum = 0
|
1972 | ;
|
1973 | while (length--) {
|
1974 | sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
|
1975 | multiple ^= 1;
|
1976 | }
|
1977 | return (sum % 10 === 0 && sum > 0);
|
1978 | },
|
1979 |
|
1980 | minCount: function(value, minCount) {
|
1981 | if(minCount == 0) {
|
1982 | return true;
|
1983 | }
|
1984 | if(minCount == 1) {
|
1985 | return (value !== '');
|
1986 | }
|
1987 | return (value.split(',').length >= minCount);
|
1988 | },
|
1989 |
|
1990 | exactCount: function(value, exactCount) {
|
1991 | if(exactCount == 0) {
|
1992 | return (value === '');
|
1993 | }
|
1994 | if(exactCount == 1) {
|
1995 | return (value !== '' && value.search(',') === -1);
|
1996 | }
|
1997 | return (value.split(',').length == exactCount);
|
1998 | },
|
1999 |
|
2000 | maxCount: function(value, maxCount) {
|
2001 | if(maxCount == 0) {
|
2002 | return false;
|
2003 | }
|
2004 | if(maxCount == 1) {
|
2005 | return (value.search(',') === -1);
|
2006 | }
|
2007 | return (value.split(',').length <= maxCount);
|
2008 | }
|
2009 | }
|
2010 |
|
2011 | };
|
2012 |
|
2013 | })( jQuery, window, document );
|