UNPKG

973 kBJavaScriptView Raw
1/*
2 * # Fomantic UI - 2.8.8
3 * https://github.com/fomantic/Fomantic-UI
4 * http://fomantic-ui.com/
5 *
6 * Copyright 2021 Contributors
7 * Released under the MIT license
8 * http://opensource.org/licenses/MIT
9 *
10 */
11/*!
12 * # Fomantic-UI 2.8.8 - Site
13 * http://github.com/fomantic/Fomantic-UI/
14 *
15 *
16 * Released under the MIT license
17 * http://opensource.org/licenses/MIT
18 *
19 */
20
21;(function ($, window, document, undefined) {
22
23$.isFunction = $.isFunction || function(obj) {
24 return typeof obj === "function" && typeof obj.nodeType !== "number";
25};
26
27$.site = $.fn.site = function(parameters) {
28 var
29 time = new Date().getTime(),
30 performance = [],
31
32 query = arguments[0],
33 methodInvoked = (typeof query == 'string'),
34 queryArguments = [].slice.call(arguments, 1),
35
36 settings = ( $.isPlainObject(parameters) )
37 ? $.extend(true, {}, $.site.settings, parameters)
38 : $.extend({}, $.site.settings),
39
40 namespace = settings.namespace,
41 error = settings.error,
42
43 moduleNamespace = 'module-' + namespace,
44
45 $document = $(document),
46 $module = $document,
47 element = this,
48 instance = $module.data(moduleNamespace),
49
50 module,
51 returnedValue
52 ;
53 module = {
54
55 initialize: function() {
56 module.instantiate();
57 },
58
59 instantiate: function() {
60 module.verbose('Storing instance of site', module);
61 instance = module;
62 $module
63 .data(moduleNamespace, module)
64 ;
65 },
66
67 normalize: function() {
68 module.fix.console();
69 module.fix.requestAnimationFrame();
70 },
71
72 fix: {
73 console: function() {
74 module.debug('Normalizing window.console');
75 if (console === undefined || console.log === undefined) {
76 module.verbose('Console not available, normalizing events');
77 module.disable.console();
78 }
79 if (typeof console.group == 'undefined' || typeof console.groupEnd == 'undefined' || typeof console.groupCollapsed == 'undefined') {
80 module.verbose('Console group not available, normalizing events');
81 window.console.group = function() {};
82 window.console.groupEnd = function() {};
83 window.console.groupCollapsed = function() {};
84 }
85 if (typeof console.markTimeline == 'undefined') {
86 module.verbose('Mark timeline not available, normalizing events');
87 window.console.markTimeline = function() {};
88 }
89 },
90 consoleClear: function() {
91 module.debug('Disabling programmatic console clearing');
92 window.console.clear = function() {};
93 },
94 requestAnimationFrame: function() {
95 module.debug('Normalizing requestAnimationFrame');
96 if(window.requestAnimationFrame === undefined) {
97 module.debug('RequestAnimationFrame not available, normalizing event');
98 window.requestAnimationFrame = window.requestAnimationFrame
99 || window.mozRequestAnimationFrame
100 || window.webkitRequestAnimationFrame
101 || window.msRequestAnimationFrame
102 || function(callback) { setTimeout(callback, 0); }
103 ;
104 }
105 }
106 },
107
108 moduleExists: function(name) {
109 return ($.fn[name] !== undefined && $.fn[name].settings !== undefined);
110 },
111
112 enabled: {
113 modules: function(modules) {
114 var
115 enabledModules = []
116 ;
117 modules = modules || settings.modules;
118 $.each(modules, function(index, name) {
119 if(module.moduleExists(name)) {
120 enabledModules.push(name);
121 }
122 });
123 return enabledModules;
124 }
125 },
126
127 disabled: {
128 modules: function(modules) {
129 var
130 disabledModules = []
131 ;
132 modules = modules || settings.modules;
133 $.each(modules, function(index, name) {
134 if(!module.moduleExists(name)) {
135 disabledModules.push(name);
136 }
137 });
138 return disabledModules;
139 }
140 },
141
142 change: {
143 setting: function(setting, value, modules, modifyExisting) {
144 modules = (typeof modules === 'string')
145 ? (modules === 'all')
146 ? settings.modules
147 : [modules]
148 : modules || settings.modules
149 ;
150 modifyExisting = (modifyExisting !== undefined)
151 ? modifyExisting
152 : true
153 ;
154 $.each(modules, function(index, name) {
155 var
156 namespace = (module.moduleExists(name))
157 ? $.fn[name].settings.namespace || false
158 : true,
159 $existingModules
160 ;
161 if(module.moduleExists(name)) {
162 module.verbose('Changing default setting', setting, value, name);
163 $.fn[name].settings[setting] = value;
164 if(modifyExisting && namespace) {
165 $existingModules = $(':data(module-' + namespace + ')');
166 if($existingModules.length > 0) {
167 module.verbose('Modifying existing settings', $existingModules);
168 $existingModules[name]('setting', setting, value);
169 }
170 }
171 }
172 });
173 },
174 settings: function(newSettings, modules, modifyExisting) {
175 modules = (typeof modules === 'string')
176 ? [modules]
177 : modules || settings.modules
178 ;
179 modifyExisting = (modifyExisting !== undefined)
180 ? modifyExisting
181 : true
182 ;
183 $.each(modules, function(index, name) {
184 var
185 $existingModules
186 ;
187 if(module.moduleExists(name)) {
188 module.verbose('Changing default setting', newSettings, name);
189 $.extend(true, $.fn[name].settings, newSettings);
190 if(modifyExisting && namespace) {
191 $existingModules = $(':data(module-' + namespace + ')');
192 if($existingModules.length > 0) {
193 module.verbose('Modifying existing settings', $existingModules);
194 $existingModules[name]('setting', newSettings);
195 }
196 }
197 }
198 });
199 }
200 },
201
202 enable: {
203 console: function() {
204 module.console(true);
205 },
206 debug: function(modules, modifyExisting) {
207 modules = modules || settings.modules;
208 module.debug('Enabling debug for modules', modules);
209 module.change.setting('debug', true, modules, modifyExisting);
210 },
211 verbose: function(modules, modifyExisting) {
212 modules = modules || settings.modules;
213 module.debug('Enabling verbose debug for modules', modules);
214 module.change.setting('verbose', true, modules, modifyExisting);
215 }
216 },
217 disable: {
218 console: function() {
219 module.console(false);
220 },
221 debug: function(modules, modifyExisting) {
222 modules = modules || settings.modules;
223 module.debug('Disabling debug for modules', modules);
224 module.change.setting('debug', false, modules, modifyExisting);
225 },
226 verbose: function(modules, modifyExisting) {
227 modules = modules || settings.modules;
228 module.debug('Disabling verbose debug for modules', modules);
229 module.change.setting('verbose', false, modules, modifyExisting);
230 }
231 },
232
233 console: function(enable) {
234 if(enable) {
235 if(instance.cache.console === undefined) {
236 module.error(error.console);
237 return;
238 }
239 module.debug('Restoring console function');
240 window.console = instance.cache.console;
241 }
242 else {
243 module.debug('Disabling console function');
244 instance.cache.console = window.console;
245 window.console = {
246 clear : function(){},
247 error : function(){},
248 group : function(){},
249 groupCollapsed : function(){},
250 groupEnd : function(){},
251 info : function(){},
252 log : function(){},
253 markTimeline : function(){},
254 warn : function(){}
255 };
256 }
257 },
258
259 destroy: function() {
260 module.verbose('Destroying previous site for', $module);
261 $module
262 .removeData(moduleNamespace)
263 ;
264 },
265
266 cache: {},
267
268 setting: function(name, value) {
269 if( $.isPlainObject(name) ) {
270 $.extend(true, settings, name);
271 }
272 else if(value !== undefined) {
273 settings[name] = value;
274 }
275 else {
276 return settings[name];
277 }
278 },
279 internal: function(name, value) {
280 if( $.isPlainObject(name) ) {
281 $.extend(true, module, name);
282 }
283 else if(value !== undefined) {
284 module[name] = value;
285 }
286 else {
287 return module[name];
288 }
289 },
290 debug: function() {
291 if(settings.debug) {
292 if(settings.performance) {
293 module.performance.log(arguments);
294 }
295 else {
296 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
297 module.debug.apply(console, arguments);
298 }
299 }
300 },
301 verbose: function() {
302 if(settings.verbose && settings.debug) {
303 if(settings.performance) {
304 module.performance.log(arguments);
305 }
306 else {
307 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
308 module.verbose.apply(console, arguments);
309 }
310 }
311 },
312 error: function() {
313 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
314 module.error.apply(console, arguments);
315 },
316 performance: {
317 log: function(message) {
318 var
319 currentTime,
320 executionTime,
321 previousTime
322 ;
323 if(settings.performance) {
324 currentTime = new Date().getTime();
325 previousTime = time || currentTime;
326 executionTime = currentTime - previousTime;
327 time = currentTime;
328 performance.push({
329 'Element' : element,
330 'Name' : message[0],
331 'Arguments' : [].slice.call(message, 1) || '',
332 'Execution Time' : executionTime
333 });
334 }
335 clearTimeout(module.performance.timer);
336 module.performance.timer = setTimeout(module.performance.display, 500);
337 },
338 display: function() {
339 var
340 title = settings.name + ':',
341 totalTime = 0
342 ;
343 time = false;
344 clearTimeout(module.performance.timer);
345 $.each(performance, function(index, data) {
346 totalTime += data['Execution Time'];
347 });
348 title += ' ' + totalTime + 'ms';
349 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
350 console.groupCollapsed(title);
351 if(console.table) {
352 console.table(performance);
353 }
354 else {
355 $.each(performance, function(index, data) {
356 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
357 });
358 }
359 console.groupEnd();
360 }
361 performance = [];
362 }
363 },
364 invoke: function(query, passedArguments, context) {
365 var
366 object = instance,
367 maxDepth,
368 found,
369 response
370 ;
371 passedArguments = passedArguments || queryArguments;
372 context = element || context;
373 if(typeof query == 'string' && object !== undefined) {
374 query = query.split(/[\. ]/);
375 maxDepth = query.length - 1;
376 $.each(query, function(depth, value) {
377 var camelCaseValue = (depth != maxDepth)
378 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
379 : query
380 ;
381 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
382 object = object[camelCaseValue];
383 }
384 else if( object[camelCaseValue] !== undefined ) {
385 found = object[camelCaseValue];
386 return false;
387 }
388 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
389 object = object[value];
390 }
391 else if( object[value] !== undefined ) {
392 found = object[value];
393 return false;
394 }
395 else {
396 module.error(error.method, query);
397 return false;
398 }
399 });
400 }
401 if ( $.isFunction( found ) ) {
402 response = found.apply(context, passedArguments);
403 }
404 else if(found !== undefined) {
405 response = found;
406 }
407 if(Array.isArray(returnedValue)) {
408 returnedValue.push(response);
409 }
410 else if(returnedValue !== undefined) {
411 returnedValue = [returnedValue, response];
412 }
413 else if(response !== undefined) {
414 returnedValue = response;
415 }
416 return found;
417 }
418 };
419
420 if(methodInvoked) {
421 if(instance === undefined) {
422 module.initialize();
423 }
424 module.invoke(query);
425 }
426 else {
427 if(instance !== undefined) {
428 module.destroy();
429 }
430 module.initialize();
431 }
432 return (returnedValue !== undefined)
433 ? returnedValue
434 : this
435 ;
436};
437
438$.site.settings = {
439
440 name : 'Site',
441 namespace : 'site',
442
443 error : {
444 console : 'Console cannot be restored, most likely it was overwritten outside of module',
445 method : 'The method you called is not defined.'
446 },
447
448 debug : false,
449 verbose : false,
450 performance : true,
451
452 modules: [
453 'accordion',
454 'api',
455 'calendar',
456 'checkbox',
457 'dimmer',
458 'dropdown',
459 'embed',
460 'form',
461 'modal',
462 'nag',
463 'popup',
464 'slider',
465 'rating',
466 'shape',
467 'sidebar',
468 'state',
469 'sticky',
470 'tab',
471 'toast',
472 'transition',
473 'visibility',
474 'visit'
475 ],
476
477 siteNamespace : 'site',
478 namespaceStub : {
479 cache : {},
480 config : {},
481 sections : {},
482 section : {},
483 utilities : {}
484 }
485
486};
487
488// allows for selection of elements with data attributes
489$.extend($.expr[ ":" ], {
490 data: ($.expr.createPseudo)
491 ? $.expr.createPseudo(function(dataName) {
492 return function(elem) {
493 return !!$.data(elem, dataName);
494 };
495 })
496 : function(elem, i, match) {
497 // support: jQuery < 1.8
498 return !!$.data(elem, match[ 3 ]);
499 }
500});
501
502
503})( jQuery, window, document );
504
505/*!
506 * # Fomantic-UI 2.8.8 - Form Validation
507 * http://github.com/fomantic/Fomantic-UI/
508 *
509 *
510 * Released under the MIT license
511 * http://opensource.org/licenses/MIT
512 *
513 */
514
515;(function ($, window, document, undefined) {
516
517'use strict';
518
519$.isFunction = $.isFunction || function(obj) {
520 return typeof obj === "function" && typeof obj.nodeType !== "number";
521};
522
523window = (typeof window != 'undefined' && window.Math == Math)
524 ? window
525 : (typeof self != 'undefined' && self.Math == Math)
526 ? self
527 : Function('return this')()
528;
529
530$.fn.form = function(parameters) {
531 var
532 $allModules = $(this),
533 moduleSelector = $allModules.selector || '',
534
535 time = new Date().getTime(),
536 performance = [],
537
538 query = arguments[0],
539 legacyParameters = arguments[1],
540 methodInvoked = (typeof query == 'string'),
541 queryArguments = [].slice.call(arguments, 1),
542 returnedValue
543 ;
544 $allModules
545 .each(function() {
546 var
547 $module = $(this),
548 element = this,
549
550 formErrors = [],
551 keyHeldDown = false,
552
553 // set at run-time
554 $field,
555 $group,
556 $message,
557 $prompt,
558 $submit,
559 $clear,
560 $reset,
561
562 settings,
563 validation,
564
565 metadata,
566 selector,
567 className,
568 regExp,
569 error,
570
571 namespace,
572 moduleNamespace,
573 eventNamespace,
574
575 submitting = false,
576 dirty = false,
577 history = ['clean', 'clean'],
578
579 instance,
580 module
581 ;
582
583 module = {
584
585 initialize: function() {
586
587 // settings grabbed at run time
588 module.get.settings();
589 if(methodInvoked) {
590 if(instance === undefined) {
591 module.instantiate();
592 }
593 module.invoke(query);
594 }
595 else {
596 if(instance !== undefined) {
597 instance.invoke('destroy');
598 module.refresh();
599 }
600 module.verbose('Initializing form validation', $module, settings);
601 module.bindEvents();
602 module.set.defaults();
603 if (settings.autoCheckRequired) {
604 module.set.autoCheck();
605 }
606 module.instantiate();
607 }
608 },
609
610 instantiate: function() {
611 module.verbose('Storing instance of module', module);
612 instance = module;
613 $module
614 .data(moduleNamespace, module)
615 ;
616 },
617
618 destroy: function() {
619 module.verbose('Destroying previous module', instance);
620 module.removeEvents();
621 $module
622 .removeData(moduleNamespace)
623 ;
624 },
625
626 refresh: function() {
627 module.verbose('Refreshing selector cache');
628 $field = $module.find(selector.field);
629 $group = $module.find(selector.group);
630 $message = $module.find(selector.message);
631 $prompt = $module.find(selector.prompt);
632
633 $submit = $module.find(selector.submit);
634 $clear = $module.find(selector.clear);
635 $reset = $module.find(selector.reset);
636 },
637
638 submit: function() {
639 module.verbose('Submitting form', $module);
640 submitting = true;
641 $module.submit();
642 },
643
644 attachEvents: function(selector, action) {
645 action = action || 'submit';
646 $(selector).on('click' + eventNamespace, function(event) {
647 module[action]();
648 event.preventDefault();
649 });
650 },
651
652 bindEvents: function() {
653 module.verbose('Attaching form events');
654 $module
655 .on('submit' + eventNamespace, module.validate.form)
656 .on('blur' + eventNamespace, selector.field, module.event.field.blur)
657 .on('click' + eventNamespace, selector.submit, module.submit)
658 .on('click' + eventNamespace, selector.reset, module.reset)
659 .on('click' + eventNamespace, selector.clear, module.clear)
660 ;
661 if(settings.keyboardShortcuts) {
662 $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
663 }
664 $field.each(function(index, el) {
665 var
666 $input = $(el),
667 type = $input.prop('type'),
668 inputEvent = module.get.changeEvent(type, $input)
669 ;
670 $input.on(inputEvent + eventNamespace, module.event.field.change);
671 });
672
673 // Dirty events
674 if (settings.preventLeaving) {
675 $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
676 }
677
678 $field.on('change click keyup keydown blur', function(e) {
679 module.determine.isDirty();
680 });
681
682 $module.on('dirty' + eventNamespace, function(e) {
683 settings.onDirty.call();
684 });
685
686 $module.on('clean' + eventNamespace, function(e) {
687 settings.onClean.call();
688 })
689 },
690
691 clear: function() {
692 $field.each(function (index, el) {
693 var
694 $field = $(el),
695 $element = $field.parent(),
696 $fieldGroup = $field.closest($group),
697 $prompt = $fieldGroup.find(selector.prompt),
698 $calendar = $field.closest(selector.uiCalendar),
699 defaultValue = $field.data(metadata.defaultValue) || '',
700 isCheckbox = $element.is(selector.uiCheckbox),
701 isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
702 isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
703 isErrored = $fieldGroup.hasClass(className.error)
704 ;
705 if(isErrored) {
706 module.verbose('Resetting error on field', $fieldGroup);
707 $fieldGroup.removeClass(className.error);
708 $prompt.remove();
709 }
710 if(isDropdown) {
711 module.verbose('Resetting dropdown value', $element, defaultValue);
712 $element.dropdown('clear', true);
713 }
714 else if(isCheckbox) {
715 $field.prop('checked', false);
716 }
717 else if (isCalendar) {
718 $calendar.calendar('clear');
719 }
720 else {
721 module.verbose('Resetting field value', $field, defaultValue);
722 $field.val('');
723 }
724 });
725 module.remove.states();
726 },
727
728 reset: function() {
729 $field.each(function (index, el) {
730 var
731 $field = $(el),
732 $element = $field.parent(),
733 $fieldGroup = $field.closest($group),
734 $calendar = $field.closest(selector.uiCalendar),
735 $prompt = $fieldGroup.find(selector.prompt),
736 defaultValue = $field.data(metadata.defaultValue),
737 isCheckbox = $element.is(selector.uiCheckbox),
738 isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
739 isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
740 isErrored = $fieldGroup.hasClass(className.error)
741 ;
742 if(defaultValue === undefined) {
743 return;
744 }
745 if(isErrored) {
746 module.verbose('Resetting error on field', $fieldGroup);
747 $fieldGroup.removeClass(className.error);
748 $prompt.remove();
749 }
750 if(isDropdown) {
751 module.verbose('Resetting dropdown value', $element, defaultValue);
752 $element.dropdown('restore defaults', true);
753 }
754 else if(isCheckbox) {
755 module.verbose('Resetting checkbox value', $element, defaultValue);
756 $field.prop('checked', defaultValue);
757 }
758 else if (isCalendar) {
759 $calendar.calendar('set date', defaultValue);
760 }
761 else {
762 module.verbose('Resetting field value', $field, defaultValue);
763 $field.val(defaultValue);
764 }
765 });
766 module.remove.states();
767 },
768
769 determine: {
770 isValid: function() {
771 var
772 allValid = true
773 ;
774 $.each(validation, function(fieldName, field) {
775 if( !( module.validate.field(field, fieldName, true) ) ) {
776 allValid = false;
777 }
778 });
779 return allValid;
780 },
781 isDirty: function(e) {
782 var formIsDirty = false;
783
784 $field.each(function(index, el) {
785 var
786 $el = $(el),
787 isCheckbox = ($el.filter(selector.checkbox).length > 0),
788 isDirty
789 ;
790
791 if (isCheckbox) {
792 isDirty = module.is.checkboxDirty($el);
793 } else {
794 isDirty = module.is.fieldDirty($el);
795 }
796
797 $el.data(settings.metadata.isDirty, isDirty);
798
799 formIsDirty |= isDirty;
800 });
801
802 if (formIsDirty) {
803 module.set.dirty();
804 } else {
805 module.set.clean();
806 }
807
808 }
809 },
810
811 is: {
812 bracketedRule: function(rule) {
813 return (rule.type && rule.type.match(settings.regExp.bracket));
814 },
815 // duck type rule test
816 shorthandRules: function(rules) {
817 return (typeof rules == 'string' || Array.isArray(rules));
818 },
819 empty: function($field) {
820 if(!$field || $field.length === 0) {
821 return true;
822 }
823 else if($field.is(selector.checkbox)) {
824 return !$field.is(':checked');
825 }
826 else {
827 return module.is.blank($field);
828 }
829 },
830 blank: function($field) {
831 return String($field.val()).trim() === '';
832 },
833 valid: function(field, showErrors) {
834 var
835 allValid = true
836 ;
837 if(field) {
838 module.verbose('Checking if field is valid', field);
839 return module.validate.field(validation[field], field, !!showErrors);
840 }
841 else {
842 module.verbose('Checking if form is valid');
843 $.each(validation, function(fieldName, field) {
844 if( !module.is.valid(fieldName, showErrors) ) {
845 allValid = false;
846 }
847 });
848 return allValid;
849 }
850 },
851 dirty: function() {
852 return dirty;
853 },
854 clean: function() {
855 return !dirty;
856 },
857 fieldDirty: function($el) {
858 var initialValue = $el.data(metadata.defaultValue);
859 // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
860 if (initialValue == null) { initialValue = ''; }
861 else if(Array.isArray(initialValue)) {
862 initialValue = initialValue.toString();
863 }
864 var currentValue = $el.val();
865 if (currentValue == null) { currentValue = ''; }
866 // multiple select values are returned as arrays which are never equal, so do string conversion first
867 else if(Array.isArray(currentValue)) {
868 currentValue = currentValue.toString();
869 }
870 // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
871 var boolRegex = /^(true|false)$/i;
872 var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
873 if (isBoolValue) {
874 var regex = new RegExp("^" + initialValue + "$", "i");
875 return !regex.test(currentValue);
876 }
877
878 return currentValue !== initialValue;
879 },
880 checkboxDirty: function($el) {
881 var initialValue = $el.data(metadata.defaultValue);
882 var currentValue = $el.is(":checked");
883
884 return initialValue !== currentValue;
885 },
886 justDirty: function() {
887 return (history[0] === 'dirty');
888 },
889 justClean: function() {
890 return (history[0] === 'clean');
891 }
892 },
893
894 removeEvents: function() {
895 $module.off(eventNamespace);
896 $field.off(eventNamespace);
897 $submit.off(eventNamespace);
898 $field.off(eventNamespace);
899 },
900
901 event: {
902 field: {
903 keydown: function(event) {
904 var
905 $field = $(this),
906 key = event.which,
907 isInput = $field.is(selector.input),
908 isCheckbox = $field.is(selector.checkbox),
909 isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
910 keyCode = {
911 enter : 13,
912 escape : 27
913 }
914 ;
915 if( key == keyCode.escape) {
916 module.verbose('Escape key pressed blurring field');
917 $field[0]
918 .blur()
919 ;
920 }
921 if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
922 if(!keyHeldDown) {
923 $field.one('keyup' + eventNamespace, module.event.field.keyup);
924 module.submit();
925 module.debug('Enter pressed on input submitting form');
926 }
927 keyHeldDown = true;
928 }
929 },
930 keyup: function() {
931 keyHeldDown = false;
932 },
933 blur: function(event) {
934 var
935 $field = $(this),
936 $fieldGroup = $field.closest($group),
937 validationRules = module.get.validation($field)
938 ;
939 if(validationRules && (settings.on == 'blur' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
940 module.debug('Revalidating field', $field, validationRules);
941 module.validate.field( validationRules );
942 if(!settings.inline) {
943 module.validate.form(false,true);
944 }
945 }
946 },
947 change: function(event) {
948 var
949 $field = $(this),
950 $fieldGroup = $field.closest($group),
951 validationRules = module.get.validation($field)
952 ;
953 if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
954 clearTimeout(module.timer);
955 module.timer = setTimeout(function() {
956 module.debug('Revalidating field', $field, validationRules);
957 module.validate.field( validationRules );
958 if(!settings.inline) {
959 module.validate.form(false,true);
960 }
961 }, settings.delay);
962 }
963 }
964 },
965 beforeUnload: function(event) {
966 if (module.is.dirty() && !submitting) {
967 var event = event || window.event;
968
969 // For modern browsers
970 if (event) {
971 event.returnValue = settings.text.leavingMessage;
972 }
973
974 // For olders...
975 return settings.text.leavingMessage;
976 }
977 }
978
979 },
980
981 get: {
982 ancillaryValue: function(rule) {
983 if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
984 return false;
985 }
986 return (rule.value !== undefined)
987 ? rule.value
988 : rule.type.match(settings.regExp.bracket)[1] + ''
989 ;
990 },
991 ruleName: function(rule) {
992 if( module.is.bracketedRule(rule) ) {
993 return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
994 }
995 return rule.type;
996 },
997 changeEvent: function(type, $input) {
998 if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
999 return 'change';
1000 }
1001 else {
1002 return module.get.inputEvent();
1003 }
1004 },
1005 inputEvent: function() {
1006 return (document.createElement('input').oninput !== undefined)
1007 ? 'input'
1008 : (document.createElement('input').onpropertychange !== undefined)
1009 ? 'propertychange'
1010 : 'keyup'
1011 ;
1012 },
1013 fieldsFromShorthand: function(fields) {
1014 var
1015 fullFields = {}
1016 ;
1017 $.each(fields, function(name, rules) {
1018 if (!Array.isArray(rules) && typeof rules === 'object') {
1019 fullFields[name] = rules;
1020 } else {
1021 if (typeof rules == 'string') {
1022 rules = [rules];
1023 }
1024 fullFields[name] = {
1025 rules: []
1026 };
1027 $.each(rules, function (index, rule) {
1028 fullFields[name].rules.push({type: rule});
1029 });
1030 }
1031 });
1032 return fullFields;
1033 },
1034 prompt: function(rule, field) {
1035 var
1036 ruleName = module.get.ruleName(rule),
1037 ancillary = module.get.ancillaryValue(rule),
1038 $field = module.get.field(field.identifier),
1039 value = $field.val(),
1040 prompt = $.isFunction(rule.prompt)
1041 ? rule.prompt(value)
1042 : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
1043 requiresValue = (prompt.search('{value}') !== -1),
1044 requiresName = (prompt.search('{name}') !== -1),
1045 $label,
1046 name,
1047 parts,
1048 suffixPrompt
1049 ;
1050 if(ancillary && ancillary.indexOf('..') >= 0) {
1051 parts = ancillary.split('..', 2);
1052 if(!rule.prompt) {
1053 suffixPrompt = (
1054 parts[0] === '' ? settings.prompt.maxValue.replace(/\{ruleValue\}/g,'{max}') :
1055 parts[1] === '' ? settings.prompt.minValue.replace(/\{ruleValue\}/g,'{min}') :
1056 settings.prompt.range
1057 );
1058 prompt += suffixPrompt.replace(/\{name\}/g, ' ' + settings.text.and);
1059 }
1060 prompt = prompt.replace(/\{min\}/g, parts[0]);
1061 prompt = prompt.replace(/\{max\}/g, parts[1]);
1062 }
1063 if(requiresValue) {
1064 prompt = prompt.replace(/\{value\}/g, $field.val());
1065 }
1066 if(requiresName) {
1067 $label = $field.closest(selector.group).find('label').eq(0);
1068 name = ($label.length == 1)
1069 ? $label.text()
1070 : $field.prop('placeholder') || settings.text.unspecifiedField
1071 ;
1072 prompt = prompt.replace(/\{name\}/g, name);
1073 }
1074 prompt = prompt.replace(/\{identifier\}/g, field.identifier);
1075 prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
1076 if(!rule.prompt) {
1077 module.verbose('Using default validation prompt for type', prompt, ruleName);
1078 }
1079 return prompt;
1080 },
1081 settings: function() {
1082 if($.isPlainObject(parameters)) {
1083 var
1084 keys = Object.keys(parameters),
1085 isLegacySettings = (keys.length > 0)
1086 ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
1087 : false
1088 ;
1089 if(isLegacySettings) {
1090 // 1.x (ducktyped)
1091 settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
1092 validation = $.extend({}, $.fn.form.settings.defaults, parameters);
1093 module.error(settings.error.oldSyntax, element);
1094 module.verbose('Extending settings from legacy parameters', validation, settings);
1095 }
1096 else {
1097 // 2.x
1098 if(parameters.fields) {
1099 parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
1100 }
1101 settings = $.extend(true, {}, $.fn.form.settings, parameters);
1102 validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
1103 module.verbose('Extending settings', validation, settings);
1104 }
1105 }
1106 else {
1107 settings = $.fn.form.settings;
1108 validation = $.fn.form.settings.defaults;
1109 module.verbose('Using default form validation', validation, settings);
1110 }
1111
1112 // shorthand
1113 namespace = settings.namespace;
1114 metadata = settings.metadata;
1115 selector = settings.selector;
1116 className = settings.className;
1117 regExp = settings.regExp;
1118 error = settings.error;
1119 moduleNamespace = 'module-' + namespace;
1120 eventNamespace = '.' + namespace;
1121
1122 // grab instance
1123 instance = $module.data(moduleNamespace);
1124
1125 // refresh selector cache
1126 (instance || module).refresh();
1127 },
1128 field: function(identifier) {
1129 module.verbose('Finding field with identifier', identifier);
1130 identifier = module.escape.string(identifier);
1131 var t;
1132 if((t=$field.filter('#' + identifier)).length > 0 ) {
1133 return t;
1134 }
1135 if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
1136 return t;
1137 }
1138 if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
1139 return t;
1140 }
1141 if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
1142 return t;
1143 }
1144 return $('<input/>');
1145 },
1146 fields: function(fields) {
1147 var
1148 $fields = $()
1149 ;
1150 $.each(fields, function(index, name) {
1151 $fields = $fields.add( module.get.field(name) );
1152 });
1153 return $fields;
1154 },
1155 validation: function($field) {
1156 var
1157 fieldValidation,
1158 identifier
1159 ;
1160 if(!validation) {
1161 return false;
1162 }
1163 $.each(validation, function(fieldName, field) {
1164 identifier = field.identifier || fieldName;
1165 $.each(module.get.field(identifier), function(index, groupField) {
1166 if(groupField == $field[0]) {
1167 field.identifier = identifier;
1168 fieldValidation = field;
1169 return false;
1170 }
1171 });
1172 });
1173 return fieldValidation || false;
1174 },
1175 value: function (field) {
1176 var
1177 fields = [],
1178 results
1179 ;
1180 fields.push(field);
1181 results = module.get.values.call(element, fields);
1182 return results[field];
1183 },
1184 values: function (fields) {
1185 var
1186 $fields = Array.isArray(fields)
1187 ? module.get.fields(fields)
1188 : $field,
1189 values = {}
1190 ;
1191 $fields.each(function(index, field) {
1192 var
1193 $field = $(field),
1194 $calendar = $field.closest(selector.uiCalendar),
1195 name = $field.prop('name'),
1196 value = $field.val(),
1197 isCheckbox = $field.is(selector.checkbox),
1198 isRadio = $field.is(selector.radio),
1199 isMultiple = (name.indexOf('[]') !== -1),
1200 isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
1201 isChecked = (isCheckbox)
1202 ? $field.is(':checked')
1203 : false
1204 ;
1205 if(name) {
1206 if(isMultiple) {
1207 name = name.replace('[]', '');
1208 if(!values[name]) {
1209 values[name] = [];
1210 }
1211 if(isCheckbox) {
1212 if(isChecked) {
1213 values[name].push(value || true);
1214 }
1215 else {
1216 values[name].push(false);
1217 }
1218 }
1219 else {
1220 values[name].push(value);
1221 }
1222 }
1223 else {
1224 if(isRadio) {
1225 if(values[name] === undefined || values[name] === false) {
1226 values[name] = (isChecked)
1227 ? value || true
1228 : false
1229 ;
1230 }
1231 }
1232 else if(isCheckbox) {
1233 if(isChecked) {
1234 values[name] = value || true;
1235 }
1236 else {
1237 values[name] = false;
1238 }
1239 }
1240 else if(isCalendar) {
1241 var date = $calendar.calendar('get date');
1242
1243 if (date !== null) {
1244 if (settings.dateHandling == 'date') {
1245 values[name] = date;
1246 } else if(settings.dateHandling == 'input') {
1247 values[name] = $calendar.calendar('get input date')
1248 } else if (settings.dateHandling == 'formatter') {
1249 var type = $calendar.calendar('setting', 'type');
1250
1251 switch(type) {
1252 case 'date':
1253 values[name] = settings.formatter.date(date);
1254 break;
1255
1256 case 'datetime':
1257 values[name] = settings.formatter.datetime(date);
1258 break;
1259
1260 case 'time':
1261 values[name] = settings.formatter.time(date);
1262 break;
1263
1264 case 'month':
1265 values[name] = settings.formatter.month(date);
1266 break;
1267
1268 case 'year':
1269 values[name] = settings.formatter.year(date);
1270 break;
1271
1272 default:
1273 module.debug('Wrong calendar mode', $calendar, type);
1274 values[name] = '';
1275 }
1276 }
1277 } else {
1278 values[name] = '';
1279 }
1280 } else {
1281 values[name] = value;
1282 }
1283 }
1284 }
1285 });
1286 return values;
1287 },
1288 dirtyFields: function() {
1289 return $field.filter(function(index, e) {
1290 return $(e).data(metadata.isDirty);
1291 });
1292 }
1293 },
1294
1295 has: {
1296
1297 field: function(identifier) {
1298 module.verbose('Checking for existence of a field with identifier', identifier);
1299 identifier = module.escape.string(identifier);
1300 if(typeof identifier !== 'string') {
1301 module.error(error.identifier, identifier);
1302 }
1303 if($field.filter('#' + identifier).length > 0 ) {
1304 return true;
1305 }
1306 else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
1307 return true;
1308 }
1309 else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
1310 return true;
1311 }
1312 return false;
1313 }
1314
1315 },
1316
1317 can: {
1318 useElement: function(element){
1319 if ($.fn[element] !== undefined) {
1320 return true;
1321 }
1322 module.error(error.noElement.replace('{element}',element));
1323 return false;
1324 }
1325 },
1326
1327 escape: {
1328 string: function(text) {
1329 text = String(text);
1330 return text.replace(regExp.escape, '\\$&');
1331 }
1332 },
1333
1334 add: {
1335 // alias
1336 rule: function(name, rules) {
1337 module.add.field(name, rules);
1338 },
1339 field: function(name, rules) {
1340 // Validation should have at least a standard format
1341 if(validation[name] === undefined || validation[name].rules === undefined) {
1342 validation[name] = {
1343 rules: []
1344 };
1345 }
1346 var
1347 newValidation = {
1348 rules: []
1349 }
1350 ;
1351 if(module.is.shorthandRules(rules)) {
1352 rules = Array.isArray(rules)
1353 ? rules
1354 : [rules]
1355 ;
1356 $.each(rules, function(_index, rule) {
1357 newValidation.rules.push({ type: rule });
1358 });
1359 }
1360 else {
1361 newValidation.rules = rules.rules;
1362 }
1363 // For each new rule, check if there's not already one with the same type
1364 $.each(newValidation.rules, function (_index, rule) {
1365 if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
1366 validation[name].rules.push(rule);
1367 }
1368 });
1369 module.debug('Adding rules', newValidation.rules, validation);
1370 },
1371 fields: function(fields) {
1372 validation = $.extend({}, validation, module.get.fieldsFromShorthand(fields));
1373 },
1374 prompt: function(identifier, errors, internal) {
1375 var
1376 $field = module.get.field(identifier),
1377 $fieldGroup = $field.closest($group),
1378 $prompt = $fieldGroup.children(selector.prompt),
1379 promptExists = ($prompt.length !== 0)
1380 ;
1381 errors = (typeof errors == 'string')
1382 ? [errors]
1383 : errors
1384 ;
1385 module.verbose('Adding field error state', identifier);
1386 if(!internal) {
1387 $fieldGroup
1388 .addClass(className.error)
1389 ;
1390 }
1391 if(settings.inline) {
1392 if(!promptExists) {
1393 $prompt = settings.templates.prompt(errors, className.label);
1394 $prompt
1395 .appendTo($fieldGroup)
1396 ;
1397 }
1398 $prompt
1399 .html(errors[0])
1400 ;
1401 if(!promptExists) {
1402 if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
1403 module.verbose('Displaying error with css transition', settings.transition);
1404 $prompt.transition(settings.transition + ' in', settings.duration);
1405 }
1406 else {
1407 module.verbose('Displaying error with fallback javascript animation');
1408 $prompt
1409 .fadeIn(settings.duration)
1410 ;
1411 }
1412 }
1413 else {
1414 module.verbose('Inline errors are disabled, no inline error added', identifier);
1415 }
1416 }
1417 },
1418 errors: function(errors) {
1419 module.debug('Adding form error messages', errors);
1420 module.set.error();
1421 $message
1422 .html( settings.templates.error(errors) )
1423 ;
1424 }
1425 },
1426
1427 remove: {
1428 errors: function() {
1429 module.debug('Removing form error messages');
1430 $message.empty();
1431 },
1432 states: function() {
1433 $module.removeClass(className.error).removeClass(className.success);
1434 if(!settings.inline) {
1435 module.remove.errors();
1436 }
1437 module.determine.isDirty();
1438 },
1439 rule: function(field, rule) {
1440 var
1441 rules = Array.isArray(rule)
1442 ? rule
1443 : [rule]
1444 ;
1445 if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
1446 return;
1447 }
1448 if(rule === undefined) {
1449 module.debug('Removed all rules');
1450 validation[field].rules = [];
1451 return;
1452 }
1453 $.each(validation[field].rules, function(index, rule) {
1454 if(rule && rules.indexOf(rule.type) !== -1) {
1455 module.debug('Removed rule', rule.type);
1456 validation[field].rules.splice(index, 1);
1457 }
1458 });
1459 },
1460 field: function(field) {
1461 var
1462 fields = Array.isArray(field)
1463 ? field
1464 : [field]
1465 ;
1466 $.each(fields, function(index, field) {
1467 module.remove.rule(field);
1468 });
1469 },
1470 // alias
1471 rules: function(field, rules) {
1472 if(Array.isArray(field)) {
1473 $.each(field, function(index, field) {
1474 module.remove.rule(field, rules);
1475 });
1476 }
1477 else {
1478 module.remove.rule(field, rules);
1479 }
1480 },
1481 fields: function(fields) {
1482 module.remove.field(fields);
1483 },
1484 prompt: function(identifier) {
1485 var
1486 $field = module.get.field(identifier),
1487 $fieldGroup = $field.closest($group),
1488 $prompt = $fieldGroup.children(selector.prompt)
1489 ;
1490 $fieldGroup
1491 .removeClass(className.error)
1492 ;
1493 if(settings.inline && $prompt.is(':visible')) {
1494 module.verbose('Removing prompt for field', identifier);
1495 if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
1496 $prompt.transition(settings.transition + ' out', settings.duration, function() {
1497 $prompt.remove();
1498 });
1499 }
1500 else {
1501 $prompt
1502 .fadeOut(settings.duration, function(){
1503 $prompt.remove();
1504 })
1505 ;
1506 }
1507 }
1508 }
1509 },
1510
1511 set: {
1512 success: function() {
1513 $module
1514 .removeClass(className.error)
1515 .addClass(className.success)
1516 ;
1517 },
1518 defaults: function () {
1519 $field.each(function (index, el) {
1520 var
1521 $el = $(el),
1522 $parent = $el.parent(),
1523 isCheckbox = ($el.filter(selector.checkbox).length > 0),
1524 isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
1525 $calendar = $el.closest(selector.uiCalendar),
1526 isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
1527 value = (isCheckbox)
1528 ? $el.is(':checked')
1529 : $el.val()
1530 ;
1531 if (isDropdown) {
1532 $parent.dropdown('save defaults');
1533 }
1534 else if (isCalendar) {
1535 $calendar.calendar('refresh');
1536 }
1537 $el.data(metadata.defaultValue, value);
1538 $el.data(metadata.isDirty, false);
1539 });
1540 },
1541 error: function() {
1542 $module
1543 .removeClass(className.success)
1544 .addClass(className.error)
1545 ;
1546 },
1547 value: function (field, value) {
1548 var
1549 fields = {}
1550 ;
1551 fields[field] = value;
1552 return module.set.values.call(element, fields);
1553 },
1554 values: function (fields) {
1555 if($.isEmptyObject(fields)) {
1556 return;
1557 }
1558 $.each(fields, function(key, value) {
1559 var
1560 $field = module.get.field(key),
1561 $element = $field.parent(),
1562 $calendar = $field.closest(selector.uiCalendar),
1563 isMultiple = Array.isArray(value),
1564 isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
1565 isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
1566 isRadio = ($field.is(selector.radio) && isCheckbox),
1567 isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
1568 fieldExists = ($field.length > 0),
1569 $multipleField
1570 ;
1571 if(fieldExists) {
1572 if(isMultiple && isCheckbox) {
1573 module.verbose('Selecting multiple', value, $field);
1574 $element.checkbox('uncheck');
1575 $.each(value, function(index, value) {
1576 $multipleField = $field.filter('[value="' + value + '"]');
1577 $element = $multipleField.parent();
1578 if($multipleField.length > 0) {
1579 $element.checkbox('check');
1580 }
1581 });
1582 }
1583 else if(isRadio) {
1584 module.verbose('Selecting radio value', value, $field);
1585 $field.filter('[value="' + value + '"]')
1586 .parent(selector.uiCheckbox)
1587 .checkbox('check')
1588 ;
1589 }
1590 else if(isCheckbox) {
1591 module.verbose('Setting checkbox value', value, $element);
1592 if(value === true || value === 1) {
1593 $element.checkbox('check');
1594 }
1595 else {
1596 $element.checkbox('uncheck');
1597 }
1598 }
1599 else if(isDropdown) {
1600 module.verbose('Setting dropdown value', value, $element);
1601 $element.dropdown('set selected', value);
1602 }
1603 else if (isCalendar) {
1604 $calendar.calendar('set date',value);
1605 }
1606 else {
1607 module.verbose('Setting field value', value, $field);
1608 $field.val(value);
1609 }
1610 }
1611 });
1612 },
1613 dirty: function() {
1614 module.verbose('Setting state dirty');
1615 dirty = true;
1616 history[0] = history[1];
1617 history[1] = 'dirty';
1618
1619 if (module.is.justClean()) {
1620 $module.trigger('dirty');
1621 }
1622 },
1623 clean: function() {
1624 module.verbose('Setting state clean');
1625 dirty = false;
1626 history[0] = history[1];
1627 history[1] = 'clean';
1628
1629 if (module.is.justDirty()) {
1630 $module.trigger('clean');
1631 }
1632 },
1633 asClean: function() {
1634 module.set.defaults();
1635 module.set.clean();
1636 },
1637 asDirty: function() {
1638 module.set.defaults();
1639 module.set.dirty();
1640 },
1641 autoCheck: function() {
1642 module.debug('Enabling auto check on required fields');
1643 $field.each(function (_index, el) {
1644 var
1645 $el = $(el),
1646 $elGroup = $(el).closest($group),
1647 isCheckbox = ($el.filter(selector.checkbox).length > 0),
1648 isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
1649 isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
1650 validation = module.get.validation($el),
1651 hasEmptyRule = validation
1652 ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
1653 : false,
1654 identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
1655 ;
1656 if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
1657 if (isCheckbox) {
1658 module.verbose("Adding 'checked' rule on field", identifier);
1659 module.add.rule(identifier, "checked");
1660 } else {
1661 module.verbose("Adding 'empty' rule on field", identifier);
1662 module.add.rule(identifier, "empty");
1663 }
1664 }
1665 });
1666 },
1667 optional: function(identifier, bool) {
1668 bool = (bool !== false);
1669 $.each(validation, function(fieldName, field) {
1670 if (identifier == fieldName || identifier == field.identifier) {
1671 field.optional = bool;
1672 }
1673 });
1674 }
1675 },
1676
1677 validate: {
1678
1679 form: function(event, ignoreCallbacks) {
1680 var values = module.get.values();
1681
1682 // input keydown event will fire submit repeatedly by browser default
1683 if(keyHeldDown) {
1684 return false;
1685 }
1686
1687 // reset errors
1688 formErrors = [];
1689 if( module.determine.isValid() ) {
1690 module.debug('Form has no validation errors, submitting');
1691 module.set.success();
1692 if(!settings.inline) {
1693 module.remove.errors();
1694 }
1695 if(ignoreCallbacks !== true) {
1696 return settings.onSuccess.call(element, event, values);
1697 }
1698 }
1699 else {
1700 module.debug('Form has errors');
1701 submitting = false;
1702 module.set.error();
1703 if(!settings.inline) {
1704 module.add.errors(formErrors);
1705 }
1706 // prevent ajax submit
1707 if(event && $module.data('moduleApi') !== undefined) {
1708 event.stopImmediatePropagation();
1709 }
1710 if(settings.errorFocus) {
1711 var focusElement, hasTabIndex = true;
1712 if (typeof settings.errorFocus === 'string') {
1713 focusElement = $(settings.errorFocus);
1714 hasTabIndex = focusElement.is('[tabindex]');
1715 // to be able to focus/scroll into non input elements we need a tabindex
1716 if (!hasTabIndex) {
1717 focusElement.attr('tabindex',-1);
1718 }
1719 } else {
1720 focusElement = $group.filter('.' + className.error).first().find(selector.field);
1721 }
1722 focusElement.focus();
1723 // only remove tabindex if it was dynamically created above
1724 if (!hasTabIndex){
1725 focusElement.removeAttr('tabindex');
1726 }
1727 }
1728 if(ignoreCallbacks !== true) {
1729 return settings.onFailure.call(element, formErrors, values);
1730 }
1731 }
1732 },
1733
1734 // takes a validation object and returns whether field passes validation
1735 field: function(field, fieldName, showErrors) {
1736 showErrors = (showErrors !== undefined)
1737 ? showErrors
1738 : true
1739 ;
1740 if(typeof field == 'string') {
1741 module.verbose('Validating field', field);
1742 fieldName = field;
1743 field = validation[field];
1744 }
1745 var
1746 identifier = field.identifier || fieldName,
1747 $field = module.get.field(identifier),
1748 $dependsField = (field.depends)
1749 ? module.get.field(field.depends)
1750 : false,
1751 fieldValid = true,
1752 fieldErrors = []
1753 ;
1754 if(!field.identifier) {
1755 module.debug('Using field name as identifier', identifier);
1756 field.identifier = identifier;
1757 }
1758 var isDisabled = !$field.filter(':not(:disabled)').length;
1759 if(isDisabled) {
1760 module.debug('Field is disabled. Skipping', identifier);
1761 }
1762 else if(field.optional && module.is.blank($field)){
1763 module.debug('Field is optional and blank. Skipping', identifier);
1764 }
1765 else if(field.depends && module.is.empty($dependsField)) {
1766 module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
1767 }
1768 else if(field.rules !== undefined) {
1769 if(showErrors) {
1770 $field.closest($group).removeClass(className.error);
1771 }
1772 $.each(field.rules, function(index, rule) {
1773 if( module.has.field(identifier)) {
1774 var invalidFields = module.validate.rule(field, rule,true) || [];
1775 if (invalidFields.length>0){
1776 module.debug('Field is invalid', identifier, rule.type);
1777 fieldErrors.push(module.get.prompt(rule, field));
1778 fieldValid = false;
1779 if(showErrors){
1780 $(invalidFields).closest($group).addClass(className.error);
1781 }
1782 }
1783 }
1784 });
1785 }
1786 if(fieldValid) {
1787 if(showErrors) {
1788 module.remove.prompt(identifier, fieldErrors);
1789 settings.onValid.call($field);
1790 }
1791 }
1792 else {
1793 if(showErrors) {
1794 formErrors = formErrors.concat(fieldErrors);
1795 module.add.prompt(identifier, fieldErrors, true);
1796 settings.onInvalid.call($field, fieldErrors);
1797 }
1798 return false;
1799 }
1800 return true;
1801 },
1802
1803 // takes validation rule and returns whether field passes rule
1804 rule: function(field, rule, internal) {
1805 var
1806 $field = module.get.field(field.identifier),
1807 ancillary = module.get.ancillaryValue(rule),
1808 ruleName = module.get.ruleName(rule),
1809 ruleFunction = settings.rules[ruleName],
1810 invalidFields = [],
1811 isCheckbox = $field.is(selector.checkbox),
1812 isValid = function(field){
1813 var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
1814 // cast to string avoiding encoding special values
1815 value = (value === undefined || value === '' || value === null)
1816 ? ''
1817 : (settings.shouldTrim && rule.shouldTrim !== false) || rule.shouldTrim ? String(value + '').trim() : String(value + '')
1818 ;
1819 return ruleFunction.call(field, value, ancillary, $module);
1820 }
1821 ;
1822 if( !$.isFunction(ruleFunction) ) {
1823 module.error(error.noRule, ruleName);
1824 return;
1825 }
1826 if(isCheckbox) {
1827 if (!isValid($field)) {
1828 invalidFields = $field;
1829 }
1830 } else {
1831 $.each($field, function (index, field) {
1832 if (!isValid(field)) {
1833 invalidFields.push(field);
1834 }
1835 });
1836 }
1837 return internal ? invalidFields : !(invalidFields.length>0);
1838 }
1839 },
1840
1841 setting: function(name, value) {
1842 if( $.isPlainObject(name) ) {
1843 $.extend(true, settings, name);
1844 }
1845 else if(value !== undefined) {
1846 settings[name] = value;
1847 }
1848 else {
1849 return settings[name];
1850 }
1851 },
1852 internal: function(name, value) {
1853 if( $.isPlainObject(name) ) {
1854 $.extend(true, module, name);
1855 }
1856 else if(value !== undefined) {
1857 module[name] = value;
1858 }
1859 else {
1860 return module[name];
1861 }
1862 },
1863 debug: function() {
1864 if(!settings.silent && settings.debug) {
1865 if(settings.performance) {
1866 module.performance.log(arguments);
1867 }
1868 else {
1869 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1870 module.debug.apply(console, arguments);
1871 }
1872 }
1873 },
1874 verbose: function() {
1875 if(!settings.silent && settings.verbose && settings.debug) {
1876 if(settings.performance) {
1877 module.performance.log(arguments);
1878 }
1879 else {
1880 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1881 module.verbose.apply(console, arguments);
1882 }
1883 }
1884 },
1885 error: function() {
1886 if(!settings.silent) {
1887 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1888 module.error.apply(console, arguments);
1889 }
1890 },
1891 performance: {
1892 log: function(message) {
1893 var
1894 currentTime,
1895 executionTime,
1896 previousTime
1897 ;
1898 if(settings.performance) {
1899 currentTime = new Date().getTime();
1900 previousTime = time || currentTime;
1901 executionTime = currentTime - previousTime;
1902 time = currentTime;
1903 performance.push({
1904 'Name' : message[0],
1905 'Arguments' : [].slice.call(message, 1) || '',
1906 'Element' : element,
1907 'Execution Time' : executionTime
1908 });
1909 }
1910 clearTimeout(module.performance.timer);
1911 module.performance.timer = setTimeout(module.performance.display, 500);
1912 },
1913 display: function() {
1914 var
1915 title = settings.name + ':',
1916 totalTime = 0
1917 ;
1918 time = false;
1919 clearTimeout(module.performance.timer);
1920 $.each(performance, function(index, data) {
1921 totalTime += data['Execution Time'];
1922 });
1923 title += ' ' + totalTime + 'ms';
1924 if(moduleSelector) {
1925 title += ' \'' + moduleSelector + '\'';
1926 }
1927 if($allModules.length > 1) {
1928 title += ' ' + '(' + $allModules.length + ')';
1929 }
1930 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
1931 console.groupCollapsed(title);
1932 if(console.table) {
1933 console.table(performance);
1934 }
1935 else {
1936 $.each(performance, function(index, data) {
1937 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
1938 });
1939 }
1940 console.groupEnd();
1941 }
1942 performance = [];
1943 }
1944 },
1945 invoke: function(query, passedArguments, context) {
1946 var
1947 object = instance,
1948 maxDepth,
1949 found,
1950 response
1951 ;
1952 passedArguments = passedArguments || queryArguments;
1953 context = element || context;
1954 if(typeof query == 'string' && object !== undefined) {
1955 query = query.split(/[\. ]/);
1956 maxDepth = query.length - 1;
1957 $.each(query, function(depth, value) {
1958 var camelCaseValue = (depth != maxDepth)
1959 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1960 : query
1961 ;
1962 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
1963 object = object[camelCaseValue];
1964 }
1965 else if( object[camelCaseValue] !== undefined ) {
1966 found = object[camelCaseValue];
1967 return false;
1968 }
1969 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
1970 object = object[value];
1971 }
1972 else if( object[value] !== undefined ) {
1973 found = object[value];
1974 return false;
1975 }
1976 else {
1977 return false;
1978 }
1979 });
1980 }
1981 if( $.isFunction( found ) ) {
1982 response = found.apply(context, passedArguments);
1983 }
1984 else if(found !== undefined) {
1985 response = found;
1986 }
1987 if(Array.isArray(returnedValue)) {
1988 returnedValue.push(response);
1989 }
1990 else if(returnedValue !== undefined) {
1991 returnedValue = [returnedValue, response];
1992 }
1993 else if(response !== undefined) {
1994 returnedValue = response;
1995 }
1996 return found;
1997 }
1998 };
1999 module.initialize();
2000 })
2001 ;
2002
2003 return (returnedValue !== undefined)
2004 ? returnedValue
2005 : this
2006 ;
2007};
2008
2009$.fn.form.settings = {
2010
2011 name : 'Form',
2012 namespace : 'form',
2013
2014 debug : false,
2015 verbose : false,
2016 performance : true,
2017
2018 fields : false,
2019
2020 keyboardShortcuts : true,
2021 on : 'submit',
2022 inline : false,
2023
2024 delay : 200,
2025 revalidate : true,
2026 shouldTrim : true,
2027
2028 transition : 'scale',
2029 duration : 200,
2030
2031 autoCheckRequired : false,
2032 preventLeaving : false,
2033 errorFocus : false,
2034 dateHandling : 'date', // 'date', 'input', 'formatter'
2035
2036 onValid : function() {},
2037 onInvalid : function() {},
2038 onSuccess : function() { return true; },
2039 onFailure : function() { return false; },
2040 onDirty : function() {},
2041 onClean : function() {},
2042
2043 metadata : {
2044 defaultValue : 'default',
2045 validate : 'validate',
2046 isDirty : 'isDirty'
2047 },
2048
2049 regExp: {
2050 htmlID : /^[a-zA-Z][\w:.-]*$/g,
2051 bracket : /\[(.*)\]/i,
2052 decimal : /^\d+\.?\d*$/,
2053 email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
2054 escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
2055 flags : /^\/(.*)\/(.*)?/,
2056 integer : /^\-?\d+$/,
2057 number : /^\-?\d*(\.\d+)?$/,
2058 url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
2059 },
2060
2061 text: {
2062 and : 'and',
2063 unspecifiedRule : 'Please enter a valid value',
2064 unspecifiedField : 'This field',
2065 leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
2066 },
2067
2068 prompt: {
2069 range : '{name} must be in a range from {min} to {max}',
2070 maxValue : '{name} must have a maximum value of {ruleValue}',
2071 minValue : '{name} must have a minimum value of {ruleValue}',
2072 empty : '{name} must have a value',
2073 checked : '{name} must be checked',
2074 email : '{name} must be a valid e-mail',
2075 url : '{name} must be a valid url',
2076 regExp : '{name} is not formatted correctly',
2077 integer : '{name} must be an integer',
2078 decimal : '{name} must be a decimal number',
2079 number : '{name} must be set to a number',
2080 is : '{name} must be "{ruleValue}"',
2081 isExactly : '{name} must be exactly "{ruleValue}"',
2082 not : '{name} cannot be set to "{ruleValue}"',
2083 notExactly : '{name} cannot be set to exactly "{ruleValue}"',
2084 contain : '{name} must contain "{ruleValue}"',
2085 containExactly : '{name} must contain exactly "{ruleValue}"',
2086 doesntContain : '{name} cannot contain "{ruleValue}"',
2087 doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
2088 minLength : '{name} must be at least {ruleValue} characters',
2089 length : '{name} must be at least {ruleValue} characters',
2090 exactLength : '{name} must be exactly {ruleValue} characters',
2091 maxLength : '{name} cannot be longer than {ruleValue} characters',
2092 match : '{name} must match {ruleValue} field',
2093 different : '{name} must have a different value than {ruleValue} field',
2094 creditCard : '{name} must be a valid credit card number',
2095 minCount : '{name} must have at least {ruleValue} choices',
2096 exactCount : '{name} must have exactly {ruleValue} choices',
2097 maxCount : '{name} must have {ruleValue} or less choices'
2098 },
2099
2100 selector : {
2101 checkbox : 'input[type="checkbox"], input[type="radio"]',
2102 clear : '.clear',
2103 field : 'input:not(.search):not([type="file"]), textarea, select',
2104 group : '.field',
2105 input : 'input:not([type="file"])',
2106 message : '.error.message',
2107 prompt : '.prompt.label',
2108 radio : 'input[type="radio"]',
2109 reset : '.reset:not([type="reset"])',
2110 submit : '.submit:not([type="submit"])',
2111 uiCheckbox : '.ui.checkbox',
2112 uiDropdown : '.ui.dropdown',
2113 uiCalendar : '.ui.calendar'
2114 },
2115
2116 className : {
2117 error : 'error',
2118 label : 'ui basic red pointing prompt label',
2119 pressed : 'down',
2120 success : 'success',
2121 required : 'required',
2122 disabled : 'disabled'
2123 },
2124
2125 error: {
2126 identifier : 'You must specify a string identifier for each field',
2127 method : 'The method you called is not defined.',
2128 noRule : 'There is no rule matching the one you specified',
2129 oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
2130 noElement : 'This module requires ui {element}'
2131 },
2132
2133 templates: {
2134
2135 // template that produces error message
2136 error: function(errors) {
2137 var
2138 html = '<ul class="list">'
2139 ;
2140 $.each(errors, function(index, value) {
2141 html += '<li>' + value + '</li>';
2142 });
2143 html += '</ul>';
2144 return $(html);
2145 },
2146
2147 // template that produces label
2148 prompt: function(errors, labelClasses) {
2149 return $('<div/>')
2150 .addClass(labelClasses)
2151 .html(errors[0])
2152 ;
2153 }
2154 },
2155
2156 formatter: {
2157 date: function(date) {
2158 return Intl.DateTimeFormat('en-GB').format(date);
2159 },
2160 datetime: function(date) {
2161 return Intl.DateTimeFormat('en-GB', {
2162 year: "numeric",
2163 month: "2-digit",
2164 day: "2-digit",
2165 hour: '2-digit',
2166 minute: '2-digit',
2167 second: '2-digit'
2168 }).format(date);
2169 },
2170 time: function(date) {
2171 return Intl.DateTimeFormat('en-GB', {
2172 hour: '2-digit',
2173 minute: '2-digit',
2174 second: '2-digit'
2175 }).format(date);
2176 },
2177 month: function(date) {
2178 return Intl.DateTimeFormat('en-GB', {
2179 month: '2-digit',
2180 year: 'numeric'
2181 }).format(date);
2182 },
2183 year: function(date) {
2184 return Intl.DateTimeFormat('en-GB', {
2185 year: 'numeric'
2186 }).format(date);
2187 }
2188 },
2189
2190 rules: {
2191
2192 // is not empty or blank string
2193 empty: function(value) {
2194 return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
2195 },
2196
2197 // checkbox checked
2198 checked: function() {
2199 return ($(this).filter(':checked').length > 0);
2200 },
2201
2202 // is most likely an email
2203 email: function(value){
2204 return $.fn.form.settings.regExp.email.test(value);
2205 },
2206
2207 // value is most likely url
2208 url: function(value) {
2209 return $.fn.form.settings.regExp.url.test(value);
2210 },
2211
2212 // matches specified regExp
2213 regExp: function(value, regExp) {
2214 if(regExp instanceof RegExp) {
2215 return value.match(regExp);
2216 }
2217 var
2218 regExpParts = regExp.match($.fn.form.settings.regExp.flags),
2219 flags
2220 ;
2221 // regular expression specified as /baz/gi (flags)
2222 if(regExpParts) {
2223 regExp = (regExpParts.length >= 2)
2224 ? regExpParts[1]
2225 : regExp
2226 ;
2227 flags = (regExpParts.length >= 3)
2228 ? regExpParts[2]
2229 : ''
2230 ;
2231 }
2232 return value.match( new RegExp(regExp, flags) );
2233 },
2234 minValue: function(value, range) {
2235 return $.fn.form.settings.rules.range(value, range+'..', 'number');
2236 },
2237 maxValue: function(value, range) {
2238 return $.fn.form.settings.rules.range(value, '..'+range, 'number');
2239 },
2240 // is valid integer or matches range
2241 integer: function(value, range) {
2242 return $.fn.form.settings.rules.range(value, range, 'integer');
2243 },
2244 range: function(value, range, regExp) {
2245 if(typeof regExp == "string") {
2246 regExp = $.fn.form.settings.regExp[regExp];
2247 }
2248 if(!(regExp instanceof RegExp)) {
2249 regExp = $.fn.form.settings.regExp.integer;
2250 }
2251 var
2252 min,
2253 max,
2254 parts
2255 ;
2256 if( !range || ['', '..'].indexOf(range) !== -1) {
2257 // do nothing
2258 }
2259 else if(range.indexOf('..') == -1) {
2260 if(regExp.test(range)) {
2261 min = max = range - 0;
2262 }
2263 }
2264 else {
2265 parts = range.split('..', 2);
2266 if(regExp.test(parts[0])) {
2267 min = parts[0] - 0;
2268 }
2269 if(regExp.test(parts[1])) {
2270 max = parts[1] - 0;
2271 }
2272 }
2273 return (
2274 regExp.test(value) &&
2275 (min === undefined || value >= min) &&
2276 (max === undefined || value <= max)
2277 );
2278 },
2279
2280 // is valid number (with decimal)
2281 decimal: function(value, range) {
2282 return $.fn.form.settings.rules.range(value, range, 'decimal');
2283 },
2284
2285 // is valid number
2286 number: function(value, range) {
2287 return $.fn.form.settings.rules.range(value, range, 'number');
2288 },
2289
2290 // is value (case insensitive)
2291 is: function(value, text) {
2292 text = (typeof text == 'string')
2293 ? text.toLowerCase()
2294 : text
2295 ;
2296 value = (typeof value == 'string')
2297 ? value.toLowerCase()
2298 : value
2299 ;
2300 return (value == text);
2301 },
2302
2303 // is value
2304 isExactly: function(value, text) {
2305 return (value == text);
2306 },
2307
2308 // value is not another value (case insensitive)
2309 not: function(value, notValue) {
2310 value = (typeof value == 'string')
2311 ? value.toLowerCase()
2312 : value
2313 ;
2314 notValue = (typeof notValue == 'string')
2315 ? notValue.toLowerCase()
2316 : notValue
2317 ;
2318 return (value != notValue);
2319 },
2320
2321 // value is not another value (case sensitive)
2322 notExactly: function(value, notValue) {
2323 return (value != notValue);
2324 },
2325
2326 // value contains text (insensitive)
2327 contains: function(value, text) {
2328 // escape regex characters
2329 text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
2330 return (value.search( new RegExp(text, 'i') ) !== -1);
2331 },
2332
2333 // value contains text (case sensitive)
2334 containsExactly: function(value, text) {
2335 // escape regex characters
2336 text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
2337 return (value.search( new RegExp(text) ) !== -1);
2338 },
2339
2340 // value contains text (insensitive)
2341 doesntContain: function(value, text) {
2342 // escape regex characters
2343 text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
2344 return (value.search( new RegExp(text, 'i') ) === -1);
2345 },
2346
2347 // value contains text (case sensitive)
2348 doesntContainExactly: function(value, text) {
2349 // escape regex characters
2350 text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
2351 return (value.search( new RegExp(text) ) === -1);
2352 },
2353
2354 // is at least string length
2355 minLength: function(value, requiredLength) {
2356 return (value !== undefined)
2357 ? (value.length >= requiredLength)
2358 : false
2359 ;
2360 },
2361
2362 // see rls notes for 2.0.6 (this is a duplicate of minLength)
2363 length: function(value, requiredLength) {
2364 return (value !== undefined)
2365 ? (value.length >= requiredLength)
2366 : false
2367 ;
2368 },
2369
2370 // is exactly length
2371 exactLength: function(value, requiredLength) {
2372 return (value !== undefined)
2373 ? (value.length == requiredLength)
2374 : false
2375 ;
2376 },
2377
2378 // is less than length
2379 maxLength: function(value, maxLength) {
2380 return (value !== undefined)
2381 ? (value.length <= maxLength)
2382 : false
2383 ;
2384 },
2385
2386 // matches another field
2387 match: function(value, identifier, $module) {
2388 var
2389 matchingValue,
2390 matchingElement
2391 ;
2392 if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
2393 matchingValue = matchingElement.val();
2394 }
2395 else if((matchingElement = $module.find('#' + identifier)).length > 0) {
2396 matchingValue = matchingElement.val();
2397 }
2398 else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
2399 matchingValue = matchingElement.val();
2400 }
2401 else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
2402 matchingValue = matchingElement;
2403 }
2404 return (matchingValue !== undefined)
2405 ? ( value.toString() == matchingValue.toString() )
2406 : false
2407 ;
2408 },
2409
2410 // different than another field
2411 different: function(value, identifier, $module) {
2412 // use either id or name of field
2413 var
2414 matchingValue,
2415 matchingElement
2416 ;
2417 if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
2418 matchingValue = matchingElement.val();
2419 }
2420 else if((matchingElement = $module.find('#' + identifier)).length > 0) {
2421 matchingValue = matchingElement.val();
2422 }
2423 else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
2424 matchingValue = matchingElement.val();
2425 }
2426 else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
2427 matchingValue = matchingElement;
2428 }
2429 return (matchingValue !== undefined)
2430 ? ( value.toString() !== matchingValue.toString() )
2431 : false
2432 ;
2433 },
2434
2435 creditCard: function(cardNumber, cardTypes) {
2436 var
2437 cards = {
2438 visa: {
2439 pattern : /^4/,
2440 length : [16]
2441 },
2442 amex: {
2443 pattern : /^3[47]/,
2444 length : [15]
2445 },
2446 mastercard: {
2447 pattern : /^5[1-5]/,
2448 length : [16]
2449 },
2450 discover: {
2451 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)/,
2452 length : [16]
2453 },
2454 unionPay: {
2455 pattern : /^(62|88)/,
2456 length : [16, 17, 18, 19]
2457 },
2458 jcb: {
2459 pattern : /^35(2[89]|[3-8][0-9])/,
2460 length : [16]
2461 },
2462 maestro: {
2463 pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
2464 length : [12, 13, 14, 15, 16, 17, 18, 19]
2465 },
2466 dinersClub: {
2467 pattern : /^(30[0-5]|^36)/,
2468 length : [14]
2469 },
2470 laser: {
2471 pattern : /^(6304|670[69]|6771)/,
2472 length : [16, 17, 18, 19]
2473 },
2474 visaElectron: {
2475 pattern : /^(4026|417500|4508|4844|491(3|7))/,
2476 length : [16]
2477 }
2478 },
2479 valid = {},
2480 validCard = false,
2481 requiredTypes = (typeof cardTypes == 'string')
2482 ? cardTypes.split(',')
2483 : false,
2484 unionPay,
2485 validation
2486 ;
2487
2488 if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
2489 return;
2490 }
2491
2492 // allow dashes and spaces in card
2493 cardNumber = cardNumber.replace(/[\s\-]/g, '');
2494
2495 // verify card types
2496 if(requiredTypes) {
2497 $.each(requiredTypes, function(index, type){
2498 // verify each card type
2499 validation = cards[type];
2500 if(validation) {
2501 valid = {
2502 length : ($.inArray(cardNumber.length, validation.length) !== -1),
2503 pattern : (cardNumber.search(validation.pattern) !== -1)
2504 };
2505 if(valid.length && valid.pattern) {
2506 validCard = true;
2507 }
2508 }
2509 });
2510
2511 if(!validCard) {
2512 return false;
2513 }
2514 }
2515
2516 // skip luhn for UnionPay
2517 unionPay = {
2518 number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
2519 pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
2520 };
2521 if(unionPay.number && unionPay.pattern) {
2522 return true;
2523 }
2524
2525 // verify luhn, adapted from <https://gist.github.com/2134376>
2526 var
2527 length = cardNumber.length,
2528 multiple = 0,
2529 producedValue = [
2530 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
2531 [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
2532 ],
2533 sum = 0
2534 ;
2535 while (length--) {
2536 sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
2537 multiple ^= 1;
2538 }
2539 return (sum % 10 === 0 && sum > 0);
2540 },
2541
2542 minCount: function(value, minCount) {
2543 if(minCount == 0) {
2544 return true;
2545 }
2546 if(minCount == 1) {
2547 return (value !== '');
2548 }
2549 return (value.split(',').length >= minCount);
2550 },
2551
2552 exactCount: function(value, exactCount) {
2553 if(exactCount == 0) {
2554 return (value === '');
2555 }
2556 if(exactCount == 1) {
2557 return (value !== '' && value.search(',') === -1);
2558 }
2559 return (value.split(',').length == exactCount);
2560 },
2561
2562 maxCount: function(value, maxCount) {
2563 if(maxCount == 0) {
2564 return false;
2565 }
2566 if(maxCount == 1) {
2567 return (value.search(',') === -1);
2568 }
2569 return (value.split(',').length <= maxCount);
2570 }
2571 }
2572
2573};
2574
2575})( jQuery, window, document );
2576
2577/*!
2578 * # Fomantic-UI 2.8.8 - Accordion
2579 * http://github.com/fomantic/Fomantic-UI/
2580 *
2581 *
2582 * Released under the MIT license
2583 * http://opensource.org/licenses/MIT
2584 *
2585 */
2586
2587;(function ($, window, document, undefined) {
2588
2589'use strict';
2590
2591$.isFunction = $.isFunction || function(obj) {
2592 return typeof obj === "function" && typeof obj.nodeType !== "number";
2593};
2594
2595window = (typeof window != 'undefined' && window.Math == Math)
2596 ? window
2597 : (typeof self != 'undefined' && self.Math == Math)
2598 ? self
2599 : Function('return this')()
2600;
2601
2602$.fn.accordion = function(parameters) {
2603 var
2604 $allModules = $(this),
2605
2606 time = new Date().getTime(),
2607 performance = [],
2608
2609 query = arguments[0],
2610 methodInvoked = (typeof query == 'string'),
2611 queryArguments = [].slice.call(arguments, 1),
2612
2613 returnedValue
2614 ;
2615 $allModules
2616 .each(function() {
2617 var
2618 settings = ( $.isPlainObject(parameters) )
2619 ? $.extend(true, {}, $.fn.accordion.settings, parameters)
2620 : $.extend({}, $.fn.accordion.settings),
2621
2622 className = settings.className,
2623 namespace = settings.namespace,
2624 selector = settings.selector,
2625 error = settings.error,
2626
2627 eventNamespace = '.' + namespace,
2628 moduleNamespace = 'module-' + namespace,
2629 moduleSelector = $allModules.selector || '',
2630
2631 $module = $(this),
2632 $title = $module.find(selector.title),
2633 $content = $module.find(selector.content),
2634
2635 element = this,
2636 instance = $module.data(moduleNamespace),
2637 observer,
2638 module
2639 ;
2640
2641 module = {
2642
2643 initialize: function() {
2644 module.debug('Initializing', $module);
2645 module.bind.events();
2646 if(settings.observeChanges) {
2647 module.observeChanges();
2648 }
2649 module.instantiate();
2650 },
2651
2652 instantiate: function() {
2653 instance = module;
2654 $module
2655 .data(moduleNamespace, module)
2656 ;
2657 },
2658
2659 destroy: function() {
2660 module.debug('Destroying previous instance', $module);
2661 $module
2662 .off(eventNamespace)
2663 .removeData(moduleNamespace)
2664 ;
2665 },
2666
2667 refresh: function() {
2668 $title = $module.find(selector.title);
2669 $content = $module.find(selector.content);
2670 },
2671
2672 observeChanges: function() {
2673 if('MutationObserver' in window) {
2674 observer = new MutationObserver(function(mutations) {
2675 module.debug('DOM tree modified, updating selector cache');
2676 module.refresh();
2677 });
2678 observer.observe(element, {
2679 childList : true,
2680 subtree : true
2681 });
2682 module.debug('Setting up mutation observer', observer);
2683 }
2684 },
2685
2686 bind: {
2687 events: function() {
2688 module.debug('Binding delegated events');
2689 $module
2690 .on(settings.on + eventNamespace, selector.trigger, module.event.click)
2691 ;
2692 }
2693 },
2694
2695 event: {
2696 click: function() {
2697 module.toggle.call(this);
2698 }
2699 },
2700
2701 toggle: function(query) {
2702 var
2703 $activeTitle = (query !== undefined)
2704 ? (typeof query === 'number')
2705 ? $title.eq(query)
2706 : $(query).closest(selector.title)
2707 : $(this).closest(selector.title),
2708 $activeContent = $activeTitle.next($content),
2709 isAnimating = $activeContent.hasClass(className.animating),
2710 isActive = $activeContent.hasClass(className.active),
2711 isOpen = (isActive && !isAnimating),
2712 isOpening = (!isActive && isAnimating)
2713 ;
2714 module.debug('Toggling visibility of content', $activeTitle);
2715 if(isOpen || isOpening) {
2716 if(settings.collapsible) {
2717 module.close.call($activeTitle);
2718 }
2719 else {
2720 module.debug('Cannot close accordion content collapsing is disabled');
2721 }
2722 }
2723 else {
2724 module.open.call($activeTitle);
2725 }
2726 },
2727
2728 open: function(query) {
2729 var
2730 $activeTitle = (query !== undefined)
2731 ? (typeof query === 'number')
2732 ? $title.eq(query)
2733 : $(query).closest(selector.title)
2734 : $(this).closest(selector.title),
2735 $activeContent = $activeTitle.next($content),
2736 isAnimating = $activeContent.hasClass(className.animating),
2737 isActive = $activeContent.hasClass(className.active),
2738 isOpen = (isActive || isAnimating)
2739 ;
2740 if(isOpen) {
2741 module.debug('Accordion already open, skipping', $activeContent);
2742 return;
2743 }
2744 module.debug('Opening accordion content', $activeTitle);
2745 settings.onOpening.call($activeContent);
2746 settings.onChanging.call($activeContent);
2747 if(settings.exclusive) {
2748 module.closeOthers.call($activeTitle);
2749 }
2750 $activeTitle
2751 .addClass(className.active)
2752 ;
2753 $activeContent
2754 .stop(true, true)
2755 .addClass(className.animating)
2756 ;
2757 if(settings.animateChildren) {
2758 if($.fn.transition !== undefined && $module.transition('is supported')) {
2759 $activeContent
2760 .children()
2761 .transition({
2762 animation : 'fade in',
2763 queue : false,
2764 useFailSafe : true,
2765 debug : settings.debug,
2766 verbose : settings.verbose,
2767 duration : settings.duration,
2768 skipInlineHidden : true,
2769 onComplete: function() {
2770 $activeContent.children().removeClass(className.transition);
2771 }
2772 })
2773 ;
2774 }
2775 else {
2776 $activeContent
2777 .children()
2778 .stop(true, true)
2779 .animate({
2780 opacity: 1
2781 }, settings.duration, module.resetOpacity)
2782 ;
2783 }
2784 }
2785 $activeContent
2786 .slideDown(settings.duration, settings.easing, function() {
2787 $activeContent
2788 .removeClass(className.animating)
2789 .addClass(className.active)
2790 ;
2791 module.reset.display.call(this);
2792 settings.onOpen.call(this);
2793 settings.onChange.call(this);
2794 })
2795 ;
2796 },
2797
2798 close: function(query) {
2799 var
2800 $activeTitle = (query !== undefined)
2801 ? (typeof query === 'number')
2802 ? $title.eq(query)
2803 : $(query).closest(selector.title)
2804 : $(this).closest(selector.title),
2805 $activeContent = $activeTitle.next($content),
2806 isAnimating = $activeContent.hasClass(className.animating),
2807 isActive = $activeContent.hasClass(className.active),
2808 isOpening = (!isActive && isAnimating),
2809 isClosing = (isActive && isAnimating)
2810 ;
2811 if((isActive || isOpening) && !isClosing) {
2812 module.debug('Closing accordion content', $activeContent);
2813 settings.onClosing.call($activeContent);
2814 settings.onChanging.call($activeContent);
2815 $activeTitle
2816 .removeClass(className.active)
2817 ;
2818 $activeContent
2819 .stop(true, true)
2820 .addClass(className.animating)
2821 ;
2822 if(settings.animateChildren) {
2823 if($.fn.transition !== undefined && $module.transition('is supported')) {
2824 $activeContent
2825 .children()
2826 .transition({
2827 animation : 'fade out',
2828 queue : false,
2829 useFailSafe : true,
2830 debug : settings.debug,
2831 verbose : settings.verbose,
2832 duration : settings.duration,
2833 skipInlineHidden : true
2834 })
2835 ;
2836 }
2837 else {
2838 $activeContent
2839 .children()
2840 .stop(true, true)
2841 .animate({
2842 opacity: 0
2843 }, settings.duration, module.resetOpacity)
2844 ;
2845 }
2846 }
2847 $activeContent
2848 .slideUp(settings.duration, settings.easing, function() {
2849 $activeContent
2850 .removeClass(className.animating)
2851 .removeClass(className.active)
2852 ;
2853 module.reset.display.call(this);
2854 settings.onClose.call(this);
2855 settings.onChange.call(this);
2856 })
2857 ;
2858 }
2859 },
2860
2861 closeOthers: function(index) {
2862 var
2863 $activeTitle = (index !== undefined)
2864 ? $title.eq(index)
2865 : $(this).closest(selector.title),
2866 $parentTitles = $activeTitle.parents(selector.content).prev(selector.title),
2867 $activeAccordion = $activeTitle.closest(selector.accordion),
2868 activeSelector = selector.title + '.' + className.active + ':visible',
2869 activeContent = selector.content + '.' + className.active + ':visible',
2870 $openTitles,
2871 $nestedTitles,
2872 $openContents
2873 ;
2874 if(settings.closeNested) {
2875 $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
2876 $openContents = $openTitles.next($content);
2877 }
2878 else {
2879 $openTitles = $activeAccordion.find(activeSelector).not($parentTitles);
2880 $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
2881 $openTitles = $openTitles.not($nestedTitles);
2882 $openContents = $openTitles.next($content);
2883 }
2884 if( ($openTitles.length > 0) ) {
2885 module.debug('Exclusive enabled, closing other content', $openTitles);
2886 $openTitles
2887 .removeClass(className.active)
2888 ;
2889 $openContents
2890 .removeClass(className.animating)
2891 .stop(true, true)
2892 ;
2893 if(settings.animateChildren) {
2894 if($.fn.transition !== undefined && $module.transition('is supported')) {
2895 $openContents
2896 .children()
2897 .transition({
2898 animation : 'fade out',
2899 useFailSafe : true,
2900 debug : settings.debug,
2901 verbose : settings.verbose,
2902 duration : settings.duration,
2903 skipInlineHidden : true
2904 })
2905 ;
2906 }
2907 else {
2908 $openContents
2909 .children()
2910 .stop(true, true)
2911 .animate({
2912 opacity: 0
2913 }, settings.duration, module.resetOpacity)
2914 ;
2915 }
2916 }
2917 $openContents
2918 .slideUp(settings.duration , settings.easing, function() {
2919 $(this).removeClass(className.active);
2920 module.reset.display.call(this);
2921 })
2922 ;
2923 }
2924 },
2925
2926 reset: {
2927
2928 display: function() {
2929 module.verbose('Removing inline display from element', this);
2930 $(this).css('display', '');
2931 if( $(this).attr('style') === '') {
2932 $(this)
2933 .attr('style', '')
2934 .removeAttr('style')
2935 ;
2936 }
2937 },
2938
2939 opacity: function() {
2940 module.verbose('Removing inline opacity from element', this);
2941 $(this).css('opacity', '');
2942 if( $(this).attr('style') === '') {
2943 $(this)
2944 .attr('style', '')
2945 .removeAttr('style')
2946 ;
2947 }
2948 },
2949
2950 },
2951
2952 setting: function(name, value) {
2953 module.debug('Changing setting', name, value);
2954 if( $.isPlainObject(name) ) {
2955 $.extend(true, settings, name);
2956 }
2957 else if(value !== undefined) {
2958 if($.isPlainObject(settings[name])) {
2959 $.extend(true, settings[name], value);
2960 }
2961 else {
2962 settings[name] = value;
2963 }
2964 }
2965 else {
2966 return settings[name];
2967 }
2968 },
2969 internal: function(name, value) {
2970 module.debug('Changing internal', name, value);
2971 if(value !== undefined) {
2972 if( $.isPlainObject(name) ) {
2973 $.extend(true, module, name);
2974 }
2975 else {
2976 module[name] = value;
2977 }
2978 }
2979 else {
2980 return module[name];
2981 }
2982 },
2983 debug: function() {
2984 if(!settings.silent && settings.debug) {
2985 if(settings.performance) {
2986 module.performance.log(arguments);
2987 }
2988 else {
2989 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
2990 module.debug.apply(console, arguments);
2991 }
2992 }
2993 },
2994 verbose: function() {
2995 if(!settings.silent && settings.verbose && settings.debug) {
2996 if(settings.performance) {
2997 module.performance.log(arguments);
2998 }
2999 else {
3000 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
3001 module.verbose.apply(console, arguments);
3002 }
3003 }
3004 },
3005 error: function() {
3006 if(!settings.silent) {
3007 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
3008 module.error.apply(console, arguments);
3009 }
3010 },
3011 performance: {
3012 log: function(message) {
3013 var
3014 currentTime,
3015 executionTime,
3016 previousTime
3017 ;
3018 if(settings.performance) {
3019 currentTime = new Date().getTime();
3020 previousTime = time || currentTime;
3021 executionTime = currentTime - previousTime;
3022 time = currentTime;
3023 performance.push({
3024 'Name' : message[0],
3025 'Arguments' : [].slice.call(message, 1) || '',
3026 'Element' : element,
3027 'Execution Time' : executionTime
3028 });
3029 }
3030 clearTimeout(module.performance.timer);
3031 module.performance.timer = setTimeout(module.performance.display, 500);
3032 },
3033 display: function() {
3034 var
3035 title = settings.name + ':',
3036 totalTime = 0
3037 ;
3038 time = false;
3039 clearTimeout(module.performance.timer);
3040 $.each(performance, function(index, data) {
3041 totalTime += data['Execution Time'];
3042 });
3043 title += ' ' + totalTime + 'ms';
3044 if(moduleSelector) {
3045 title += ' \'' + moduleSelector + '\'';
3046 }
3047 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
3048 console.groupCollapsed(title);
3049 if(console.table) {
3050 console.table(performance);
3051 }
3052 else {
3053 $.each(performance, function(index, data) {
3054 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
3055 });
3056 }
3057 console.groupEnd();
3058 }
3059 performance = [];
3060 }
3061 },
3062 invoke: function(query, passedArguments, context) {
3063 var
3064 object = instance,
3065 maxDepth,
3066 found,
3067 response
3068 ;
3069 passedArguments = passedArguments || queryArguments;
3070 context = element || context;
3071 if(typeof query == 'string' && object !== undefined) {
3072 query = query.split(/[\. ]/);
3073 maxDepth = query.length - 1;
3074 $.each(query, function(depth, value) {
3075 var camelCaseValue = (depth != maxDepth)
3076 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
3077 : query
3078 ;
3079 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
3080 object = object[camelCaseValue];
3081 }
3082 else if( object[camelCaseValue] !== undefined ) {
3083 found = object[camelCaseValue];
3084 return false;
3085 }
3086 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
3087 object = object[value];
3088 }
3089 else if( object[value] !== undefined ) {
3090 found = object[value];
3091 return false;
3092 }
3093 else {
3094 module.error(error.method, query);
3095 return false;
3096 }
3097 });
3098 }
3099 if ( $.isFunction( found ) ) {
3100 response = found.apply(context, passedArguments);
3101 }
3102 else if(found !== undefined) {
3103 response = found;
3104 }
3105 if(Array.isArray(returnedValue)) {
3106 returnedValue.push(response);
3107 }
3108 else if(returnedValue !== undefined) {
3109 returnedValue = [returnedValue, response];
3110 }
3111 else if(response !== undefined) {
3112 returnedValue = response;
3113 }
3114 return found;
3115 }
3116 };
3117 if(methodInvoked) {
3118 if(instance === undefined) {
3119 module.initialize();
3120 }
3121 module.invoke(query);
3122 }
3123 else {
3124 if(instance !== undefined) {
3125 instance.invoke('destroy');
3126 }
3127 module.initialize();
3128 }
3129 })
3130 ;
3131 return (returnedValue !== undefined)
3132 ? returnedValue
3133 : this
3134 ;
3135};
3136
3137$.fn.accordion.settings = {
3138
3139 name : 'Accordion',
3140 namespace : 'accordion',
3141
3142 silent : false,
3143 debug : false,
3144 verbose : false,
3145 performance : true,
3146
3147 on : 'click', // event on title that opens accordion
3148
3149 observeChanges : true, // whether accordion should automatically refresh on DOM insertion
3150
3151 exclusive : true, // whether a single accordion content panel should be open at once
3152 collapsible : true, // whether accordion content can be closed
3153 closeNested : false, // whether nested content should be closed when a panel is closed
3154 animateChildren : true, // whether children opacity should be animated
3155
3156 duration : 350, // duration of animation
3157 easing : 'easeOutQuad', // easing equation for animation
3158
3159 onOpening : function(){}, // callback before open animation
3160 onClosing : function(){}, // callback before closing animation
3161 onChanging : function(){}, // callback before closing or opening animation
3162
3163 onOpen : function(){}, // callback after open animation
3164 onClose : function(){}, // callback after closing animation
3165 onChange : function(){}, // callback after closing or opening animation
3166
3167 error: {
3168 method : 'The method you called is not defined'
3169 },
3170
3171 className : {
3172 active : 'active',
3173 animating : 'animating',
3174 transition: 'transition'
3175 },
3176
3177 selector : {
3178 accordion : '.accordion',
3179 title : '.title',
3180 trigger : '.title',
3181 content : '.content'
3182 }
3183
3184};
3185
3186// Adds easing
3187$.extend( $.easing, {
3188 easeOutQuad: function (x, t, b, c, d) {
3189 return -c *(t/=d)*(t-2) + b;
3190 }
3191});
3192
3193})( jQuery, window, document );
3194
3195
3196/*!
3197 * # Fomantic-UI 2.8.8 - Calendar
3198 * http://github.com/fomantic/Fomantic-UI/
3199 *
3200 *
3201 * Released under the MIT license
3202 * http://opensource.org/licenses/MIT
3203 *
3204 */
3205
3206;(function ($, window, document, undefined) {
3207
3208'use strict';
3209
3210$.isFunction = $.isFunction || function(obj) {
3211 return typeof obj === "function" && typeof obj.nodeType !== "number";
3212};
3213
3214window = (typeof window != 'undefined' && window.Math == Math)
3215 ? window
3216 : (typeof self != 'undefined' && self.Math == Math)
3217 ? self
3218 : Function('return this')()
3219;
3220
3221$.fn.calendar = function(parameters) {
3222 var
3223 $allModules = $(this),
3224
3225 moduleSelector = $allModules.selector || '',
3226
3227 time = new Date().getTime(),
3228 performance = [],
3229
3230 query = arguments[0],
3231 methodInvoked = (typeof query == 'string'),
3232 queryArguments = [].slice.call(arguments, 1),
3233 returnedValue,
3234 timeGapTable = {
3235 '5': {'row': 4, 'column': 3 },
3236 '10': {'row': 3, 'column': 2 },
3237 '15': {'row': 2, 'column': 2 },
3238 '20': {'row': 3, 'column': 1 },
3239 '30': {'row': 2, 'column': 1 }
3240 },
3241 numberText = ['','one','two','three','four','five','six','seven','eight']
3242 ;
3243
3244 $allModules
3245 .each(function () {
3246 var
3247 settings = ( $.isPlainObject(parameters) )
3248 ? $.extend(true, {}, $.fn.calendar.settings, parameters)
3249 : $.extend({}, $.fn.calendar.settings),
3250
3251 className = settings.className,
3252 namespace = settings.namespace,
3253 selector = settings.selector,
3254 formatter = settings.formatter,
3255 parser = settings.parser,
3256 metadata = settings.metadata,
3257 timeGap = timeGapTable[settings.minTimeGap],
3258 error = settings.error,
3259
3260 eventNamespace = '.' + namespace,
3261 moduleNamespace = 'module-' + namespace,
3262
3263 $module = $(this),
3264 $input = $module.find(selector.input),
3265 $container = $module.find(selector.popup),
3266 $activator = $module.find(selector.activator),
3267
3268 element = this,
3269 instance = $module.data(moduleNamespace),
3270
3271 isTouch,
3272 isTouchDown = false,
3273 isInverted = $module.hasClass(className.inverted),
3274 focusDateUsedForRange = false,
3275 selectionComplete = false,
3276 classObserver,
3277 module
3278 ;
3279
3280 module = {
3281
3282 initialize: function () {
3283 module.debug('Initializing calendar for', element, $module);
3284
3285 isTouch = module.get.isTouch();
3286 module.setup.config();
3287 module.setup.popup();
3288 module.setup.inline();
3289 module.setup.input();
3290 module.setup.date();
3291 module.create.calendar();
3292
3293 module.bind.events();
3294 module.observeChanges();
3295 module.instantiate();
3296 },
3297
3298 instantiate: function () {
3299 module.verbose('Storing instance of calendar');
3300 instance = module;
3301 $module.data(moduleNamespace, instance);
3302 },
3303
3304 destroy: function () {
3305 module.verbose('Destroying previous calendar for', element);
3306 $module.removeData(moduleNamespace);
3307 module.unbind.events();
3308 module.disconnect.classObserver();
3309 },
3310
3311 setup: {
3312 config: function () {
3313 if (module.get.minDate() !== null) {
3314 module.set.minDate($module.data(metadata.minDate));
3315 }
3316 if (module.get.maxDate() !== null) {
3317 module.set.maxDate($module.data(metadata.maxDate));
3318 }
3319 module.setting('type', module.get.type());
3320 module.setting('on', settings.on || ($input.length ? 'focus' : 'click'));
3321 },
3322 popup: function () {
3323 if (settings.inline) {
3324 return;
3325 }
3326 if (!$activator.length) {
3327 $activator = $module.children().first();
3328 if (!$activator.length) {
3329 return;
3330 }
3331 }
3332 if ($.fn.popup === undefined) {
3333 module.error(error.popup);
3334 return;
3335 }
3336 if (!$container.length) {
3337 //prepend the popup element to the activator's parent so that it has less chance of messing with
3338 //the styling (eg input action button needs to be the last child to have correct border radius)
3339 var $activatorParent = $activator.parent(),
3340 domPositionFunction = $activatorParent.closest(selector.append).length !== 0 ? 'appendTo' : 'prependTo';
3341 $container = $('<div/>').addClass(className.popup)[domPositionFunction]($activatorParent);
3342 }
3343 $container.addClass(className.calendar);
3344 if(isInverted){
3345 $container.addClass(className.inverted);
3346 }
3347 var onVisible = function () {
3348 module.refreshTooltips();
3349 return settings.onVisible.apply($container, arguments);
3350 };
3351 var onHidden = settings.onHidden;
3352 if (!$input.length) {
3353 //no input, $container has to handle focus/blur
3354 $container.attr('tabindex', '0');
3355 onVisible = function () {
3356 module.refreshTooltips();
3357 module.focus();
3358 return settings.onVisible.apply($container, arguments);
3359 };
3360 onHidden = function () {
3361 module.blur();
3362 return settings.onHidden.apply($container, arguments);
3363 };
3364 }
3365 var onShow = function () {
3366 //reset the focus date onShow
3367 module.set.focusDate(module.get.date());
3368 module.set.mode(module.get.validatedMode(settings.startMode));
3369 return settings.onShow.apply($container, arguments);
3370 };
3371 var on = module.setting('on');
3372 var options = $.extend({}, settings.popupOptions, {
3373 popup: $container,
3374 on: on,
3375 hoverable: on === 'hover',
3376 closable: on === 'click',
3377 onShow: onShow,
3378 onVisible: onVisible,
3379 onHide: settings.onHide,
3380 onHidden: onHidden
3381 });
3382 module.popup(options);
3383 },
3384 inline: function () {
3385 if ($activator.length && !settings.inline) {
3386 return;
3387 }
3388 settings.inline = true;
3389 $container = $('<div/>').addClass(className.calendar).appendTo($module);
3390 if (!$input.length) {
3391 $container.attr('tabindex', '0');
3392 }
3393 },
3394 input: function () {
3395 if (settings.touchReadonly && $input.length && isTouch) {
3396 $input.prop('readonly', true);
3397 }
3398 module.check.disabled();
3399 },
3400 date: function () {
3401 var date;
3402 if (settings.initialDate) {
3403 date = parser.date(settings.initialDate, settings);
3404 } else if ($module.data(metadata.date) !== undefined) {
3405 date = parser.date($module.data(metadata.date), settings);
3406 } else if ($input.length) {
3407 date = parser.date($input.val(), settings);
3408 }
3409 module.set.date(date, settings.formatInput, false);
3410 module.set.mode(module.get.mode(), false);
3411 }
3412 },
3413
3414 trigger: {
3415 change: function() {
3416 var
3417 inputElement = $input[0]
3418 ;
3419 if(inputElement) {
3420 var events = document.createEvent('HTMLEvents');
3421 module.verbose('Triggering native change event');
3422 events.initEvent('change', true, false);
3423 inputElement.dispatchEvent(events);
3424 }
3425 }
3426 },
3427
3428 create: {
3429 calendar: function () {
3430 var i, r, c, p, row, cell, pageGrid;
3431
3432 var
3433 mode = module.get.mode(),
3434 today = new Date(),
3435 date = module.get.date(),
3436 focusDate = module.get.focusDate(),
3437 display = module.helper.dateInRange(focusDate || date || settings.initialDate || today)
3438 ;
3439
3440 if (!focusDate) {
3441 focusDate = display;
3442 module.set.focusDate(focusDate, false, false);
3443 }
3444
3445 var
3446 isYear = mode === 'year',
3447 isMonth = mode === 'month',
3448 isDay = mode === 'day',
3449 isHour = mode === 'hour',
3450 isMinute = mode === 'minute',
3451 isTimeOnly = settings.type === 'time'
3452 ;
3453
3454 var multiMonth = Math.max(settings.multiMonth, 1);
3455 var monthOffset = !isDay ? 0 : module.get.monthOffset();
3456
3457 var
3458 minute = display.getMinutes(),
3459 hour = display.getHours(),
3460 day = display.getDate(),
3461 startMonth = display.getMonth() + monthOffset,
3462 year = display.getFullYear()
3463 ;
3464
3465 var columns = isDay ? settings.showWeekNumbers ? 8 : 7 : isHour ? 4 : timeGap['column'];
3466 var rows = isDay || isHour ? 6 : timeGap['row'];
3467 var pages = isDay ? multiMonth : 1;
3468
3469 var container = $container;
3470 var tooltipPosition = container.hasClass("left") ? "right center" : "left center";
3471 container.empty();
3472 if (pages > 1) {
3473 pageGrid = $('<div/>').addClass(className.grid).appendTo(container);
3474 }
3475
3476 for (p = 0; p < pages; p++) {
3477 if (pages > 1) {
3478 var pageColumn = $('<div/>').addClass(className.column).appendTo(pageGrid);
3479 container = pageColumn;
3480 }
3481
3482 var month = startMonth + p;
3483 var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7;
3484 if (!settings.constantHeight && isDay) {
3485 var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn;
3486 rows = Math.ceil(requiredCells / 7);
3487 }
3488
3489 var
3490 yearChange = isYear ? 10 : isMonth ? 1 : 0,
3491 monthChange = isDay ? 1 : 0,
3492 dayChange = isHour || isMinute ? 1 : 0,
3493 prevNextDay = isHour || isMinute ? day : 1,
3494 prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour),
3495 nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour),
3496 prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) :
3497 isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1),
3498 nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) :
3499 isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1)
3500 ;
3501
3502 var tempMode = mode;
3503 if (isDay && settings.showWeekNumbers){
3504 tempMode += ' andweek';
3505 }
3506 var table = $('<table/>').addClass(className.table).addClass(tempMode).addClass(numberText[columns] + ' column').appendTo(container);
3507 if(isInverted){
3508 table.addClass(className.inverted);
3509 }
3510 var textColumns = columns;
3511 //no header for time-only mode
3512 if (!isTimeOnly) {
3513 var thead = $('<thead/>').appendTo(table);
3514
3515 row = $('<tr/>').appendTo(thead);
3516 cell = $('<th/>').attr('colspan', '' + columns).appendTo(row);
3517
3518 var headerDate = isYear || isMonth ? new Date(year, 0, 1) :
3519 isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute);
3520 var headerText = $('<span/>').addClass(className.link).appendTo(cell);
3521 headerText.text(formatter.header(headerDate, mode, settings));
3522 var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') :
3523 isDay ? (settings.disableMonth ? 'year' : 'month') : 'day';
3524 headerText.data(metadata.mode, newMode);
3525
3526 if (p === 0) {
3527 var prev = $('<span/>').addClass(className.prev).appendTo(cell);
3528 prev.data(metadata.focusDate, prevDate);
3529 prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
3530 $('<i/>').addClass(className.prevIcon).appendTo(prev);
3531 }
3532
3533 if (p === pages - 1) {
3534 var next = $('<span/>').addClass(className.next).appendTo(cell);
3535 next.data(metadata.focusDate, nextDate);
3536 next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
3537 $('<i/>').addClass(className.nextIcon).appendTo(next);
3538 }
3539 if (isDay) {
3540 row = $('<tr/>').appendTo(thead);
3541 if(settings.showWeekNumbers) {
3542 cell = $('<th/>').appendTo(row);
3543 cell.text(settings.text.weekNo);
3544 cell.addClass(className.weekCell);
3545 textColumns--;
3546 }
3547 for (i = 0; i < textColumns; i++) {
3548 cell = $('<th/>').appendTo(row);
3549 cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
3550 }
3551 }
3552 }
3553
3554 var tbody = $('<tbody/>').appendTo(table);
3555 i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0;
3556 for (r = 0; r < rows; r++) {
3557 row = $('<tr/>').appendTo(tbody);
3558 if(isDay && settings.showWeekNumbers){
3559 cell = $('<th/>').appendTo(row);
3560 cell.text(module.get.weekOfYear(year,month,i+1-settings.firstDayOfWeek));
3561 cell.addClass(className.weekCell);
3562 }
3563 for (c = 0; c < textColumns; c++, i++) {
3564 var cellDate = isYear ? new Date(i, month, 1, hour, minute) :
3565 isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) :
3566 isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap);
3567 var cellText = isYear ? i :
3568 isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() :
3569 formatter.time(cellDate, settings, true);
3570 cell = $('<td/>').addClass(className.cell).appendTo(row);
3571 cell.text(cellText);
3572 cell.data(metadata.date, cellDate);
3573 var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
3574 var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
3575 var eventDate;
3576 if (disabled) {
3577 var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
3578 if (disabledDate !== null && disabledDate[metadata.message]) {
3579 cell.attr("data-tooltip", disabledDate[metadata.message]);
3580 cell.attr("data-position", disabledDate[metadata.position] || tooltipPosition);
3581 if(disabledDate[metadata.inverted] || (isInverted && disabledDate[metadata.inverted] === undefined)) {
3582 cell.attr("data-inverted", '');
3583 }
3584 if(disabledDate[metadata.variation]) {
3585 cell.attr("data-variation", disabledDate[metadata.variation]);
3586 }
3587 }
3588 } else {
3589 eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
3590 if (eventDate !== null) {
3591 cell.addClass(eventDate[metadata.class] || settings.eventClass);
3592 if (eventDate[metadata.message]) {
3593 cell.attr("data-tooltip", eventDate[metadata.message]);
3594 cell.attr("data-position", eventDate[metadata.position] || tooltipPosition);
3595 if(eventDate[metadata.inverted] || (isInverted && eventDate[metadata.inverted] === undefined)) {
3596 cell.attr("data-inverted", '');
3597 }
3598 if(eventDate[metadata.variation]) {
3599 cell.attr("data-variation", eventDate[metadata.variation]);
3600 }
3601 }
3602 }
3603 }
3604 var active = module.helper.dateEqual(cellDate, date, mode);
3605 var isToday = module.helper.dateEqual(cellDate, today, mode);
3606 cell.toggleClass(className.adjacentCell, adjacent && !eventDate);
3607 cell.toggleClass(className.disabledCell, disabled);
3608 cell.toggleClass(className.activeCell, active && !(adjacent && disabled));
3609 if (!isHour && !isMinute) {
3610 cell.toggleClass(className.todayCell, !adjacent && isToday);
3611 }
3612
3613 // Allow for external modifications of each cell
3614 var cellOptions = {
3615 mode: mode,
3616 adjacent: adjacent,
3617 disabled: disabled,
3618 active: active,
3619 today: isToday
3620 };
3621 formatter.cell(cell, cellDate, cellOptions);
3622
3623 if (module.helper.dateEqual(cellDate, focusDate, mode)) {
3624 //ensure that the focus date is exactly equal to the cell date
3625 //so that, if selected, the correct value is set
3626 module.set.focusDate(cellDate, false, false);
3627 }
3628 }
3629 }
3630
3631 if (settings.today) {
3632 var todayRow = $('<tr/>').appendTo(tbody);
3633 var todayButton = $('<td/>').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow);
3634 todayButton.text(formatter.today(settings));
3635 todayButton.data(metadata.date, today);
3636 }
3637
3638 module.update.focus(false, table);
3639
3640 if(settings.inline){
3641 module.refreshTooltips();
3642 }
3643 }
3644 }
3645 },
3646
3647 update: {
3648 focus: function (updateRange, container) {
3649 container = container || $container;
3650 var mode = module.get.mode();
3651 var date = module.get.date();
3652 var focusDate = module.get.focusDate();
3653 var startDate = module.get.startDate();
3654 var endDate = module.get.endDate();
3655 var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null);
3656
3657 container.find('td').each(function () {
3658 var cell = $(this);
3659 var cellDate = cell.data(metadata.date);
3660 if (!cellDate) {
3661 return;
3662 }
3663 var disabled = cell.hasClass(className.disabledCell);
3664 var active = cell.hasClass(className.activeCell);
3665 var adjacent = cell.hasClass(className.adjacentCell);
3666 var focused = module.helper.dateEqual(cellDate, focusDate, mode);
3667 var inRange = !rangeDate ? false :
3668 ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) ||
3669 (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate)));
3670 cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled);
3671
3672 if (module.helper.isTodayButton(cell)) {
3673 return;
3674 }
3675 cell.toggleClass(className.rangeCell, inRange && !active && !disabled);
3676 });
3677 }
3678 },
3679
3680 refresh: function () {
3681 module.create.calendar();
3682 },
3683
3684 refreshTooltips: function() {
3685 var winWidth = $(window).width();
3686 $container.find('td[data-position]').each(function () {
3687 var cell = $(this);
3688 var tooltipWidth = window.getComputedStyle(cell[0], ':after').width.replace(/[^0-9\.]/g,'');
3689 var tooltipPosition = cell.attr('data-position');
3690 // use a fallback width of 250 (calendar width) for IE/Edge (which return "auto")
3691 var calcPosition = (winWidth - cell.width() - (parseInt(tooltipWidth,10) || 250)) > cell.offset().left ? 'right' : 'left';
3692 if(tooltipPosition.indexOf(calcPosition) === -1) {
3693 cell.attr('data-position',tooltipPosition.replace(/(left|right)/,calcPosition));
3694 }
3695 });
3696 },
3697
3698 bind: {
3699 events: function () {
3700 module.debug('Binding events');
3701 $container.on('mousedown' + eventNamespace, module.event.mousedown);
3702 $container.on('touchstart' + eventNamespace, module.event.mousedown);
3703 $container.on('mouseup' + eventNamespace, module.event.mouseup);
3704 $container.on('touchend' + eventNamespace, module.event.mouseup);
3705 $container.on('mouseover' + eventNamespace, module.event.mouseover);
3706 if ($input.length) {
3707 $input.on('input' + eventNamespace, module.event.inputChange);
3708 $input.on('focus' + eventNamespace, module.event.inputFocus);
3709 $input.on('blur' + eventNamespace, module.event.inputBlur);
3710 $input.on('keydown' + eventNamespace, module.event.keydown);
3711 } else {
3712 $container.on('keydown' + eventNamespace, module.event.keydown);
3713 }
3714 }
3715 },
3716
3717 unbind: {
3718 events: function () {
3719 module.debug('Unbinding events');
3720 $container.off(eventNamespace);
3721 if ($input.length) {
3722 $input.off(eventNamespace);
3723 }
3724 }
3725 },
3726
3727 event: {
3728 mouseover: function (event) {
3729 var target = $(event.target);
3730 var date = target.data(metadata.date);
3731 var mousedown = event.buttons === 1;
3732 if (date) {
3733 module.set.focusDate(date, false, true, mousedown);
3734 }
3735 },
3736 mousedown: function (event) {
3737 if ($input.length) {
3738 //prevent the mousedown on the calendar causing the input to lose focus
3739 event.preventDefault();
3740 }
3741 isTouchDown = event.type.indexOf('touch') >= 0;
3742 var target = $(event.target);
3743 var date = target.data(metadata.date);
3744 if (date) {
3745 module.set.focusDate(date, false, true, true);
3746 }
3747 },
3748 mouseup: function (event) {
3749 //ensure input has focus so that it receives keydown events for calendar navigation
3750 module.focus();
3751 event.preventDefault();
3752 event.stopPropagation();
3753 isTouchDown = false;
3754 var target = $(event.target);
3755 if (target.hasClass("disabled")) {
3756 return;
3757 }
3758 var parent = target.parent();
3759 if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) {
3760 //clicked on a child element, switch to parent (used when clicking directly on prev/next <i> icon element)
3761 target = parent;
3762 }
3763 var date = target.data(metadata.date);
3764 var focusDate = target.data(metadata.focusDate);
3765 var mode = target.data(metadata.mode);
3766 if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
3767 var forceSet = target.hasClass(className.today);
3768 module.selectDate(date, forceSet);
3769 }
3770 else if (focusDate) {
3771 module.set.focusDate(focusDate);
3772 }
3773 else if (mode) {
3774 module.set.mode(mode);
3775 }
3776 },
3777 keydown: function (event) {
3778 var keyCode = event.which;
3779 if (keyCode === 27 || keyCode === 9) {
3780 //esc || tab
3781 module.popup('hide');
3782 }
3783
3784 if (module.popup('is visible')) {
3785 if (keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40) {
3786 //arrow keys
3787 var mode = module.get.mode();
3788 var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : mode === 'minute' ? timeGap['column'] : 3;
3789 var increment = keyCode === 37 ? -1 : keyCode === 38 ? -bigIncrement : keyCode == 39 ? 1 : bigIncrement;
3790 increment *= mode === 'minute' ? settings.minTimeGap : 1;
3791 var focusDate = module.get.focusDate() || module.get.date() || new Date();
3792 var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0);
3793 var month = focusDate.getMonth() + (mode === 'month' ? increment : 0);
3794 var day = focusDate.getDate() + (mode === 'day' ? increment : 0);
3795 var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0);
3796 var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0);
3797 var newFocusDate = new Date(year, month, day, hour, minute);
3798 if (settings.type === 'time') {
3799 newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate);
3800 }
3801 if (module.helper.isDateInRange(newFocusDate, mode)) {
3802 module.set.focusDate(newFocusDate);
3803 }
3804 } else if (keyCode === 13) {
3805 //enter
3806 var mode = module.get.mode();
3807 var date = module.get.focusDate();
3808 if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) {
3809 module.selectDate(date);
3810 }
3811 //disable form submission:
3812 event.preventDefault();
3813 event.stopPropagation();
3814 }
3815 }
3816
3817 if (keyCode === 38 || keyCode === 40) {
3818 //arrow-up || arrow-down
3819 event.preventDefault(); //don't scroll
3820 module.popup('show');
3821 }
3822 },
3823 inputChange: function () {
3824 var val = $input.val();
3825 var date = parser.date(val, settings);
3826 module.set.date(date, false);
3827 },
3828 inputFocus: function () {
3829 $container.addClass(className.active);
3830 },
3831 inputBlur: function () {
3832 $container.removeClass(className.active);
3833 if (settings.formatInput) {
3834 var date = module.get.date();
3835 var text = formatter.datetime(date, settings);
3836 $input.val(text);
3837 }
3838 if(selectionComplete){
3839 module.trigger.change();
3840 selectionComplete = false;
3841 }
3842 },
3843 class: {
3844 mutation: function(mutations) {
3845 mutations.forEach(function(mutation) {
3846 if(mutation.attributeName === "class") {
3847 module.check.disabled();
3848 }
3849 });
3850 }
3851 }
3852 },
3853
3854 observeChanges: function() {
3855 if('MutationObserver' in window) {
3856 classObserver = new MutationObserver(module.event.class.mutation);
3857 module.debug('Setting up mutation observer', classObserver);
3858 module.observe.class();
3859 }
3860 },
3861
3862 disconnect: {
3863 classObserver: function() {
3864 if($input.length && classObserver) {
3865 classObserver.disconnect();
3866 }
3867 }
3868 },
3869
3870 observe: {
3871 class: function() {
3872 if($input.length && classObserver) {
3873 classObserver.observe($module[0], {
3874 attributes : true
3875 });
3876 }
3877 }
3878 },
3879
3880 is: {
3881 disabled: function() {
3882 return $module.hasClass(className.disabled);
3883 }
3884 },
3885
3886 check: {
3887 disabled: function(){
3888 $input.attr('tabindex',module.is.disabled() ? -1 : 0);
3889 }
3890 },
3891
3892 get: {
3893 weekOfYear: function(weekYear,weekMonth,weekDay) {
3894 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
3895 var ms1d = 864e5, // milliseconds in a day
3896 ms7d = 7 * ms1d; // milliseconds in a week
3897
3898 return function() { // return a closure so constants get calculated only once
3899 var DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an Absolute Day Number
3900 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
3901 Wyr = new Date(AWN * ms7d).getUTCFullYear();
3902
3903 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
3904 }();
3905 },
3906 date: function () {
3907 return module.helper.sanitiseDate($module.data(metadata.date)) || null;
3908 },
3909 inputDate: function() {
3910 return $input.val();
3911 },
3912 focusDate: function () {
3913 return $module.data(metadata.focusDate) || null;
3914 },
3915 startDate: function () {
3916 var startModule = module.get.calendarModule(settings.startCalendar);
3917 return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null;
3918 },
3919 endDate: function () {
3920 var endModule = module.get.calendarModule(settings.endCalendar);
3921 return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null;
3922 },
3923 minDate: function() {
3924 return $module.data(metadata.minDate) || null;
3925 },
3926 maxDate: function() {
3927 return $module.data(metadata.maxDate) || null;
3928 },
3929 monthOffset: function () {
3930 return $module.data(metadata.monthOffset) || 0;
3931 },
3932 mode: function () {
3933 //only returns valid modes for the current settings
3934 var mode = $module.data(metadata.mode) || settings.startMode;
3935 return module.get.validatedMode(mode);
3936 },
3937 validatedMode: function(mode){
3938 var validModes = module.get.validModes();
3939 if ($.inArray(mode, validModes) >= 0) {
3940 return mode;
3941 }
3942 return settings.type === 'time' ? 'hour' :
3943 settings.type === 'month' ? 'month' :
3944 settings.type === 'year' ? 'year' : 'day';
3945 },
3946 type: function() {
3947 return $module.data(metadata.type) || settings.type;
3948 },
3949 validModes: function () {
3950 var validModes = [];
3951 if (settings.type !== 'time') {
3952 if (!settings.disableYear || settings.type === 'year') {
3953 validModes.push('year');
3954 }
3955 if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') {
3956 validModes.push('month');
3957 }
3958 if (settings.type.indexOf('date') >= 0) {
3959 validModes.push('day');
3960 }
3961 }
3962 if (settings.type.indexOf('time') >= 0) {
3963 validModes.push('hour');
3964 if (!settings.disableMinute) {
3965 validModes.push('minute');
3966 }
3967 }
3968 return validModes;
3969 },
3970 isTouch: function () {
3971 try {
3972 document.createEvent('TouchEvent');
3973 return true;
3974 }
3975 catch (e) {
3976 return false;
3977 }
3978 },
3979 calendarModule: function (selector) {
3980 if (!selector) {
3981 return null;
3982 }
3983 if (!(selector instanceof $)) {
3984 selector = $(selector).first();
3985 }
3986 //assume range related calendars are using the same namespace
3987 return selector.data(moduleNamespace);
3988 }
3989 },
3990
3991 set: {
3992 date: function (date, updateInput, fireChange) {
3993 updateInput = updateInput !== false;
3994 fireChange = fireChange !== false;
3995 date = module.helper.sanitiseDate(date);
3996 date = module.helper.dateInRange(date);
3997
3998 var mode = module.get.mode();
3999 var text = formatter.datetime(date, settings);
4000
4001 if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) {
4002 return false;
4003 }
4004
4005 module.set.focusDate(date);
4006
4007 if (settings.isDisabled(date, mode)) {
4008 return false;
4009 }
4010
4011 var endDate = module.get.endDate();
4012 if (!!endDate && !!date && date > endDate) {
4013 //selected date is greater than end date in range, so clear end date
4014 module.set.endDate(undefined);
4015 }
4016 module.set.dataKeyValue(metadata.date, date);
4017
4018 if (updateInput && $input.length) {
4019 $input.val(text);
4020 }
4021
4022 if (fireChange) {
4023 settings.onChange.call(element, date, text, mode);
4024 }
4025 },
4026 startDate: function (date, refreshCalendar) {
4027 date = module.helper.sanitiseDate(date);
4028 var startModule = module.get.calendarModule(settings.startCalendar);
4029 if (startModule) {
4030 startModule.set.date(date);
4031 }
4032 module.set.dataKeyValue(metadata.startDate, date, refreshCalendar);
4033 },
4034 endDate: function (date, refreshCalendar) {
4035 date = module.helper.sanitiseDate(date);
4036 var endModule = module.get.calendarModule(settings.endCalendar);
4037 if (endModule) {
4038 endModule.set.date(date);
4039 }
4040 module.set.dataKeyValue(metadata.endDate, date, refreshCalendar);
4041 },
4042 focusDate: function (date, refreshCalendar, updateFocus, updateRange) {
4043 date = module.helper.sanitiseDate(date);
4044 date = module.helper.dateInRange(date);
4045 var isDay = module.get.mode() === 'day';
4046 var oldFocusDate = module.get.focusDate();
4047 if (isDay && date && oldFocusDate) {
4048 var yearDelta = date.getFullYear() - oldFocusDate.getFullYear();
4049 var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth();
4050 if (monthDelta) {
4051 var monthOffset = module.get.monthOffset() - monthDelta;
4052 module.set.monthOffset(monthOffset, false);
4053 }
4054 }
4055 var changed = module.set.dataKeyValue(metadata.focusDate, date, !!date && refreshCalendar);
4056 updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange;
4057 focusDateUsedForRange = updateRange;
4058 if (updateFocus) {
4059 module.update.focus(updateRange);
4060 }
4061 },
4062 minDate: function (date) {
4063 date = module.helper.sanitiseDate(date);
4064 if (settings.maxDate !== null && settings.maxDate <= date) {
4065 module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate);
4066 } else {
4067 module.setting('minDate', date);
4068 module.set.dataKeyValue(metadata.minDate, date);
4069 }
4070 },
4071 maxDate: function (date) {
4072 date = module.helper.sanitiseDate(date);
4073 if (settings.minDate !== null && settings.minDate >= date) {
4074 module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate);
4075 } else {
4076 module.setting('maxDate', date);
4077 module.set.dataKeyValue(metadata.maxDate, date);
4078 }
4079 },
4080 monthOffset: function (monthOffset, refreshCalendar) {
4081 var multiMonth = Math.max(settings.multiMonth, 1);
4082 monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset));
4083 module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar);
4084 },
4085 mode: function (mode, refreshCalendar) {
4086 module.set.dataKeyValue(metadata.mode, mode, refreshCalendar);
4087 },
4088 dataKeyValue: function (key, value, refreshCalendar) {
4089 var oldValue = $module.data(key);
4090 var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects
4091 if (value) {
4092 $module.data(key, value);
4093 } else {
4094 $module.removeData(key);
4095 }
4096 refreshCalendar = refreshCalendar !== false && !equal;
4097 if (refreshCalendar) {
4098 module.refresh();
4099 }
4100 return !equal;
4101 }
4102 },
4103
4104 selectDate: function (date, forceSet) {
4105 module.verbose('New date selection', date);
4106 var mode = module.get.mode();
4107 var complete = forceSet || mode === 'minute' ||
4108 (settings.disableMinute && mode === 'hour') ||
4109 (settings.type === 'date' && mode === 'day') ||
4110 (settings.type === 'month' && mode === 'month') ||
4111 (settings.type === 'year' && mode === 'year');
4112 if (complete) {
4113 var canceled = module.set.date(date) === false;
4114 if (!canceled) {
4115 selectionComplete = true;
4116 if(settings.closable) {
4117 module.popup('hide');
4118 //if this is a range calendar, focus the container or input. This will open the popup from its event listeners.
4119 var endModule = module.get.calendarModule(settings.endCalendar);
4120 if (endModule) {
4121 if (endModule.setting('on') !== 'focus') {
4122 endModule.popup('show');
4123 }
4124 endModule.focus();
4125 }
4126 }
4127 }
4128 } else {
4129 var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') :
4130 mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute';
4131 module.set.mode(newMode);
4132 if (mode === 'hour' || (mode === 'day' && module.get.date())) {
4133 //the user has chosen enough to consider a valid date/time has been chosen
4134 module.set.date(date, true, false);
4135 } else {
4136 module.set.focusDate(date);
4137 }
4138 }
4139 },
4140
4141 changeDate: function (date) {
4142 module.set.date(date);
4143 },
4144
4145 clear: function () {
4146 module.set.date(undefined);
4147 },
4148
4149 popup: function () {
4150 return $activator.popup.apply($activator, arguments);
4151 },
4152
4153 focus: function () {
4154 if ($input.length) {
4155 $input.focus();
4156 } else {
4157 $container.focus();
4158 }
4159 },
4160 blur: function () {
4161 if ($input.length) {
4162 $input.blur();
4163 } else {
4164 $container.blur();
4165 }
4166 },
4167
4168 helper: {
4169 isDisabled: function(date, mode) {
4170 return (mode === 'day' || mode === 'month' || mode === 'year') && ((mode === 'day' && settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function(d){
4171 if(typeof d === 'string') {
4172 d = module.helper.sanitiseDate(d);
4173 }
4174 if (d instanceof Date) {
4175 return module.helper.dateEqual(date, d, mode);
4176 }
4177 if (d !== null && typeof d === 'object') {
4178 if (d[metadata.year]) {
4179 if (typeof d[metadata.year] === 'number') {
4180 return date.getFullYear() == d[metadata.year];
4181 } else if (Array.isArray(d[metadata.year])) {
4182 return d[metadata.year].indexOf(date.getFullYear()) > -1;
4183 }
4184 } else if (d[metadata.month]) {
4185 if (typeof d[metadata.month] === 'number') {
4186 return date.getMonth() == d[metadata.month];
4187 } else if (Array.isArray(d[metadata.month])) {
4188 return d[metadata.month].indexOf(date.getMonth()) > -1;
4189 } else if (d[metadata.month] instanceof Date) {
4190 var sdate = module.helper.sanitiseDate(d[metadata.month]);
4191 return (date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear())
4192 }
4193 } else if (d[metadata.date] && mode === 'day') {
4194 if (d[metadata.date] instanceof Date) {
4195 return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
4196 } else if (Array.isArray(d[metadata.date])) {
4197 return d[metadata.date].some(function(idate) {
4198 return module.helper.dateEqual(date, idate, mode);
4199 });
4200 }
4201 }
4202 }
4203 }));
4204 },
4205 isEnabled: function(date, mode) {
4206 if (mode === 'day') {
4207 return settings.enabledDates.length === 0 || settings.enabledDates.some(function(d){
4208 if(typeof d === 'string') {
4209 d = module.helper.sanitiseDate(d);
4210 }
4211 if (d instanceof Date) {
4212 return module.helper.dateEqual(date, d, mode);
4213 }
4214 if (d !== null && typeof d === 'object' && d[metadata.date]) {
4215 return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
4216 }
4217 });
4218 } else {
4219 return true;
4220 }
4221 },
4222 findDayAsObject: function(date, mode, dates) {
4223 if (mode === 'day' || mode === 'month' || mode === 'year') {
4224 var d;
4225 for (var i = 0; i < dates.length; i++) {
4226 d = dates[i];
4227 if(typeof d === 'string') {
4228 d = module.helper.sanitiseDate(d);
4229 }
4230 if (d instanceof Date && module.helper.dateEqual(date, d, mode)) {
4231 var dateObject = {};
4232 dateObject[metadata.date] = d;
4233 return dateObject;
4234 }
4235 else if (d !== null && typeof d === 'object') {
4236 if (d[metadata.year]) {
4237 if (typeof d[metadata.year] === 'number' && date.getFullYear() == d[metadata.year]) {
4238 return d;
4239 } else if (Array.isArray(d[metadata.year])) {
4240 if (d[metadata.year].indexOf(date.getFullYear()) > -1) {
4241 return d;
4242 }
4243 }
4244 } else if (d[metadata.month]) {
4245 if (typeof d[metadata.month] === 'number' && date.getMonth() == d[metadata.month]) {
4246 return d;
4247 } else if (Array.isArray(d[metadata.month])) {
4248 if (d[metadata.month].indexOf(date.getMonth()) > -1) {
4249 return d;
4250 }
4251 } else if (d[metadata.month] instanceof Date) {
4252 var sdate = module.helper.sanitiseDate(d[metadata.month]);
4253 if ((date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear())) {
4254 return d;
4255 }
4256 }
4257 } else if (d[metadata.date] && mode === 'day') {
4258 if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode)) {
4259 return d;
4260 } else if (Array.isArray(d[metadata.date])) {
4261 if(d[metadata.date].some(function(idate) { return module.helper.dateEqual(date, idate, mode); })) {
4262 return d;
4263 }
4264 }
4265 }
4266 }
4267 }
4268 }
4269 return null;
4270 },
4271 sanitiseDate: function (date) {
4272 if (!(date instanceof Date)) {
4273 date = parser.date('' + date, settings);
4274 }
4275 if (!date || isNaN(date.getTime())) {
4276 return null;
4277 }
4278 return date;
4279 },
4280 dateDiff: function (date1, date2, mode) {
4281 mode = mode || 'day';
4282 var isTimeOnly = settings.type === 'time';
4283 var isYear = mode === 'year';
4284 var isYearOrMonth = isYear || mode === 'month';
4285 var isMinute = mode === 'minute';
4286 var isHourOrMinute = isMinute || mode === 'hour';
4287 //only care about a minute accuracy of settings.minTimeGap
4288 date1 = new Date(
4289 isTimeOnly ? 2000 : date1.getFullYear(),
4290 isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(),
4291 isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(),
4292 !isHourOrMinute ? 0 : date1.getHours(),
4293 !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap));
4294 date2 = new Date(
4295 isTimeOnly ? 2000 : date2.getFullYear(),
4296 isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(),
4297 isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(),
4298 !isHourOrMinute ? 0 : date2.getHours(),
4299 !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap));
4300 return date2.getTime() - date1.getTime();
4301 },
4302 dateEqual: function (date1, date2, mode) {
4303 return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0;
4304 },
4305 isDateInRange: function (date, mode, minDate, maxDate) {
4306 if (!minDate && !maxDate) {
4307 var startDate = module.get.startDate();
4308 minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
4309 maxDate = settings.maxDate;
4310 }
4311 minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
4312 return !(!date ||
4313 (minDate && module.helper.dateDiff(date, minDate, mode) > 0) ||
4314 (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0));
4315 },
4316 dateInRange: function (date, minDate, maxDate) {
4317 if (!minDate && !maxDate) {
4318 var startDate = module.get.startDate();
4319 minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
4320 maxDate = settings.maxDate;
4321 }
4322 minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
4323 var isTimeOnly = settings.type === 'time';
4324 return !date ? date :
4325 (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ?
4326 (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) :
4327 (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ?
4328 (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) :
4329 date;
4330 },
4331 mergeDateTime: function (date, time) {
4332 return (!date || !time) ? time :
4333 new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());
4334 },
4335 isTodayButton: function(element) {
4336 return element.text() === settings.text.today;
4337 }
4338 },
4339
4340 setting: function (name, value) {
4341 module.debug('Changing setting', name, value);
4342 if ($.isPlainObject(name)) {
4343 $.extend(true, settings, name);
4344 }
4345 else if (value !== undefined) {
4346 if ($.isPlainObject(settings[name])) {
4347 $.extend(true, settings[name], value);
4348 }
4349 else {
4350 settings[name] = value;
4351 }
4352 }
4353 else {
4354 return settings[name];
4355 }
4356 },
4357 internal: function (name, value) {
4358 if( $.isPlainObject(name) ) {
4359 $.extend(true, module, name);
4360 }
4361 else if(value !== undefined) {
4362 module[name] = value;
4363 }
4364 else {
4365 return module[name];
4366 }
4367 },
4368 debug: function () {
4369 if (!settings.silent && settings.debug) {
4370 if (settings.performance) {
4371 module.performance.log(arguments);
4372 }
4373 else {
4374 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
4375 module.debug.apply(console, arguments);
4376 }
4377 }
4378 },
4379 verbose: function () {
4380 if (!settings.silent && settings.verbose && settings.debug) {
4381 if (settings.performance) {
4382 module.performance.log(arguments);
4383 }
4384 else {
4385 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
4386 module.verbose.apply(console, arguments);
4387 }
4388 }
4389 },
4390 error: function () {
4391 if (!settings.silent) {
4392 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
4393 module.error.apply(console, arguments);
4394 }
4395 },
4396 performance: {
4397 log: function (message) {
4398 var
4399 currentTime,
4400 executionTime,
4401 previousTime
4402 ;
4403 if (settings.performance) {
4404 currentTime = new Date().getTime();
4405 previousTime = time || currentTime;
4406 executionTime = currentTime - previousTime;
4407 time = currentTime;
4408 performance.push({
4409 'Name': message[0],
4410 'Arguments': [].slice.call(message, 1) || '',
4411 'Element': element,
4412 'Execution Time': executionTime
4413 });
4414 }
4415 clearTimeout(module.performance.timer);
4416 module.performance.timer = setTimeout(module.performance.display, 500);
4417 },
4418 display: function () {
4419 var
4420 title = settings.name + ':',
4421 totalTime = 0
4422 ;
4423 time = false;
4424 clearTimeout(module.performance.timer);
4425 $.each(performance, function (index, data) {
4426 totalTime += data['Execution Time'];
4427 });
4428 title += ' ' + totalTime + 'ms';
4429 if (moduleSelector) {
4430 title += ' \'' + moduleSelector + '\'';
4431 }
4432 if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) {
4433 console.groupCollapsed(title);
4434 if (console.table) {
4435 console.table(performance);
4436 }
4437 else {
4438 $.each(performance, function (index, data) {
4439 console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
4440 });
4441 }
4442 console.groupEnd();
4443 }
4444 performance = [];
4445 }
4446 },
4447 invoke: function (query, passedArguments, context) {
4448 var
4449 object = instance,
4450 maxDepth,
4451 found,
4452 response
4453 ;
4454 passedArguments = passedArguments || queryArguments;
4455 context = element || context;
4456 if (typeof query == 'string' && object !== undefined) {
4457 query = query.split(/[\. ]/);
4458 maxDepth = query.length - 1;
4459 $.each(query, function (depth, value) {
4460 var camelCaseValue = (depth != maxDepth)
4461 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
4462 : query
4463 ;
4464 if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) {
4465 object = object[camelCaseValue];
4466 }
4467 else if (object[camelCaseValue] !== undefined) {
4468 found = object[camelCaseValue];
4469 return false;
4470 }
4471 else if ($.isPlainObject(object[value]) && (depth != maxDepth)) {
4472 object = object[value];
4473 }
4474 else if (object[value] !== undefined) {
4475 found = object[value];
4476 return false;
4477 }
4478 else {
4479 module.error(error.method, query);
4480 return false;
4481 }
4482 });
4483 }
4484 if ($.isFunction(found)) {
4485 response = found.apply(context, passedArguments);
4486 }
4487 else if (found !== undefined) {
4488 response = found;
4489 }
4490 if (Array.isArray(returnedValue)) {
4491 returnedValue.push(response);
4492 }
4493 else if (returnedValue !== undefined) {
4494 returnedValue = [returnedValue, response];
4495 }
4496 else if (response !== undefined) {
4497 returnedValue = response;
4498 }
4499 return found;
4500 }
4501 };
4502
4503 if (methodInvoked) {
4504 if (instance === undefined) {
4505 module.initialize();
4506 }
4507 module.invoke(query);
4508 }
4509 else {
4510 if (instance !== undefined) {
4511 instance.invoke('destroy');
4512 }
4513 module.initialize();
4514 }
4515 })
4516 ;
4517 return (returnedValue !== undefined)
4518 ? returnedValue
4519 : this
4520 ;
4521};
4522
4523$.fn.calendar.settings = {
4524
4525 name : 'Calendar',
4526 namespace : 'calendar',
4527
4528 silent: false,
4529 debug: false,
4530 verbose: false,
4531 performance: false,
4532
4533 type : 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year'
4534 firstDayOfWeek : 0, // day for first day column (0 = Sunday)
4535 constantHeight : true, // add rows to shorter months to keep day calendar height consistent (6 rows)
4536 today : false, // show a 'today/now' button at the bottom of the calendar
4537 closable : true, // close the popup after selecting a date/time
4538 monthFirst : true, // month before day when parsing/converting date from/to text
4539 touchReadonly : true, // set input to readonly on touch devices
4540 inline : false, // create the calendar inline instead of inside a popup
4541 on : null, // when to show the popup (defaults to 'focus' for input, 'click' for others)
4542 initialDate : null, // date to display initially when no date is selected (null = now)
4543 startMode : false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day')
4544 minDate : null, // minimum date/time that can be selected, dates/times before are disabled
4545 maxDate : null, // maximum date/time that can be selected, dates/times after are disabled
4546 ampm : true, // show am/pm in time mode
4547 disableYear : false, // disable year selection mode
4548 disableMonth : false, // disable month selection mode
4549 disableMinute : false, // disable minute selection mode
4550 formatInput : true, // format the input text upon input blur and module creation
4551 startCalendar : null, // jquery object or selector for another calendar that represents the start date of a date range
4552 endCalendar : null, // jquery object or selector for another calendar that represents the end date of a date range
4553 multiMonth : 1, // show multiple months when in 'day' mode
4554 minTimeGap : 5,
4555 showWeekNumbers : null, // show Number of Week at the very first column of a dayView
4556 disabledDates : [], // specific day(s) which won't be selectable and contain additional information.
4557 disabledDaysOfWeek : [], // day(s) which won't be selectable(s) (0 = Sunday)
4558 enabledDates : [], // specific day(s) which will be selectable, all other days will be disabled
4559 eventDates : [], // specific day(s) which will be shown in a different color and using tooltips
4560 centuryBreak : 60, // starting short year until 99 where it will be assumed to belong to the last century
4561 currentCentury : 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1)
4562 selectAdjacentDays : false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable.
4563 // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden)
4564 popupOptions: {
4565 position: 'bottom left',
4566 lastResort: 'bottom left',
4567 prefer: 'opposite',
4568 hideOnScroll: false
4569 },
4570
4571 text: {
4572 days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
4573 months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
4574 monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
4575 today: 'Today',
4576 now: 'Now',
4577 am: 'AM',
4578 pm: 'PM',
4579 weekNo: 'Week'
4580 },
4581
4582 formatter: {
4583 header: function (date, mode, settings) {
4584 return mode === 'year' ? settings.formatter.yearHeader(date, settings) :
4585 mode === 'month' ? settings.formatter.monthHeader(date, settings) :
4586 mode === 'day' ? settings.formatter.dayHeader(date, settings) :
4587 mode === 'hour' ? settings.formatter.hourHeader(date, settings) :
4588 settings.formatter.minuteHeader(date, settings);
4589 },
4590 yearHeader: function (date, settings) {
4591 var decadeYear = Math.ceil(date.getFullYear() / 10) * 10;
4592 return (decadeYear - 9) + ' - ' + (decadeYear + 2);
4593 },
4594 monthHeader: function (date, settings) {
4595 return date.getFullYear();
4596 },
4597 dayHeader: function (date, settings) {
4598 var month = settings.text.months[date.getMonth()];
4599 var year = date.getFullYear();
4600 return month + ' ' + year;
4601 },
4602 hourHeader: function (date, settings) {
4603 return settings.formatter.date(date, settings);
4604 },
4605 minuteHeader: function (date, settings) {
4606 return settings.formatter.date(date, settings);
4607 },
4608 dayColumnHeader: function (day, settings) {
4609 return settings.text.days[day];
4610 },
4611 datetime: function (date, settings) {
4612 if (!date) {
4613 return '';
4614 }
4615 var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings);
4616 var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false);
4617 var separator = settings.type === 'datetime' ? ' ' : '';
4618 return day + separator + time;
4619 },
4620 date: function (date, settings) {
4621 if (!date) {
4622 return '';
4623 }
4624 var day = date.getDate();
4625 var month = settings.text.months[date.getMonth()];
4626 var year = date.getFullYear();
4627 return settings.type === 'year' ? year :
4628 settings.type === 'month' ? month + ' ' + year :
4629 (settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year;
4630 },
4631 time: function (date, settings, forCalendar) {
4632 if (!date) {
4633 return '';
4634 }
4635 var hour = date.getHours();
4636 var minute = date.getMinutes();
4637 var ampm = '';
4638 if (settings.ampm) {
4639 ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm);
4640 hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
4641 }
4642 return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm;
4643 },
4644 today: function (settings) {
4645 return settings.type === 'date' ? settings.text.today : settings.text.now;
4646 },
4647 cell: function (cell, date, cellOptions) {
4648 }
4649 },
4650
4651 parser: {
4652 date: function (text, settings) {
4653 if (text instanceof Date) {
4654 return text;
4655 }
4656 if (!text) {
4657 return null;
4658 }
4659 text = String(text).trim();
4660 if (text.length === 0) {
4661 return null;
4662 }
4663 if(text.match(/^[0-9]{4}[\/\-\.][0-9]{1,2}[\/\-\.][0-9]{1,2}$/)){
4664 text = text.replace(/[\/\-\.]/g,'/') + ' 00:00:00';
4665 }
4666 // Reverse date and month in some cases
4667 text = settings.monthFirst || !text.match(/^[0-9]{1,2}[\/\-\.]/) ? text : text.replace(/[\/\-\.]/g,'/').replace(/([0-9]+)\/([0-9]+)/,'$2/$1');
4668 var textDate = new Date(text);
4669 var numberOnly = text.match(/^[0-9]+$/) !== null;
4670 if(!numberOnly && !isNaN(textDate.getDate())) {
4671 return textDate;
4672 }
4673 text = text.toLowerCase();
4674
4675 var i, j, k;
4676 var minute = -1, hour = -1, day = -1, month = -1, year = -1;
4677 var isAm = undefined;
4678
4679 var isTimeOnly = settings.type === 'time';
4680 var isDateOnly = settings.type.indexOf('time') < 0;
4681
4682 var words = text.split(settings.regExp.dateWords), word;
4683 var numbers = text.split(settings.regExp.dateNumbers), number;
4684
4685 var parts;
4686 var monthString;
4687
4688 if (!isDateOnly) {
4689 //am/pm
4690 isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true :
4691 $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined;
4692
4693 //time with ':'
4694 for (i = 0; i < numbers.length; i++) {
4695 number = numbers[i];
4696 if (number.indexOf(':') >= 0) {
4697 if (hour < 0 || minute < 0) {
4698 parts = number.split(':');
4699 for (k = 0; k < Math.min(2, parts.length); k++) {
4700 j = parseInt(parts[k]);
4701 if (isNaN(j)) {
4702 j = 0;
4703 }
4704 if (k === 0) {
4705 hour = j % 24;
4706 } else {
4707 minute = j % 60;
4708 }
4709 }
4710 }
4711 numbers.splice(i, 1);
4712 }
4713 }
4714 }
4715
4716 if (!isTimeOnly) {
4717 //textual month
4718 for (i = 0; i < words.length; i++) {
4719 word = words[i];
4720 if (word.length <= 0) {
4721 continue;
4722 }
4723 for (j = 0; j < settings.text.months.length; j++) {
4724 monthString = settings.text.months[j];
4725 monthString = monthString.substring(0, word.length).toLowerCase();
4726 if (monthString === word) {
4727 month = j + 1;
4728 break;
4729 }
4730 }
4731 if (month >= 0) {
4732 break;
4733 }
4734 }
4735
4736 //year > settings.centuryBreak
4737 for (i = 0; i < numbers.length; i++) {
4738 j = parseInt(numbers[i]);
4739 if (isNaN(j)) {
4740 continue;
4741 }
4742 if (j >= settings.centuryBreak && i === numbers.length-1) {
4743 if (j <= 99) {
4744 j += settings.currentCentury - 100;
4745 }
4746 year = j;
4747 numbers.splice(i, 1);
4748 break;
4749 }
4750 }
4751
4752 //numeric month
4753 if (month < 0) {
4754 for (i = 0; i < numbers.length; i++) {
4755 k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1;
4756 j = parseInt(numbers[k]);
4757 if (isNaN(j)) {
4758 continue;
4759 }
4760 if (1 <= j && j <= 12) {
4761 month = j;
4762 numbers.splice(k, 1);
4763 break;
4764 }
4765 }
4766 }
4767
4768 //day
4769 for (i = 0; i < numbers.length; i++) {
4770 j = parseInt(numbers[i]);
4771 if (isNaN(j)) {
4772 continue;
4773 }
4774 if (1 <= j && j <= 31) {
4775 day = j;
4776 numbers.splice(i, 1);
4777 break;
4778 }
4779 }
4780
4781 //year <= settings.centuryBreak
4782 if (year < 0) {
4783 for (i = numbers.length - 1; i >= 0; i--) {
4784 j = parseInt(numbers[i]);
4785 if (isNaN(j)) {
4786 continue;
4787 }
4788 if (j <= 99) {
4789 j += settings.currentCentury;
4790 }
4791 year = j;
4792 numbers.splice(i, 1);
4793 break;
4794 }
4795 }
4796 }
4797
4798 if (!isDateOnly) {
4799 //hour
4800 if (hour < 0) {
4801 for (i = 0; i < numbers.length; i++) {
4802 j = parseInt(numbers[i]);
4803 if (isNaN(j)) {
4804 continue;
4805 }
4806 if (0 <= j && j <= 23) {
4807 hour = j;
4808 numbers.splice(i, 1);
4809 break;
4810 }
4811 }
4812 }
4813
4814 //minute
4815 if (minute < 0) {
4816 for (i = 0; i < numbers.length; i++) {
4817 j = parseInt(numbers[i]);
4818 if (isNaN(j)) {
4819 continue;
4820 }
4821 if (0 <= j && j <= 59) {
4822 minute = j;
4823 numbers.splice(i, 1);
4824 break;
4825 }
4826 }
4827 }
4828 }
4829
4830 if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) {
4831 return null;
4832 }
4833
4834 if (minute < 0) {
4835 minute = 0;
4836 }
4837 if (hour < 0) {
4838 hour = 0;
4839 }
4840 if (day < 0) {
4841 day = 1;
4842 }
4843 if (month < 0) {
4844 month = 1;
4845 }
4846 if (year < 0) {
4847 year = new Date().getFullYear();
4848 }
4849
4850 if (isAm !== undefined) {
4851 if (isAm) {
4852 if (hour === 12) {
4853 hour = 0;
4854 }
4855 } else if (hour < 12) {
4856 hour += 12;
4857 }
4858 }
4859
4860 var date = new Date(year, month - 1, day, hour, minute);
4861 if (date.getMonth() !== month - 1 || date.getFullYear() !== year) {
4862 //month or year don't match up, switch to last day of the month
4863 date = new Date(year, month, 0, hour, minute);
4864 }
4865 return isNaN(date.getTime()) ? null : date;
4866 }
4867 },
4868
4869 // callback before date is changed, return false to cancel the change
4870 onBeforeChange: function (date, text, mode) {
4871 return true;
4872 },
4873
4874 // callback when date changes
4875 onChange: function (date, text, mode) {
4876 },
4877
4878 // callback before show animation, return false to prevent show
4879 onShow: function () {
4880 },
4881
4882 // callback after show animation
4883 onVisible: function () {
4884 },
4885
4886 // callback before hide animation, return false to prevent hide
4887 onHide: function () {
4888 },
4889
4890 // callback after hide animation
4891 onHidden: function () {
4892 },
4893
4894 // callback before item is selected, return false to prevent selection
4895 onSelect: function (date, mode) {
4896 },
4897
4898 // is the given date disabled?
4899 isDisabled: function (date, mode) {
4900 return false;
4901 },
4902
4903 selector: {
4904 popup: '.ui.popup',
4905 input: 'input',
4906 activator: 'input',
4907 append: '.inline.field,.inline.fields'
4908 },
4909
4910 regExp: {
4911 dateWords: /[^A-Za-z\u00C0-\u024F]+/g,
4912 dateNumbers: /[^\d:]+/g
4913 },
4914
4915 error: {
4916 popup: 'UI Popup, a required component is not included in this page',
4917 method: 'The method you called is not defined.'
4918 },
4919
4920 className: {
4921 calendar: 'calendar',
4922 active: 'active',
4923 popup: 'ui popup',
4924 grid: 'ui equal width grid',
4925 column: 'column',
4926 table: 'ui celled center aligned unstackable table',
4927 inverted: 'inverted',
4928 prev: 'prev link',
4929 next: 'next link',
4930 prevIcon: 'chevron left icon',
4931 nextIcon: 'chevron right icon',
4932 link: 'link',
4933 cell: 'link',
4934 disabledCell: 'disabled',
4935 weekCell: 'disabled',
4936 adjacentCell: 'adjacent',
4937 activeCell: 'active',
4938 rangeCell: 'range',
4939 focusCell: 'focus',
4940 todayCell: 'today',
4941 today: 'today link',
4942 disabled: 'disabled'
4943 },
4944
4945 metadata: {
4946 date: 'date',
4947 focusDate: 'focusDate',
4948 startDate: 'startDate',
4949 endDate: 'endDate',
4950 minDate: 'minDate',
4951 maxDate: 'maxDate',
4952 mode: 'mode',
4953 type: 'type',
4954 monthOffset: 'monthOffset',
4955 message: 'message',
4956 class: 'class',
4957 inverted: 'inverted',
4958 variation: 'variation',
4959 position: 'position',
4960 month: 'month',
4961 year: 'year'
4962 },
4963
4964 eventClass: 'blue'
4965};
4966
4967})(jQuery, window, document);
4968
4969/*!
4970 * # Fomantic-UI 2.8.8 - Checkbox
4971 * http://github.com/fomantic/Fomantic-UI/
4972 *
4973 *
4974 * Released under the MIT license
4975 * http://opensource.org/licenses/MIT
4976 *
4977 */
4978
4979;(function ($, window, document, undefined) {
4980
4981'use strict';
4982
4983$.isFunction = $.isFunction || function(obj) {
4984 return typeof obj === "function" && typeof obj.nodeType !== "number";
4985};
4986
4987window = (typeof window != 'undefined' && window.Math == Math)
4988 ? window
4989 : (typeof self != 'undefined' && self.Math == Math)
4990 ? self
4991 : Function('return this')()
4992;
4993
4994$.fn.checkbox = function(parameters) {
4995 var
4996 $allModules = $(this),
4997 moduleSelector = $allModules.selector || '',
4998
4999 time = new Date().getTime(),
5000 performance = [],
5001
5002 query = arguments[0],
5003 methodInvoked = (typeof query == 'string'),
5004 queryArguments = [].slice.call(arguments, 1),
5005 returnedValue
5006 ;
5007
5008 $allModules
5009 .each(function() {
5010 var
5011 settings = $.extend(true, {}, $.fn.checkbox.settings, parameters),
5012
5013 className = settings.className,
5014 namespace = settings.namespace,
5015 selector = settings.selector,
5016 error = settings.error,
5017
5018 eventNamespace = '.' + namespace,
5019 moduleNamespace = 'module-' + namespace,
5020
5021 $module = $(this),
5022 $label = $(this).children(selector.label),
5023 $input = $(this).children(selector.input),
5024 input = $input[0],
5025
5026 initialLoad = false,
5027 shortcutPressed = false,
5028 instance = $module.data(moduleNamespace),
5029
5030 observer,
5031 element = this,
5032 module
5033 ;
5034
5035 module = {
5036
5037 initialize: function() {
5038 module.verbose('Initializing checkbox', settings);
5039
5040 module.create.label();
5041 module.bind.events();
5042
5043 module.set.tabbable();
5044 module.hide.input();
5045
5046 module.observeChanges();
5047 module.instantiate();
5048 module.setup();
5049 },
5050
5051 instantiate: function() {
5052 module.verbose('Storing instance of module', module);
5053 instance = module;
5054 $module
5055 .data(moduleNamespace, module)
5056 ;
5057 },
5058
5059 destroy: function() {
5060 module.verbose('Destroying module');
5061 module.unbind.events();
5062 module.show.input();
5063 $module.removeData(moduleNamespace);
5064 },
5065
5066 fix: {
5067 reference: function() {
5068 if( $module.is(selector.input) ) {
5069 module.debug('Behavior called on <input> adjusting invoked element');
5070 $module = $module.closest(selector.checkbox);
5071 module.refresh();
5072 }
5073 }
5074 },
5075
5076 setup: function() {
5077 module.set.initialLoad();
5078 if( module.is.indeterminate() ) {
5079 module.debug('Initial value is indeterminate');
5080 module.indeterminate();
5081 }
5082 else if( module.is.checked() ) {
5083 module.debug('Initial value is checked');
5084 module.check();
5085 }
5086 else {
5087 module.debug('Initial value is unchecked');
5088 module.uncheck();
5089 }
5090 module.remove.initialLoad();
5091 },
5092
5093 refresh: function() {
5094 $label = $module.children(selector.label);
5095 $input = $module.children(selector.input);
5096 input = $input[0];
5097 },
5098
5099 hide: {
5100 input: function() {
5101 module.verbose('Modifying <input> z-index to be unselectable');
5102 $input.addClass(className.hidden);
5103 }
5104 },
5105 show: {
5106 input: function() {
5107 module.verbose('Modifying <input> z-index to be selectable');
5108 $input.removeClass(className.hidden);
5109 }
5110 },
5111
5112 observeChanges: function() {
5113 if('MutationObserver' in window) {
5114 observer = new MutationObserver(function(mutations) {
5115 module.debug('DOM tree modified, updating selector cache');
5116 module.refresh();
5117 });
5118 observer.observe(element, {
5119 childList : true,
5120 subtree : true
5121 });
5122 module.debug('Setting up mutation observer', observer);
5123 }
5124 },
5125
5126 attachEvents: function(selector, event) {
5127 var
5128 $element = $(selector)
5129 ;
5130 event = $.isFunction(module[event])
5131 ? module[event]
5132 : module.toggle
5133 ;
5134 if($element.length > 0) {
5135 module.debug('Attaching checkbox events to element', selector, event);
5136 $element
5137 .on('click' + eventNamespace, event)
5138 ;
5139 }
5140 else {
5141 module.error(error.notFound);
5142 }
5143 },
5144
5145 preventDefaultOnInputTarget: function() {
5146 if(typeof event !== 'undefined' && event !== null && $(event.target).is(selector.input)) {
5147 module.verbose('Preventing default check action after manual check action');
5148 event.preventDefault();
5149 }
5150 },
5151
5152 event: {
5153 change: function(event) {
5154 if( !module.should.ignoreCallbacks() ) {
5155 settings.onChange.call(input);
5156 }
5157 },
5158 click: function(event) {
5159 var
5160 $target = $(event.target)
5161 ;
5162 if( $target.is(selector.input) ) {
5163 module.verbose('Using default check action on initialized checkbox');
5164 return;
5165 }
5166 if( $target.is(selector.link) ) {
5167 module.debug('Clicking link inside checkbox, skipping toggle');
5168 return;
5169 }
5170 module.toggle();
5171 $input.focus();
5172 event.preventDefault();
5173 },
5174 keydown: function(event) {
5175 var
5176 key = event.which,
5177 keyCode = {
5178 enter : 13,
5179 space : 32,
5180 escape : 27,
5181 left : 37,
5182 up : 38,
5183 right : 39,
5184 down : 40
5185 }
5186 ;
5187
5188 var r = module.get.radios(),
5189 rIndex = r.index($module),
5190 rLen = r.length,
5191 checkIndex = false;
5192
5193 if(key == keyCode.left || key == keyCode.up) {
5194 checkIndex = (rIndex === 0 ? rLen : rIndex) - 1;
5195 } else if(key == keyCode.right || key == keyCode.down) {
5196 checkIndex = rIndex === rLen-1 ? 0 : rIndex+1;
5197 }
5198
5199 if (!module.should.ignoreCallbacks() && checkIndex !== false) {
5200 if(settings.beforeUnchecked.apply(input)===false) {
5201 module.verbose('Option not allowed to be unchecked, cancelling key navigation');
5202 return false;
5203 }
5204 if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) {
5205 module.verbose('Next option should not allow check, cancelling key navigation');
5206 return false;
5207 }
5208 }
5209
5210 if(key == keyCode.escape) {
5211 module.verbose('Escape key pressed blurring field');
5212 $input.blur();
5213 shortcutPressed = true;
5214 }
5215 else if(!event.ctrlKey && ( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey)) ) {
5216 module.verbose('Enter/space key pressed, toggling checkbox');
5217 module.toggle();
5218 shortcutPressed = true;
5219 }
5220 else {
5221 shortcutPressed = false;
5222 }
5223 },
5224 keyup: function(event) {
5225 if(shortcutPressed) {
5226 event.preventDefault();
5227 }
5228 }
5229 },
5230
5231 check: function() {
5232 if( !module.should.allowCheck() ) {
5233 return;
5234 }
5235 module.debug('Checking checkbox', $input);
5236 module.set.checked();
5237 if( !module.should.ignoreCallbacks() ) {
5238 settings.onChecked.call(input);
5239 module.trigger.change();
5240 }
5241 module.preventDefaultOnInputTarget();
5242 },
5243
5244 uncheck: function() {
5245 if( !module.should.allowUncheck() ) {
5246 return;
5247 }
5248 module.debug('Unchecking checkbox');
5249 module.set.unchecked();
5250 if( !module.should.ignoreCallbacks() ) {
5251 settings.onUnchecked.call(input);
5252 module.trigger.change();
5253 }
5254 module.preventDefaultOnInputTarget();
5255 },
5256
5257 indeterminate: function() {
5258 if( module.should.allowIndeterminate() ) {
5259 module.debug('Checkbox is already indeterminate');
5260 return;
5261 }
5262 module.debug('Making checkbox indeterminate');
5263 module.set.indeterminate();
5264 if( !module.should.ignoreCallbacks() ) {
5265 settings.onIndeterminate.call(input);
5266 module.trigger.change();
5267 }
5268 },
5269
5270 determinate: function() {
5271 if( module.should.allowDeterminate() ) {
5272 module.debug('Checkbox is already determinate');
5273 return;
5274 }
5275 module.debug('Making checkbox determinate');
5276 module.set.determinate();
5277 if( !module.should.ignoreCallbacks() ) {
5278 settings.onDeterminate.call(input);
5279 module.trigger.change();
5280 }
5281 },
5282
5283 enable: function() {
5284 if( module.is.enabled() ) {
5285 module.debug('Checkbox is already enabled');
5286 return;
5287 }
5288 module.debug('Enabling checkbox');
5289 module.set.enabled();
5290 if( !module.should.ignoreCallbacks() ) {
5291 settings.onEnable.call(input);
5292 // preserve legacy callbacks
5293 settings.onEnabled.call(input);
5294 module.trigger.change();
5295 }
5296 },
5297
5298 disable: function() {
5299 if( module.is.disabled() ) {
5300 module.debug('Checkbox is already disabled');
5301 return;
5302 }
5303 module.debug('Disabling checkbox');
5304 module.set.disabled();
5305 if( !module.should.ignoreCallbacks() ) {
5306 settings.onDisable.call(input);
5307 // preserve legacy callbacks
5308 settings.onDisabled.call(input);
5309 module.trigger.change();
5310 }
5311 },
5312
5313 get: {
5314 radios: function() {
5315 var
5316 name = module.get.name()
5317 ;
5318 return $('input[name="' + name + '"]').closest(selector.checkbox);
5319 },
5320 otherRadios: function() {
5321 return module.get.radios().not($module);
5322 },
5323 name: function() {
5324 return $input.attr('name');
5325 }
5326 },
5327
5328 is: {
5329 initialLoad: function() {
5330 return initialLoad;
5331 },
5332 radio: function() {
5333 return ($input.hasClass(className.radio) || $input.attr('type') == 'radio');
5334 },
5335 indeterminate: function() {
5336 return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate');
5337 },
5338 checked: function() {
5339 return $input.prop('checked') !== undefined && $input.prop('checked');
5340 },
5341 disabled: function() {
5342 return $input.prop('disabled') !== undefined && $input.prop('disabled');
5343 },
5344 enabled: function() {
5345 return !module.is.disabled();
5346 },
5347 determinate: function() {
5348 return !module.is.indeterminate();
5349 },
5350 unchecked: function() {
5351 return !module.is.checked();
5352 }
5353 },
5354
5355 should: {
5356 allowCheck: function() {
5357 if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) {
5358 module.debug('Should not allow check, checkbox is already checked');
5359 return false;
5360 }
5361 if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) {
5362 module.debug('Should not allow check, beforeChecked cancelled');
5363 return false;
5364 }
5365 return true;
5366 },
5367 allowUncheck: function() {
5368 if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) {
5369 module.debug('Should not allow uncheck, checkbox is already unchecked');
5370 return false;
5371 }
5372 if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) {
5373 module.debug('Should not allow uncheck, beforeUnchecked cancelled');
5374 return false;
5375 }
5376 return true;
5377 },
5378 allowIndeterminate: function() {
5379 if(module.is.indeterminate() && !module.is.initialLoad() ) {
5380 module.debug('Should not allow indeterminate, checkbox is already indeterminate');
5381 return false;
5382 }
5383 if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) {
5384 module.debug('Should not allow indeterminate, beforeIndeterminate cancelled');
5385 return false;
5386 }
5387 return true;
5388 },
5389 allowDeterminate: function() {
5390 if(module.is.determinate() && !module.is.initialLoad() ) {
5391 module.debug('Should not allow determinate, checkbox is already determinate');
5392 return false;
5393 }
5394 if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) {
5395 module.debug('Should not allow determinate, beforeDeterminate cancelled');
5396 return false;
5397 }
5398 return true;
5399 },
5400 ignoreCallbacks: function() {
5401 return (initialLoad && !settings.fireOnInit);
5402 }
5403 },
5404
5405 can: {
5406 change: function() {
5407 return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') );
5408 },
5409 uncheck: function() {
5410 return (typeof settings.uncheckable === 'boolean')
5411 ? settings.uncheckable
5412 : !module.is.radio()
5413 ;
5414 }
5415 },
5416
5417 set: {
5418 initialLoad: function() {
5419 initialLoad = true;
5420 },
5421 checked: function() {
5422 module.verbose('Setting class to checked');
5423 $module
5424 .removeClass(className.indeterminate)
5425 .addClass(className.checked)
5426 ;
5427 if( module.is.radio() ) {
5428 module.uncheckOthers();
5429 }
5430 if(!module.is.indeterminate() && module.is.checked()) {
5431 module.debug('Input is already checked, skipping input property change');
5432 return;
5433 }
5434 module.verbose('Setting state to checked', input);
5435 $input
5436 .prop('indeterminate', false)
5437 .prop('checked', true)
5438 ;
5439 },
5440 unchecked: function() {
5441 module.verbose('Removing checked class');
5442 $module
5443 .removeClass(className.indeterminate)
5444 .removeClass(className.checked)
5445 ;
5446 if(!module.is.indeterminate() && module.is.unchecked() ) {
5447 module.debug('Input is already unchecked');
5448 return;
5449 }
5450 module.debug('Setting state to unchecked');
5451 $input
5452 .prop('indeterminate', false)
5453 .prop('checked', false)
5454 ;
5455 },
5456 indeterminate: function() {
5457 module.verbose('Setting class to indeterminate');
5458 $module
5459 .addClass(className.indeterminate)
5460 ;
5461 if( module.is.indeterminate() ) {
5462 module.debug('Input is already indeterminate, skipping input property change');
5463 return;
5464 }
5465 module.debug('Setting state to indeterminate');
5466 $input
5467 .prop('indeterminate', true)
5468 ;
5469 },
5470 determinate: function() {
5471 module.verbose('Removing indeterminate class');
5472 $module
5473 .removeClass(className.indeterminate)
5474 ;
5475 if( module.is.determinate() ) {
5476 module.debug('Input is already determinate, skipping input property change');
5477 return;
5478 }
5479 module.debug('Setting state to determinate');
5480 $input
5481 .prop('indeterminate', false)
5482 ;
5483 },
5484 disabled: function() {
5485 module.verbose('Setting class to disabled');
5486 $module
5487 .addClass(className.disabled)
5488 ;
5489 if( module.is.disabled() ) {
5490 module.debug('Input is already disabled, skipping input property change');
5491 return;
5492 }
5493 module.debug('Setting state to disabled');
5494 $input
5495 .prop('disabled', 'disabled')
5496 ;
5497 },
5498 enabled: function() {
5499 module.verbose('Removing disabled class');
5500 $module.removeClass(className.disabled);
5501 if( module.is.enabled() ) {
5502 module.debug('Input is already enabled, skipping input property change');
5503 return;
5504 }
5505 module.debug('Setting state to enabled');
5506 $input
5507 .prop('disabled', false)
5508 ;
5509 },
5510 tabbable: function() {
5511 module.verbose('Adding tabindex to checkbox');
5512 if( $input.attr('tabindex') === undefined) {
5513 $input.attr('tabindex', 0);
5514 }
5515 }
5516 },
5517
5518 remove: {
5519 initialLoad: function() {
5520 initialLoad = false;
5521 }
5522 },
5523
5524 trigger: {
5525 change: function() {
5526 var
5527 inputElement = $input[0]
5528 ;
5529 if(inputElement) {
5530 var events = document.createEvent('HTMLEvents');
5531 module.verbose('Triggering native change event');
5532 events.initEvent('change', true, false);
5533 inputElement.dispatchEvent(events);
5534 }
5535 }
5536 },
5537
5538
5539 create: {
5540 label: function() {
5541 if($input.prevAll(selector.label).length > 0) {
5542 $input.prev(selector.label).detach().insertAfter($input);
5543 module.debug('Moving existing label', $label);
5544 }
5545 else if( !module.has.label() ) {
5546 $label = $('<label>').insertAfter($input);
5547 module.debug('Creating label', $label);
5548 }
5549 }
5550 },
5551
5552 has: {
5553 label: function() {
5554 return ($label.length > 0);
5555 }
5556 },
5557
5558 bind: {
5559 events: function() {
5560 module.verbose('Attaching checkbox events');
5561 $module
5562 .on('click' + eventNamespace, module.event.click)
5563 .on('change' + eventNamespace, module.event.change)
5564 .on('keydown' + eventNamespace, selector.input, module.event.keydown)
5565 .on('keyup' + eventNamespace, selector.input, module.event.keyup)
5566 ;
5567 }
5568 },
5569
5570 unbind: {
5571 events: function() {
5572 module.debug('Removing events');
5573 $module
5574 .off(eventNamespace)
5575 ;
5576 }
5577 },
5578
5579 uncheckOthers: function() {
5580 var
5581 $radios = module.get.otherRadios()
5582 ;
5583 module.debug('Unchecking other radios', $radios);
5584 $radios.removeClass(className.checked);
5585 },
5586
5587 toggle: function() {
5588 if( !module.can.change() ) {
5589 if(!module.is.radio()) {
5590 module.debug('Checkbox is read-only or disabled, ignoring toggle');
5591 }
5592 return;
5593 }
5594 if( module.is.indeterminate() || module.is.unchecked() ) {
5595 module.debug('Currently unchecked');
5596 module.check();
5597 }
5598 else if( module.is.checked() && module.can.uncheck() ) {
5599 module.debug('Currently checked');
5600 module.uncheck();
5601 }
5602 },
5603 setting: function(name, value) {
5604 module.debug('Changing setting', name, value);
5605 if( $.isPlainObject(name) ) {
5606 $.extend(true, settings, name);
5607 }
5608 else if(value !== undefined) {
5609 if($.isPlainObject(settings[name])) {
5610 $.extend(true, settings[name], value);
5611 }
5612 else {
5613 settings[name] = value;
5614 }
5615 }
5616 else {
5617 return settings[name];
5618 }
5619 },
5620 internal: function(name, value) {
5621 if( $.isPlainObject(name) ) {
5622 $.extend(true, module, name);
5623 }
5624 else if(value !== undefined) {
5625 module[name] = value;
5626 }
5627 else {
5628 return module[name];
5629 }
5630 },
5631 debug: function() {
5632 if(!settings.silent && settings.debug) {
5633 if(settings.performance) {
5634 module.performance.log(arguments);
5635 }
5636 else {
5637 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
5638 module.debug.apply(console, arguments);
5639 }
5640 }
5641 },
5642 verbose: function() {
5643 if(!settings.silent && settings.verbose && settings.debug) {
5644 if(settings.performance) {
5645 module.performance.log(arguments);
5646 }
5647 else {
5648 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
5649 module.verbose.apply(console, arguments);
5650 }
5651 }
5652 },
5653 error: function() {
5654 if(!settings.silent) {
5655 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
5656 module.error.apply(console, arguments);
5657 }
5658 },
5659 performance: {
5660 log: function(message) {
5661 var
5662 currentTime,
5663 executionTime,
5664 previousTime
5665 ;
5666 if(settings.performance) {
5667 currentTime = new Date().getTime();
5668 previousTime = time || currentTime;
5669 executionTime = currentTime - previousTime;
5670 time = currentTime;
5671 performance.push({
5672 'Name' : message[0],
5673 'Arguments' : [].slice.call(message, 1) || '',
5674 'Element' : element,
5675 'Execution Time' : executionTime
5676 });
5677 }
5678 clearTimeout(module.performance.timer);
5679 module.performance.timer = setTimeout(module.performance.display, 500);
5680 },
5681 display: function() {
5682 var
5683 title = settings.name + ':',
5684 totalTime = 0
5685 ;
5686 time = false;
5687 clearTimeout(module.performance.timer);
5688 $.each(performance, function(index, data) {
5689 totalTime += data['Execution Time'];
5690 });
5691 title += ' ' + totalTime + 'ms';
5692 if(moduleSelector) {
5693 title += ' \'' + moduleSelector + '\'';
5694 }
5695 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
5696 console.groupCollapsed(title);
5697 if(console.table) {
5698 console.table(performance);
5699 }
5700 else {
5701 $.each(performance, function(index, data) {
5702 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
5703 });
5704 }
5705 console.groupEnd();
5706 }
5707 performance = [];
5708 }
5709 },
5710 invoke: function(query, passedArguments, context) {
5711 var
5712 object = instance,
5713 maxDepth,
5714 found,
5715 response
5716 ;
5717 passedArguments = passedArguments || queryArguments;
5718 context = element || context;
5719 if(typeof query == 'string' && object !== undefined) {
5720 query = query.split(/[\. ]/);
5721 maxDepth = query.length - 1;
5722 $.each(query, function(depth, value) {
5723 var camelCaseValue = (depth != maxDepth)
5724 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
5725 : query
5726 ;
5727 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
5728 object = object[camelCaseValue];
5729 }
5730 else if( object[camelCaseValue] !== undefined ) {
5731 found = object[camelCaseValue];
5732 return false;
5733 }
5734 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
5735 object = object[value];
5736 }
5737 else if( object[value] !== undefined ) {
5738 found = object[value];
5739 return false;
5740 }
5741 else {
5742 module.error(error.method, query);
5743 return false;
5744 }
5745 });
5746 }
5747 if ( $.isFunction( found ) ) {
5748 response = found.apply(context, passedArguments);
5749 }
5750 else if(found !== undefined) {
5751 response = found;
5752 }
5753 if(Array.isArray(returnedValue)) {
5754 returnedValue.push(response);
5755 }
5756 else if(returnedValue !== undefined) {
5757 returnedValue = [returnedValue, response];
5758 }
5759 else if(response !== undefined) {
5760 returnedValue = response;
5761 }
5762 return found;
5763 }
5764 };
5765
5766 if(methodInvoked) {
5767 if(instance === undefined) {
5768 module.initialize();
5769 }
5770 module.invoke(query);
5771 }
5772 else {
5773 if(instance !== undefined) {
5774 instance.invoke('destroy');
5775 }
5776 module.initialize();
5777 }
5778 })
5779 ;
5780
5781 return (returnedValue !== undefined)
5782 ? returnedValue
5783 : this
5784 ;
5785};
5786
5787$.fn.checkbox.settings = {
5788
5789 name : 'Checkbox',
5790 namespace : 'checkbox',
5791
5792 silent : false,
5793 debug : false,
5794 verbose : true,
5795 performance : true,
5796
5797 // delegated event context
5798 uncheckable : 'auto',
5799 fireOnInit : false,
5800 enableEnterKey : true,
5801
5802 onChange : function(){},
5803
5804 beforeChecked : function(){},
5805 beforeUnchecked : function(){},
5806 beforeDeterminate : function(){},
5807 beforeIndeterminate : function(){},
5808
5809 onChecked : function(){},
5810 onUnchecked : function(){},
5811
5812 onDeterminate : function() {},
5813 onIndeterminate : function() {},
5814
5815 onEnable : function(){},
5816 onDisable : function(){},
5817
5818 // preserve misspelled callbacks (will be removed in 3.0)
5819 onEnabled : function(){},
5820 onDisabled : function(){},
5821
5822 className : {
5823 checked : 'checked',
5824 indeterminate : 'indeterminate',
5825 disabled : 'disabled',
5826 hidden : 'hidden',
5827 radio : 'radio',
5828 readOnly : 'read-only'
5829 },
5830
5831 error : {
5832 method : 'The method you called is not defined'
5833 },
5834
5835 selector : {
5836 checkbox : '.ui.checkbox',
5837 label : 'label, .box',
5838 input : 'input[type="checkbox"], input[type="radio"]',
5839 link : 'a[href]'
5840 }
5841
5842};
5843
5844})( jQuery, window, document );
5845
5846/*!
5847 * # Fomantic-UI 2.8.8 - Dimmer
5848 * http://github.com/fomantic/Fomantic-UI/
5849 *
5850 *
5851 * Released under the MIT license
5852 * http://opensource.org/licenses/MIT
5853 *
5854 */
5855
5856;(function ($, window, document, undefined) {
5857
5858'use strict';
5859
5860$.isFunction = $.isFunction || function(obj) {
5861 return typeof obj === "function" && typeof obj.nodeType !== "number";
5862};
5863
5864window = (typeof window != 'undefined' && window.Math == Math)
5865 ? window
5866 : (typeof self != 'undefined' && self.Math == Math)
5867 ? self
5868 : Function('return this')()
5869;
5870
5871$.fn.dimmer = function(parameters) {
5872 var
5873 $allModules = $(this),
5874
5875 time = new Date().getTime(),
5876 performance = [],
5877
5878 query = arguments[0],
5879 methodInvoked = (typeof query == 'string'),
5880 queryArguments = [].slice.call(arguments, 1),
5881
5882 returnedValue
5883 ;
5884
5885 $allModules
5886 .each(function() {
5887 var
5888 settings = ( $.isPlainObject(parameters) )
5889 ? $.extend(true, {}, $.fn.dimmer.settings, parameters)
5890 : $.extend({}, $.fn.dimmer.settings),
5891
5892 selector = settings.selector,
5893 namespace = settings.namespace,
5894 className = settings.className,
5895 error = settings.error,
5896
5897 eventNamespace = '.' + namespace,
5898 moduleNamespace = 'module-' + namespace,
5899 moduleSelector = $allModules.selector || '',
5900
5901 clickEvent = ('ontouchstart' in document.documentElement)
5902 ? 'touchstart'
5903 : 'click',
5904
5905 $module = $(this),
5906 $dimmer,
5907 $dimmable,
5908
5909 element = this,
5910 instance = $module.data(moduleNamespace),
5911 module
5912 ;
5913
5914 module = {
5915
5916 preinitialize: function() {
5917 if( module.is.dimmer() ) {
5918
5919 $dimmable = $module.parent();
5920 $dimmer = $module;
5921 }
5922 else {
5923 $dimmable = $module;
5924 if( module.has.dimmer() ) {
5925 if(settings.dimmerName) {
5926 $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
5927 }
5928 else {
5929 $dimmer = $dimmable.find(selector.dimmer);
5930 }
5931 }
5932 else {
5933 $dimmer = module.create();
5934 }
5935 }
5936 },
5937
5938 initialize: function() {
5939 module.debug('Initializing dimmer', settings);
5940
5941 module.bind.events();
5942 module.set.dimmable();
5943 module.instantiate();
5944 },
5945
5946 instantiate: function() {
5947 module.verbose('Storing instance of module', module);
5948 instance = module;
5949 $module
5950 .data(moduleNamespace, instance)
5951 ;
5952 },
5953
5954 destroy: function() {
5955 module.verbose('Destroying previous module', $dimmer);
5956 module.unbind.events();
5957 module.remove.variation();
5958 $dimmable
5959 .off(eventNamespace)
5960 ;
5961 },
5962
5963 bind: {
5964 events: function() {
5965 if(settings.on == 'hover') {
5966 $dimmable
5967 .on('mouseenter' + eventNamespace, module.show)
5968 .on('mouseleave' + eventNamespace, module.hide)
5969 ;
5970 }
5971 else if(settings.on == 'click') {
5972 $dimmable
5973 .on(clickEvent + eventNamespace, module.toggle)
5974 ;
5975 }
5976 if( module.is.page() ) {
5977 module.debug('Setting as a page dimmer', $dimmable);
5978 module.set.pageDimmer();
5979 }
5980
5981 if( module.is.closable() ) {
5982 module.verbose('Adding dimmer close event', $dimmer);
5983 $dimmable
5984 .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
5985 ;
5986 }
5987 }
5988 },
5989
5990 unbind: {
5991 events: function() {
5992 $module
5993 .removeData(moduleNamespace)
5994 ;
5995 $dimmable
5996 .off(eventNamespace)
5997 ;
5998 }
5999 },
6000
6001 event: {
6002 click: function(event) {
6003 module.verbose('Determining if event occurred on dimmer', event);
6004 if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
6005 module.hide();
6006 event.stopImmediatePropagation();
6007 }
6008 }
6009 },
6010
6011 addContent: function(element) {
6012 var
6013 $content = $(element)
6014 ;
6015 module.debug('Add content to dimmer', $content);
6016 if($content.parent()[0] !== $dimmer[0]) {
6017 $content.detach().appendTo($dimmer);
6018 }
6019 },
6020
6021 create: function() {
6022 var
6023 $element = $( settings.template.dimmer(settings) )
6024 ;
6025 if(settings.dimmerName) {
6026 module.debug('Creating named dimmer', settings.dimmerName);
6027 $element.addClass(settings.dimmerName);
6028 }
6029 $element
6030 .appendTo($dimmable)
6031 ;
6032 return $element;
6033 },
6034
6035 show: function(callback) {
6036 callback = $.isFunction(callback)
6037 ? callback
6038 : function(){}
6039 ;
6040 module.debug('Showing dimmer', $dimmer, settings);
6041 module.set.variation();
6042 if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
6043 module.animate.show(callback);
6044 settings.onShow.call(element);
6045 settings.onChange.call(element);
6046 }
6047 else {
6048 module.debug('Dimmer is already shown or disabled');
6049 }
6050 },
6051
6052 hide: function(callback) {
6053 callback = $.isFunction(callback)
6054 ? callback
6055 : function(){}
6056 ;
6057 if( module.is.dimmed() || module.is.animating() ) {
6058 module.debug('Hiding dimmer', $dimmer);
6059 module.animate.hide(callback);
6060 settings.onHide.call(element);
6061 settings.onChange.call(element);
6062 }
6063 else {
6064 module.debug('Dimmer is not visible');
6065 }
6066 },
6067
6068 toggle: function() {
6069 module.verbose('Toggling dimmer visibility', $dimmer);
6070 if( !module.is.dimmed() ) {
6071 module.show();
6072 }
6073 else {
6074 if ( module.is.closable() ) {
6075 module.hide();
6076 }
6077 }
6078 },
6079
6080 animate: {
6081 show: function(callback) {
6082 callback = $.isFunction(callback)
6083 ? callback
6084 : function(){}
6085 ;
6086 if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
6087 if(settings.useFlex) {
6088 module.debug('Using flex dimmer');
6089 module.remove.legacy();
6090 }
6091 else {
6092 module.debug('Using legacy non-flex dimmer');
6093 module.set.legacy();
6094 }
6095 if(settings.opacity !== 'auto') {
6096 module.set.opacity();
6097 }
6098 $dimmer
6099 .transition({
6100 displayType : settings.useFlex
6101 ? 'flex'
6102 : 'block',
6103 animation : (settings.transition.showMethod || settings.transition) + ' in',
6104 queue : false,
6105 duration : module.get.duration(),
6106 useFailSafe : true,
6107 onStart : function() {
6108 module.set.dimmed();
6109 },
6110 onComplete : function() {
6111 module.set.active();
6112 callback();
6113 }
6114 })
6115 ;
6116 }
6117 else {
6118 module.verbose('Showing dimmer animation with javascript');
6119 module.set.dimmed();
6120 if(settings.opacity == 'auto') {
6121 settings.opacity = 0.8;
6122 }
6123 $dimmer
6124 .stop()
6125 .css({
6126 opacity : 0,
6127 width : '100%',
6128 height : '100%'
6129 })
6130 .fadeTo(module.get.duration(), settings.opacity, function() {
6131 $dimmer.removeAttr('style');
6132 module.set.active();
6133 callback();
6134 })
6135 ;
6136 }
6137 },
6138 hide: function(callback) {
6139 callback = $.isFunction(callback)
6140 ? callback
6141 : function(){}
6142 ;
6143 if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
6144 module.verbose('Hiding dimmer with css');
6145 $dimmer
6146 .transition({
6147 displayType : settings.useFlex
6148 ? 'flex'
6149 : 'block',
6150 animation : (settings.transition.hideMethod || settings.transition) + ' out',
6151 queue : false,
6152 duration : module.get.duration(),
6153 useFailSafe : true,
6154 onComplete : function() {
6155 module.remove.dimmed();
6156 module.remove.variation();
6157 module.remove.active();
6158 callback();
6159 }
6160 })
6161 ;
6162 }
6163 else {
6164 module.verbose('Hiding dimmer with javascript');
6165 $dimmer
6166 .stop()
6167 .fadeOut(module.get.duration(), function() {
6168 module.remove.dimmed();
6169 module.remove.active();
6170 $dimmer.removeAttr('style');
6171 callback();
6172 })
6173 ;
6174 }
6175 }
6176 },
6177
6178 get: {
6179 dimmer: function() {
6180 return $dimmer;
6181 },
6182 duration: function() {
6183 if( module.is.active() ) {
6184 return settings.transition.hideDuration || settings.duration.hide || settings.duration;
6185 }
6186 else {
6187 return settings.transition.showDuration || settings.duration.show || settings.duration;
6188 }
6189 }
6190 },
6191
6192 has: {
6193 dimmer: function() {
6194 if(settings.dimmerName) {
6195 return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
6196 }
6197 else {
6198 return ( $module.find(selector.dimmer).length > 0 );
6199 }
6200 }
6201 },
6202
6203 is: {
6204 active: function() {
6205 return $dimmer.hasClass(className.active);
6206 },
6207 animating: function() {
6208 return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
6209 },
6210 closable: function() {
6211 if(settings.closable == 'auto') {
6212 if(settings.on == 'hover') {
6213 return false;
6214 }
6215 return true;
6216 }
6217 return settings.closable;
6218 },
6219 dimmer: function() {
6220 return $module.hasClass(className.dimmer);
6221 },
6222 dimmable: function() {
6223 return $module.hasClass(className.dimmable);
6224 },
6225 dimmed: function() {
6226 return $dimmable.hasClass(className.dimmed);
6227 },
6228 disabled: function() {
6229 return $dimmable.hasClass(className.disabled);
6230 },
6231 enabled: function() {
6232 return !module.is.disabled();
6233 },
6234 page: function () {
6235 return $dimmable.is('body');
6236 },
6237 pageDimmer: function() {
6238 return $dimmer.hasClass(className.pageDimmer);
6239 }
6240 },
6241
6242 can: {
6243 show: function() {
6244 return !$dimmer.hasClass(className.disabled);
6245 }
6246 },
6247
6248 set: {
6249 opacity: function(opacity) {
6250 var
6251 color = $dimmer.css('background-color'),
6252 colorArray = color.split(','),
6253 isRGB = (colorArray && colorArray.length >= 3)
6254 ;
6255 opacity = settings.opacity === 0 ? 0 : settings.opacity || opacity;
6256 if(isRGB) {
6257 colorArray[2] = colorArray[2].replace(')','');
6258 colorArray[3] = opacity + ')';
6259 color = colorArray.join(',');
6260 }
6261 else {
6262 color = 'rgba(0, 0, 0, ' + opacity + ')';
6263 }
6264 module.debug('Setting opacity to', opacity);
6265 $dimmer.css('background-color', color);
6266 },
6267 legacy: function() {
6268 $dimmer.addClass(className.legacy);
6269 },
6270 active: function() {
6271 $dimmer.addClass(className.active);
6272 },
6273 dimmable: function() {
6274 $dimmable.addClass(className.dimmable);
6275 },
6276 dimmed: function() {
6277 $dimmable.addClass(className.dimmed);
6278 },
6279 pageDimmer: function() {
6280 $dimmer.addClass(className.pageDimmer);
6281 },
6282 disabled: function() {
6283 $dimmer.addClass(className.disabled);
6284 },
6285 variation: function(variation) {
6286 variation = variation || settings.variation;
6287 if(variation) {
6288 $dimmer.addClass(variation);
6289 }
6290 }
6291 },
6292
6293 remove: {
6294 active: function() {
6295 $dimmer
6296 .removeClass(className.active)
6297 ;
6298 },
6299 legacy: function() {
6300 $dimmer.removeClass(className.legacy);
6301 },
6302 dimmed: function() {
6303 $dimmable.removeClass(className.dimmed);
6304 },
6305 disabled: function() {
6306 $dimmer.removeClass(className.disabled);
6307 },
6308 variation: function(variation) {
6309 variation = variation || settings.variation;
6310 if(variation) {
6311 $dimmer.removeClass(variation);
6312 }
6313 }
6314 },
6315
6316 setting: function(name, value) {
6317 module.debug('Changing setting', name, value);
6318 if( $.isPlainObject(name) ) {
6319 $.extend(true, settings, name);
6320 }
6321 else if(value !== undefined) {
6322 if($.isPlainObject(settings[name])) {
6323 $.extend(true, settings[name], value);
6324 }
6325 else {
6326 settings[name] = value;
6327 }
6328 }
6329 else {
6330 return settings[name];
6331 }
6332 },
6333 internal: function(name, value) {
6334 if( $.isPlainObject(name) ) {
6335 $.extend(true, module, name);
6336 }
6337 else if(value !== undefined) {
6338 module[name] = value;
6339 }
6340 else {
6341 return module[name];
6342 }
6343 },
6344 debug: function() {
6345 if(!settings.silent && settings.debug) {
6346 if(settings.performance) {
6347 module.performance.log(arguments);
6348 }
6349 else {
6350 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
6351 module.debug.apply(console, arguments);
6352 }
6353 }
6354 },
6355 verbose: function() {
6356 if(!settings.silent && settings.verbose && settings.debug) {
6357 if(settings.performance) {
6358 module.performance.log(arguments);
6359 }
6360 else {
6361 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
6362 module.verbose.apply(console, arguments);
6363 }
6364 }
6365 },
6366 error: function() {
6367 if(!settings.silent) {
6368 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
6369 module.error.apply(console, arguments);
6370 }
6371 },
6372 performance: {
6373 log: function(message) {
6374 var
6375 currentTime,
6376 executionTime,
6377 previousTime
6378 ;
6379 if(settings.performance) {
6380 currentTime = new Date().getTime();
6381 previousTime = time || currentTime;
6382 executionTime = currentTime - previousTime;
6383 time = currentTime;
6384 performance.push({
6385 'Name' : message[0],
6386 'Arguments' : [].slice.call(message, 1) || '',
6387 'Element' : element,
6388 'Execution Time' : executionTime
6389 });
6390 }
6391 clearTimeout(module.performance.timer);
6392 module.performance.timer = setTimeout(module.performance.display, 500);
6393 },
6394 display: function() {
6395 var
6396 title = settings.name + ':',
6397 totalTime = 0
6398 ;
6399 time = false;
6400 clearTimeout(module.performance.timer);
6401 $.each(performance, function(index, data) {
6402 totalTime += data['Execution Time'];
6403 });
6404 title += ' ' + totalTime + 'ms';
6405 if(moduleSelector) {
6406 title += ' \'' + moduleSelector + '\'';
6407 }
6408 if($allModules.length > 1) {
6409 title += ' ' + '(' + $allModules.length + ')';
6410 }
6411 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
6412 console.groupCollapsed(title);
6413 if(console.table) {
6414 console.table(performance);
6415 }
6416 else {
6417 $.each(performance, function(index, data) {
6418 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
6419 });
6420 }
6421 console.groupEnd();
6422 }
6423 performance = [];
6424 }
6425 },
6426 invoke: function(query, passedArguments, context) {
6427 var
6428 object = instance,
6429 maxDepth,
6430 found,
6431 response
6432 ;
6433 passedArguments = passedArguments || queryArguments;
6434 context = element || context;
6435 if(typeof query == 'string' && object !== undefined) {
6436 query = query.split(/[\. ]/);
6437 maxDepth = query.length - 1;
6438 $.each(query, function(depth, value) {
6439 var camelCaseValue = (depth != maxDepth)
6440 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
6441 : query
6442 ;
6443 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
6444 object = object[camelCaseValue];
6445 }
6446 else if( object[camelCaseValue] !== undefined ) {
6447 found = object[camelCaseValue];
6448 return false;
6449 }
6450 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
6451 object = object[value];
6452 }
6453 else if( object[value] !== undefined ) {
6454 found = object[value];
6455 return false;
6456 }
6457 else {
6458 module.error(error.method, query);
6459 return false;
6460 }
6461 });
6462 }
6463 if ( $.isFunction( found ) ) {
6464 response = found.apply(context, passedArguments);
6465 }
6466 else if(found !== undefined) {
6467 response = found;
6468 }
6469 if(Array.isArray(returnedValue)) {
6470 returnedValue.push(response);
6471 }
6472 else if(returnedValue !== undefined) {
6473 returnedValue = [returnedValue, response];
6474 }
6475 else if(response !== undefined) {
6476 returnedValue = response;
6477 }
6478 return found;
6479 }
6480 };
6481
6482 module.preinitialize();
6483
6484 if(methodInvoked) {
6485 if(instance === undefined) {
6486 module.initialize();
6487 }
6488 module.invoke(query);
6489 }
6490 else {
6491 if(instance !== undefined) {
6492 instance.invoke('destroy');
6493 }
6494 module.initialize();
6495 }
6496 })
6497 ;
6498
6499 return (returnedValue !== undefined)
6500 ? returnedValue
6501 : this
6502 ;
6503};
6504
6505$.fn.dimmer.settings = {
6506
6507 name : 'Dimmer',
6508 namespace : 'dimmer',
6509
6510 silent : false,
6511 debug : false,
6512 verbose : false,
6513 performance : true,
6514
6515 // whether should use flex layout
6516 useFlex : true,
6517
6518 // name to distinguish between multiple dimmers in context
6519 dimmerName : false,
6520
6521 // whether to add a variation type
6522 variation : false,
6523
6524 // whether to bind close events
6525 closable : 'auto',
6526
6527 // whether to use css animations
6528 useCSS : true,
6529
6530 // css animation to use
6531 transition : 'fade',
6532
6533 // event to bind to
6534 on : false,
6535
6536 // overriding opacity value
6537 opacity : 'auto',
6538
6539 // transition durations
6540 duration : {
6541 show : 500,
6542 hide : 500
6543 },
6544// whether the dynamically created dimmer should have a loader
6545 displayLoader: false,
6546 loaderText : false,
6547 loaderVariation : '',
6548
6549 onChange : function(){},
6550 onShow : function(){},
6551 onHide : function(){},
6552
6553 error : {
6554 method : 'The method you called is not defined.'
6555 },
6556
6557 className : {
6558 active : 'active',
6559 animating : 'animating',
6560 dimmable : 'dimmable',
6561 dimmed : 'dimmed',
6562 dimmer : 'dimmer',
6563 disabled : 'disabled',
6564 hide : 'hide',
6565 legacy : 'legacy',
6566 pageDimmer : 'page',
6567 show : 'show',
6568 loader : 'ui loader'
6569 },
6570
6571 selector: {
6572 dimmer : '> .ui.dimmer',
6573 content : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
6574 },
6575
6576 template: {
6577 dimmer: function(settings) {
6578 var d = $('<div/>').addClass('ui dimmer'),l;
6579 if(settings.displayLoader) {
6580 l = $('<div/>')
6581 .addClass(settings.className.loader)
6582 .addClass(settings.loaderVariation);
6583 if(!!settings.loaderText){
6584 l.text(settings.loaderText);
6585 l.addClass('text');
6586 }
6587 d.append(l);
6588 }
6589 return d;
6590 }
6591 }
6592
6593};
6594
6595})( jQuery, window, document );
6596
6597/*!
6598 * # Fomantic-UI 2.8.8 - Dropdown
6599 * http://github.com/fomantic/Fomantic-UI/
6600 *
6601 *
6602 * Released under the MIT license
6603 * http://opensource.org/licenses/MIT
6604 *
6605 */
6606
6607;(function ($, window, document, undefined) {
6608
6609'use strict';
6610
6611$.isFunction = $.isFunction || function(obj) {
6612 return typeof obj === "function" && typeof obj.nodeType !== "number";
6613};
6614
6615window = (typeof window != 'undefined' && window.Math == Math)
6616 ? window
6617 : (typeof self != 'undefined' && self.Math == Math)
6618 ? self
6619 : Function('return this')()
6620;
6621
6622$.fn.dropdown = function(parameters) {
6623 var
6624 $allModules = $(this),
6625 $document = $(document),
6626
6627 moduleSelector = $allModules.selector || '',
6628
6629 hasTouch = ('ontouchstart' in document.documentElement),
6630 clickEvent = hasTouch
6631 ? 'touchstart'
6632 : 'click',
6633
6634 time = new Date().getTime(),
6635 performance = [],
6636
6637 query = arguments[0],
6638 methodInvoked = (typeof query == 'string'),
6639 queryArguments = [].slice.call(arguments, 1),
6640 returnedValue
6641 ;
6642
6643 $allModules
6644 .each(function(elementIndex) {
6645 var
6646 settings = ( $.isPlainObject(parameters) )
6647 ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
6648 : $.extend({}, $.fn.dropdown.settings),
6649
6650 className = settings.className,
6651 message = settings.message,
6652 fields = settings.fields,
6653 keys = settings.keys,
6654 metadata = settings.metadata,
6655 namespace = settings.namespace,
6656 regExp = settings.regExp,
6657 selector = settings.selector,
6658 error = settings.error,
6659 templates = settings.templates,
6660
6661 eventNamespace = '.' + namespace,
6662 moduleNamespace = 'module-' + namespace,
6663
6664 $module = $(this),
6665 $context = $(settings.context),
6666 $text = $module.find(selector.text),
6667 $search = $module.find(selector.search),
6668 $sizer = $module.find(selector.sizer),
6669 $input = $module.find(selector.input),
6670 $icon = $module.find(selector.icon),
6671 $clear = $module.find(selector.clearIcon),
6672
6673 $combo = ($module.prev().find(selector.text).length > 0)
6674 ? $module.prev().find(selector.text)
6675 : $module.prev(),
6676
6677 $menu = $module.children(selector.menu),
6678 $item = $menu.find(selector.item),
6679 $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
6680
6681 activated = false,
6682 itemActivated = false,
6683 internalChange = false,
6684 iconClicked = false,
6685 element = this,
6686 focused = false,
6687 instance = $module.data(moduleNamespace),
6688
6689 selectActionActive,
6690 initialLoad,
6691 pageLostFocus,
6692 willRefocus,
6693 elementNamespace,
6694 id,
6695 selectObserver,
6696 menuObserver,
6697 classObserver,
6698 module
6699 ;
6700
6701 module = {
6702
6703 initialize: function() {
6704 module.debug('Initializing dropdown', settings);
6705
6706 if( module.is.alreadySetup() ) {
6707 module.setup.reference();
6708 }
6709 else {
6710 if (settings.ignoreDiacritics && !String.prototype.normalize) {
6711 settings.ignoreDiacritics = false;
6712 module.error(error.noNormalize, element);
6713 }
6714
6715 module.setup.layout();
6716
6717 if(settings.values) {
6718 module.set.initialLoad();
6719 module.change.values(settings.values);
6720 module.remove.initialLoad();
6721 }
6722
6723 module.refreshData();
6724
6725 module.save.defaults();
6726 module.restore.selected();
6727
6728 module.create.id();
6729 module.bind.events();
6730
6731 module.observeChanges();
6732 module.instantiate();
6733 }
6734
6735 },
6736
6737 instantiate: function() {
6738 module.verbose('Storing instance of dropdown', module);
6739 instance = module;
6740 $module
6741 .data(moduleNamespace, module)
6742 ;
6743 },
6744
6745 destroy: function() {
6746 module.verbose('Destroying previous dropdown', $module);
6747 module.remove.tabbable();
6748 module.remove.active();
6749 $menu.transition('stop all');
6750 $menu.removeClass(className.visible).addClass(className.hidden);
6751 $module
6752 .off(eventNamespace)
6753 .removeData(moduleNamespace)
6754 ;
6755 $menu
6756 .off(eventNamespace)
6757 ;
6758 $document
6759 .off(elementNamespace)
6760 ;
6761 module.disconnect.menuObserver();
6762 module.disconnect.selectObserver();
6763 module.disconnect.classObserver();
6764 },
6765
6766 observeChanges: function() {
6767 if('MutationObserver' in window) {
6768 selectObserver = new MutationObserver(module.event.select.mutation);
6769 menuObserver = new MutationObserver(module.event.menu.mutation);
6770 classObserver = new MutationObserver(module.event.class.mutation);
6771 module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
6772 module.observe.select();
6773 module.observe.menu();
6774 module.observe.class();
6775 }
6776 },
6777
6778 disconnect: {
6779 menuObserver: function() {
6780 if(menuObserver) {
6781 menuObserver.disconnect();
6782 }
6783 },
6784 selectObserver: function() {
6785 if(selectObserver) {
6786 selectObserver.disconnect();
6787 }
6788 },
6789 classObserver: function() {
6790 if(classObserver) {
6791 classObserver.disconnect();
6792 }
6793 }
6794 },
6795 observe: {
6796 select: function() {
6797 if(module.has.input() && selectObserver) {
6798 selectObserver.observe($module[0], {
6799 childList : true,
6800 subtree : true
6801 });
6802 }
6803 },
6804 menu: function() {
6805 if(module.has.menu() && menuObserver) {
6806 menuObserver.observe($menu[0], {
6807 childList : true,
6808 subtree : true
6809 });
6810 }
6811 },
6812 class: function() {
6813 if(module.has.search() && classObserver) {
6814 classObserver.observe($module[0], {
6815 attributes : true
6816 });
6817 }
6818 }
6819 },
6820
6821 create: {
6822 id: function() {
6823 id = (Math.random().toString(16) + '000000000').substr(2, 8);
6824 elementNamespace = '.' + id;
6825 module.verbose('Creating unique id for element', id);
6826 },
6827 userChoice: function(values) {
6828 var
6829 $userChoices,
6830 $userChoice,
6831 isUserValue,
6832 html
6833 ;
6834 values = values || module.get.userValues();
6835 if(!values) {
6836 return false;
6837 }
6838 values = Array.isArray(values)
6839 ? values
6840 : [values]
6841 ;
6842 $.each(values, function(index, value) {
6843 if(module.get.item(value) === false) {
6844 html = settings.templates.addition( module.add.variables(message.addResult, value) );
6845 $userChoice = $('<div />')
6846 .html(html)
6847 .attr('data-' + metadata.value, value)
6848 .attr('data-' + metadata.text, value)
6849 .addClass(className.addition)
6850 .addClass(className.item)
6851 ;
6852 if(settings.hideAdditions) {
6853 $userChoice.addClass(className.hidden);
6854 }
6855 $userChoices = ($userChoices === undefined)
6856 ? $userChoice
6857 : $userChoices.add($userChoice)
6858 ;
6859 module.verbose('Creating user choices for value', value, $userChoice);
6860 }
6861 });
6862 return $userChoices;
6863 },
6864 userLabels: function(value) {
6865 var
6866 userValues = module.get.userValues()
6867 ;
6868 if(userValues) {
6869 module.debug('Adding user labels', userValues);
6870 $.each(userValues, function(index, value) {
6871 module.verbose('Adding custom user value');
6872 module.add.label(value, value);
6873 });
6874 }
6875 },
6876 menu: function() {
6877 $menu = $('<div />')
6878 .addClass(className.menu)
6879 .appendTo($module)
6880 ;
6881 },
6882 sizer: function() {
6883 $sizer = $('<span />')
6884 .addClass(className.sizer)
6885 .insertAfter($search)
6886 ;
6887 }
6888 },
6889
6890 search: function(query) {
6891 query = (query !== undefined)
6892 ? query
6893 : module.get.query()
6894 ;
6895 module.verbose('Searching for query', query);
6896 if(settings.fireOnInit === false && module.is.initialLoad()) {
6897 module.verbose('Skipping callback on initial load', settings.onSearch);
6898 } else if(module.has.minCharacters(query) && settings.onSearch.call(element, query) !== false) {
6899 module.filter(query);
6900 }
6901 else {
6902 module.hide(null,true);
6903 }
6904 },
6905
6906 select: {
6907 firstUnfiltered: function() {
6908 module.verbose('Selecting first non-filtered element');
6909 module.remove.selectedItem();
6910 $item
6911 .not(selector.unselectable)
6912 .not(selector.addition + selector.hidden)
6913 .eq(0)
6914 .addClass(className.selected)
6915 ;
6916 },
6917 nextAvailable: function($selected) {
6918 $selected = $selected.eq(0);
6919 var
6920 $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
6921 $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
6922 hasNext = ($nextAvailable.length > 0)
6923 ;
6924 if(hasNext) {
6925 module.verbose('Moving selection to', $nextAvailable);
6926 $nextAvailable.addClass(className.selected);
6927 }
6928 else {
6929 module.verbose('Moving selection to', $prevAvailable);
6930 $prevAvailable.addClass(className.selected);
6931 }
6932 }
6933 },
6934
6935 setup: {
6936 api: function() {
6937 var
6938 apiSettings = {
6939 debug : settings.debug,
6940 urlData : {
6941 value : module.get.value(),
6942 query : module.get.query()
6943 },
6944 on : false
6945 }
6946 ;
6947 module.verbose('First request, initializing API');
6948 $module
6949 .api(apiSettings)
6950 ;
6951 },
6952 layout: function() {
6953 if( $module.is('select') ) {
6954 module.setup.select();
6955 module.setup.returnedObject();
6956 }
6957 if( !module.has.menu() ) {
6958 module.create.menu();
6959 }
6960 if ( module.is.clearable() && !module.has.clearItem() ) {
6961 module.verbose('Adding clear icon');
6962 $clear = $('<i />')
6963 .addClass('remove icon')
6964 .insertBefore($text)
6965 ;
6966 }
6967 if( module.is.search() && !module.has.search() ) {
6968 module.verbose('Adding search input');
6969 $search = $('<input />')
6970 .addClass(className.search)
6971 .prop('autocomplete', module.is.chrome() ? 'fomantic-search' : 'off')
6972 .insertBefore($text)
6973 ;
6974 }
6975 if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
6976 module.create.sizer();
6977 }
6978 if(settings.allowTab) {
6979 module.set.tabbable();
6980 }
6981 },
6982 select: function() {
6983 var
6984 selectValues = module.get.selectValues()
6985 ;
6986 module.debug('Dropdown initialized on a select', selectValues);
6987 if( $module.is('select') ) {
6988 $input = $module;
6989 }
6990 // see if select is placed correctly already
6991 if($input.parent(selector.dropdown).length > 0) {
6992 module.debug('UI dropdown already exists. Creating dropdown menu only');
6993 $module = $input.closest(selector.dropdown);
6994 if( !module.has.menu() ) {
6995 module.create.menu();
6996 }
6997 $menu = $module.children(selector.menu);
6998 module.setup.menu(selectValues);
6999 }
7000 else {
7001 module.debug('Creating entire dropdown from select');
7002 $module = $('<div />')
7003 .attr('class', $input.attr('class') )
7004 .addClass(className.selection)
7005 .addClass(className.dropdown)
7006 .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
7007 .insertBefore($input)
7008 ;
7009 if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
7010 module.error(error.missingMultiple);
7011 $input.prop('multiple', true);
7012 }
7013 if($input.is('[multiple]')) {
7014 module.set.multiple();
7015 }
7016 if ($input.prop('disabled')) {
7017 module.debug('Disabling dropdown');
7018 $module.addClass(className.disabled);
7019 }
7020 $input
7021 .removeAttr('required')
7022 .removeAttr('class')
7023 .detach()
7024 .prependTo($module)
7025 ;
7026 }
7027 module.refresh();
7028 },
7029 menu: function(values) {
7030 $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
7031 $item = $menu.find(selector.item);
7032 $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
7033 },
7034 reference: function() {
7035 module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
7036 // replace module reference
7037 $module = $module.parent(selector.dropdown);
7038 instance = $module.data(moduleNamespace);
7039 element = $module.get(0);
7040 module.refresh();
7041 module.setup.returnedObject();
7042 },
7043 returnedObject: function() {
7044 var
7045 $firstModules = $allModules.slice(0, elementIndex),
7046 $lastModules = $allModules.slice(elementIndex + 1)
7047 ;
7048 // adjust all modules to use correct reference
7049 $allModules = $firstModules.add($module).add($lastModules);
7050 }
7051 },
7052
7053 refresh: function() {
7054 module.refreshSelectors();
7055 module.refreshData();
7056 },
7057
7058 refreshItems: function() {
7059 $item = $menu.find(selector.item);
7060 $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
7061 },
7062
7063 refreshSelectors: function() {
7064 module.verbose('Refreshing selector cache');
7065 $text = $module.find(selector.text);
7066 $search = $module.find(selector.search);
7067 $input = $module.find(selector.input);
7068 $icon = $module.find(selector.icon);
7069 $combo = ($module.prev().find(selector.text).length > 0)
7070 ? $module.prev().find(selector.text)
7071 : $module.prev()
7072 ;
7073 $menu = $module.children(selector.menu);
7074 $item = $menu.find(selector.item);
7075 $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
7076 },
7077
7078 refreshData: function() {
7079 module.verbose('Refreshing cached metadata');
7080 $item
7081 .removeData(metadata.text)
7082 .removeData(metadata.value)
7083 ;
7084 },
7085
7086 clearData: function() {
7087 module.verbose('Clearing metadata');
7088 $item
7089 .removeData(metadata.text)
7090 .removeData(metadata.value)
7091 ;
7092 $module
7093 .removeData(metadata.defaultText)
7094 .removeData(metadata.defaultValue)
7095 .removeData(metadata.placeholderText)
7096 ;
7097 },
7098
7099 clearItems: function() {
7100 $menu.empty();
7101 module.refreshItems();
7102 },
7103
7104 toggle: function() {
7105 module.verbose('Toggling menu visibility');
7106 if( !module.is.active() ) {
7107 module.show();
7108 }
7109 else {
7110 module.hide();
7111 }
7112 },
7113
7114 show: function(callback, preventFocus) {
7115 callback = $.isFunction(callback)
7116 ? callback
7117 : function(){}
7118 ;
7119 if ((focused || iconClicked) && module.is.remote() && module.is.noApiCache()) {
7120 module.clearItems();
7121 }
7122 if(!module.can.show() && module.is.remote()) {
7123 module.debug('No API results retrieved, searching before show');
7124 module.queryRemote(module.get.query(), module.show, [callback, preventFocus]);
7125 }
7126 if( module.can.show() && !module.is.active() ) {
7127 module.debug('Showing dropdown');
7128 if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
7129 module.remove.message();
7130 }
7131 if(module.is.allFiltered()) {
7132 return true;
7133 }
7134 if(settings.onShow.call(element) !== false) {
7135 module.animate.show(function() {
7136 if( module.can.click() ) {
7137 module.bind.intent();
7138 }
7139 if(module.has.search() && !preventFocus) {
7140 module.focusSearch();
7141 }
7142 module.set.visible();
7143 callback.call(element);
7144 });
7145 }
7146 }
7147 },
7148
7149 hide: function(callback, preventBlur) {
7150 callback = $.isFunction(callback)
7151 ? callback
7152 : function(){}
7153 ;
7154 if( module.is.active() && !module.is.animatingOutward() ) {
7155 module.debug('Hiding dropdown');
7156 if(settings.onHide.call(element) !== false) {
7157 module.animate.hide(function() {
7158 module.remove.visible();
7159 // hidding search focus
7160 if ( module.is.focusedOnSearch() && preventBlur !== true ) {
7161 $search.blur();
7162 }
7163 callback.call(element);
7164 });
7165 }
7166 } else if( module.can.click() ) {
7167 module.unbind.intent();
7168 }
7169 iconClicked = false;
7170 focused = false;
7171 },
7172
7173 hideOthers: function() {
7174 module.verbose('Finding other dropdowns to hide');
7175 $allModules
7176 .not($module)
7177 .has(selector.menu + '.' + className.visible)
7178 .dropdown('hide')
7179 ;
7180 },
7181
7182 hideMenu: function() {
7183 module.verbose('Hiding menu instantaneously');
7184 module.remove.active();
7185 module.remove.visible();
7186 $menu.transition('hide');
7187 },
7188
7189 hideSubMenus: function() {
7190 var
7191 $subMenus = $menu.children(selector.item).find(selector.menu)
7192 ;
7193 module.verbose('Hiding sub menus', $subMenus);
7194 $subMenus.transition('hide');
7195 },
7196
7197 bind: {
7198 events: function() {
7199 module.bind.keyboardEvents();
7200 module.bind.inputEvents();
7201 module.bind.mouseEvents();
7202 },
7203 keyboardEvents: function() {
7204 module.verbose('Binding keyboard events');
7205 $module
7206 .on('keydown' + eventNamespace, module.event.keydown)
7207 ;
7208 if( module.has.search() ) {
7209 $module
7210 .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
7211 ;
7212 }
7213 if( module.is.multiple() ) {
7214 $document
7215 .on('keydown' + elementNamespace, module.event.document.keydown)
7216 ;
7217 }
7218 },
7219 inputEvents: function() {
7220 module.verbose('Binding input change events');
7221 $module
7222 .on('change' + eventNamespace, selector.input, module.event.change)
7223 ;
7224 },
7225 mouseEvents: function() {
7226 module.verbose('Binding mouse events');
7227 if(module.is.multiple()) {
7228 $module
7229 .on(clickEvent + eventNamespace, selector.label, module.event.label.click)
7230 .on(clickEvent + eventNamespace, selector.remove, module.event.remove.click)
7231 ;
7232 }
7233 if( module.is.searchSelection() ) {
7234 $module
7235 .on('mousedown' + eventNamespace, module.event.mousedown)
7236 .on('mouseup' + eventNamespace, module.event.mouseup)
7237 .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
7238 .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
7239 .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
7240 .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
7241 .on('focus' + eventNamespace, selector.search, module.event.search.focus)
7242 .on(clickEvent + eventNamespace, selector.search, module.event.search.focus)
7243 .on('blur' + eventNamespace, selector.search, module.event.search.blur)
7244 .on(clickEvent + eventNamespace, selector.text, module.event.text.focus)
7245 ;
7246 if(module.is.multiple()) {
7247 $module
7248 .on(clickEvent + eventNamespace, module.event.click)
7249 .on(clickEvent + eventNamespace, module.event.search.focus)
7250 ;
7251 }
7252 }
7253 else {
7254 if(settings.on == 'click') {
7255 $module
7256 .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
7257 .on(clickEvent + eventNamespace, module.event.test.toggle)
7258 ;
7259 }
7260 else if(settings.on == 'hover') {
7261 $module
7262 .on('mouseenter' + eventNamespace, module.delay.show)
7263 .on('mouseleave' + eventNamespace, module.delay.hide)
7264 ;
7265 }
7266 else {
7267 $module
7268 .on(settings.on + eventNamespace, module.toggle)
7269 ;
7270 }
7271 $module
7272 .on('mousedown' + eventNamespace, module.event.mousedown)
7273 .on('mouseup' + eventNamespace, module.event.mouseup)
7274 .on('focus' + eventNamespace, module.event.focus)
7275 .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
7276 ;
7277 if(module.has.menuSearch() ) {
7278 $module
7279 .on('blur' + eventNamespace, selector.search, module.event.search.blur)
7280 ;
7281 }
7282 else {
7283 $module
7284 .on('blur' + eventNamespace, module.event.blur)
7285 ;
7286 }
7287 }
7288 $menu
7289 .on((hasTouch ? 'touchstart' : 'mouseenter') + eventNamespace, selector.item, module.event.item.mouseenter)
7290 .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
7291 .on('click' + eventNamespace, selector.item, module.event.item.click)
7292 ;
7293 },
7294 intent: function() {
7295 module.verbose('Binding hide intent event to document');
7296 if(hasTouch) {
7297 $document
7298 .on('touchstart' + elementNamespace, module.event.test.touch)
7299 .on('touchmove' + elementNamespace, module.event.test.touch)
7300 ;
7301 }
7302 $document
7303 .on(clickEvent + elementNamespace, module.event.test.hide)
7304 ;
7305 }
7306 },
7307
7308 unbind: {
7309 intent: function() {
7310 module.verbose('Removing hide intent event from document');
7311 if(hasTouch) {
7312 $document
7313 .off('touchstart' + elementNamespace)
7314 .off('touchmove' + elementNamespace)
7315 ;
7316 }
7317 $document
7318 .off(clickEvent + elementNamespace)
7319 ;
7320 }
7321 },
7322
7323 filter: function(query) {
7324 var
7325 searchTerm = (query !== undefined)
7326 ? query
7327 : module.get.query(),
7328 afterFiltered = function() {
7329 if(module.is.multiple()) {
7330 module.filterActive();
7331 }
7332 if(query || (!query && module.get.activeItem().length == 0)) {
7333 module.select.firstUnfiltered();
7334 }
7335 if( module.has.allResultsFiltered() ) {
7336 if( settings.onNoResults.call(element, searchTerm) ) {
7337 if(settings.allowAdditions) {
7338 if(settings.hideAdditions) {
7339 module.verbose('User addition with no menu, setting empty style');
7340 module.set.empty();
7341 module.hideMenu();
7342 }
7343 }
7344 else {
7345 module.verbose('All items filtered, showing message', searchTerm);
7346 module.add.message(message.noResults);
7347 }
7348 }
7349 else {
7350 module.verbose('All items filtered, hiding dropdown', searchTerm);
7351 module.hideMenu();
7352 }
7353 }
7354 else {
7355 module.remove.empty();
7356 module.remove.message();
7357 }
7358 if(settings.allowAdditions) {
7359 module.add.userSuggestion(module.escape.htmlEntities(query));
7360 }
7361 if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
7362 module.show();
7363 }
7364 }
7365 ;
7366 if(settings.useLabels && module.has.maxSelections()) {
7367 return;
7368 }
7369 if(settings.apiSettings) {
7370 if( module.can.useAPI() ) {
7371 module.queryRemote(searchTerm, function() {
7372 if(settings.filterRemoteData) {
7373 module.filterItems(searchTerm);
7374 }
7375 var preSelected = $input.val();
7376 if(!Array.isArray(preSelected)) {
7377 preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
7378 }
7379 if (module.is.multiple()) {
7380 $.each(preSelected,function(index,value){
7381 $item.filter('[data-value="'+value+'"]')
7382 .addClass(className.filtered)
7383 ;
7384 });
7385 }
7386 module.focusSearch(true);
7387 afterFiltered();
7388 });
7389 }
7390 else {
7391 module.error(error.noAPI);
7392 }
7393 }
7394 else {
7395 module.filterItems(searchTerm);
7396 afterFiltered();
7397 }
7398 },
7399
7400 queryRemote: function(query, callback, callbackParameters) {
7401 if(!Array.isArray(callbackParameters)){
7402 callbackParameters = [callbackParameters];
7403 }
7404 var
7405 apiSettings = {
7406 errorDuration : false,
7407 cache : 'local',
7408 throttle : settings.throttle,
7409 urlData : {
7410 query: query
7411 },
7412 onError: function() {
7413 module.add.message(message.serverError);
7414 iconClicked = false;
7415 focused = false;
7416 callback.apply(null, callbackParameters);
7417 },
7418 onFailure: function() {
7419 module.add.message(message.serverError);
7420 iconClicked = false;
7421 focused = false;
7422 callback.apply(null, callbackParameters);
7423 },
7424 onSuccess : function(response) {
7425 var
7426 values = response[fields.remoteValues]
7427 ;
7428 if (!Array.isArray(values)){
7429 values = [];
7430 }
7431 module.remove.message();
7432 var menuConfig = {};
7433 menuConfig[fields.values] = values;
7434 module.setup.menu(menuConfig);
7435
7436 if(values.length===0 && !settings.allowAdditions) {
7437 module.add.message(message.noResults);
7438 }
7439 else {
7440 var value = module.is.multiple() ? module.get.values() : module.get.value();
7441 if (value !== '') {
7442 module.verbose('Value(s) present after click icon, select value(s) in items');
7443 module.set.selected(value, null, null, true);
7444 }
7445 }
7446 iconClicked = false;
7447 focused = false;
7448 callback.apply(null, callbackParameters);
7449 }
7450 }
7451 ;
7452 if( !$module.api('get request') ) {
7453 module.setup.api();
7454 }
7455 apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
7456 $module
7457 .api('setting', apiSettings)
7458 .api('query')
7459 ;
7460 },
7461
7462 filterItems: function(query) {
7463 var
7464 searchTerm = module.remove.diacritics(query !== undefined
7465 ? query
7466 : module.get.query()
7467 ),
7468 results = null,
7469 escapedTerm = module.escape.string(searchTerm),
7470 regExpFlags = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
7471 beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
7472 ;
7473 // avoid loop if we're matching nothing
7474 if( module.has.query() ) {
7475 results = [];
7476
7477 module.verbose('Searching for matching values', searchTerm);
7478 $item
7479 .each(function(){
7480 var
7481 $choice = $(this),
7482 text,
7483 value
7484 ;
7485 if($choice.hasClass(className.unfilterable)) {
7486 results.push(this);
7487 return true;
7488 }
7489 if(settings.match === 'both' || settings.match === 'text') {
7490 text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
7491 if(text.search(beginsWithRegExp) !== -1) {
7492 results.push(this);
7493 return true;
7494 }
7495 else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
7496 results.push(this);
7497 return true;
7498 }
7499 else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
7500 results.push(this);
7501 return true;
7502 }
7503 }
7504 if(settings.match === 'both' || settings.match === 'value') {
7505 value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
7506 if(value.search(beginsWithRegExp) !== -1) {
7507 results.push(this);
7508 return true;
7509 }
7510 else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
7511 results.push(this);
7512 return true;
7513 }
7514 else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
7515 results.push(this);
7516 return true;
7517 }
7518 }
7519 })
7520 ;
7521 }
7522 module.debug('Showing only matched items', searchTerm);
7523 module.remove.filteredItem();
7524 if(results) {
7525 $item
7526 .not(results)
7527 .addClass(className.filtered)
7528 ;
7529 }
7530
7531 if(!module.has.query()) {
7532 $divider
7533 .removeClass(className.hidden);
7534 } else if(settings.hideDividers === true) {
7535 $divider
7536 .addClass(className.hidden);
7537 } else if(settings.hideDividers === 'empty') {
7538 $divider
7539 .removeClass(className.hidden)
7540 .filter(function() {
7541 // First find the last divider in this divider group
7542 // Dividers which are direct siblings are considered a group
7543 var lastDivider = $(this).nextUntil(selector.item);
7544
7545 return (lastDivider.length ? lastDivider : $(this))
7546 // Count all non-filtered items until the next divider (or end of the dropdown)
7547 .nextUntil(selector.divider)
7548 .filter(selector.item + ":not(." + className.filtered + ")")
7549 // Hide divider if no items are found
7550 .length === 0;
7551 })
7552 .addClass(className.hidden);
7553 }
7554 },
7555
7556 fuzzySearch: function(query, term) {
7557 var
7558 termLength = term.length,
7559 queryLength = query.length
7560 ;
7561 query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
7562 term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
7563 if(queryLength > termLength) {
7564 return false;
7565 }
7566 if(queryLength === termLength) {
7567 return (query === term);
7568 }
7569 search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
7570 var
7571 queryCharacter = query.charCodeAt(characterIndex)
7572 ;
7573 while(nextCharacterIndex < termLength) {
7574 if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
7575 continue search;
7576 }
7577 }
7578 return false;
7579 }
7580 return true;
7581 },
7582 exactSearch: function (query, term) {
7583 query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
7584 term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
7585 return term.indexOf(query) > -1;
7586
7587 },
7588 filterActive: function() {
7589 if(settings.useLabels) {
7590 $item.filter('.' + className.active)
7591 .addClass(className.filtered)
7592 ;
7593 }
7594 },
7595
7596 focusSearch: function(skipHandler) {
7597 if( module.has.search() && !module.is.focusedOnSearch() ) {
7598 if(skipHandler) {
7599 $module.off('focus' + eventNamespace, selector.search);
7600 $search.focus();
7601 $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
7602 }
7603 else {
7604 $search.focus();
7605 }
7606 }
7607 },
7608
7609 blurSearch: function() {
7610 if( module.has.search() ) {
7611 $search.blur();
7612 }
7613 },
7614
7615 forceSelection: function() {
7616 var
7617 $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
7618 $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
7619 $selectedItem = ($currentlySelected.length > 0)
7620 ? $currentlySelected
7621 : $activeItem,
7622 hasSelected = ($selectedItem.length > 0)
7623 ;
7624 if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
7625 module.debug('Forcing partial selection to selected item', $selectedItem);
7626 module.event.item.click.call($selectedItem, {}, true);
7627 }
7628 else {
7629 module.remove.searchTerm();
7630 }
7631 },
7632
7633 change: {
7634 values: function(values) {
7635 if(!settings.allowAdditions) {
7636 module.clear();
7637 }
7638 module.debug('Creating dropdown with specified values', values);
7639 var menuConfig = {};
7640 menuConfig[fields.values] = values;
7641 module.setup.menu(menuConfig);
7642 $.each(values, function(index, item) {
7643 if(item.selected == true) {
7644 module.debug('Setting initial selection to', item[fields.value]);
7645 module.set.selected(item[fields.value]);
7646 if(!module.is.multiple()) {
7647 return false;
7648 }
7649 }
7650 });
7651
7652 if(module.has.selectInput()) {
7653 module.disconnect.selectObserver();
7654 $input.html('');
7655 $input.append('<option disabled selected value></option>');
7656 $.each(values, function(index, item) {
7657 var
7658 value = settings.templates.deQuote(item[fields.value]),
7659 name = settings.templates.escape(
7660 item[fields.name] || '',
7661 settings.preserveHTML
7662 )
7663 ;
7664 $input.append('<option value="' + value + '">' + name + '</option>');
7665 });
7666 module.observe.select();
7667 }
7668 }
7669 },
7670
7671 event: {
7672 change: function() {
7673 if(!internalChange) {
7674 module.debug('Input changed, updating selection');
7675 module.set.selected();
7676 }
7677 },
7678 focus: function() {
7679 if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
7680 focused = true;
7681 module.show();
7682 }
7683 },
7684 blur: function(event) {
7685 pageLostFocus = (document.activeElement === this);
7686 if(!activated && !pageLostFocus) {
7687 module.remove.activeLabel();
7688 module.hide();
7689 }
7690 },
7691 mousedown: function() {
7692 if(module.is.searchSelection()) {
7693 // prevent menu hiding on immediate re-focus
7694 willRefocus = true;
7695 }
7696 else {
7697 // prevents focus callback from occurring on mousedown
7698 activated = true;
7699 }
7700 },
7701 mouseup: function() {
7702 if(module.is.searchSelection()) {
7703 // prevent menu hiding on immediate re-focus
7704 willRefocus = false;
7705 }
7706 else {
7707 activated = false;
7708 }
7709 },
7710 click: function(event) {
7711 var
7712 $target = $(event.target)
7713 ;
7714 // focus search
7715 if($target.is($module)) {
7716 if(!module.is.focusedOnSearch()) {
7717 module.focusSearch();
7718 }
7719 else {
7720 module.show();
7721 }
7722 }
7723 },
7724 search: {
7725 focus: function(event) {
7726 activated = true;
7727 if(module.is.multiple()) {
7728 module.remove.activeLabel();
7729 }
7730 if(!focused && !module.is.active() && (settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin'))) {
7731 focused = true;
7732 module.search();
7733 }
7734 },
7735 blur: function(event) {
7736 pageLostFocus = (document.activeElement === this);
7737 if(module.is.searchSelection() && !willRefocus) {
7738 if(!itemActivated && !pageLostFocus) {
7739 if(settings.forceSelection) {
7740 module.forceSelection();
7741 } else if(!settings.allowAdditions){
7742 module.remove.searchTerm();
7743 }
7744 module.hide();
7745 }
7746 }
7747 willRefocus = false;
7748 }
7749 },
7750 clearIcon: {
7751 click: function(event) {
7752 module.clear();
7753 if(module.is.searchSelection()) {
7754 module.remove.searchTerm();
7755 }
7756 module.hide();
7757 event.stopPropagation();
7758 }
7759 },
7760 icon: {
7761 click: function(event) {
7762 iconClicked=true;
7763 if(module.has.search()) {
7764 if(!module.is.active()) {
7765 if(settings.showOnFocus){
7766 module.focusSearch();
7767 } else {
7768 module.toggle();
7769 }
7770 } else {
7771 module.blurSearch();
7772 }
7773 } else {
7774 module.toggle();
7775 }
7776 event.stopPropagation();
7777 }
7778 },
7779 text: {
7780 focus: function(event) {
7781 activated = true;
7782 module.focusSearch();
7783 }
7784 },
7785 input: function(event) {
7786 if(module.is.multiple() || module.is.searchSelection()) {
7787 module.set.filtered();
7788 }
7789 clearTimeout(module.timer);
7790 module.timer = setTimeout(module.search, settings.delay.search);
7791 },
7792 label: {
7793 click: function(event) {
7794 var
7795 $label = $(this),
7796 $labels = $module.find(selector.label),
7797 $activeLabels = $labels.filter('.' + className.active),
7798 $nextActive = $label.nextAll('.' + className.active),
7799 $prevActive = $label.prevAll('.' + className.active),
7800 $range = ($nextActive.length > 0)
7801 ? $label.nextUntil($nextActive).add($activeLabels).add($label)
7802 : $label.prevUntil($prevActive).add($activeLabels).add($label)
7803 ;
7804 if(event.shiftKey) {
7805 $activeLabels.removeClass(className.active);
7806 $range.addClass(className.active);
7807 }
7808 else if(event.ctrlKey) {
7809 $label.toggleClass(className.active);
7810 }
7811 else {
7812 $activeLabels.removeClass(className.active);
7813 $label.addClass(className.active);
7814 }
7815 settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
7816 event.stopPropagation();
7817 }
7818 },
7819 remove: {
7820 click: function(event) {
7821 var
7822 $label = $(this).parent()
7823 ;
7824 if( $label.hasClass(className.active) ) {
7825 // remove all selected labels
7826 module.remove.activeLabels();
7827 }
7828 else {
7829 // remove this label only
7830 module.remove.activeLabels( $label );
7831 }
7832 event.stopPropagation();
7833 }
7834 },
7835 test: {
7836 toggle: function(event) {
7837 var
7838 toggleBehavior = (module.is.multiple())
7839 ? module.show
7840 : module.toggle
7841 ;
7842 if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
7843 return;
7844 }
7845 if (!module.is.multiple() || (module.is.multiple() && !module.is.active())) {
7846 focused = true;
7847 }
7848 if( module.determine.eventOnElement(event, toggleBehavior) ) {
7849 event.preventDefault();
7850 }
7851 },
7852 touch: function(event) {
7853 module.determine.eventOnElement(event, function() {
7854 if(event.type == 'touchstart') {
7855 module.timer = setTimeout(function() {
7856 module.hide();
7857 }, settings.delay.touch);
7858 }
7859 else if(event.type == 'touchmove') {
7860 clearTimeout(module.timer);
7861 }
7862 });
7863 event.stopPropagation();
7864 },
7865 hide: function(event) {
7866 if(module.determine.eventInModule(event, module.hide)){
7867 if(element.id && $(event.target).attr('for') === element.id){
7868 event.preventDefault();
7869 }
7870 }
7871 }
7872 },
7873 class: {
7874 mutation: function(mutations) {
7875 mutations.forEach(function(mutation) {
7876 if(mutation.attributeName === "class") {
7877 module.check.disabled();
7878 }
7879 });
7880 }
7881 },
7882 select: {
7883 mutation: function(mutations) {
7884 module.debug('<select> modified, recreating menu');
7885 if(module.is.selectMutation(mutations)) {
7886 module.disconnect.selectObserver();
7887 module.refresh();
7888 module.setup.select();
7889 module.set.selected();
7890 module.observe.select();
7891 }
7892 }
7893 },
7894 menu: {
7895 mutation: function(mutations) {
7896 var
7897 mutation = mutations[0],
7898 $addedNode = mutation.addedNodes
7899 ? $(mutation.addedNodes[0])
7900 : $(false),
7901 $removedNode = mutation.removedNodes
7902 ? $(mutation.removedNodes[0])
7903 : $(false),
7904 $changedNodes = $addedNode.add($removedNode),
7905 isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
7906 isMessage = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
7907 ;
7908 if(isUserAddition || isMessage) {
7909 module.debug('Updating item selector cache');
7910 module.refreshItems();
7911 }
7912 else {
7913 module.debug('Menu modified, updating selector cache');
7914 module.refresh();
7915 }
7916 },
7917 mousedown: function() {
7918 itemActivated = true;
7919 },
7920 mouseup: function() {
7921 itemActivated = false;
7922 }
7923 },
7924 item: {
7925 mouseenter: function(event) {
7926 var
7927 $target = $(event.target),
7928 $item = $(this),
7929 $subMenu = $item.children(selector.menu),
7930 $otherMenus = $item.siblings(selector.item).children(selector.menu),
7931 hasSubMenu = ($subMenu.length > 0),
7932 isBubbledEvent = ($subMenu.find($target).length > 0)
7933 ;
7934 if( !isBubbledEvent && hasSubMenu ) {
7935 clearTimeout(module.itemTimer);
7936 module.itemTimer = setTimeout(function() {
7937 module.verbose('Showing sub-menu', $subMenu);
7938 $.each($otherMenus, function() {
7939 module.animate.hide(false, $(this));
7940 });
7941 module.animate.show(false, $subMenu);
7942 }, settings.delay.show);
7943 event.preventDefault();
7944 }
7945 },
7946 mouseleave: function(event) {
7947 var
7948 $subMenu = $(this).children(selector.menu)
7949 ;
7950 if($subMenu.length > 0) {
7951 clearTimeout(module.itemTimer);
7952 module.itemTimer = setTimeout(function() {
7953 module.verbose('Hiding sub-menu', $subMenu);
7954 module.animate.hide(false, $subMenu);
7955 }, settings.delay.hide);
7956 }
7957 },
7958 click: function (event, skipRefocus) {
7959 var
7960 $choice = $(this),
7961 $target = (event)
7962 ? $(event.target)
7963 : $(''),
7964 $subMenu = $choice.find(selector.menu),
7965 text = module.get.choiceText($choice),
7966 value = module.get.choiceValue($choice, text),
7967 hasSubMenu = ($subMenu.length > 0),
7968 isBubbledEvent = ($subMenu.find($target).length > 0)
7969 ;
7970 // prevents IE11 bug where menu receives focus even though `tabindex=-1`
7971 if (document.activeElement.tagName.toLowerCase() !== 'input') {
7972 $(document.activeElement).blur();
7973 }
7974 if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
7975 if(module.is.searchSelection()) {
7976 if(settings.allowAdditions) {
7977 module.remove.userAddition();
7978 }
7979 module.remove.searchTerm();
7980 if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
7981 module.focusSearch(true);
7982 }
7983 }
7984 if(!settings.useLabels) {
7985 module.remove.filteredItem();
7986 module.set.scrollPosition($choice);
7987 }
7988 module.determine.selectAction.call(this, text, value);
7989 }
7990 }
7991 },
7992
7993 document: {
7994 // label selection should occur even when element has no focus
7995 keydown: function(event) {
7996 var
7997 pressedKey = event.which,
7998 isShortcutKey = module.is.inObject(pressedKey, keys)
7999 ;
8000 if(isShortcutKey) {
8001 var
8002 $label = $module.find(selector.label),
8003 $activeLabel = $label.filter('.' + className.active),
8004 activeValue = $activeLabel.data(metadata.value),
8005 labelIndex = $label.index($activeLabel),
8006 labelCount = $label.length,
8007 hasActiveLabel = ($activeLabel.length > 0),
8008 hasMultipleActive = ($activeLabel.length > 1),
8009 isFirstLabel = (labelIndex === 0),
8010 isLastLabel = (labelIndex + 1 == labelCount),
8011 isSearch = module.is.searchSelection(),
8012 isFocusedOnSearch = module.is.focusedOnSearch(),
8013 isFocused = module.is.focused(),
8014 caretAtStart = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
8015 isSelectedSearch = (caretAtStart && module.get.caretPosition(true) !== 0),
8016 $nextLabel
8017 ;
8018 if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
8019 return;
8020 }
8021
8022 if(pressedKey == keys.leftArrow) {
8023 // activate previous label
8024 if((isFocused || caretAtStart) && !hasActiveLabel) {
8025 module.verbose('Selecting previous label');
8026 $label.last().addClass(className.active);
8027 }
8028 else if(hasActiveLabel) {
8029 if(!event.shiftKey) {
8030 module.verbose('Selecting previous label');
8031 $label.removeClass(className.active);
8032 }
8033 else {
8034 module.verbose('Adding previous label to selection');
8035 }
8036 if(isFirstLabel && !hasMultipleActive) {
8037 $activeLabel.addClass(className.active);
8038 }
8039 else {
8040 $activeLabel.prev(selector.siblingLabel)
8041 .addClass(className.active)
8042 .end()
8043 ;
8044 }
8045 event.preventDefault();
8046 }
8047 }
8048 else if(pressedKey == keys.rightArrow) {
8049 // activate first label
8050 if(isFocused && !hasActiveLabel) {
8051 $label.first().addClass(className.active);
8052 }
8053 // activate next label
8054 if(hasActiveLabel) {
8055 if(!event.shiftKey) {
8056 module.verbose('Selecting next label');
8057 $label.removeClass(className.active);
8058 }
8059 else {
8060 module.verbose('Adding next label to selection');
8061 }
8062 if(isLastLabel) {
8063 if(isSearch) {
8064 if(!isFocusedOnSearch) {
8065 module.focusSearch();
8066 }
8067 else {
8068 $label.removeClass(className.active);
8069 }
8070 }
8071 else if(hasMultipleActive) {
8072 $activeLabel.next(selector.siblingLabel).addClass(className.active);
8073 }
8074 else {
8075 $activeLabel.addClass(className.active);
8076 }
8077 }
8078 else {
8079 $activeLabel.next(selector.siblingLabel).addClass(className.active);
8080 }
8081 event.preventDefault();
8082 }
8083 }
8084 else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
8085 if(hasActiveLabel) {
8086 module.verbose('Removing active labels');
8087 if(isLastLabel) {
8088 if(isSearch && !isFocusedOnSearch) {
8089 module.focusSearch();
8090 }
8091 }
8092 $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
8093 module.remove.activeLabels($activeLabel);
8094 event.preventDefault();
8095 }
8096 else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
8097 module.verbose('Removing last label on input backspace');
8098 $activeLabel = $label.last().addClass(className.active);
8099 module.remove.activeLabels($activeLabel);
8100 }
8101 }
8102 else {
8103 $activeLabel.removeClass(className.active);
8104 }
8105 }
8106 }
8107 },
8108
8109 keydown: function(event) {
8110 var
8111 pressedKey = event.which,
8112 isShortcutKey = module.is.inObject(pressedKey, keys)
8113 ;
8114 if(isShortcutKey) {
8115 var
8116 $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
8117 $activeItem = $menu.children('.' + className.active).eq(0),
8118 $selectedItem = ($currentlySelected.length > 0)
8119 ? $currentlySelected
8120 : $activeItem,
8121 $visibleItems = ($selectedItem.length > 0)
8122 ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
8123 : $menu.children(':not(.' + className.filtered +')'),
8124 $subMenu = $selectedItem.children(selector.menu),
8125 $parentMenu = $selectedItem.closest(selector.menu),
8126 inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
8127 hasSubMenu = ($subMenu.length> 0),
8128 hasSelectedItem = ($selectedItem.length > 0),
8129 selectedIsSelectable = ($selectedItem.not(selector.unselectable).length > 0),
8130 delimiterPressed = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
8131 isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
8132 $nextItem,
8133 isSubMenuItem,
8134 newIndex
8135 ;
8136 // allow selection with menu closed
8137 if(isAdditionWithoutMenu) {
8138 module.verbose('Selecting item from keyboard shortcut', $selectedItem);
8139 module.event.item.click.call($selectedItem, event);
8140 if(module.is.searchSelection()) {
8141 module.remove.searchTerm();
8142 }
8143 if(module.is.multiple()){
8144 event.preventDefault();
8145 }
8146 }
8147
8148 // visible menu keyboard shortcuts
8149 if( module.is.visible() ) {
8150
8151 // enter (select or open sub-menu)
8152 if(pressedKey == keys.enter || delimiterPressed) {
8153 if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
8154 module.verbose('Pressed enter on unselectable category, opening sub menu');
8155 pressedKey = keys.rightArrow;
8156 }
8157 else if(selectedIsSelectable) {
8158 module.verbose('Selecting item from keyboard shortcut', $selectedItem);
8159 module.event.item.click.call($selectedItem, event);
8160 if(module.is.searchSelection()) {
8161 module.remove.searchTerm();
8162 if(module.is.multiple()) {
8163 $search.focus();
8164 }
8165 }
8166 }
8167 event.preventDefault();
8168 }
8169
8170 // sub-menu actions
8171 if(hasSelectedItem) {
8172
8173 if(pressedKey == keys.leftArrow) {
8174
8175 isSubMenuItem = ($parentMenu[0] !== $menu[0]);
8176
8177 if(isSubMenuItem) {
8178 module.verbose('Left key pressed, closing sub-menu');
8179 module.animate.hide(false, $parentMenu);
8180 $selectedItem
8181 .removeClass(className.selected)
8182 ;
8183 $parentMenu
8184 .closest(selector.item)
8185 .addClass(className.selected)
8186 ;
8187 event.preventDefault();
8188 }
8189 }
8190
8191 // right arrow (show sub-menu)
8192 if(pressedKey == keys.rightArrow) {
8193 if(hasSubMenu) {
8194 module.verbose('Right key pressed, opening sub-menu');
8195 module.animate.show(false, $subMenu);
8196 $selectedItem
8197 .removeClass(className.selected)
8198 ;
8199 $subMenu
8200 .find(selector.item).eq(0)
8201 .addClass(className.selected)
8202 ;
8203 event.preventDefault();
8204 }
8205 }
8206 }
8207
8208 // up arrow (traverse menu up)
8209 if(pressedKey == keys.upArrow) {
8210 $nextItem = (hasSelectedItem && inVisibleMenu)
8211 ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
8212 : $item.eq(0)
8213 ;
8214 if($visibleItems.index( $nextItem ) < 0) {
8215 module.verbose('Up key pressed but reached top of current menu');
8216 event.preventDefault();
8217 return;
8218 }
8219 else {
8220 module.verbose('Up key pressed, changing active item');
8221 $selectedItem
8222 .removeClass(className.selected)
8223 ;
8224 $nextItem
8225 .addClass(className.selected)
8226 ;
8227 module.set.scrollPosition($nextItem);
8228 if(settings.selectOnKeydown && module.is.single()) {
8229 module.set.selectedItem($nextItem);
8230 }
8231 }
8232 event.preventDefault();
8233 }
8234
8235 // down arrow (traverse menu down)
8236 if(pressedKey == keys.downArrow) {
8237 $nextItem = (hasSelectedItem && inVisibleMenu)
8238 ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
8239 : $item.eq(0)
8240 ;
8241 if($nextItem.length === 0) {
8242 module.verbose('Down key pressed but reached bottom of current menu');
8243 event.preventDefault();
8244 return;
8245 }
8246 else {
8247 module.verbose('Down key pressed, changing active item');
8248 $item
8249 .removeClass(className.selected)
8250 ;
8251 $nextItem
8252 .addClass(className.selected)
8253 ;
8254 module.set.scrollPosition($nextItem);
8255 if(settings.selectOnKeydown && module.is.single()) {
8256 module.set.selectedItem($nextItem);
8257 }
8258 }
8259 event.preventDefault();
8260 }
8261
8262 // page down (show next page)
8263 if(pressedKey == keys.pageUp) {
8264 module.scrollPage('up');
8265 event.preventDefault();
8266 }
8267 if(pressedKey == keys.pageDown) {
8268 module.scrollPage('down');
8269 event.preventDefault();
8270 }
8271
8272 // escape (close menu)
8273 if(pressedKey == keys.escape) {
8274 module.verbose('Escape key pressed, closing dropdown');
8275 module.hide();
8276 }
8277
8278 }
8279 else {
8280 // delimiter key
8281 if(delimiterPressed) {
8282 event.preventDefault();
8283 }
8284 // down arrow (open menu)
8285 if(pressedKey == keys.downArrow && !module.is.visible()) {
8286 module.verbose('Down key pressed, showing dropdown');
8287 module.show();
8288 event.preventDefault();
8289 }
8290 }
8291 }
8292 else {
8293 if( !module.has.search() ) {
8294 module.set.selectedLetter( String.fromCharCode(pressedKey) );
8295 }
8296 }
8297 }
8298 },
8299
8300 trigger: {
8301 change: function() {
8302 var
8303 inputElement = $input[0]
8304 ;
8305 if(inputElement) {
8306 var events = document.createEvent('HTMLEvents');
8307 module.verbose('Triggering native change event');
8308 events.initEvent('change', true, false);
8309 inputElement.dispatchEvent(events);
8310 }
8311 }
8312 },
8313
8314 determine: {
8315 selectAction: function(text, value) {
8316 selectActionActive = true;
8317 module.verbose('Determining action', settings.action);
8318 if( $.isFunction( module.action[settings.action] ) ) {
8319 module.verbose('Triggering preset action', settings.action, text, value);
8320 module.action[ settings.action ].call(element, text, value, this);
8321 }
8322 else if( $.isFunction(settings.action) ) {
8323 module.verbose('Triggering user action', settings.action, text, value);
8324 settings.action.call(element, text, value, this);
8325 }
8326 else {
8327 module.error(error.action, settings.action);
8328 }
8329 selectActionActive = false;
8330 },
8331 eventInModule: function(event, callback) {
8332 var
8333 $target = $(event.target),
8334 inDocument = ($target.closest(document.documentElement).length > 0),
8335 inModule = ($target.closest($module).length > 0)
8336 ;
8337 callback = $.isFunction(callback)
8338 ? callback
8339 : function(){}
8340 ;
8341 if(inDocument && !inModule) {
8342 module.verbose('Triggering event', callback);
8343 callback();
8344 return true;
8345 }
8346 else {
8347 module.verbose('Event occurred in dropdown, canceling callback');
8348 return false;
8349 }
8350 },
8351 eventOnElement: function(event, callback) {
8352 var
8353 $target = $(event.target),
8354 $label = $target.closest(selector.siblingLabel),
8355 inVisibleDOM = document.body.contains(event.target),
8356 notOnLabel = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
8357 notInMenu = ($target.closest($menu).length === 0)
8358 ;
8359 callback = $.isFunction(callback)
8360 ? callback
8361 : function(){}
8362 ;
8363 if(inVisibleDOM && notOnLabel && notInMenu) {
8364 module.verbose('Triggering event', callback);
8365 callback();
8366 return true;
8367 }
8368 else {
8369 module.verbose('Event occurred in dropdown menu, canceling callback');
8370 return false;
8371 }
8372 }
8373 },
8374
8375 action: {
8376
8377 nothing: function() {},
8378
8379 activate: function(text, value, element) {
8380 value = (value !== undefined)
8381 ? value
8382 : text
8383 ;
8384 if( module.can.activate( $(element) ) ) {
8385 module.set.selected(value, $(element));
8386 if(!module.is.multiple()) {
8387 module.hideAndClear();
8388 }
8389 }
8390 },
8391
8392 select: function(text, value, element) {
8393 value = (value !== undefined)
8394 ? value
8395 : text
8396 ;
8397 if( module.can.activate( $(element) ) ) {
8398 module.set.value(value, text, $(element));
8399 if(!module.is.multiple()) {
8400 module.hideAndClear();
8401 }
8402 }
8403 },
8404
8405 combo: function(text, value, element) {
8406 value = (value !== undefined)
8407 ? value
8408 : text
8409 ;
8410 module.set.selected(value, $(element));
8411 module.hideAndClear();
8412 },
8413
8414 hide: function(text, value, element) {
8415 module.set.value(value, text, $(element));
8416 module.hideAndClear();
8417 }
8418
8419 },
8420
8421 get: {
8422 id: function() {
8423 return id;
8424 },
8425 defaultText: function() {
8426 return $module.data(metadata.defaultText);
8427 },
8428 defaultValue: function() {
8429 return $module.data(metadata.defaultValue);
8430 },
8431 placeholderText: function() {
8432 if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
8433 return settings.placeholder;
8434 }
8435 return $module.data(metadata.placeholderText) || '';
8436 },
8437 text: function() {
8438 return settings.preserveHTML ? $text.html() : $text.text();
8439 },
8440 query: function() {
8441 return String($search.val()).trim();
8442 },
8443 searchWidth: function(value) {
8444 value = (value !== undefined)
8445 ? value
8446 : $search.val()
8447 ;
8448 $sizer.text(value);
8449 // prevent rounding issues
8450 return Math.ceil( $sizer.width() + 1);
8451 },
8452 selectionCount: function() {
8453 var
8454 values = module.get.values(),
8455 count
8456 ;
8457 count = ( module.is.multiple() )
8458 ? Array.isArray(values)
8459 ? values.length
8460 : 0
8461 : (module.get.value() !== '')
8462 ? 1
8463 : 0
8464 ;
8465 return count;
8466 },
8467 transition: function($subMenu) {
8468 return (settings.transition === 'auto')
8469 ? module.is.upward($subMenu)
8470 ? 'slide up'
8471 : 'slide down'
8472 : settings.transition
8473 ;
8474 },
8475 userValues: function() {
8476 var
8477 values = module.get.values()
8478 ;
8479 if(!values) {
8480 return false;
8481 }
8482 values = Array.isArray(values)
8483 ? values
8484 : [values]
8485 ;
8486 return $.grep(values, function(value) {
8487 return (module.get.item(value) === false);
8488 });
8489 },
8490 uniqueArray: function(array) {
8491 return $.grep(array, function (value, index) {
8492 return $.inArray(value, array) === index;
8493 });
8494 },
8495 caretPosition: function(returnEndPos) {
8496 var
8497 input = $search.get(0),
8498 range,
8499 rangeLength
8500 ;
8501 if(returnEndPos && 'selectionEnd' in input){
8502 return input.selectionEnd;
8503 }
8504 else if(!returnEndPos && 'selectionStart' in input) {
8505 return input.selectionStart;
8506 }
8507 if (document.selection) {
8508 input.focus();
8509 range = document.selection.createRange();
8510 rangeLength = range.text.length;
8511 if(returnEndPos) {
8512 return rangeLength;
8513 }
8514 range.moveStart('character', -input.value.length);
8515 return range.text.length - rangeLength;
8516 }
8517 },
8518 value: function() {
8519 var
8520 value = ($input.length > 0)
8521 ? $input.val()
8522 : $module.data(metadata.value),
8523 isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
8524 ;
8525 // prevents placeholder element from being selected when multiple
8526 return (value === undefined || isEmptyMultiselect)
8527 ? ''
8528 : value
8529 ;
8530 },
8531 values: function(raw) {
8532 var
8533 value = module.get.value()
8534 ;
8535 if(value === '') {
8536 return '';
8537 }
8538 return ( !module.has.selectInput() && module.is.multiple() )
8539 ? (typeof value == 'string') // delimited string
8540 ? (raw ? value : module.escape.htmlEntities(value)).split(settings.delimiter)
8541 : ''
8542 : value
8543 ;
8544 },
8545 remoteValues: function() {
8546 var
8547 values = module.get.values(),
8548 remoteValues = false
8549 ;
8550 if(values) {
8551 if(typeof values == 'string') {
8552 values = [values];
8553 }
8554 $.each(values, function(index, value) {
8555 var
8556 name = module.read.remoteData(value)
8557 ;
8558 module.verbose('Restoring value from session data', name, value);
8559 if(name) {
8560 if(!remoteValues) {
8561 remoteValues = {};
8562 }
8563 remoteValues[value] = name;
8564 }
8565 });
8566 }
8567 return remoteValues;
8568 },
8569 choiceText: function($choice, preserveHTML) {
8570 preserveHTML = (preserveHTML !== undefined)
8571 ? preserveHTML
8572 : settings.preserveHTML
8573 ;
8574 if($choice) {
8575 if($choice.find(selector.menu).length > 0) {
8576 module.verbose('Retrieving text of element with sub-menu');
8577 $choice = $choice.clone();
8578 $choice.find(selector.menu).remove();
8579 $choice.find(selector.menuIcon).remove();
8580 }
8581 return ($choice.data(metadata.text) !== undefined)
8582 ? $choice.data(metadata.text)
8583 : (preserveHTML)
8584 ? $choice.html() && $choice.html().trim()
8585 : $choice.text() && $choice.text().trim()
8586 ;
8587 }
8588 },
8589 choiceValue: function($choice, choiceText) {
8590 choiceText = choiceText || module.get.choiceText($choice);
8591 if(!$choice) {
8592 return false;
8593 }
8594 return ($choice.data(metadata.value) !== undefined)
8595 ? String( $choice.data(metadata.value) )
8596 : (typeof choiceText === 'string')
8597 ? String(
8598 settings.ignoreSearchCase
8599 ? choiceText.toLowerCase()
8600 : choiceText
8601 ).trim()
8602 : String(choiceText)
8603 ;
8604 },
8605 inputEvent: function() {
8606 var
8607 input = $search[0]
8608 ;
8609 if(input) {
8610 return (input.oninput !== undefined)
8611 ? 'input'
8612 : (input.onpropertychange !== undefined)
8613 ? 'propertychange'
8614 : 'keyup'
8615 ;
8616 }
8617 return false;
8618 },
8619 selectValues: function() {
8620 var
8621 select = {},
8622 oldGroup = [],
8623 values = []
8624 ;
8625 $module
8626 .find('option')
8627 .each(function() {
8628 var
8629 $option = $(this),
8630 name = $option.html(),
8631 disabled = $option.attr('disabled'),
8632 value = ( $option.attr('value') !== undefined )
8633 ? $option.attr('value')
8634 : name,
8635 text = ( $option.data(metadata.text) !== undefined )
8636 ? $option.data(metadata.text)
8637 : name,
8638 group = $option.parent('optgroup')
8639 ;
8640 if(settings.placeholder === 'auto' && value === '') {
8641 select.placeholder = name;
8642 }
8643 else {
8644 if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
8645 values.push({
8646 type: 'header',
8647 divider: settings.headerDivider,
8648 name: group.attr('label') || ''
8649 });
8650 oldGroup = group;
8651 }
8652 values.push({
8653 name : name,
8654 value : value,
8655 text : text,
8656 disabled : disabled
8657 });
8658 }
8659 })
8660 ;
8661 if(settings.placeholder && settings.placeholder !== 'auto') {
8662 module.debug('Setting placeholder value to', settings.placeholder);
8663 select.placeholder = settings.placeholder;
8664 }
8665 if(settings.sortSelect) {
8666 if(settings.sortSelect === true) {
8667 values.sort(function(a, b) {
8668 return a.name.localeCompare(b.name);
8669 });
8670 } else if(settings.sortSelect === 'natural') {
8671 values.sort(function(a, b) {
8672 return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
8673 });
8674 } else if($.isFunction(settings.sortSelect)) {
8675 values.sort(settings.sortSelect);
8676 }
8677 select[fields.values] = values;
8678 module.debug('Retrieved and sorted values from select', select);
8679 }
8680 else {
8681 select[fields.values] = values;
8682 module.debug('Retrieved values from select', select);
8683 }
8684 return select;
8685 },
8686 activeItem: function() {
8687 return $item.filter('.' + className.active);
8688 },
8689 selectedItem: function() {
8690 var
8691 $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
8692 ;
8693 return ($selectedItem.length > 0)
8694 ? $selectedItem
8695 : $item.eq(0)
8696 ;
8697 },
8698 itemWithAdditions: function(value) {
8699 var
8700 $items = module.get.item(value),
8701 $userItems = module.create.userChoice(value),
8702 hasUserItems = ($userItems && $userItems.length > 0)
8703 ;
8704 if(hasUserItems) {
8705 $items = ($items.length > 0)
8706 ? $items.add($userItems)
8707 : $userItems
8708 ;
8709 }
8710 return $items;
8711 },
8712 item: function(value, strict) {
8713 var
8714 $selectedItem = false,
8715 shouldSearch,
8716 isMultiple
8717 ;
8718 value = (value !== undefined)
8719 ? value
8720 : ( module.get.values() !== undefined)
8721 ? module.get.values()
8722 : module.get.text()
8723 ;
8724 isMultiple = (module.is.multiple() && Array.isArray(value));
8725 shouldSearch = (isMultiple)
8726 ? (value.length > 0)
8727 : (value !== undefined && value !== null)
8728 ;
8729 strict = (value === '' || value === false || value === true)
8730 ? true
8731 : strict || false
8732 ;
8733 if(shouldSearch) {
8734 $item
8735 .each(function() {
8736 var
8737 $choice = $(this),
8738 optionText = module.get.choiceText($choice),
8739 optionValue = module.get.choiceValue($choice, optionText)
8740 ;
8741 // safe early exit
8742 if(optionValue === null || optionValue === undefined) {
8743 return;
8744 }
8745 if(isMultiple) {
8746 if($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(function(v){return String(v);})) !== -1) {
8747 $selectedItem = ($selectedItem)
8748 ? $selectedItem.add($choice)
8749 : $choice
8750 ;
8751 }
8752 }
8753 else if(strict) {
8754 module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
8755 if( optionValue === value) {
8756 $selectedItem = $choice;
8757 return true;
8758 }
8759 }
8760 else {
8761 if(settings.ignoreCase) {
8762 optionValue = optionValue.toLowerCase();
8763 value = value.toLowerCase();
8764 }
8765 if(module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
8766 module.verbose('Found select item by value', optionValue, value);
8767 $selectedItem = $choice;
8768 return true;
8769 }
8770 }
8771 })
8772 ;
8773 }
8774 return $selectedItem;
8775 },
8776 displayType: function() {
8777 return $module.hasClass('column') ? 'flex' : settings.displayType;
8778 }
8779 },
8780
8781 check: {
8782 maxSelections: function(selectionCount) {
8783 if(settings.maxSelections) {
8784 selectionCount = (selectionCount !== undefined)
8785 ? selectionCount
8786 : module.get.selectionCount()
8787 ;
8788 if(selectionCount >= settings.maxSelections) {
8789 module.debug('Maximum selection count reached');
8790 if(settings.useLabels) {
8791 $item.addClass(className.filtered);
8792 module.add.message(message.maxSelections);
8793 }
8794 return true;
8795 }
8796 else {
8797 module.verbose('No longer at maximum selection count');
8798 module.remove.message();
8799 module.remove.filteredItem();
8800 if(module.is.searchSelection()) {
8801 module.filterItems();
8802 }
8803 return false;
8804 }
8805 }
8806 return true;
8807 },
8808 disabled: function(){
8809 $search.attr('tabindex',module.is.disabled() ? -1 : 0);
8810 }
8811 },
8812
8813 restore: {
8814 defaults: function(preventChangeTrigger) {
8815 module.clear(preventChangeTrigger);
8816 module.restore.defaultText();
8817 module.restore.defaultValue();
8818 },
8819 defaultText: function() {
8820 var
8821 defaultText = module.get.defaultText(),
8822 placeholderText = module.get.placeholderText
8823 ;
8824 if(defaultText === placeholderText) {
8825 module.debug('Restoring default placeholder text', defaultText);
8826 module.set.placeholderText(defaultText);
8827 }
8828 else {
8829 module.debug('Restoring default text', defaultText);
8830 module.set.text(defaultText);
8831 }
8832 },
8833 placeholderText: function() {
8834 module.set.placeholderText();
8835 },
8836 defaultValue: function() {
8837 var
8838 defaultValue = module.get.defaultValue()
8839 ;
8840 if(defaultValue !== undefined) {
8841 module.debug('Restoring default value', defaultValue);
8842 if(defaultValue !== '') {
8843 module.set.value(defaultValue);
8844 module.set.selected();
8845 }
8846 else {
8847 module.remove.activeItem();
8848 module.remove.selectedItem();
8849 }
8850 }
8851 },
8852 labels: function() {
8853 if(settings.allowAdditions) {
8854 if(!settings.useLabels) {
8855 module.error(error.labels);
8856 settings.useLabels = true;
8857 }
8858 module.debug('Restoring selected values');
8859 module.create.userLabels();
8860 }
8861 module.check.maxSelections();
8862 },
8863 selected: function() {
8864 module.restore.values();
8865 if(module.is.multiple()) {
8866 module.debug('Restoring previously selected values and labels');
8867 module.restore.labels();
8868 }
8869 else {
8870 module.debug('Restoring previously selected values');
8871 }
8872 },
8873 values: function() {
8874 // prevents callbacks from occurring on initial load
8875 module.set.initialLoad();
8876 if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
8877 module.restore.remoteValues();
8878 }
8879 else {
8880 module.set.selected();
8881 }
8882 var value = module.get.value();
8883 if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
8884 $input.removeClass(className.noselection);
8885 } else {
8886 $input.addClass(className.noselection);
8887 }
8888 module.remove.initialLoad();
8889 },
8890 remoteValues: function() {
8891 var
8892 values = module.get.remoteValues()
8893 ;
8894 module.debug('Recreating selected from session data', values);
8895 if(values) {
8896 if( module.is.single() ) {
8897 $.each(values, function(value, name) {
8898 module.set.text(name);
8899 });
8900 }
8901 else {
8902 $.each(values, function(value, name) {
8903 module.add.label(value, name);
8904 });
8905 }
8906 }
8907 }
8908 },
8909
8910 read: {
8911 remoteData: function(value) {
8912 var
8913 name
8914 ;
8915 if(window.Storage === undefined) {
8916 module.error(error.noStorage);
8917 return;
8918 }
8919 name = sessionStorage.getItem(value);
8920 return (name !== undefined)
8921 ? name
8922 : false
8923 ;
8924 }
8925 },
8926
8927 save: {
8928 defaults: function() {
8929 module.save.defaultText();
8930 module.save.placeholderText();
8931 module.save.defaultValue();
8932 },
8933 defaultValue: function() {
8934 var
8935 value = module.get.value()
8936 ;
8937 module.verbose('Saving default value as', value);
8938 $module.data(metadata.defaultValue, value);
8939 },
8940 defaultText: function() {
8941 var
8942 text = module.get.text()
8943 ;
8944 module.verbose('Saving default text as', text);
8945 $module.data(metadata.defaultText, text);
8946 },
8947 placeholderText: function() {
8948 var
8949 text
8950 ;
8951 if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
8952 text = module.get.text();
8953 module.verbose('Saving placeholder text as', text);
8954 $module.data(metadata.placeholderText, text);
8955 }
8956 },
8957 remoteData: function(name, value) {
8958 if(window.Storage === undefined) {
8959 module.error(error.noStorage);
8960 return;
8961 }
8962 module.verbose('Saving remote data to session storage', value, name);
8963 sessionStorage.setItem(value, name);
8964 }
8965 },
8966
8967 clear: function(preventChangeTrigger) {
8968 if(module.is.multiple() && settings.useLabels) {
8969 module.remove.labels($module.find(selector.label), preventChangeTrigger);
8970 }
8971 else {
8972 module.remove.activeItem();
8973 module.remove.selectedItem();
8974 module.remove.filteredItem();
8975 }
8976 module.set.placeholderText();
8977 module.clearValue(preventChangeTrigger);
8978 },
8979
8980 clearValue: function(preventChangeTrigger) {
8981 module.set.value('', null, null, preventChangeTrigger);
8982 },
8983
8984 scrollPage: function(direction, $selectedItem) {
8985 var
8986 $currentItem = $selectedItem || module.get.selectedItem(),
8987 $menu = $currentItem.closest(selector.menu),
8988 menuHeight = $menu.outerHeight(),
8989 currentScroll = $menu.scrollTop(),
8990 itemHeight = $item.eq(0).outerHeight(),
8991 itemsPerPage = Math.floor(menuHeight / itemHeight),
8992 maxScroll = $menu.prop('scrollHeight'),
8993 newScroll = (direction == 'up')
8994 ? currentScroll - (itemHeight * itemsPerPage)
8995 : currentScroll + (itemHeight * itemsPerPage),
8996 $selectableItem = $item.not(selector.unselectable),
8997 isWithinRange,
8998 $nextSelectedItem,
8999 elementIndex
9000 ;
9001 elementIndex = (direction == 'up')
9002 ? $selectableItem.index($currentItem) - itemsPerPage
9003 : $selectableItem.index($currentItem) + itemsPerPage
9004 ;
9005 isWithinRange = (direction == 'up')
9006 ? (elementIndex >= 0)
9007 : (elementIndex < $selectableItem.length)
9008 ;
9009 $nextSelectedItem = (isWithinRange)
9010 ? $selectableItem.eq(elementIndex)
9011 : (direction == 'up')
9012 ? $selectableItem.first()
9013 : $selectableItem.last()
9014 ;
9015 if($nextSelectedItem.length > 0) {
9016 module.debug('Scrolling page', direction, $nextSelectedItem);
9017 $currentItem
9018 .removeClass(className.selected)
9019 ;
9020 $nextSelectedItem
9021 .addClass(className.selected)
9022 ;
9023 if(settings.selectOnKeydown && module.is.single()) {
9024 module.set.selectedItem($nextSelectedItem);
9025 }
9026 $menu
9027 .scrollTop(newScroll)
9028 ;
9029 }
9030 },
9031
9032 set: {
9033 filtered: function() {
9034 var
9035 isMultiple = module.is.multiple(),
9036 isSearch = module.is.searchSelection(),
9037 isSearchMultiple = (isMultiple && isSearch),
9038 searchValue = (isSearch)
9039 ? module.get.query()
9040 : '',
9041 hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
9042 searchWidth = module.get.searchWidth(),
9043 valueIsSet = searchValue !== ''
9044 ;
9045 if(isMultiple && hasSearchValue) {
9046 module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
9047 $search.css('width', searchWidth);
9048 }
9049 if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
9050 module.verbose('Hiding placeholder text');
9051 $text.addClass(className.filtered);
9052 }
9053 else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
9054 module.verbose('Showing placeholder text');
9055 $text.removeClass(className.filtered);
9056 }
9057 },
9058 empty: function() {
9059 $module.addClass(className.empty);
9060 },
9061 loading: function() {
9062 $module.addClass(className.loading);
9063 },
9064 placeholderText: function(text) {
9065 text = text || module.get.placeholderText();
9066 module.debug('Setting placeholder text', text);
9067 module.set.text(text);
9068 $text.addClass(className.placeholder);
9069 },
9070 tabbable: function() {
9071 if( module.is.searchSelection() ) {
9072 module.debug('Added tabindex to searchable dropdown');
9073 $search
9074 .val('')
9075 ;
9076 module.check.disabled();
9077 $menu
9078 .attr('tabindex', -1)
9079 ;
9080 }
9081 else {
9082 module.debug('Added tabindex to dropdown');
9083 if( $module.attr('tabindex') === undefined) {
9084 $module
9085 .attr('tabindex', 0)
9086 ;
9087 $menu
9088 .attr('tabindex', -1)
9089 ;
9090 }
9091 }
9092 },
9093 initialLoad: function() {
9094 module.verbose('Setting initial load');
9095 initialLoad = true;
9096 },
9097 activeItem: function($item) {
9098 if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
9099 $item.addClass(className.filtered);
9100 }
9101 else {
9102 $item.addClass(className.active);
9103 }
9104 },
9105 partialSearch: function(text) {
9106 var
9107 length = module.get.query().length
9108 ;
9109 $search.val( text.substr(0, length));
9110 },
9111 scrollPosition: function($item, forceScroll) {
9112 var
9113 edgeTolerance = 5,
9114 $menu,
9115 hasActive,
9116 offset,
9117 itemHeight,
9118 itemOffset,
9119 menuOffset,
9120 menuScroll,
9121 menuHeight,
9122 abovePage,
9123 belowPage
9124 ;
9125
9126 $item = $item || module.get.selectedItem();
9127 $menu = $item.closest(selector.menu);
9128 hasActive = ($item && $item.length > 0);
9129 forceScroll = (forceScroll !== undefined)
9130 ? forceScroll
9131 : false
9132 ;
9133 if(module.get.activeItem().length === 0){
9134 forceScroll = false;
9135 }
9136 if($item && $menu.length > 0 && hasActive) {
9137 itemOffset = $item.position().top;
9138
9139 $menu.addClass(className.loading);
9140 menuScroll = $menu.scrollTop();
9141 menuOffset = $menu.offset().top;
9142 itemOffset = $item.offset().top;
9143 offset = menuScroll - menuOffset + itemOffset;
9144 if(!forceScroll) {
9145 menuHeight = $menu.height();
9146 belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
9147 abovePage = ((offset - edgeTolerance) < menuScroll);
9148 }
9149 module.debug('Scrolling to active item', offset);
9150 if(forceScroll || abovePage || belowPage) {
9151 $menu.scrollTop(offset);
9152 }
9153 $menu.removeClass(className.loading);
9154 }
9155 },
9156 text: function(text) {
9157 if(settings.action === 'combo') {
9158 module.debug('Changing combo button text', text, $combo);
9159 if(settings.preserveHTML) {
9160 $combo.html(text);
9161 }
9162 else {
9163 $combo.text(text);
9164 }
9165 }
9166 else if(settings.action === 'activate') {
9167 if(text !== module.get.placeholderText()) {
9168 $text.removeClass(className.placeholder);
9169 }
9170 module.debug('Changing text', text, $text);
9171 $text
9172 .removeClass(className.filtered)
9173 ;
9174 if(settings.preserveHTML) {
9175 $text.html(text);
9176 }
9177 else {
9178 $text.text(text);
9179 }
9180 }
9181 },
9182 selectedItem: function($item) {
9183 var
9184 value = module.get.choiceValue($item),
9185 searchText = module.get.choiceText($item, false),
9186 text = module.get.choiceText($item, true)
9187 ;
9188 module.debug('Setting user selection to item', $item);
9189 module.remove.activeItem();
9190 module.set.partialSearch(searchText);
9191 module.set.activeItem($item);
9192 module.set.selected(value, $item);
9193 module.set.text(text);
9194 },
9195 selectedLetter: function(letter) {
9196 var
9197 $selectedItem = $item.filter('.' + className.selected),
9198 alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
9199 $nextValue = false,
9200 $nextItem
9201 ;
9202 // check next of same letter
9203 if(alreadySelectedLetter) {
9204 $nextItem = $selectedItem.nextAll($item).eq(0);
9205 if( module.has.firstLetter($nextItem, letter) ) {
9206 $nextValue = $nextItem;
9207 }
9208 }
9209 // check all values
9210 if(!$nextValue) {
9211 $item
9212 .each(function(){
9213 if(module.has.firstLetter($(this), letter)) {
9214 $nextValue = $(this);
9215 return false;
9216 }
9217 })
9218 ;
9219 }
9220 // set next value
9221 if($nextValue) {
9222 module.verbose('Scrolling to next value with letter', letter);
9223 module.set.scrollPosition($nextValue);
9224 $selectedItem.removeClass(className.selected);
9225 $nextValue.addClass(className.selected);
9226 if(settings.selectOnKeydown && module.is.single()) {
9227 module.set.selectedItem($nextValue);
9228 }
9229 }
9230 },
9231 direction: function($menu) {
9232 if(settings.direction == 'auto') {
9233 // reset position, remove upward if it's base menu
9234 if (!$menu) {
9235 module.remove.upward();
9236 } else if (module.is.upward($menu)) {
9237 //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
9238 module.remove.upward($menu);
9239 }
9240
9241 if(module.can.openDownward($menu)) {
9242 module.remove.upward($menu);
9243 }
9244 else {
9245 module.set.upward($menu);
9246 }
9247 if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
9248 module.set.leftward($menu);
9249 }
9250 }
9251 else if(settings.direction == 'upward') {
9252 module.set.upward($menu);
9253 }
9254 },
9255 upward: function($currentMenu) {
9256 var $element = $currentMenu || $module;
9257 $element.addClass(className.upward);
9258 },
9259 leftward: function($currentMenu) {
9260 var $element = $currentMenu || $menu;
9261 $element.addClass(className.leftward);
9262 },
9263 value: function(value, text, $selected, preventChangeTrigger) {
9264 if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
9265 $input.removeClass(className.noselection);
9266 } else {
9267 $input.addClass(className.noselection);
9268 }
9269 var
9270 escapedValue = module.escape.value(value),
9271 hasInput = ($input.length > 0),
9272 currentValue = module.get.values(),
9273 stringValue = (value !== undefined)
9274 ? String(value)
9275 : value,
9276 newValue
9277 ;
9278 if(hasInput) {
9279 if(!settings.allowReselection && stringValue == currentValue) {
9280 module.verbose('Skipping value update already same value', value, currentValue);
9281 if(!module.is.initialLoad()) {
9282 return;
9283 }
9284 }
9285
9286 if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
9287 module.debug('Adding user option', value);
9288 module.add.optionValue(value);
9289 }
9290 module.debug('Updating input value', escapedValue, currentValue);
9291 internalChange = true;
9292 $input
9293 .val(escapedValue)
9294 ;
9295 if(settings.fireOnInit === false && module.is.initialLoad()) {
9296 module.debug('Input native change event ignored on initial load');
9297 }
9298 else if(preventChangeTrigger !== true) {
9299 module.trigger.change();
9300 }
9301 internalChange = false;
9302 }
9303 else {
9304 module.verbose('Storing value in metadata', escapedValue, $input);
9305 if(escapedValue !== currentValue) {
9306 $module.data(metadata.value, stringValue);
9307 }
9308 }
9309 if(settings.fireOnInit === false && module.is.initialLoad()) {
9310 module.verbose('No callback on initial load', settings.onChange);
9311 }
9312 else if(preventChangeTrigger !== true) {
9313 settings.onChange.call(element, value, text, $selected);
9314 }
9315 },
9316 active: function() {
9317 $module
9318 .addClass(className.active)
9319 ;
9320 },
9321 multiple: function() {
9322 $module.addClass(className.multiple);
9323 },
9324 visible: function() {
9325 $module.addClass(className.visible);
9326 },
9327 exactly: function(value, $selectedItem) {
9328 module.debug('Setting selected to exact values');
9329 module.clear();
9330 module.set.selected(value, $selectedItem);
9331 },
9332 selected: function(value, $selectedItem, preventChangeTrigger, keepSearchTerm) {
9333 var
9334 isMultiple = module.is.multiple()
9335 ;
9336 $selectedItem = (settings.allowAdditions)
9337 ? $selectedItem || module.get.itemWithAdditions(value)
9338 : $selectedItem || module.get.item(value)
9339 ;
9340 if(!$selectedItem) {
9341 return;
9342 }
9343 module.debug('Setting selected menu item to', $selectedItem);
9344 if(module.is.multiple()) {
9345 module.remove.searchWidth();
9346 }
9347 if(module.is.single()) {
9348 module.remove.activeItem();
9349 module.remove.selectedItem();
9350 }
9351 else if(settings.useLabels) {
9352 module.remove.selectedItem();
9353 }
9354 // select each item
9355 $selectedItem
9356 .each(function() {
9357 var
9358 $selected = $(this),
9359 selectedText = module.get.choiceText($selected),
9360 selectedValue = module.get.choiceValue($selected, selectedText),
9361
9362 isFiltered = $selected.hasClass(className.filtered),
9363 isActive = $selected.hasClass(className.active),
9364 isUserValue = $selected.hasClass(className.addition),
9365 shouldAnimate = (isMultiple && $selectedItem.length == 1)
9366 ;
9367 if(isMultiple) {
9368 if(!isActive || isUserValue) {
9369 if(settings.apiSettings && settings.saveRemoteData) {
9370 module.save.remoteData(selectedText, selectedValue);
9371 }
9372 if(settings.useLabels) {
9373 module.add.label(selectedValue, selectedText, shouldAnimate);
9374 module.add.value(selectedValue, selectedText, $selected);
9375 module.set.activeItem($selected);
9376 module.filterActive();
9377 module.select.nextAvailable($selectedItem);
9378 }
9379 else {
9380 module.add.value(selectedValue, selectedText, $selected);
9381 module.set.text(module.add.variables(message.count));
9382 module.set.activeItem($selected);
9383 }
9384 }
9385 else if(!isFiltered && (settings.useLabels || selectActionActive)) {
9386 module.debug('Selected active value, removing label');
9387 module.remove.selected(selectedValue);
9388 }
9389 }
9390 else {
9391 if(settings.apiSettings && settings.saveRemoteData) {
9392 module.save.remoteData(selectedText, selectedValue);
9393 }
9394 if (!keepSearchTerm) {
9395 module.set.text(selectedText);
9396 }
9397 module.set.value(selectedValue, selectedText, $selected, preventChangeTrigger);
9398 $selected
9399 .addClass(className.active)
9400 .addClass(className.selected)
9401 ;
9402 }
9403 })
9404 ;
9405 if (!keepSearchTerm) {
9406 module.remove.searchTerm();
9407 }
9408 }
9409 },
9410
9411 add: {
9412 label: function(value, text, shouldAnimate) {
9413 var
9414 $next = module.is.searchSelection()
9415 ? $search
9416 : $text,
9417 escapedValue = module.escape.value(value),
9418 $label
9419 ;
9420 if(settings.ignoreCase) {
9421 escapedValue = escapedValue.toLowerCase();
9422 }
9423 $label = $('<a />')
9424 .addClass(className.label)
9425 .attr('data-' + metadata.value, escapedValue)
9426 .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
9427 ;
9428 $label = settings.onLabelCreate.call($label, escapedValue, text);
9429
9430 if(module.has.label(value)) {
9431 module.debug('User selection already exists, skipping', escapedValue);
9432 return;
9433 }
9434 if(settings.label.variation) {
9435 $label.addClass(settings.label.variation);
9436 }
9437 if(shouldAnimate === true) {
9438 module.debug('Animating in label', $label);
9439 $label
9440 .addClass(className.hidden)
9441 .insertBefore($next)
9442 .transition({
9443 animation : settings.label.transition,
9444 debug : settings.debug,
9445 verbose : settings.verbose,
9446 duration : settings.label.duration
9447 })
9448 ;
9449 }
9450 else {
9451 module.debug('Adding selection label', $label);
9452 $label
9453 .insertBefore($next)
9454 ;
9455 }
9456 },
9457 message: function(message) {
9458 var
9459 $message = $menu.children(selector.message),
9460 html = settings.templates.message(module.add.variables(message))
9461 ;
9462 if($message.length > 0) {
9463 $message
9464 .html(html)
9465 ;
9466 }
9467 else {
9468 $message = $('<div/>')
9469 .html(html)
9470 .addClass(className.message)
9471 .appendTo($menu)
9472 ;
9473 }
9474 },
9475 optionValue: function(value) {
9476 var
9477 escapedValue = module.escape.value(value),
9478 $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
9479 hasOption = ($option.length > 0)
9480 ;
9481 if(hasOption) {
9482 return;
9483 }
9484 // temporarily disconnect observer
9485 module.disconnect.selectObserver();
9486 if( module.is.single() ) {
9487 module.verbose('Removing previous user addition');
9488 $input.find('option.' + className.addition).remove();
9489 }
9490 $('<option/>')
9491 .prop('value', escapedValue)
9492 .addClass(className.addition)
9493 .html(value)
9494 .appendTo($input)
9495 ;
9496 module.verbose('Adding user addition as an <option>', value);
9497 module.observe.select();
9498 },
9499 userSuggestion: function(value) {
9500 var
9501 $addition = $menu.children(selector.addition),
9502 $existingItem = module.get.item(value),
9503 alreadyHasValue = $existingItem && $existingItem.not(selector.addition).length,
9504 hasUserSuggestion = $addition.length > 0,
9505 html
9506 ;
9507 if(settings.useLabels && module.has.maxSelections()) {
9508 return;
9509 }
9510 if(value === '' || alreadyHasValue) {
9511 $addition.remove();
9512 return;
9513 }
9514 if(hasUserSuggestion) {
9515 $addition
9516 .data(metadata.value, value)
9517 .data(metadata.text, value)
9518 .attr('data-' + metadata.value, value)
9519 .attr('data-' + metadata.text, value)
9520 .removeClass(className.filtered)
9521 ;
9522 if(!settings.hideAdditions) {
9523 html = settings.templates.addition( module.add.variables(message.addResult, value) );
9524 $addition
9525 .html(html)
9526 ;
9527 }
9528 module.verbose('Replacing user suggestion with new value', $addition);
9529 }
9530 else {
9531 $addition = module.create.userChoice(value);
9532 $addition
9533 .prependTo($menu)
9534 ;
9535 module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
9536 }
9537 if(!settings.hideAdditions || module.is.allFiltered()) {
9538 $addition
9539 .addClass(className.selected)
9540 .siblings()
9541 .removeClass(className.selected)
9542 ;
9543 }
9544 module.refreshItems();
9545 },
9546 variables: function(message, term) {
9547 var
9548 hasCount = (message.search('{count}') !== -1),
9549 hasMaxCount = (message.search('{maxCount}') !== -1),
9550 hasTerm = (message.search('{term}') !== -1),
9551 count,
9552 query
9553 ;
9554 module.verbose('Adding templated variables to message', message);
9555 if(hasCount) {
9556 count = module.get.selectionCount();
9557 message = message.replace('{count}', count);
9558 }
9559 if(hasMaxCount) {
9560 count = module.get.selectionCount();
9561 message = message.replace('{maxCount}', settings.maxSelections);
9562 }
9563 if(hasTerm) {
9564 query = term || module.get.query();
9565 message = message.replace('{term}', query);
9566 }
9567 return message;
9568 },
9569 value: function(addedValue, addedText, $selectedItem) {
9570 var
9571 currentValue = module.get.values(true),
9572 newValue
9573 ;
9574 if(module.has.value(addedValue)) {
9575 module.debug('Value already selected');
9576 return;
9577 }
9578 if(addedValue === '') {
9579 module.debug('Cannot select blank values from multiselect');
9580 return;
9581 }
9582 // extend current array
9583 if(Array.isArray(currentValue)) {
9584 newValue = currentValue.concat([addedValue]);
9585 newValue = module.get.uniqueArray(newValue);
9586 }
9587 else {
9588 newValue = [addedValue];
9589 }
9590 // add values
9591 if( module.has.selectInput() ) {
9592 if(module.can.extendSelect()) {
9593 module.debug('Adding value to select', addedValue, newValue, $input);
9594 module.add.optionValue(addedValue);
9595 }
9596 }
9597 else {
9598 newValue = newValue.join(settings.delimiter);
9599 module.debug('Setting hidden input to delimited value', newValue, $input);
9600 }
9601
9602 if(settings.fireOnInit === false && module.is.initialLoad()) {
9603 module.verbose('Skipping onadd callback on initial load', settings.onAdd);
9604 }
9605 else {
9606 settings.onAdd.call(element, addedValue, addedText, $selectedItem);
9607 }
9608 module.set.value(newValue, addedText, $selectedItem);
9609 module.check.maxSelections();
9610 },
9611 },
9612
9613 remove: {
9614 active: function() {
9615 $module.removeClass(className.active);
9616 },
9617 activeLabel: function() {
9618 $module.find(selector.label).removeClass(className.active);
9619 },
9620 empty: function() {
9621 $module.removeClass(className.empty);
9622 },
9623 loading: function() {
9624 $module.removeClass(className.loading);
9625 },
9626 initialLoad: function() {
9627 initialLoad = false;
9628 },
9629 upward: function($currentMenu) {
9630 var $element = $currentMenu || $module;
9631 $element.removeClass(className.upward);
9632 },
9633 leftward: function($currentMenu) {
9634 var $element = $currentMenu || $menu;
9635 $element.removeClass(className.leftward);
9636 },
9637 visible: function() {
9638 $module.removeClass(className.visible);
9639 },
9640 activeItem: function() {
9641 $item.removeClass(className.active);
9642 },
9643 filteredItem: function() {
9644 if(settings.useLabels && module.has.maxSelections() ) {
9645 return;
9646 }
9647 if(settings.useLabels && module.is.multiple()) {
9648 $item.not('.' + className.active).removeClass(className.filtered);
9649 }
9650 else {
9651 $item.removeClass(className.filtered);
9652 }
9653 if(settings.hideDividers) {
9654 $divider.removeClass(className.hidden);
9655 }
9656 module.remove.empty();
9657 },
9658 optionValue: function(value) {
9659 var
9660 escapedValue = module.escape.value(value),
9661 $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
9662 hasOption = ($option.length > 0)
9663 ;
9664 if(!hasOption || !$option.hasClass(className.addition)) {
9665 return;
9666 }
9667 // temporarily disconnect observer
9668 if(selectObserver) {
9669 selectObserver.disconnect();
9670 module.verbose('Temporarily disconnecting mutation observer');
9671 }
9672 $option.remove();
9673 module.verbose('Removing user addition as an <option>', escapedValue);
9674 if(selectObserver) {
9675 selectObserver.observe($input[0], {
9676 childList : true,
9677 subtree : true
9678 });
9679 }
9680 },
9681 message: function() {
9682 $menu.children(selector.message).remove();
9683 },
9684 searchWidth: function() {
9685 $search.css('width', '');
9686 },
9687 searchTerm: function() {
9688 module.verbose('Cleared search term');
9689 $search.val('');
9690 module.set.filtered();
9691 },
9692 userAddition: function() {
9693 $item.filter(selector.addition).remove();
9694 },
9695 selected: function(value, $selectedItem, preventChangeTrigger) {
9696 $selectedItem = (settings.allowAdditions)
9697 ? $selectedItem || module.get.itemWithAdditions(value)
9698 : $selectedItem || module.get.item(value)
9699 ;
9700
9701 if(!$selectedItem) {
9702 return false;
9703 }
9704
9705 $selectedItem
9706 .each(function() {
9707 var
9708 $selected = $(this),
9709 selectedText = module.get.choiceText($selected),
9710 selectedValue = module.get.choiceValue($selected, selectedText)
9711 ;
9712 if(module.is.multiple()) {
9713 if(settings.useLabels) {
9714 module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
9715 module.remove.label(selectedValue);
9716 }
9717 else {
9718 module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
9719 if(module.get.selectionCount() === 0) {
9720 module.set.placeholderText();
9721 }
9722 else {
9723 module.set.text(module.add.variables(message.count));
9724 }
9725 }
9726 }
9727 else {
9728 module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
9729 }
9730 $selected
9731 .removeClass(className.filtered)
9732 .removeClass(className.active)
9733 ;
9734 if(settings.useLabels) {
9735 $selected.removeClass(className.selected);
9736 }
9737 })
9738 ;
9739 },
9740 selectedItem: function() {
9741 $item.removeClass(className.selected);
9742 },
9743 value: function(removedValue, removedText, $removedItem, preventChangeTrigger) {
9744 var
9745 values = module.get.values(),
9746 newValue
9747 ;
9748 removedValue = module.escape.htmlEntities(removedValue);
9749 if( module.has.selectInput() ) {
9750 module.verbose('Input is <select> removing selected option', removedValue);
9751 newValue = module.remove.arrayValue(removedValue, values);
9752 module.remove.optionValue(removedValue);
9753 }
9754 else {
9755 module.verbose('Removing from delimited values', removedValue);
9756 newValue = module.remove.arrayValue(removedValue, values);
9757 newValue = newValue.join(settings.delimiter);
9758 }
9759 if(settings.fireOnInit === false && module.is.initialLoad()) {
9760 module.verbose('No callback on initial load', settings.onRemove);
9761 }
9762 else {
9763 settings.onRemove.call(element, removedValue, removedText, $removedItem);
9764 }
9765 module.set.value(newValue, removedText, $removedItem, preventChangeTrigger);
9766 module.check.maxSelections();
9767 },
9768 arrayValue: function(removedValue, values) {
9769 if( !Array.isArray(values) ) {
9770 values = [values];
9771 }
9772 values = $.grep(values, function(value){
9773 return (removedValue != value);
9774 });
9775 module.verbose('Removed value from delimited string', removedValue, values);
9776 return values;
9777 },
9778 label: function(value, shouldAnimate) {
9779 var
9780 escapedValue = module.escape.value(value),
9781 $labels = $module.find(selector.label),
9782 $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? escapedValue.toLowerCase() : escapedValue) +'"]')
9783 ;
9784 module.verbose('Removing label', $removedLabel);
9785 $removedLabel.remove();
9786 },
9787 activeLabels: function($activeLabels) {
9788 $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
9789 module.verbose('Removing active label selections', $activeLabels);
9790 module.remove.labels($activeLabels);
9791 },
9792 labels: function($labels, preventChangeTrigger) {
9793 $labels = $labels || $module.find(selector.label);
9794 module.verbose('Removing labels', $labels);
9795 $labels
9796 .each(function(){
9797 var
9798 $label = $(this),
9799 value = $label.data(metadata.value),
9800 stringValue = (value !== undefined)
9801 ? String(value)
9802 : value,
9803 isUserValue = module.is.userValue(stringValue)
9804 ;
9805 if(settings.onLabelRemove.call($label, value) === false) {
9806 module.debug('Label remove callback cancelled removal');
9807 return;
9808 }
9809 module.remove.message();
9810 if(isUserValue) {
9811 module.remove.value(stringValue, stringValue, module.get.item(stringValue), preventChangeTrigger);
9812 module.remove.label(stringValue);
9813 }
9814 else {
9815 // selected will also remove label
9816 module.remove.selected(stringValue, false, preventChangeTrigger);
9817 }
9818 })
9819 ;
9820 },
9821 tabbable: function() {
9822 if( module.is.searchSelection() ) {
9823 module.debug('Searchable dropdown initialized');
9824 $search
9825 .removeAttr('tabindex')
9826 ;
9827 $menu
9828 .removeAttr('tabindex')
9829 ;
9830 }
9831 else {
9832 module.debug('Simple selection dropdown initialized');
9833 $module
9834 .removeAttr('tabindex')
9835 ;
9836 $menu
9837 .removeAttr('tabindex')
9838 ;
9839 }
9840 },
9841 diacritics: function(text) {
9842 return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
9843 }
9844 },
9845
9846 has: {
9847 menuSearch: function() {
9848 return (module.has.search() && $search.closest($menu).length > 0);
9849 },
9850 clearItem: function() {
9851 return ($clear.length > 0);
9852 },
9853 search: function() {
9854 return ($search.length > 0);
9855 },
9856 sizer: function() {
9857 return ($sizer.length > 0);
9858 },
9859 selectInput: function() {
9860 return ( $input.is('select') );
9861 },
9862 minCharacters: function(searchTerm) {
9863 if(settings.minCharacters && !iconClicked) {
9864 searchTerm = (searchTerm !== undefined)
9865 ? String(searchTerm)
9866 : String(module.get.query())
9867 ;
9868 return (searchTerm.length >= settings.minCharacters);
9869 }
9870 iconClicked=false;
9871 return true;
9872 },
9873 firstLetter: function($item, letter) {
9874 var
9875 text,
9876 firstLetter
9877 ;
9878 if(!$item || $item.length === 0 || typeof letter !== 'string') {
9879 return false;
9880 }
9881 text = module.get.choiceText($item, false);
9882 letter = letter.toLowerCase();
9883 firstLetter = String(text).charAt(0).toLowerCase();
9884 return (letter == firstLetter);
9885 },
9886 input: function() {
9887 return ($input.length > 0);
9888 },
9889 items: function() {
9890 return ($item.length > 0);
9891 },
9892 menu: function() {
9893 return ($menu.length > 0);
9894 },
9895 subMenu: function($currentMenu) {
9896 return ($currentMenu || $menu).find(selector.menu).length > 0;
9897 },
9898 message: function() {
9899 return ($menu.children(selector.message).length !== 0);
9900 },
9901 label: function(value) {
9902 var
9903 escapedValue = module.escape.value(value),
9904 $labels = $module.find(selector.label)
9905 ;
9906 if(settings.ignoreCase) {
9907 escapedValue = escapedValue.toLowerCase();
9908 }
9909 return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
9910 },
9911 maxSelections: function() {
9912 return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
9913 },
9914 allResultsFiltered: function() {
9915 var
9916 $normalResults = $item.not(selector.addition)
9917 ;
9918 return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
9919 },
9920 userSuggestion: function() {
9921 return ($menu.children(selector.addition).length > 0);
9922 },
9923 query: function() {
9924 return (module.get.query() !== '');
9925 },
9926 value: function(value) {
9927 return (settings.ignoreCase)
9928 ? module.has.valueIgnoringCase(value)
9929 : module.has.valueMatchingCase(value)
9930 ;
9931 },
9932 valueMatchingCase: function(value) {
9933 var
9934 values = module.get.values(true),
9935 hasValue = Array.isArray(values)
9936 ? values && ($.inArray(value, values) !== -1)
9937 : (values == value)
9938 ;
9939 return (hasValue)
9940 ? true
9941 : false
9942 ;
9943 },
9944 valueIgnoringCase: function(value) {
9945 var
9946 values = module.get.values(true),
9947 hasValue = false
9948 ;
9949 if(!Array.isArray(values)) {
9950 values = [values];
9951 }
9952 $.each(values, function(index, existingValue) {
9953 if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
9954 hasValue = true;
9955 return false;
9956 }
9957 });
9958 return hasValue;
9959 }
9960 },
9961
9962 is: {
9963 active: function() {
9964 return $module.hasClass(className.active);
9965 },
9966 animatingInward: function() {
9967 return $menu.transition('is inward');
9968 },
9969 animatingOutward: function() {
9970 return $menu.transition('is outward');
9971 },
9972 bubbledLabelClick: function(event) {
9973 return $(event.target).is('select, input') && $module.closest('label').length > 0;
9974 },
9975 bubbledIconClick: function(event) {
9976 return $(event.target).closest($icon).length > 0;
9977 },
9978 chrome: function() {
9979 return !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
9980 },
9981 alreadySetup: function() {
9982 return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
9983 },
9984 animating: function($subMenu) {
9985 return ($subMenu)
9986 ? $subMenu.transition && $subMenu.transition('is animating')
9987 : $menu.transition && $menu.transition('is animating')
9988 ;
9989 },
9990 leftward: function($subMenu) {
9991 var $selectedMenu = $subMenu || $menu;
9992 return $selectedMenu.hasClass(className.leftward);
9993 },
9994 clearable: function() {
9995 return ($module.hasClass(className.clearable) || settings.clearable);
9996 },
9997 disabled: function() {
9998 return $module.hasClass(className.disabled);
9999 },
10000 focused: function() {
10001 return (document.activeElement === $module[0]);
10002 },
10003 focusedOnSearch: function() {
10004 return (document.activeElement === $search[0]);
10005 },
10006 allFiltered: function() {
10007 return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
10008 },
10009 hidden: function($subMenu) {
10010 return !module.is.visible($subMenu);
10011 },
10012 initialLoad: function() {
10013 return initialLoad;
10014 },
10015 inObject: function(needle, object) {
10016 var
10017 found = false
10018 ;
10019 $.each(object, function(index, property) {
10020 if(property == needle) {
10021 found = true;
10022 return true;
10023 }
10024 });
10025 return found;
10026 },
10027 multiple: function() {
10028 return $module.hasClass(className.multiple);
10029 },
10030 remote: function() {
10031 return settings.apiSettings && module.can.useAPI();
10032 },
10033 noApiCache: function() {
10034 return settings.apiSettings && !settings.apiSettings.cache
10035 },
10036 single: function() {
10037 return !module.is.multiple();
10038 },
10039 selectMutation: function(mutations) {
10040 var
10041 selectChanged = false
10042 ;
10043 $.each(mutations, function(index, mutation) {
10044 if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
10045 selectChanged = true;
10046 return false;
10047 }
10048 });
10049 return selectChanged;
10050 },
10051 search: function() {
10052 return $module.hasClass(className.search);
10053 },
10054 searchSelection: function() {
10055 return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
10056 },
10057 selection: function() {
10058 return $module.hasClass(className.selection);
10059 },
10060 userValue: function(value) {
10061 return ($.inArray(value, module.get.userValues()) !== -1);
10062 },
10063 upward: function($menu) {
10064 var $element = $menu || $module;
10065 return $element.hasClass(className.upward);
10066 },
10067 visible: function($subMenu) {
10068 return ($subMenu)
10069 ? $subMenu.hasClass(className.visible)
10070 : $menu.hasClass(className.visible)
10071 ;
10072 },
10073 verticallyScrollableContext: function() {
10074 var
10075 overflowY = ($context.get(0) !== window)
10076 ? $context.css('overflow-y')
10077 : false
10078 ;
10079 return (overflowY == 'auto' || overflowY == 'scroll');
10080 },
10081 horizontallyScrollableContext: function() {
10082 var
10083 overflowX = ($context.get(0) !== window)
10084 ? $context.css('overflow-X')
10085 : false
10086 ;
10087 return (overflowX == 'auto' || overflowX == 'scroll');
10088 }
10089 },
10090
10091 can: {
10092 activate: function($item) {
10093 if(settings.useLabels) {
10094 return true;
10095 }
10096 if(!module.has.maxSelections()) {
10097 return true;
10098 }
10099 if(module.has.maxSelections() && $item.hasClass(className.active)) {
10100 return true;
10101 }
10102 return false;
10103 },
10104 openDownward: function($subMenu) {
10105 var
10106 $currentMenu = $subMenu || $menu,
10107 canOpenDownward = true,
10108 onScreen = {},
10109 calculations
10110 ;
10111 $currentMenu
10112 .addClass(className.loading)
10113 ;
10114 calculations = {
10115 context: {
10116 offset : ($context.get(0) === window)
10117 ? { top: 0, left: 0}
10118 : $context.offset(),
10119 scrollTop : $context.scrollTop(),
10120 height : $context.outerHeight()
10121 },
10122 menu : {
10123 offset: $currentMenu.offset(),
10124 height: $currentMenu.outerHeight()
10125 }
10126 };
10127 if(module.is.verticallyScrollableContext()) {
10128 calculations.menu.offset.top += calculations.context.scrollTop;
10129 }
10130 if(module.has.subMenu($currentMenu)) {
10131 calculations.menu.height += $currentMenu.find(selector.menu).first().outerHeight();
10132 }
10133 onScreen = {
10134 above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
10135 below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
10136 };
10137 if(onScreen.below) {
10138 module.verbose('Dropdown can fit in context downward', onScreen);
10139 canOpenDownward = true;
10140 }
10141 else if(!onScreen.below && !onScreen.above) {
10142 module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
10143 canOpenDownward = true;
10144 }
10145 else {
10146 module.verbose('Dropdown cannot fit below, opening upward', onScreen);
10147 canOpenDownward = false;
10148 }
10149 $currentMenu.removeClass(className.loading);
10150 return canOpenDownward;
10151 },
10152 openRightward: function($subMenu) {
10153 var
10154 $currentMenu = $subMenu || $menu,
10155 canOpenRightward = true,
10156 isOffscreenRight = false,
10157 calculations
10158 ;
10159 $currentMenu
10160 .addClass(className.loading)
10161 ;
10162 calculations = {
10163 context: {
10164 offset : ($context.get(0) === window)
10165 ? { top: 0, left: 0}
10166 : $context.offset(),
10167 scrollLeft : $context.scrollLeft(),
10168 width : $context.outerWidth()
10169 },
10170 menu: {
10171 offset : $currentMenu.offset(),
10172 width : $currentMenu.outerWidth()
10173 }
10174 };
10175 if(module.is.horizontallyScrollableContext()) {
10176 calculations.menu.offset.left += calculations.context.scrollLeft;
10177 }
10178 isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
10179 if(isOffscreenRight) {
10180 module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
10181 canOpenRightward = false;
10182 }
10183 $currentMenu.removeClass(className.loading);
10184 return canOpenRightward;
10185 },
10186 click: function() {
10187 return (hasTouch || settings.on == 'click');
10188 },
10189 extendSelect: function() {
10190 return settings.allowAdditions || settings.apiSettings;
10191 },
10192 show: function() {
10193 return !module.is.disabled() && (module.has.items() || module.has.message());
10194 },
10195 useAPI: function() {
10196 return $.fn.api !== undefined;
10197 }
10198 },
10199
10200 animate: {
10201 show: function(callback, $subMenu) {
10202 var
10203 $currentMenu = $subMenu || $menu,
10204 start = ($subMenu)
10205 ? function() {}
10206 : function() {
10207 module.hideSubMenus();
10208 module.hideOthers();
10209 module.set.active();
10210 },
10211 transition
10212 ;
10213 callback = $.isFunction(callback)
10214 ? callback
10215 : function(){}
10216 ;
10217 module.verbose('Doing menu show animation', $currentMenu);
10218 module.set.direction($subMenu);
10219 transition = settings.transition.showMethod || module.get.transition($subMenu);
10220 if( module.is.selection() ) {
10221 module.set.scrollPosition(module.get.selectedItem(), true);
10222 }
10223 if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
10224 if(transition === 'none') {
10225 start();
10226 $currentMenu.transition({
10227 displayType: module.get.displayType()
10228 }).transition('show');
10229 callback.call(element);
10230 }
10231 else if($.fn.transition !== undefined && $module.transition('is supported')) {
10232 $currentMenu
10233 .transition({
10234 animation : transition + ' in',
10235 debug : settings.debug,
10236 verbose : settings.verbose,
10237 duration : settings.transition.showDuration || settings.duration,
10238 queue : true,
10239 onStart : start,
10240 displayType: module.get.displayType(),
10241 onComplete : function() {
10242 callback.call(element);
10243 }
10244 })
10245 ;
10246 }
10247 else {
10248 module.error(error.noTransition, transition);
10249 }
10250 }
10251 },
10252 hide: function(callback, $subMenu) {
10253 var
10254 $currentMenu = $subMenu || $menu,
10255 start = ($subMenu)
10256 ? function() {}
10257 : function() {
10258 if( module.can.click() ) {
10259 module.unbind.intent();
10260 }
10261 module.remove.active();
10262 },
10263 transition = settings.transition.hideMethod || module.get.transition($subMenu)
10264 ;
10265 callback = $.isFunction(callback)
10266 ? callback
10267 : function(){}
10268 ;
10269 if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
10270 module.verbose('Doing menu hide animation', $currentMenu);
10271
10272 if(transition === 'none') {
10273 start();
10274 $currentMenu.transition({
10275 displayType: module.get.displayType()
10276 }).transition('hide');
10277 callback.call(element);
10278 }
10279 else if($.fn.transition !== undefined && $module.transition('is supported')) {
10280 $currentMenu
10281 .transition({
10282 animation : transition + ' out',
10283 duration : settings.transition.hideDuration || settings.duration,
10284 debug : settings.debug,
10285 verbose : settings.verbose,
10286 queue : false,
10287 onStart : start,
10288 displayType: module.get.displayType(),
10289 onComplete : function() {
10290 callback.call(element);
10291 }
10292 })
10293 ;
10294 }
10295 else {
10296 module.error(error.transition);
10297 }
10298 }
10299 }
10300 },
10301
10302 hideAndClear: function() {
10303 module.remove.searchTerm();
10304 if( module.has.maxSelections() ) {
10305 return;
10306 }
10307 if(module.has.search()) {
10308 module.hide(function() {
10309 module.remove.filteredItem();
10310 });
10311 }
10312 else {
10313 module.hide();
10314 }
10315 },
10316
10317 delay: {
10318 show: function() {
10319 module.verbose('Delaying show event to ensure user intent');
10320 clearTimeout(module.timer);
10321 module.timer = setTimeout(module.show, settings.delay.show);
10322 },
10323 hide: function() {
10324 module.verbose('Delaying hide event to ensure user intent');
10325 clearTimeout(module.timer);
10326 module.timer = setTimeout(module.hide, settings.delay.hide);
10327 }
10328 },
10329
10330 escape: {
10331 value: function(value) {
10332 var
10333 multipleValues = Array.isArray(value),
10334 stringValue = (typeof value === 'string'),
10335 isUnparsable = (!stringValue && !multipleValues),
10336 hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
10337 values = []
10338 ;
10339 if(isUnparsable || !hasQuotes) {
10340 return value;
10341 }
10342 module.debug('Encoding quote values for use in select', value);
10343 if(multipleValues) {
10344 $.each(value, function(index, value){
10345 values.push(value.replace(regExp.quote, '&quot;'));
10346 });
10347 return values;
10348 }
10349 return value.replace(regExp.quote, '&quot;');
10350 },
10351 string: function(text) {
10352 text = String(text);
10353 return text.replace(regExp.escape, '\\$&');
10354 },
10355 htmlEntities: function(string) {
10356 var
10357 badChars = /[<>"'`]/g,
10358 shouldEscape = /[&<>"'`]/,
10359 escape = {
10360 "<": "&lt;",
10361 ">": "&gt;",
10362 '"': "&quot;",
10363 "'": "&#x27;",
10364 "`": "&#x60;"
10365 },
10366 escapedChar = function(chr) {
10367 return escape[chr];
10368 }
10369 ;
10370 if(shouldEscape.test(string)) {
10371 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
10372 return string.replace(badChars, escapedChar);
10373 }
10374 return string;
10375 }
10376 },
10377
10378 setting: function(name, value) {
10379 module.debug('Changing setting', name, value);
10380 if( $.isPlainObject(name) ) {
10381 $.extend(true, settings, name);
10382 }
10383 else if(value !== undefined) {
10384 if($.isPlainObject(settings[name])) {
10385 $.extend(true, settings[name], value);
10386 }
10387 else {
10388 settings[name] = value;
10389 }
10390 }
10391 else {
10392 return settings[name];
10393 }
10394 },
10395 internal: function(name, value) {
10396 if( $.isPlainObject(name) ) {
10397 $.extend(true, module, name);
10398 }
10399 else if(value !== undefined) {
10400 module[name] = value;
10401 }
10402 else {
10403 return module[name];
10404 }
10405 },
10406 debug: function() {
10407 if(!settings.silent && settings.debug) {
10408 if(settings.performance) {
10409 module.performance.log(arguments);
10410 }
10411 else {
10412 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
10413 module.debug.apply(console, arguments);
10414 }
10415 }
10416 },
10417 verbose: function() {
10418 if(!settings.silent && settings.verbose && settings.debug) {
10419 if(settings.performance) {
10420 module.performance.log(arguments);
10421 }
10422 else {
10423 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
10424 module.verbose.apply(console, arguments);
10425 }
10426 }
10427 },
10428 error: function() {
10429 if(!settings.silent) {
10430 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
10431 module.error.apply(console, arguments);
10432 }
10433 },
10434 performance: {
10435 log: function(message) {
10436 var
10437 currentTime,
10438 executionTime,
10439 previousTime
10440 ;
10441 if(settings.performance) {
10442 currentTime = new Date().getTime();
10443 previousTime = time || currentTime;
10444 executionTime = currentTime - previousTime;
10445 time = currentTime;
10446 performance.push({
10447 'Name' : message[0],
10448 'Arguments' : [].slice.call(message, 1) || '',
10449 'Element' : element,
10450 'Execution Time' : executionTime
10451 });
10452 }
10453 clearTimeout(module.performance.timer);
10454 module.performance.timer = setTimeout(module.performance.display, 500);
10455 },
10456 display: function() {
10457 var
10458 title = settings.name + ':',
10459 totalTime = 0
10460 ;
10461 time = false;
10462 clearTimeout(module.performance.timer);
10463 $.each(performance, function(index, data) {
10464 totalTime += data['Execution Time'];
10465 });
10466 title += ' ' + totalTime + 'ms';
10467 if(moduleSelector) {
10468 title += ' \'' + moduleSelector + '\'';
10469 }
10470 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
10471 console.groupCollapsed(title);
10472 if(console.table) {
10473 console.table(performance);
10474 }
10475 else {
10476 $.each(performance, function(index, data) {
10477 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
10478 });
10479 }
10480 console.groupEnd();
10481 }
10482 performance = [];
10483 }
10484 },
10485 invoke: function(query, passedArguments, context) {
10486 var
10487 object = instance,
10488 maxDepth,
10489 found,
10490 response
10491 ;
10492 passedArguments = passedArguments || queryArguments;
10493 context = element || context;
10494 if(typeof query == 'string' && object !== undefined) {
10495 query = query.split(/[\. ]/);
10496 maxDepth = query.length - 1;
10497 $.each(query, function(depth, value) {
10498 var camelCaseValue = (depth != maxDepth)
10499 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
10500 : query
10501 ;
10502 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
10503 object = object[camelCaseValue];
10504 }
10505 else if( object[camelCaseValue] !== undefined ) {
10506 found = object[camelCaseValue];
10507 return false;
10508 }
10509 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
10510 object = object[value];
10511 }
10512 else if( object[value] !== undefined ) {
10513 found = object[value];
10514 return false;
10515 }
10516 else {
10517 module.error(error.method, query);
10518 return false;
10519 }
10520 });
10521 }
10522 if ( $.isFunction( found ) ) {
10523 response = found.apply(context, passedArguments);
10524 }
10525 else if(found !== undefined) {
10526 response = found;
10527 }
10528 if(Array.isArray(returnedValue)) {
10529 returnedValue.push(response);
10530 }
10531 else if(returnedValue !== undefined) {
10532 returnedValue = [returnedValue, response];
10533 }
10534 else if(response !== undefined) {
10535 returnedValue = response;
10536 }
10537 return found;
10538 }
10539 };
10540
10541 if(methodInvoked) {
10542 if(instance === undefined) {
10543 module.initialize();
10544 }
10545 module.invoke(query);
10546 }
10547 else {
10548 if(instance !== undefined) {
10549 instance.invoke('destroy');
10550 }
10551 module.initialize();
10552 }
10553 })
10554 ;
10555 return (returnedValue !== undefined)
10556 ? returnedValue
10557 : $allModules
10558 ;
10559};
10560
10561$.fn.dropdown.settings = {
10562
10563 silent : false,
10564 debug : false,
10565 verbose : false,
10566 performance : true,
10567
10568 on : 'click', // what event should show menu action on item selection
10569 action : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
10570
10571 values : false, // specify values to use for dropdown
10572
10573 clearable : false, // whether the value of the dropdown can be cleared
10574
10575 apiSettings : false,
10576 selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
10577 minCharacters : 0, // Minimum characters required to trigger API call
10578
10579 filterRemoteData : false, // Whether API results should be filtered after being returned for query term
10580 saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
10581
10582 throttle : 200, // How long to wait after last user input to search remotely
10583
10584 context : window, // Context to use when determining if on screen
10585 direction : 'auto', // Whether dropdown should always open in one direction
10586 keepOnScreen : true, // Whether dropdown should check whether it is on screen before showing
10587
10588 match : 'both', // what to match against with search selection (both, text, or label)
10589 fullTextSearch : false, // search anywhere in value (set to 'exact' to require exact matches)
10590 ignoreDiacritics : false, // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
10591 hideDividers : false, // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
10592
10593 placeholder : 'auto', // whether to convert blank <select> values to placeholder text
10594 preserveHTML : true, // preserve html when selecting value
10595 sortSelect : false, // sort selection on init
10596
10597 forceSelection : true, // force a choice on blur with search selection
10598
10599 allowAdditions : false, // whether multiple select should allow user added values
10600 ignoreCase : false, // whether to consider case sensitivity when creating labels
10601 ignoreSearchCase : true, // whether to consider case sensitivity when filtering items
10602 hideAdditions : true, // whether or not to hide special message prompting a user they can enter a value
10603
10604 maxSelections : false, // When set to a number limits the number of selections to this count
10605 useLabels : true, // whether multiple select should filter currently active selections from choices
10606 delimiter : ',', // when multiselect uses normal <input> the values will be delimited with this character
10607
10608 showOnFocus : true, // show menu on focus
10609 allowReselection : false, // whether current value should trigger callbacks when reselected
10610 allowTab : true, // add tabindex to element
10611 allowCategorySelection : false, // allow elements with sub-menus to be selected
10612
10613 fireOnInit : false, // Whether callbacks should fire when initializing dropdown values
10614
10615 transition : 'auto', // auto transition will slide down or up based on direction
10616 duration : 200, // duration of transition
10617 displayType : false, // displayType of transition
10618
10619 glyphWidth : 1.037, // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
10620
10621 headerDivider : true, // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
10622
10623 // label settings on multi-select
10624 label: {
10625 transition : 'scale',
10626 duration : 200,
10627 variation : false
10628 },
10629
10630 // delay before event
10631 delay : {
10632 hide : 300,
10633 show : 200,
10634 search : 20,
10635 touch : 50
10636 },
10637
10638 /* Callbacks */
10639 onChange : function(value, text, $selected){},
10640 onAdd : function(value, text, $selected){},
10641 onRemove : function(value, text, $selected){},
10642 onSearch : function(searchTerm){},
10643
10644 onLabelSelect : function($selectedLabels){},
10645 onLabelCreate : function(value, text) { return $(this); },
10646 onLabelRemove : function(value) { return true; },
10647 onNoResults : function(searchTerm) { return true; },
10648 onShow : function(){},
10649 onHide : function(){},
10650
10651 /* Component */
10652 name : 'Dropdown',
10653 namespace : 'dropdown',
10654
10655 message: {
10656 addResult : 'Add <b>{term}</b>',
10657 count : '{count} selected',
10658 maxSelections : 'Max {maxCount} selections',
10659 noResults : 'No results found.',
10660 serverError : 'There was an error contacting the server'
10661 },
10662
10663 error : {
10664 action : 'You called a dropdown action that was not defined',
10665 alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
10666 labels : 'Allowing user additions currently requires the use of labels.',
10667 missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
10668 method : 'The method you called is not defined.',
10669 noAPI : 'The API module is required to load resources remotely',
10670 noStorage : 'Saving remote data requires session storage',
10671 noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
10672 noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
10673 },
10674
10675 regExp : {
10676 escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
10677 quote : /"/g
10678 },
10679
10680 metadata : {
10681 defaultText : 'defaultText',
10682 defaultValue : 'defaultValue',
10683 placeholderText : 'placeholder',
10684 text : 'text',
10685 value : 'value'
10686 },
10687
10688 // property names for remote query
10689 fields: {
10690 remoteValues : 'results', // grouping for api results
10691 values : 'values', // grouping for all dropdown values
10692 disabled : 'disabled', // whether value should be disabled
10693 name : 'name', // displayed dropdown text
10694 description : 'description', // displayed dropdown description
10695 descriptionVertical : 'descriptionVertical', // whether description should be vertical
10696 value : 'value', // actual dropdown value
10697 text : 'text', // displayed text when selected
10698 type : 'type', // type of dropdown element
10699 image : 'image', // optional image path
10700 imageClass : 'imageClass', // optional individual class for image
10701 icon : 'icon', // optional icon name
10702 iconClass : 'iconClass', // optional individual class for icon (for example to use flag instead)
10703 class : 'class', // optional individual class for item/header
10704 divider : 'divider' // optional divider append for group headers
10705 },
10706
10707 keys : {
10708 backspace : 8,
10709 delimiter : 188, // comma
10710 deleteKey : 46,
10711 enter : 13,
10712 escape : 27,
10713 pageUp : 33,
10714 pageDown : 34,
10715 leftArrow : 37,
10716 upArrow : 38,
10717 rightArrow : 39,
10718 downArrow : 40
10719 },
10720
10721 selector : {
10722 addition : '.addition',
10723 divider : '.divider, .header',
10724 dropdown : '.ui.dropdown',
10725 hidden : '.hidden',
10726 icon : '> .dropdown.icon',
10727 input : '> input[type="hidden"], > select',
10728 item : '.item',
10729 label : '> .label',
10730 remove : '> .label > .delete.icon',
10731 siblingLabel : '.label',
10732 menu : '.menu',
10733 message : '.message',
10734 menuIcon : '.dropdown.icon',
10735 search : 'input.search, .menu > .search > input, .menu input.search',
10736 sizer : '> span.sizer',
10737 text : '> .text:not(.icon)',
10738 unselectable : '.disabled, .filtered',
10739 clearIcon : '> .remove.icon'
10740 },
10741
10742 className : {
10743 active : 'active',
10744 addition : 'addition',
10745 animating : 'animating',
10746 description : 'description',
10747 descriptionVertical : 'vertical',
10748 disabled : 'disabled',
10749 empty : 'empty',
10750 dropdown : 'ui dropdown',
10751 filtered : 'filtered',
10752 hidden : 'hidden transition',
10753 icon : 'icon',
10754 image : 'image',
10755 item : 'item',
10756 label : 'ui label',
10757 loading : 'loading',
10758 menu : 'menu',
10759 message : 'message',
10760 multiple : 'multiple',
10761 placeholder : 'default',
10762 sizer : 'sizer',
10763 search : 'search',
10764 selected : 'selected',
10765 selection : 'selection',
10766 text : 'text',
10767 upward : 'upward',
10768 leftward : 'left',
10769 visible : 'visible',
10770 clearable : 'clearable',
10771 noselection : 'noselection',
10772 delete : 'delete',
10773 header : 'header',
10774 divider : 'divider',
10775 groupIcon : '',
10776 unfilterable : 'unfilterable'
10777 }
10778
10779};
10780
10781/* Templates */
10782$.fn.dropdown.settings.templates = {
10783 deQuote: function(string, encode) {
10784 return String(string).replace(/"/g,encode ? "&quot;" : "");
10785 },
10786 escape: function(string, preserveHTML) {
10787 if (preserveHTML){
10788 return string;
10789 }
10790 var
10791 badChars = /[<>"'`]/g,
10792 shouldEscape = /[&<>"'`]/,
10793 escape = {
10794 "<": "&lt;",
10795 ">": "&gt;",
10796 '"': "&quot;",
10797 "'": "&#x27;",
10798 "`": "&#x60;"
10799 },
10800 escapedChar = function(chr) {
10801 return escape[chr];
10802 }
10803 ;
10804 if(shouldEscape.test(string)) {
10805 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
10806 return string.replace(badChars, escapedChar);
10807 }
10808 return string;
10809 },
10810 // generates dropdown from select values
10811 dropdown: function(select, fields, preserveHTML, className) {
10812 var
10813 placeholder = select.placeholder || false,
10814 html = '',
10815 escape = $.fn.dropdown.settings.templates.escape
10816 ;
10817 html += '<i class="dropdown icon"></i>';
10818 if(placeholder) {
10819 html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
10820 }
10821 else {
10822 html += '<div class="text"></div>';
10823 }
10824 html += '<div class="'+className.menu+'">';
10825 html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
10826 html += '</div>';
10827 return html;
10828 },
10829
10830 // generates just menu from select
10831 menu: function(response, fields, preserveHTML, className) {
10832 var
10833 values = response[fields.values] || [],
10834 html = '',
10835 escape = $.fn.dropdown.settings.templates.escape,
10836 deQuote = $.fn.dropdown.settings.templates.deQuote
10837 ;
10838 $.each(values, function(index, option) {
10839 var
10840 itemType = (option[fields.type])
10841 ? option[fields.type]
10842 : 'item',
10843 isMenu = itemType.indexOf('menu') !== -1
10844 ;
10845
10846 if( itemType === 'item' || isMenu) {
10847 var
10848 maybeText = (option[fields.text])
10849 ? ' data-text="' + deQuote(option[fields.text],true) + '"'
10850 : '',
10851 maybeDisabled = (option[fields.disabled])
10852 ? className.disabled+' '
10853 : '',
10854 maybeDescriptionVertical = (option[fields.descriptionVertical])
10855 ? className.descriptionVertical+' '
10856 : '',
10857 hasDescription = (escape(option[fields.description] || '', preserveHTML) != '')
10858 ;
10859 html += '<div class="'+ maybeDisabled + maybeDescriptionVertical + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + deQuote(option[fields.value],true) + '"' + maybeText + '>';
10860 if (isMenu) {
10861 html += '<i class="'+ (itemType.indexOf('left') !== -1 ? 'left' : '') + ' dropdown icon"></i>';
10862 }
10863 if(option[fields.image]) {
10864 html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
10865 }
10866 if(option[fields.icon]) {
10867 html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
10868 }
10869 if(hasDescription){
10870 html += '<span class="'+ className.description +'">'+ escape(option[fields.description] || '', preserveHTML) + '</span>';
10871 html += (!isMenu) ? '<span class="'+ className.text + '">' : '';
10872 }
10873 if (isMenu) {
10874 html += '<span class="' + className.text + '">';
10875 }
10876 html += escape(option[fields.name] || '', preserveHTML);
10877 if (isMenu) {
10878 html += '</span>';
10879 html += '<div class="' + itemType + '">';
10880 html += $.fn.dropdown.settings.templates.menu(option, fields, preserveHTML, className);
10881 html += '</div>';
10882 } else if(hasDescription){
10883 html += '</span>';
10884 }
10885 html += '</div>';
10886 } else if (itemType === 'header') {
10887 var groupName = escape(option[fields.name] || '', preserveHTML),
10888 groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
10889 ;
10890 if(groupName !== '' || groupIcon !== '') {
10891 html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
10892 if (groupIcon !== '') {
10893 html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
10894 }
10895 html += groupName;
10896 html += '</div>';
10897 }
10898 if(option[fields.divider]){
10899 html += '<div class="'+className.divider+'"></div>';
10900 }
10901 }
10902 });
10903 return html;
10904 },
10905
10906 // generates label for multiselect
10907 label: function(value, text, preserveHTML, className) {
10908 var
10909 escape = $.fn.dropdown.settings.templates.escape;
10910 return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
10911 },
10912
10913
10914 // generates messages like "No results"
10915 message: function(message) {
10916 return message;
10917 },
10918
10919 // generates user addition to selection menu
10920 addition: function(choice) {
10921 return choice;
10922 }
10923
10924};
10925
10926})( jQuery, window, document );
10927
10928/*!
10929 * # Fomantic-UI 2.8.8 - Embed
10930 * http://github.com/fomantic/Fomantic-UI/
10931 *
10932 *
10933 * Released under the MIT license
10934 * http://opensource.org/licenses/MIT
10935 *
10936 */
10937
10938;(function ($, window, document, undefined) {
10939
10940"use strict";
10941
10942$.isFunction = $.isFunction || function(obj) {
10943 return typeof obj === "function" && typeof obj.nodeType !== "number";
10944};
10945
10946window = (typeof window != 'undefined' && window.Math == Math)
10947 ? window
10948 : (typeof self != 'undefined' && self.Math == Math)
10949 ? self
10950 : Function('return this')()
10951;
10952
10953$.fn.embed = function(parameters) {
10954
10955 var
10956 $allModules = $(this),
10957
10958 moduleSelector = $allModules.selector || '',
10959
10960 time = new Date().getTime(),
10961 performance = [],
10962
10963 query = arguments[0],
10964 methodInvoked = (typeof query == 'string'),
10965 queryArguments = [].slice.call(arguments, 1),
10966
10967 returnedValue
10968 ;
10969
10970 $allModules
10971 .each(function() {
10972 var
10973 settings = ( $.isPlainObject(parameters) )
10974 ? $.extend(true, {}, $.fn.embed.settings, parameters)
10975 : $.extend({}, $.fn.embed.settings),
10976
10977 selector = settings.selector,
10978 className = settings.className,
10979 sources = settings.sources,
10980 error = settings.error,
10981 metadata = settings.metadata,
10982 namespace = settings.namespace,
10983 templates = settings.templates,
10984
10985 eventNamespace = '.' + namespace,
10986 moduleNamespace = 'module-' + namespace,
10987
10988 $module = $(this),
10989 $placeholder = $module.find(selector.placeholder),
10990 $icon = $module.find(selector.icon),
10991 $embed = $module.find(selector.embed),
10992
10993 element = this,
10994 instance = $module.data(moduleNamespace),
10995 module
10996 ;
10997
10998 module = {
10999
11000 initialize: function() {
11001 module.debug('Initializing embed');
11002 module.determine.autoplay();
11003 module.create();
11004 module.bind.events();
11005 module.instantiate();
11006 },
11007
11008 instantiate: function() {
11009 module.verbose('Storing instance of module', module);
11010 instance = module;
11011 $module
11012 .data(moduleNamespace, module)
11013 ;
11014 },
11015
11016 destroy: function() {
11017 module.verbose('Destroying previous instance of embed');
11018 module.reset();
11019 $module
11020 .removeData(moduleNamespace)
11021 .off(eventNamespace)
11022 ;
11023 },
11024
11025 refresh: function() {
11026 module.verbose('Refreshing selector cache');
11027 $placeholder = $module.find(selector.placeholder);
11028 $icon = $module.find(selector.icon);
11029 $embed = $module.find(selector.embed);
11030 },
11031
11032 bind: {
11033 events: function() {
11034 if( module.has.placeholder() ) {
11035 module.debug('Adding placeholder events');
11036 $module
11037 .on('click' + eventNamespace, selector.placeholder, module.createAndShow)
11038 .on('click' + eventNamespace, selector.icon, module.createAndShow)
11039 ;
11040 }
11041 }
11042 },
11043
11044 create: function() {
11045 var
11046 placeholder = module.get.placeholder()
11047 ;
11048 if(placeholder) {
11049 module.createPlaceholder();
11050 }
11051 else {
11052 module.createAndShow();
11053 }
11054 },
11055
11056 createPlaceholder: function(placeholder) {
11057 var
11058 icon = module.get.icon(),
11059 url = module.get.url(),
11060 embed = module.generate.embed(url)
11061 ;
11062 placeholder = placeholder || module.get.placeholder();
11063 $module.html( templates.placeholder(placeholder, icon) );
11064 module.debug('Creating placeholder for embed', placeholder, icon);
11065 },
11066
11067 createEmbed: function(url) {
11068 module.refresh();
11069 url = url || module.get.url();
11070 $embed = $('<div/>')
11071 .addClass(className.embed)
11072 .html( module.generate.embed(url) )
11073 .appendTo($module)
11074 ;
11075 settings.onCreate.call(element, url);
11076 module.debug('Creating embed object', $embed);
11077 },
11078
11079 changeEmbed: function(url) {
11080 $embed
11081 .html( module.generate.embed(url) )
11082 ;
11083 },
11084
11085 createAndShow: function() {
11086 module.createEmbed();
11087 module.show();
11088 },
11089
11090 // sets new embed
11091 change: function(source, id, url) {
11092 module.debug('Changing video to ', source, id, url);
11093 $module
11094 .data(metadata.source, source)
11095 .data(metadata.id, id)
11096 ;
11097 if(url) {
11098 $module.data(metadata.url, url);
11099 }
11100 else {
11101 $module.removeData(metadata.url);
11102 }
11103 if(module.has.embed()) {
11104 module.changeEmbed();
11105 }
11106 else {
11107 module.create();
11108 }
11109 },
11110
11111 // clears embed
11112 reset: function() {
11113 module.debug('Clearing embed and showing placeholder');
11114 module.remove.data();
11115 module.remove.active();
11116 module.remove.embed();
11117 module.showPlaceholder();
11118 settings.onReset.call(element);
11119 },
11120
11121 // shows current embed
11122 show: function() {
11123 module.debug('Showing embed');
11124 module.set.active();
11125 settings.onDisplay.call(element);
11126 },
11127
11128 hide: function() {
11129 module.debug('Hiding embed');
11130 module.showPlaceholder();
11131 },
11132
11133 showPlaceholder: function() {
11134 module.debug('Showing placeholder image');
11135 module.remove.active();
11136 settings.onPlaceholderDisplay.call(element);
11137 },
11138
11139 get: {
11140 id: function() {
11141 return settings.id || $module.data(metadata.id);
11142 },
11143 placeholder: function() {
11144 return settings.placeholder || $module.data(metadata.placeholder);
11145 },
11146 icon: function() {
11147 return (settings.icon)
11148 ? settings.icon
11149 : ($module.data(metadata.icon) !== undefined)
11150 ? $module.data(metadata.icon)
11151 : module.determine.icon()
11152 ;
11153 },
11154 source: function(url) {
11155 return (settings.source)
11156 ? settings.source
11157 : ($module.data(metadata.source) !== undefined)
11158 ? $module.data(metadata.source)
11159 : module.determine.source()
11160 ;
11161 },
11162 type: function() {
11163 var source = module.get.source();
11164 return (sources[source] !== undefined)
11165 ? sources[source].type
11166 : false
11167 ;
11168 },
11169 url: function() {
11170 return (settings.url)
11171 ? settings.url
11172 : ($module.data(metadata.url) !== undefined)
11173 ? $module.data(metadata.url)
11174 : module.determine.url()
11175 ;
11176 }
11177 },
11178
11179 determine: {
11180 autoplay: function() {
11181 if(module.should.autoplay()) {
11182 settings.autoplay = true;
11183 }
11184 },
11185 source: function(url) {
11186 var
11187 matchedSource = false
11188 ;
11189 url = url || module.get.url();
11190 if(url) {
11191 $.each(sources, function(name, source) {
11192 if(url.search(source.domain) !== -1) {
11193 matchedSource = name;
11194 return false;
11195 }
11196 });
11197 }
11198 return matchedSource;
11199 },
11200 icon: function() {
11201 var
11202 source = module.get.source()
11203 ;
11204 return (sources[source] !== undefined)
11205 ? sources[source].icon
11206 : false
11207 ;
11208 },
11209 url: function() {
11210 var
11211 id = settings.id || $module.data(metadata.id),
11212 source = settings.source || $module.data(metadata.source),
11213 url
11214 ;
11215 url = (sources[source] !== undefined)
11216 ? sources[source].url.replace('{id}', id)
11217 : false
11218 ;
11219 if(url) {
11220 $module.data(metadata.url, url);
11221 }
11222 return url;
11223 }
11224 },
11225
11226
11227 set: {
11228 active: function() {
11229 $module.addClass(className.active);
11230 }
11231 },
11232
11233 remove: {
11234 data: function() {
11235 $module
11236 .removeData(metadata.id)
11237 .removeData(metadata.icon)
11238 .removeData(metadata.placeholder)
11239 .removeData(metadata.source)
11240 .removeData(metadata.url)
11241 ;
11242 },
11243 active: function() {
11244 $module.removeClass(className.active);
11245 },
11246 embed: function() {
11247 $embed.empty();
11248 }
11249 },
11250
11251 encode: {
11252 parameters: function(parameters) {
11253 var
11254 urlString = [],
11255 index
11256 ;
11257 for (index in parameters) {
11258 urlString.push( encodeURIComponent(index) + '=' + encodeURIComponent( parameters[index] ) );
11259 }
11260 return urlString.join('&amp;');
11261 }
11262 },
11263
11264 generate: {
11265 embed: function(url) {
11266 module.debug('Generating embed html');
11267 var
11268 source = module.get.source(),
11269 html,
11270 parameters
11271 ;
11272 url = module.get.url(url);
11273 if(url) {
11274 parameters = module.generate.parameters(source);
11275 html = templates.iframe(url, parameters);
11276 }
11277 else {
11278 module.error(error.noURL, $module);
11279 }
11280 return html;
11281 },
11282 parameters: function(source, extraParameters) {
11283 var
11284 parameters = (sources[source] && sources[source].parameters !== undefined)
11285 ? sources[source].parameters(settings)
11286 : {}
11287 ;
11288 extraParameters = extraParameters || settings.parameters;
11289 if(extraParameters) {
11290 parameters = $.extend({}, parameters, extraParameters);
11291 }
11292 parameters = settings.onEmbed(parameters);
11293 return module.encode.parameters(parameters);
11294 }
11295 },
11296
11297 has: {
11298 embed: function() {
11299 return ($embed.length > 0);
11300 },
11301 placeholder: function() {
11302 return settings.placeholder || $module.data(metadata.placeholder);
11303 }
11304 },
11305
11306 should: {
11307 autoplay: function() {
11308 return (settings.autoplay === 'auto')
11309 ? (settings.placeholder || $module.data(metadata.placeholder) !== undefined)
11310 : settings.autoplay
11311 ;
11312 }
11313 },
11314
11315 is: {
11316 video: function() {
11317 return module.get.type() == 'video';
11318 }
11319 },
11320
11321 setting: function(name, value) {
11322 module.debug('Changing setting', name, value);
11323 if( $.isPlainObject(name) ) {
11324 $.extend(true, settings, name);
11325 }
11326 else if(value !== undefined) {
11327 if($.isPlainObject(settings[name])) {
11328 $.extend(true, settings[name], value);
11329 }
11330 else {
11331 settings[name] = value;
11332 }
11333 }
11334 else {
11335 return settings[name];
11336 }
11337 },
11338 internal: function(name, value) {
11339 if( $.isPlainObject(name) ) {
11340 $.extend(true, module, name);
11341 }
11342 else if(value !== undefined) {
11343 module[name] = value;
11344 }
11345 else {
11346 return module[name];
11347 }
11348 },
11349 debug: function() {
11350 if(!settings.silent && settings.debug) {
11351 if(settings.performance) {
11352 module.performance.log(arguments);
11353 }
11354 else {
11355 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
11356 module.debug.apply(console, arguments);
11357 }
11358 }
11359 },
11360 verbose: function() {
11361 if(!settings.silent && settings.verbose && settings.debug) {
11362 if(settings.performance) {
11363 module.performance.log(arguments);
11364 }
11365 else {
11366 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
11367 module.verbose.apply(console, arguments);
11368 }
11369 }
11370 },
11371 error: function() {
11372 if(!settings.silent) {
11373 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
11374 module.error.apply(console, arguments);
11375 }
11376 },
11377 performance: {
11378 log: function(message) {
11379 var
11380 currentTime,
11381 executionTime,
11382 previousTime
11383 ;
11384 if(settings.performance) {
11385 currentTime = new Date().getTime();
11386 previousTime = time || currentTime;
11387 executionTime = currentTime - previousTime;
11388 time = currentTime;
11389 performance.push({
11390 'Name' : message[0],
11391 'Arguments' : [].slice.call(message, 1) || '',
11392 'Element' : element,
11393 'Execution Time' : executionTime
11394 });
11395 }
11396 clearTimeout(module.performance.timer);
11397 module.performance.timer = setTimeout(module.performance.display, 500);
11398 },
11399 display: function() {
11400 var
11401 title = settings.name + ':',
11402 totalTime = 0
11403 ;
11404 time = false;
11405 clearTimeout(module.performance.timer);
11406 $.each(performance, function(index, data) {
11407 totalTime += data['Execution Time'];
11408 });
11409 title += ' ' + totalTime + 'ms';
11410 if(moduleSelector) {
11411 title += ' \'' + moduleSelector + '\'';
11412 }
11413 if($allModules.length > 1) {
11414 title += ' ' + '(' + $allModules.length + ')';
11415 }
11416 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
11417 console.groupCollapsed(title);
11418 if(console.table) {
11419 console.table(performance);
11420 }
11421 else {
11422 $.each(performance, function(index, data) {
11423 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
11424 });
11425 }
11426 console.groupEnd();
11427 }
11428 performance = [];
11429 }
11430 },
11431 invoke: function(query, passedArguments, context) {
11432 var
11433 object = instance,
11434 maxDepth,
11435 found,
11436 response
11437 ;
11438 passedArguments = passedArguments || queryArguments;
11439 context = element || context;
11440 if(typeof query == 'string' && object !== undefined) {
11441 query = query.split(/[\. ]/);
11442 maxDepth = query.length - 1;
11443 $.each(query, function(depth, value) {
11444 var camelCaseValue = (depth != maxDepth)
11445 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
11446 : query
11447 ;
11448 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
11449 object = object[camelCaseValue];
11450 }
11451 else if( object[camelCaseValue] !== undefined ) {
11452 found = object[camelCaseValue];
11453 return false;
11454 }
11455 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
11456 object = object[value];
11457 }
11458 else if( object[value] !== undefined ) {
11459 found = object[value];
11460 return false;
11461 }
11462 else {
11463 module.error(error.method, query);
11464 return false;
11465 }
11466 });
11467 }
11468 if ( $.isFunction( found ) ) {
11469 response = found.apply(context, passedArguments);
11470 }
11471 else if(found !== undefined) {
11472 response = found;
11473 }
11474 if(Array.isArray(returnedValue)) {
11475 returnedValue.push(response);
11476 }
11477 else if(returnedValue !== undefined) {
11478 returnedValue = [returnedValue, response];
11479 }
11480 else if(response !== undefined) {
11481 returnedValue = response;
11482 }
11483 return found;
11484 }
11485 };
11486
11487 if(methodInvoked) {
11488 if(instance === undefined) {
11489 module.initialize();
11490 }
11491 module.invoke(query);
11492 }
11493 else {
11494 if(instance !== undefined) {
11495 instance.invoke('destroy');
11496 }
11497 module.initialize();
11498 }
11499 })
11500 ;
11501 return (returnedValue !== undefined)
11502 ? returnedValue
11503 : this
11504 ;
11505};
11506
11507$.fn.embed.settings = {
11508
11509 name : 'Embed',
11510 namespace : 'embed',
11511
11512 silent : false,
11513 debug : false,
11514 verbose : false,
11515 performance : true,
11516
11517 icon : false,
11518 source : false,
11519 url : false,
11520 id : false,
11521
11522 // standard video settings
11523 autoplay : 'auto',
11524 color : '#444444',
11525 hd : true,
11526 brandedUI : false,
11527
11528 // additional parameters to include with the embed
11529 parameters: false,
11530
11531 onDisplay : function() {},
11532 onPlaceholderDisplay : function() {},
11533 onReset : function() {},
11534 onCreate : function(url) {},
11535 onEmbed : function(parameters) {
11536 return parameters;
11537 },
11538
11539 metadata : {
11540 id : 'id',
11541 icon : 'icon',
11542 placeholder : 'placeholder',
11543 source : 'source',
11544 url : 'url'
11545 },
11546
11547 error : {
11548 noURL : 'No URL specified',
11549 method : 'The method you called is not defined'
11550 },
11551
11552 className : {
11553 active : 'active',
11554 embed : 'embed'
11555 },
11556
11557 selector : {
11558 embed : '.embed',
11559 placeholder : '.placeholder',
11560 icon : '.icon'
11561 },
11562
11563 sources: {
11564 youtube: {
11565 name : 'youtube',
11566 type : 'video',
11567 icon : 'video play',
11568 domain : 'youtube.com',
11569 url : '//www.youtube.com/embed/{id}',
11570 parameters: function(settings) {
11571 return {
11572 autohide : !settings.brandedUI,
11573 autoplay : settings.autoplay,
11574 color : settings.color || undefined,
11575 hq : settings.hd,
11576 jsapi : settings.api,
11577 modestbranding : !settings.brandedUI
11578 };
11579 }
11580 },
11581 vimeo: {
11582 name : 'vimeo',
11583 type : 'video',
11584 icon : 'video play',
11585 domain : 'vimeo.com',
11586 url : '//player.vimeo.com/video/{id}',
11587 parameters: function(settings) {
11588 return {
11589 api : settings.api,
11590 autoplay : settings.autoplay,
11591 byline : settings.brandedUI,
11592 color : settings.color || undefined,
11593 portrait : settings.brandedUI,
11594 title : settings.brandedUI
11595 };
11596 }
11597 }
11598 },
11599
11600 templates: {
11601 iframe : function(url, parameters) {
11602 var src = url;
11603 if (parameters) {
11604 src += '?' + parameters;
11605 }
11606 return ''
11607 + '<iframe src="' + src + '"'
11608 + ' width="100%" height="100%"'
11609 + ' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'
11610 ;
11611 },
11612 placeholder : function(image, icon) {
11613 var
11614 html = ''
11615 ;
11616 if(icon) {
11617 html += '<i class="' + icon + ' icon"></i>';
11618 }
11619 if(image) {
11620 html += '<img class="placeholder" src="' + image + '">';
11621 }
11622 return html;
11623 }
11624 },
11625
11626 // NOT YET IMPLEMENTED
11627 api : false,
11628 onPause : function() {},
11629 onPlay : function() {},
11630 onStop : function() {}
11631
11632};
11633
11634
11635
11636})( jQuery, window, document );
11637
11638/*!
11639 * # Fomantic-UI 2.8.8 - Modal
11640 * http://github.com/fomantic/Fomantic-UI/
11641 *
11642 *
11643 * Released under the MIT license
11644 * http://opensource.org/licenses/MIT
11645 *
11646 */
11647
11648;(function ($, window, document, undefined) {
11649
11650'use strict';
11651
11652$.isFunction = $.isFunction || function(obj) {
11653 return typeof obj === "function" && typeof obj.nodeType !== "number";
11654};
11655
11656window = (typeof window != 'undefined' && window.Math == Math)
11657 ? window
11658 : (typeof self != 'undefined' && self.Math == Math)
11659 ? self
11660 : Function('return this')()
11661;
11662
11663$.fn.modal = function(parameters) {
11664 var
11665 $allModules = $(this),
11666 $window = $(window),
11667 $document = $(document),
11668 $body = $('body'),
11669
11670 moduleSelector = $allModules.selector || '',
11671
11672 time = new Date().getTime(),
11673 performance = [],
11674
11675 query = arguments[0],
11676 methodInvoked = (typeof query == 'string'),
11677 queryArguments = [].slice.call(arguments, 1),
11678
11679 requestAnimationFrame = window.requestAnimationFrame
11680 || window.mozRequestAnimationFrame
11681 || window.webkitRequestAnimationFrame
11682 || window.msRequestAnimationFrame
11683 || function(callback) { setTimeout(callback, 0); },
11684
11685 returnedValue
11686 ;
11687
11688 $allModules
11689 .each(function() {
11690 var
11691 settings = ( $.isPlainObject(parameters) )
11692 ? $.extend(true, {}, $.fn.modal.settings, parameters)
11693 : $.extend({}, $.fn.modal.settings),
11694
11695 selector = settings.selector,
11696 className = settings.className,
11697 namespace = settings.namespace,
11698 fields = settings.fields,
11699 error = settings.error,
11700
11701 eventNamespace = '.' + namespace,
11702 moduleNamespace = 'module-' + namespace,
11703
11704 $module = $(this),
11705 $context = $(settings.context),
11706 $close = $module.find(selector.close),
11707
11708 $allModals,
11709 $otherModals,
11710 $focusedElement,
11711 $dimmable,
11712 $dimmer,
11713
11714 element = this,
11715 instance = $module.hasClass('modal') ? $module.data(moduleNamespace) : undefined,
11716
11717 ignoreRepeatedEvents = false,
11718
11719 initialMouseDownInModal,
11720 initialMouseDownInScrollbar,
11721 initialBodyMargin = '',
11722 tempBodyMargin = '',
11723
11724 elementEventNamespace,
11725 id,
11726 observer,
11727 module
11728 ;
11729 module = {
11730
11731 initialize: function() {
11732 if(!$module.hasClass('modal')) {
11733 module.create.modal();
11734 if(!$.isFunction(settings.onHidden)) {
11735 settings.onHidden = function () {
11736 module.destroy();
11737 $module.remove();
11738 };
11739 }
11740 }
11741 $module.addClass(settings.class);
11742 if (settings.title !== '') {
11743 $module.find(selector.title).html(module.helpers.escape(settings.title, settings.preserveHTML)).addClass(settings.classTitle);
11744 }
11745 if (settings.content !== '') {
11746 $module.find(selector.content).html(module.helpers.escape(settings.content, settings.preserveHTML)).addClass(settings.classContent);
11747 }
11748 if(module.has.configActions()){
11749 var $actions = $module.find(selector.actions).addClass(settings.classActions);
11750 if ($actions.length === 0) {
11751 $actions = $('<div/>', {class: className.actions + ' ' + (settings.classActions || '')}).appendTo($module);
11752 } else {
11753 $actions.empty();
11754 }
11755 settings.actions.forEach(function (el) {
11756 var icon = el[fields.icon] ? '<i class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>' : '',
11757 text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
11758 cls = module.helpers.deQuote(el[fields.class] || ''),
11759 click = el[fields.click] && $.isFunction(el[fields.click]) ? el[fields.click] : function () {};
11760 $actions.append($('<button/>', {
11761 html: icon + text,
11762 class: className.button + ' ' + cls,
11763 click: function () {
11764 if (click.call(element, $module) === false) {
11765 return;
11766 }
11767 module.hide();
11768 }
11769 }));
11770 });
11771 }
11772 module.cache = {};
11773 module.verbose('Initializing dimmer', $context);
11774
11775 module.create.id();
11776 module.create.dimmer();
11777
11778 if ( settings.allowMultiple ) {
11779 module.create.innerDimmer();
11780 }
11781 if (!settings.centered){
11782 $module.addClass('top aligned');
11783 }
11784 module.refreshModals();
11785
11786 module.bind.events();
11787 if(settings.observeChanges) {
11788 module.observeChanges();
11789 }
11790 module.instantiate();
11791 if(settings.autoShow){
11792 module.show();
11793 }
11794 },
11795
11796 instantiate: function() {
11797 module.verbose('Storing instance of modal');
11798 instance = module;
11799 $module
11800 .data(moduleNamespace, instance)
11801 ;
11802 },
11803
11804 create: {
11805 modal: function() {
11806 $module = $('<div/>', {class: className.modal});
11807 if (settings.closeIcon) {
11808 $close = $('<i/>', {class: className.close})
11809 $module.append($close);
11810 }
11811 if (settings.title !== '') {
11812 $('<div/>', {class: className.title}).appendTo($module);
11813 }
11814 if (settings.content !== '') {
11815 $('<div/>', {class: className.content}).appendTo($module);
11816 }
11817 if (module.has.configActions()) {
11818 $('<div/>', {class: className.actions}).appendTo($module);
11819 }
11820 $context.append($module);
11821 },
11822 dimmer: function() {
11823 var
11824 defaultSettings = {
11825 debug : settings.debug,
11826 dimmerName : 'modals'
11827 },
11828 dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
11829 ;
11830 if($.fn.dimmer === undefined) {
11831 module.error(error.dimmer);
11832 return;
11833 }
11834 module.debug('Creating dimmer');
11835 $dimmable = $context.dimmer(dimmerSettings);
11836 if(settings.detachable) {
11837 module.verbose('Modal is detachable, moving content into dimmer');
11838 $dimmable.dimmer('add content', $module);
11839 }
11840 else {
11841 module.set.undetached();
11842 }
11843 $dimmer = $dimmable.dimmer('get dimmer');
11844 },
11845 id: function() {
11846 id = (Math.random().toString(16) + '000000000').substr(2, 8);
11847 elementEventNamespace = '.' + id;
11848 module.verbose('Creating unique id for element', id);
11849 },
11850 innerDimmer: function() {
11851 if ( $module.find(selector.dimmer).length == 0 ) {
11852 $module.prepend('<div class="ui inverted dimmer"></div>');
11853 }
11854 }
11855 },
11856
11857 destroy: function() {
11858 if (observer) {
11859 observer.disconnect();
11860 }
11861 module.verbose('Destroying previous modal');
11862 $module
11863 .removeData(moduleNamespace)
11864 .off(eventNamespace)
11865 ;
11866 $window.off(elementEventNamespace);
11867 $dimmer.off(elementEventNamespace);
11868 $close.off(eventNamespace);
11869 $context.dimmer('destroy');
11870 },
11871
11872 observeChanges: function() {
11873 if('MutationObserver' in window) {
11874 observer = new MutationObserver(function(mutations) {
11875 module.debug('DOM tree modified, refreshing');
11876 module.refresh();
11877 });
11878 observer.observe(element, {
11879 childList : true,
11880 subtree : true
11881 });
11882 module.debug('Setting up mutation observer', observer);
11883 }
11884 },
11885
11886 refresh: function() {
11887 module.remove.scrolling();
11888 module.cacheSizes();
11889 if(!module.can.useFlex()) {
11890 module.set.modalOffset();
11891 }
11892 module.set.screenHeight();
11893 module.set.type();
11894 },
11895
11896 refreshModals: function() {
11897 $otherModals = $module.siblings(selector.modal);
11898 $allModals = $otherModals.add($module);
11899 },
11900
11901 attachEvents: function(selector, event) {
11902 var
11903 $toggle = $(selector)
11904 ;
11905 event = $.isFunction(module[event])
11906 ? module[event]
11907 : module.toggle
11908 ;
11909 if($toggle.length > 0) {
11910 module.debug('Attaching modal events to element', selector, event);
11911 $toggle
11912 .off(eventNamespace)
11913 .on('click' + eventNamespace, event)
11914 ;
11915 }
11916 else {
11917 module.error(error.notFound, selector);
11918 }
11919 },
11920
11921 bind: {
11922 events: function() {
11923 module.verbose('Attaching events');
11924 $module
11925 .on('click' + eventNamespace, selector.close, module.event.close)
11926 .on('click' + eventNamespace, selector.approve, module.event.approve)
11927 .on('click' + eventNamespace, selector.deny, module.event.deny)
11928 ;
11929 $window
11930 .on('resize' + elementEventNamespace, module.event.resize)
11931 ;
11932 },
11933 scrollLock: function() {
11934 // touch events default to passive, due to changes in chrome to optimize mobile perf
11935 $dimmable.get(0).addEventListener('touchmove', module.event.preventScroll, { passive: false });
11936 }
11937 },
11938
11939 unbind: {
11940 scrollLock: function() {
11941 $dimmable.get(0).removeEventListener('touchmove', module.event.preventScroll, { passive: false });
11942 }
11943 },
11944
11945 get: {
11946 id: function() {
11947 return (Math.random().toString(16) + '000000000').substr(2, 8);
11948 },
11949 element: function() {
11950 return $module;
11951 },
11952 settings: function() {
11953 return settings;
11954 }
11955 },
11956
11957 event: {
11958 approve: function() {
11959 if(ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
11960 module.verbose('Approve callback returned false cancelling hide');
11961 return;
11962 }
11963 ignoreRepeatedEvents = true;
11964 module.hide(function() {
11965 ignoreRepeatedEvents = false;
11966 });
11967 },
11968 preventScroll: function(event) {
11969 if(event.target.className.indexOf('dimmer') !== -1) {
11970 event.preventDefault();
11971 }
11972 },
11973 deny: function() {
11974 if(ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
11975 module.verbose('Deny callback returned false cancelling hide');
11976 return;
11977 }
11978 ignoreRepeatedEvents = true;
11979 module.hide(function() {
11980 ignoreRepeatedEvents = false;
11981 });
11982 },
11983 close: function() {
11984 module.hide();
11985 },
11986 mousedown: function(event) {
11987 var
11988 $target = $(event.target),
11989 isRtl = module.is.rtl();
11990 ;
11991 initialMouseDownInModal = ($target.closest(selector.modal).length > 0);
11992 if(initialMouseDownInModal) {
11993 module.verbose('Mouse down event registered inside the modal');
11994 }
11995 initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $(window).outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
11996 if(initialMouseDownInScrollbar) {
11997 module.verbose('Mouse down event registered inside the scrollbar');
11998 }
11999 },
12000 mouseup: function(event) {
12001 if(!settings.closable) {
12002 module.verbose('Dimmer clicked but closable setting is disabled');
12003 return;
12004 }
12005 if(initialMouseDownInModal) {
12006 module.debug('Dimmer clicked but mouse down was initially registered inside the modal');
12007 return;
12008 }
12009 if(initialMouseDownInScrollbar){
12010 module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');
12011 return;
12012 }
12013 var
12014 $target = $(event.target),
12015 isInModal = ($target.closest(selector.modal).length > 0),
12016 isInDOM = $.contains(document.documentElement, event.target)
12017 ;
12018 if(!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front) ) {
12019 module.debug('Dimmer clicked, hiding all modals');
12020 if(settings.allowMultiple) {
12021 if(!module.hideAll()) {
12022 return;
12023 }
12024 }
12025 else if(!module.hide()){
12026 return;
12027 }
12028 module.remove.clickaway();
12029 }
12030 },
12031 debounce: function(method, delay) {
12032 clearTimeout(module.timer);
12033 module.timer = setTimeout(method, delay);
12034 },
12035 keyboard: function(event) {
12036 var
12037 keyCode = event.which,
12038 escapeKey = 27
12039 ;
12040 if(keyCode == escapeKey) {
12041 if(settings.closable) {
12042 module.debug('Escape key pressed hiding modal');
12043 if ( $module.hasClass(className.front) ) {
12044 module.hide();
12045 }
12046 }
12047 else {
12048 module.debug('Escape key pressed, but closable is set to false');
12049 }
12050 event.preventDefault();
12051 }
12052 },
12053 resize: function() {
12054 if( $dimmable.dimmer('is active') && ( module.is.animating() || module.is.active() ) ) {
12055 requestAnimationFrame(module.refresh);
12056 }
12057 }
12058 },
12059
12060 toggle: function() {
12061 if( module.is.active() || module.is.animating() ) {
12062 module.hide();
12063 }
12064 else {
12065 module.show();
12066 }
12067 },
12068
12069 show: function(callback) {
12070 callback = $.isFunction(callback)
12071 ? callback
12072 : function(){}
12073 ;
12074 module.refreshModals();
12075 module.set.dimmerSettings();
12076 module.set.dimmerStyles();
12077
12078 module.showModal(callback);
12079 },
12080
12081 hide: function(callback) {
12082 callback = $.isFunction(callback)
12083 ? callback
12084 : function(){}
12085 ;
12086 module.refreshModals();
12087 return module.hideModal(callback);
12088 },
12089
12090 showModal: function(callback) {
12091 callback = $.isFunction(callback)
12092 ? callback
12093 : function(){}
12094 ;
12095 if( module.is.animating() || !module.is.active() ) {
12096 module.showDimmer();
12097 module.cacheSizes();
12098 module.set.bodyMargin();
12099 if(module.can.useFlex()) {
12100 module.remove.legacy();
12101 }
12102 else {
12103 module.set.legacy();
12104 module.set.modalOffset();
12105 module.debug('Using non-flex legacy modal positioning.');
12106 }
12107 module.set.screenHeight();
12108 module.set.type();
12109 module.set.clickaway();
12110
12111 if( !settings.allowMultiple && module.others.active() ) {
12112 module.hideOthers(module.showModal);
12113 }
12114 else {
12115 ignoreRepeatedEvents = false;
12116 if( settings.allowMultiple ) {
12117 if ( module.others.active() ) {
12118 $otherModals.filter('.' + className.active).find(selector.dimmer).addClass('active');
12119 }
12120
12121 if ( settings.detachable ) {
12122 $module.detach().appendTo($dimmer);
12123 }
12124 }
12125 settings.onShow.call(element);
12126 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
12127 module.debug('Showing modal with css animations');
12128 $module
12129 .transition({
12130 debug : settings.debug,
12131 animation : (settings.transition.showMethod || settings.transition) + ' in',
12132 queue : settings.queue,
12133 duration : settings.transition.showDuration || settings.duration,
12134 useFailSafe : true,
12135 onComplete : function() {
12136 settings.onVisible.apply(element);
12137 if(settings.keyboardShortcuts) {
12138 module.add.keyboardShortcuts();
12139 }
12140 module.save.focus();
12141 module.set.active();
12142 if(settings.autofocus) {
12143 module.set.autofocus();
12144 }
12145 callback();
12146 }
12147 })
12148 ;
12149 }
12150 else {
12151 module.error(error.noTransition);
12152 }
12153 }
12154 }
12155 else {
12156 module.debug('Modal is already visible');
12157 }
12158 },
12159
12160 hideModal: function(callback, keepDimmed, hideOthersToo) {
12161 var
12162 $previousModal = $otherModals.filter('.' + className.active).last()
12163 ;
12164 callback = $.isFunction(callback)
12165 ? callback
12166 : function(){}
12167 ;
12168 module.debug('Hiding modal');
12169 if(settings.onHide.call(element, $(this)) === false) {
12170 module.verbose('Hide callback returned false cancelling hide');
12171 ignoreRepeatedEvents = false;
12172 return false;
12173 }
12174
12175 if( module.is.animating() || module.is.active() ) {
12176 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
12177 module.remove.active();
12178 $module
12179 .transition({
12180 debug : settings.debug,
12181 animation : (settings.transition.hideMethod || settings.transition) + ' out',
12182 queue : settings.queue,
12183 duration : settings.transition.hideDuration || settings.duration,
12184 useFailSafe : true,
12185 onStart : function() {
12186 if(!module.others.active() && !module.others.animating() && !keepDimmed) {
12187 module.hideDimmer();
12188 }
12189 if( settings.keyboardShortcuts && !module.others.active() ) {
12190 module.remove.keyboardShortcuts();
12191 }
12192 },
12193 onComplete : function() {
12194 module.unbind.scrollLock();
12195 if ( settings.allowMultiple ) {
12196 $previousModal.addClass(className.front);
12197 $module.removeClass(className.front);
12198
12199 if ( hideOthersToo ) {
12200 $allModals.find(selector.dimmer).removeClass('active');
12201 }
12202 else {
12203 $previousModal.find(selector.dimmer).removeClass('active');
12204 }
12205 }
12206 if($.isFunction(settings.onHidden)) {
12207 settings.onHidden.call(element);
12208 }
12209 module.remove.dimmerStyles();
12210 module.restore.focus();
12211 callback();
12212 }
12213 })
12214 ;
12215 }
12216 else {
12217 module.error(error.noTransition);
12218 }
12219 }
12220 },
12221
12222 showDimmer: function() {
12223 if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
12224 module.save.bodyMargin();
12225 module.debug('Showing dimmer');
12226 $dimmable.dimmer('show');
12227 }
12228 else {
12229 module.debug('Dimmer already visible');
12230 }
12231 },
12232
12233 hideDimmer: function() {
12234 if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
12235 module.unbind.scrollLock();
12236 $dimmable.dimmer('hide', function() {
12237 module.restore.bodyMargin();
12238 module.remove.clickaway();
12239 module.remove.screenHeight();
12240 });
12241 }
12242 else {
12243 module.debug('Dimmer is not visible cannot hide');
12244 return;
12245 }
12246 },
12247
12248 hideAll: function(callback) {
12249 var
12250 $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
12251 ;
12252 callback = $.isFunction(callback)
12253 ? callback
12254 : function(){}
12255 ;
12256 if( $visibleModals.length > 0 ) {
12257 module.debug('Hiding all visible modals');
12258 var hideOk = true;
12259//check in reverse order trying to hide most top displayed modal first
12260 $($visibleModals.get().reverse()).each(function(index,element){
12261 if(hideOk){
12262 hideOk = $(element).modal('hide modal', callback, false, true);
12263 }
12264 });
12265 if(hideOk) {
12266 module.hideDimmer();
12267 }
12268 return hideOk;
12269 }
12270 },
12271
12272 hideOthers: function(callback) {
12273 var
12274 $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
12275 ;
12276 callback = $.isFunction(callback)
12277 ? callback
12278 : function(){}
12279 ;
12280 if( $visibleModals.length > 0 ) {
12281 module.debug('Hiding other modals', $otherModals);
12282 $visibleModals
12283 .modal('hide modal', callback, true)
12284 ;
12285 }
12286 },
12287
12288 others: {
12289 active: function() {
12290 return ($otherModals.filter('.' + className.active).length > 0);
12291 },
12292 animating: function() {
12293 return ($otherModals.filter('.' + className.animating).length > 0);
12294 }
12295 },
12296
12297
12298 add: {
12299 keyboardShortcuts: function() {
12300 module.verbose('Adding keyboard shortcuts');
12301 $document
12302 .on('keyup' + eventNamespace, module.event.keyboard)
12303 ;
12304 }
12305 },
12306
12307 save: {
12308 focus: function() {
12309 var
12310 $activeElement = $(document.activeElement),
12311 inCurrentModal = $activeElement.closest($module).length > 0
12312 ;
12313 if(!inCurrentModal) {
12314 $focusedElement = $(document.activeElement).blur();
12315 }
12316 },
12317 bodyMargin: function() {
12318 initialBodyMargin = $body.css('margin-'+(module.can.leftBodyScrollbar() ? 'left':'right'));
12319 var bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, '')),
12320 bodyScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
12321 tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
12322 }
12323 },
12324
12325 restore: {
12326 focus: function() {
12327 if($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
12328 $focusedElement.focus();
12329 }
12330 },
12331 bodyMargin: function() {
12332 var position = module.can.leftBodyScrollbar() ? 'left':'right';
12333 $body.css('margin-'+position, initialBodyMargin);
12334 $body.find(selector.bodyFixed.replace('right',position)).each(function(){
12335 var el = $(this),
12336 attribute = el.css('position') === 'fixed' ? 'padding-'+position : position
12337 ;
12338 el.css(attribute, '');
12339 });
12340 }
12341 },
12342
12343 remove: {
12344 active: function() {
12345 $module.removeClass(className.active);
12346 },
12347 legacy: function() {
12348 $module.removeClass(className.legacy);
12349 },
12350 clickaway: function() {
12351 if (!settings.detachable) {
12352 $module
12353 .off('mousedown' + elementEventNamespace)
12354 ;
12355 }
12356 $dimmer
12357 .off('mousedown' + elementEventNamespace)
12358 ;
12359 $dimmer
12360 .off('mouseup' + elementEventNamespace)
12361 ;
12362 },
12363 dimmerStyles: function() {
12364 $dimmer.removeClass(className.inverted);
12365 $dimmable.removeClass(className.blurring);
12366 },
12367 bodyStyle: function() {
12368 if($body.attr('style') === '') {
12369 module.verbose('Removing style attribute');
12370 $body.removeAttr('style');
12371 }
12372 },
12373 screenHeight: function() {
12374 module.debug('Removing page height');
12375 $body
12376 .css('height', '')
12377 ;
12378 },
12379 keyboardShortcuts: function() {
12380 module.verbose('Removing keyboard shortcuts');
12381 $document
12382 .off('keyup' + eventNamespace)
12383 ;
12384 },
12385 scrolling: function() {
12386 $dimmable.removeClass(className.scrolling);
12387 $module.removeClass(className.scrolling);
12388 }
12389 },
12390
12391 cacheSizes: function() {
12392 $module.addClass(className.loading);
12393 var
12394 scrollHeight = $module.prop('scrollHeight'),
12395 modalWidth = $module.outerWidth(),
12396 modalHeight = $module.outerHeight()
12397 ;
12398 if(module.cache.pageHeight === undefined || modalHeight !== 0) {
12399 $.extend(module.cache, {
12400 pageHeight : $(document).outerHeight(),
12401 width : modalWidth,
12402 height : modalHeight + settings.offset,
12403 scrollHeight : scrollHeight + settings.offset,
12404 contextHeight : (settings.context == 'body')
12405 ? $(window).height()
12406 : $dimmable.height(),
12407 });
12408 module.cache.topOffset = -(module.cache.height / 2);
12409 }
12410 $module.removeClass(className.loading);
12411 module.debug('Caching modal and container sizes', module.cache);
12412 },
12413 helpers: {
12414 deQuote: function(string) {
12415 return String(string).replace(/"/g,"");
12416 },
12417 escape: function(string, preserveHTML) {
12418 if (preserveHTML){
12419 return string;
12420 }
12421 var
12422 badChars = /[<>"'`]/g,
12423 shouldEscape = /[&<>"'`]/,
12424 escape = {
12425 "<": "&lt;",
12426 ">": "&gt;",
12427 '"': "&quot;",
12428 "'": "&#x27;",
12429 "`": "&#x60;"
12430 },
12431 escapedChar = function(chr) {
12432 return escape[chr];
12433 }
12434 ;
12435 if(shouldEscape.test(string)) {
12436 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
12437 return string.replace(badChars, escapedChar);
12438 }
12439 return string;
12440 }
12441 },
12442 can: {
12443 leftBodyScrollbar: function(){
12444 if(module.cache.leftBodyScrollbar === undefined) {
12445 module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
12446 }
12447 return module.cache.leftBodyScrollbar;
12448 },
12449 useFlex: function() {
12450 if (settings.useFlex === 'auto') {
12451 return settings.detachable && !module.is.ie();
12452 }
12453 if(settings.useFlex && module.is.ie()) {
12454 module.debug('useFlex true is not supported in IE');
12455 } else if(settings.useFlex && !settings.detachable) {
12456 module.debug('useFlex true in combination with detachable false is not supported');
12457 }
12458 return settings.useFlex;
12459 },
12460 fit: function() {
12461 var
12462 contextHeight = module.cache.contextHeight,
12463 verticalCenter = module.cache.contextHeight / 2,
12464 topOffset = module.cache.topOffset,
12465 scrollHeight = module.cache.scrollHeight,
12466 height = module.cache.height,
12467 paddingHeight = settings.padding,
12468 startPosition = (verticalCenter + topOffset)
12469 ;
12470 return (scrollHeight > height)
12471 ? (startPosition + scrollHeight + paddingHeight < contextHeight)
12472 : (height + (paddingHeight * 2) < contextHeight)
12473 ;
12474 }
12475 },
12476 has: {
12477 configActions: function () {
12478 return Array.isArray(settings.actions) && settings.actions.length > 0;
12479 }
12480 },
12481 is: {
12482 active: function() {
12483 return $module.hasClass(className.active);
12484 },
12485 ie: function() {
12486 if(module.cache.isIE === undefined) {
12487 var
12488 isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
12489 isIE = ('ActiveXObject' in window)
12490 ;
12491 module.cache.isIE = (isIE11 || isIE);
12492 }
12493 return module.cache.isIE;
12494 },
12495 animating: function() {
12496 return $module.transition('is supported')
12497 ? $module.transition('is animating')
12498 : $module.is(':visible')
12499 ;
12500 },
12501 scrolling: function() {
12502 return $dimmable.hasClass(className.scrolling);
12503 },
12504 modernBrowser: function() {
12505 // appName for IE11 reports 'Netscape' can no longer use
12506 return !(window.ActiveXObject || 'ActiveXObject' in window);
12507 },
12508 rtl: function() {
12509 if(module.cache.isRTL === undefined) {
12510 module.cache.isRTL = $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl';
12511 }
12512 return module.cache.isRTL;
12513 },
12514 safari: function() {
12515 if(module.cache.isSafari === undefined) {
12516 module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
12517 }
12518 return module.cache.isSafari;
12519 },
12520 edge: function(){
12521 if(module.cache.isEdge === undefined) {
12522 module.cache.isEdge = !!window.setImmediate && !module.is.ie();
12523 }
12524 return module.cache.isEdge;
12525 },
12526 firefox: function(){
12527 if(module.cache.isFirefox === undefined) {
12528 module.cache.isFirefox = !!window.InstallTrigger;
12529 }
12530 return module.cache.isFirefox;
12531 },
12532 iframe: function() {
12533 return !(self === top);
12534 }
12535 },
12536
12537 set: {
12538 autofocus: function() {
12539 var
12540 $inputs = $module.find('[tabindex], :input').filter(':visible').filter(function() {
12541 return $(this).closest('.disabled').length === 0;
12542 }),
12543 $autofocus = $inputs.filter('[autofocus]'),
12544 $input = ($autofocus.length > 0)
12545 ? $autofocus.first()
12546 : $inputs.first()
12547 ;
12548 if($input.length > 0) {
12549 $input.focus();
12550 }
12551 },
12552 bodyMargin: function() {
12553 var position = module.can.leftBodyScrollbar() ? 'left':'right';
12554 if(settings.detachable || module.can.fit()) {
12555 $body.css('margin-'+position, tempBodyMargin + 'px');
12556 }
12557 $body.find(selector.bodyFixed.replace('right',position)).each(function(){
12558 var el = $(this),
12559 attribute = el.css('position') === 'fixed' ? 'padding-'+position : position
12560 ;
12561 el.css(attribute, 'calc(' + el.css(attribute) + ' + ' + tempBodyMargin + 'px)');
12562 });
12563 },
12564 clickaway: function() {
12565 if (!settings.detachable) {
12566 $module
12567 .on('mousedown' + elementEventNamespace, module.event.mousedown)
12568 ;
12569 }
12570 $dimmer
12571 .on('mousedown' + elementEventNamespace, module.event.mousedown)
12572 ;
12573 $dimmer
12574 .on('mouseup' + elementEventNamespace, module.event.mouseup)
12575 ;
12576 },
12577 dimmerSettings: function() {
12578 if($.fn.dimmer === undefined) {
12579 module.error(error.dimmer);
12580 return;
12581 }
12582 var
12583 defaultSettings = {
12584 debug : settings.debug,
12585 dimmerName : 'modals',
12586 closable : 'auto',
12587 useFlex : module.can.useFlex(),
12588 duration : {
12589 show : settings.transition.showDuration || settings.duration,
12590 hide : settings.transition.hideDuration || settings.duration
12591 }
12592 },
12593 dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
12594 ;
12595 if(settings.inverted) {
12596 dimmerSettings.variation = (dimmerSettings.variation !== undefined)
12597 ? dimmerSettings.variation + ' inverted'
12598 : 'inverted'
12599 ;
12600 }
12601 $context.dimmer('setting', dimmerSettings);
12602 },
12603 dimmerStyles: function() {
12604 if(settings.inverted) {
12605 $dimmer.addClass(className.inverted);
12606 }
12607 else {
12608 $dimmer.removeClass(className.inverted);
12609 }
12610 if(settings.blurring) {
12611 $dimmable.addClass(className.blurring);
12612 }
12613 else {
12614 $dimmable.removeClass(className.blurring);
12615 }
12616 },
12617 modalOffset: function() {
12618 if (!settings.detachable) {
12619 var canFit = module.can.fit();
12620 $module
12621 .css({
12622 top: (!$module.hasClass('aligned') && canFit)
12623 ? $(document).scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
12624 : !canFit || $module.hasClass('top')
12625 ? $(document).scrollTop() + settings.padding
12626 : $(document).scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding),
12627 marginLeft: -(module.cache.width / 2)
12628 })
12629 ;
12630 } else {
12631 $module
12632 .css({
12633 marginTop: (!$module.hasClass('aligned') && module.can.fit())
12634 ? -(module.cache.height / 2)
12635 : settings.padding / 2,
12636 marginLeft: -(module.cache.width / 2)
12637 })
12638 ;
12639 }
12640 module.verbose('Setting modal offset for legacy mode');
12641 },
12642 screenHeight: function() {
12643 if( module.can.fit() ) {
12644 $body.css('height', '');
12645 }
12646 else if(!$module.hasClass('bottom')) {
12647 module.debug('Modal is taller than page content, resizing page height');
12648 $body
12649 .css('height', module.cache.height + (settings.padding * 2) )
12650 ;
12651 }
12652 },
12653 active: function() {
12654 $module.addClass(className.active + ' ' + className.front);
12655 $otherModals.filter('.' + className.active).removeClass(className.front);
12656 },
12657 scrolling: function() {
12658 $dimmable.addClass(className.scrolling);
12659 $module.addClass(className.scrolling);
12660 module.unbind.scrollLock();
12661 },
12662 legacy: function() {
12663 $module.addClass(className.legacy);
12664 },
12665 type: function() {
12666 if(module.can.fit()) {
12667 module.verbose('Modal fits on screen');
12668 if(!module.others.active() && !module.others.animating()) {
12669 module.remove.scrolling();
12670 module.bind.scrollLock();
12671 }
12672 }
12673 else if (!$module.hasClass('bottom')){
12674 module.verbose('Modal cannot fit on screen setting to scrolling');
12675 module.set.scrolling();
12676 } else {
12677 module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
12678 }
12679 },
12680 undetached: function() {
12681 $dimmable.addClass(className.undetached);
12682 }
12683 },
12684
12685 setting: function(name, value) {
12686 module.debug('Changing setting', name, value);
12687 if( $.isPlainObject(name) ) {
12688 $.extend(true, settings, name);
12689 }
12690 else if(value !== undefined) {
12691 if($.isPlainObject(settings[name])) {
12692 $.extend(true, settings[name], value);
12693 }
12694 else {
12695 settings[name] = value;
12696 }
12697 }
12698 else {
12699 return settings[name];
12700 }
12701 },
12702 internal: function(name, value) {
12703 if( $.isPlainObject(name) ) {
12704 $.extend(true, module, name);
12705 }
12706 else if(value !== undefined) {
12707 module[name] = value;
12708 }
12709 else {
12710 return module[name];
12711 }
12712 },
12713 debug: function() {
12714 if(!settings.silent && settings.debug) {
12715 if(settings.performance) {
12716 module.performance.log(arguments);
12717 }
12718 else {
12719 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
12720 module.debug.apply(console, arguments);
12721 }
12722 }
12723 },
12724 verbose: function() {
12725 if(!settings.silent && settings.verbose && settings.debug) {
12726 if(settings.performance) {
12727 module.performance.log(arguments);
12728 }
12729 else {
12730 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
12731 module.verbose.apply(console, arguments);
12732 }
12733 }
12734 },
12735 error: function() {
12736 if(!settings.silent) {
12737 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
12738 module.error.apply(console, arguments);
12739 }
12740 },
12741 performance: {
12742 log: function(message) {
12743 var
12744 currentTime,
12745 executionTime,
12746 previousTime
12747 ;
12748 if(settings.performance) {
12749 currentTime = new Date().getTime();
12750 previousTime = time || currentTime;
12751 executionTime = currentTime - previousTime;
12752 time = currentTime;
12753 performance.push({
12754 'Name' : message[0],
12755 'Arguments' : [].slice.call(message, 1) || '',
12756 'Element' : element,
12757 'Execution Time' : executionTime
12758 });
12759 }
12760 clearTimeout(module.performance.timer);
12761 module.performance.timer = setTimeout(module.performance.display, 500);
12762 },
12763 display: function() {
12764 var
12765 title = settings.name + ':',
12766 totalTime = 0
12767 ;
12768 time = false;
12769 clearTimeout(module.performance.timer);
12770 $.each(performance, function(index, data) {
12771 totalTime += data['Execution Time'];
12772 });
12773 title += ' ' + totalTime + 'ms';
12774 if(moduleSelector) {
12775 title += ' \'' + moduleSelector + '\'';
12776 }
12777 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
12778 console.groupCollapsed(title);
12779 if(console.table) {
12780 console.table(performance);
12781 }
12782 else {
12783 $.each(performance, function(index, data) {
12784 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
12785 });
12786 }
12787 console.groupEnd();
12788 }
12789 performance = [];
12790 }
12791 },
12792 invoke: function(query, passedArguments, context) {
12793 var
12794 object = instance,
12795 maxDepth,
12796 found,
12797 response
12798 ;
12799 passedArguments = passedArguments || queryArguments;
12800 context = element || context;
12801 if(typeof query == 'string' && object !== undefined) {
12802 query = query.split(/[\. ]/);
12803 maxDepth = query.length - 1;
12804 $.each(query, function(depth, value) {
12805 var camelCaseValue = (depth != maxDepth)
12806 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
12807 : query
12808 ;
12809 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
12810 object = object[camelCaseValue];
12811 }
12812 else if( object[camelCaseValue] !== undefined ) {
12813 found = object[camelCaseValue];
12814 return false;
12815 }
12816 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
12817 object = object[value];
12818 }
12819 else if( object[value] !== undefined ) {
12820 found = object[value];
12821 return false;
12822 }
12823 else {
12824 return false;
12825 }
12826 });
12827 }
12828 if ( $.isFunction( found ) ) {
12829 response = found.apply(context, passedArguments);
12830 }
12831 else if(found !== undefined) {
12832 response = found;
12833 }
12834 if(Array.isArray(returnedValue)) {
12835 returnedValue.push(response);
12836 }
12837 else if(returnedValue !== undefined) {
12838 returnedValue = [returnedValue, response];
12839 }
12840 else if(response !== undefined) {
12841 returnedValue = response;
12842 }
12843 return found;
12844 }
12845 };
12846
12847 if(methodInvoked) {
12848 if(instance === undefined) {
12849 if ($.isFunction(settings.templates[query])) {
12850 settings.autoShow = true;
12851 settings.className.modal = settings.className.template;
12852 settings = $.extend(true, {}, settings, settings.templates[query].apply(module ,queryArguments));
12853
12854 // reassign shortcuts
12855 className = settings.className;
12856 namespace = settings.namespace;
12857 fields = settings.fields;
12858 error = settings.error;
12859 }
12860 module.initialize();
12861 }
12862 if (!$.isFunction(settings.templates[query])) {
12863 module.invoke(query);
12864 }
12865 }
12866 else {
12867 if(instance !== undefined) {
12868 instance.invoke('destroy');
12869 }
12870 module.initialize();
12871 returnedValue = $module;
12872 }
12873 })
12874 ;
12875
12876 return (returnedValue !== undefined)
12877 ? returnedValue
12878 : this
12879 ;
12880};
12881
12882$.fn.modal.settings = {
12883
12884 name : 'Modal',
12885 namespace : 'modal',
12886
12887 useFlex : 'auto',
12888 offset : 0,
12889
12890 silent : false,
12891 debug : false,
12892 verbose : false,
12893 performance : true,
12894
12895 observeChanges : false,
12896
12897 allowMultiple : false,
12898 detachable : true,
12899 closable : true,
12900 autofocus : true,
12901 restoreFocus : true,
12902 autoShow : false,
12903
12904 inverted : false,
12905 blurring : false,
12906
12907 centered : true,
12908
12909 dimmerSettings : {
12910 closable : false,
12911 useCSS : true
12912 },
12913
12914 // whether to use keyboard shortcuts
12915 keyboardShortcuts: true,
12916
12917 context : 'body',
12918
12919 queue : false,
12920 duration : 500,
12921 transition : 'scale',
12922
12923 // padding with edge of page
12924 padding : 50,
12925 scrollbarWidth: 10,
12926
12927 //dynamic content
12928 title : '',
12929 content : '',
12930 class : '',
12931 classTitle : '',
12932 classContent : '',
12933 classActions : '',
12934 closeIcon : false,
12935 actions : false,
12936 preserveHTML : true,
12937
12938 fields : {
12939 class : 'class',
12940 text : 'text',
12941 icon : 'icon',
12942 click : 'click'
12943 },
12944
12945 // called before show animation
12946 onShow : function(){},
12947
12948 // called after show animation
12949 onVisible : function(){},
12950
12951 // called before hide animation
12952 onHide : function(){ return true; },
12953
12954 // called after hide animation
12955 onHidden : false,
12956
12957 // called after approve selector match
12958 onApprove : function(){ return true; },
12959
12960 // called after deny selector match
12961 onDeny : function(){ return true; },
12962
12963 selector : {
12964 title : '> .header',
12965 content : '> .content',
12966 actions : '> .actions',
12967 close : '> .close',
12968 approve : '.actions .positive, .actions .approve, .actions .ok',
12969 deny : '.actions .negative, .actions .deny, .actions .cancel',
12970 modal : '.ui.modal',
12971 dimmer : '> .ui.dimmer',
12972 bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar, > .ui.fixed.nag, > .ui.fixed.nag > .close',
12973 prompt : '.ui.input > input'
12974 },
12975 error : {
12976 dimmer : 'UI Dimmer, a required component is not included in this page',
12977 method : 'The method you called is not defined.',
12978 notFound : 'The element you specified could not be found'
12979 },
12980 className : {
12981 active : 'active',
12982 animating : 'animating',
12983 blurring : 'blurring',
12984 inverted : 'inverted',
12985 legacy : 'legacy',
12986 loading : 'loading',
12987 scrolling : 'scrolling',
12988 undetached : 'undetached',
12989 front : 'front',
12990 close : 'close icon',
12991 button : 'ui button',
12992 modal : 'ui modal',
12993 title : 'header',
12994 content : 'content',
12995 actions : 'actions',
12996 template : 'ui tiny modal',
12997 ok : 'positive',
12998 cancel : 'negative',
12999 prompt : 'ui fluid input'
13000 },
13001 text: {
13002 ok : 'Ok',
13003 cancel: 'Cancel'
13004 }
13005};
13006
13007$.fn.modal.settings.templates = {
13008 getArguments: function(args) {
13009 var queryArguments = [].slice.call(args);
13010 if($.isPlainObject(queryArguments[0])){
13011 return $.extend({
13012 handler:function(){},
13013 content:'',
13014 title: ''
13015 }, queryArguments[0]);
13016 } else {
13017 if(!$.isFunction(queryArguments[queryArguments.length-1])) {
13018 queryArguments.push(function() {});
13019 }
13020 return {
13021 handler: queryArguments.pop(),
13022 content: queryArguments.pop() || '',
13023 title: queryArguments.pop() || ''
13024 };
13025 }
13026 },
13027 alert: function () {
13028 var settings = this.get.settings(),
13029 args = settings.templates.getArguments(arguments)
13030 ;
13031 return {
13032 title : args.title,
13033 content: args.content,
13034 actions: [{
13035 text : settings.text.ok,
13036 class: settings.className.ok,
13037 click: args.handler
13038 }]
13039 }
13040 },
13041 confirm: function () {
13042 var settings = this.get.settings(),
13043 args = settings.templates.getArguments(arguments)
13044 ;
13045 return {
13046 title : args.title,
13047 content: args.content,
13048 actions: [{
13049 text : settings.text.ok,
13050 class: settings.className.ok,
13051 click: function(){args.handler(true)}
13052 },{
13053 text: settings.text.cancel,
13054 class: settings.className.cancel,
13055 click: function(){args.handler(false)}
13056 }]
13057 }
13058 },
13059 prompt: function () {
13060 var $this = this,
13061 settings = this.get.settings(),
13062 args = settings.templates.getArguments(arguments),
13063 input = $($.parseHTML(args.content)).filter('.ui.input')
13064 ;
13065 if (input.length === 0) {
13066 args.content += '<p><div class="'+settings.className.prompt+'"><input placeholder="'+this.helpers.deQuote(args.placeholder || '')+'" type="text" value="'+this.helpers.deQuote(args.defaultValue || '')+'"></div></p>';
13067 }
13068 return {
13069 title : args.title,
13070 content: args.content,
13071 actions: [{
13072 text: settings.text.ok,
13073 class: settings.className.ok,
13074 click: function(){
13075 var settings = $this.get.settings(),
13076 inputField = $this.get.element().find(settings.selector.prompt)[0]
13077 ;
13078 args.handler($(inputField).val());
13079 }
13080 },{
13081 text: settings.text.cancel,
13082 class: settings.className.cancel,
13083 click: function(){args.handler(null)}
13084 }]
13085 }
13086 }
13087}
13088
13089})( jQuery, window, document );
13090
13091/*!
13092 * # Fomantic-UI 2.8.8 - Nag
13093 * http://github.com/fomantic/Fomantic-UI/
13094 *
13095 *
13096 * Released under the MIT license
13097 * http://opensource.org/licenses/MIT
13098 *
13099 */
13100
13101;(function ($, window, document, undefined) {
13102
13103'use strict';
13104
13105$.isFunction = $.isFunction || function(obj) {
13106 return typeof obj === "function" && typeof obj.nodeType !== "number";
13107};
13108
13109window = (typeof window != 'undefined' && window.Math == Math)
13110 ? window
13111 : (typeof self != 'undefined' && self.Math == Math)
13112 ? self
13113 : Function('return this')()
13114;
13115
13116$.fn.nag = function(parameters) {
13117 var
13118 $allModules = $(this),
13119 moduleSelector = $allModules.selector || '',
13120
13121 time = new Date().getTime(),
13122 performance = [],
13123
13124 query = arguments[0],
13125 methodInvoked = (typeof query == 'string'),
13126 queryArguments = [].slice.call(arguments, 1),
13127 returnedValue
13128 ;
13129 $allModules
13130 .each(function() {
13131 var
13132 settings = ( $.isPlainObject(parameters) )
13133 ? $.extend(true, {}, $.fn.nag.settings, parameters)
13134 : $.extend({}, $.fn.nag.settings),
13135
13136 selector = settings.selector,
13137 error = settings.error,
13138 namespace = settings.namespace,
13139
13140 eventNamespace = '.' + namespace,
13141 moduleNamespace = namespace + '-module',
13142
13143 $module = $(this),
13144
13145 $context = (settings.context)
13146 ? $(settings.context)
13147 : $('body'),
13148
13149 element = this,
13150 instance = $module.data(moduleNamespace),
13151 storage,
13152 module
13153 ;
13154 module = {
13155
13156 initialize: function() {
13157 module.verbose('Initializing element');
13158 storage = module.get.storage();
13159 $module
13160 .on('click' + eventNamespace, selector.close, module.dismiss)
13161 .data(moduleNamespace, module)
13162 ;
13163
13164 if(settings.detachable && $module.parent()[0] !== $context[0]) {
13165 $module
13166 .detach()
13167 .prependTo($context)
13168 ;
13169 }
13170
13171 if(settings.displayTime > 0) {
13172 setTimeout(module.hide, settings.displayTime);
13173 }
13174 module.show();
13175 },
13176
13177 destroy: function() {
13178 module.verbose('Destroying instance');
13179 $module
13180 .removeData(moduleNamespace)
13181 .off(eventNamespace)
13182 ;
13183 },
13184
13185 show: function() {
13186 if( module.should.show() && !$module.is(':visible') ) {
13187 if(settings.onShow.call(element) === false) {
13188 module.debug('onShow callback returned false, cancelling nag animation');
13189 return false;
13190 }
13191 module.debug('Showing nag', settings.animation.show);
13192 if(settings.animation.show === 'fade') {
13193 $module
13194 .fadeIn(settings.duration, settings.easing, settings.onVisible)
13195 ;
13196 }
13197 else {
13198 $module
13199 .slideDown(settings.duration, settings.easing, settings.onVisible)
13200 ;
13201 }
13202 }
13203 },
13204
13205 hide: function() {
13206 if(settings.onHide.call(element) === false) {
13207 module.debug('onHide callback returned false, cancelling nag animation');
13208 return false;
13209 }
13210 module.debug('Hiding nag', settings.animation.hide);
13211 if(settings.animation.hide === 'fade') {
13212 $module
13213 .fadeOut(settings.duration, settings.easing, settings.onHidden)
13214 ;
13215 }
13216 else {
13217 $module
13218 .slideUp(settings.duration, settings.easing, settings.onHidden)
13219 ;
13220 }
13221 },
13222
13223 dismiss: function(event) {
13224 if(module.hide() !== false && settings.storageMethod) {
13225 module.debug('Dismissing nag', settings.storageMethod, settings.key, settings.value, settings.expires);
13226 module.storage.set(settings.key, settings.value);
13227 }
13228 event.stopImmediatePropagation();
13229 event.preventDefault();
13230 },
13231
13232 should: {
13233 show: function() {
13234 if(settings.persist) {
13235 module.debug('Persistent nag is set, can show nag');
13236 return true;
13237 }
13238 if( module.storage.get(settings.key) != settings.value.toString() ) {
13239 module.debug('Stored value is not set, can show nag', module.storage.get(settings.key));
13240 return true;
13241 }
13242 module.debug('Stored value is set, cannot show nag', module.storage.get(settings.key));
13243 return false;
13244 }
13245 },
13246
13247 get: {
13248 expirationDate: function(expires) {
13249 if (typeof expires === 'number') {
13250 expires = new Date(Date.now() + expires * 864e5);
13251 }
13252 if(expires instanceof Date && expires.getTime() ){
13253 return expires.toUTCString();
13254 } else {
13255 module.error(error.expiresFormat);
13256 }
13257 },
13258 storage: function(){
13259 if(settings.storageMethod === 'localstorage' && window.localStorage !== undefined) {
13260 module.debug('Using local storage');
13261 return window.localStorage;
13262 }
13263 else if(settings.storageMethod === 'sessionstorage' && window.sessionStorage !== undefined) {
13264 module.debug('Using session storage');
13265 return window.sessionStorage;
13266 }
13267 else if("cookie" in document) {
13268 module.debug('Using cookie');
13269 return {
13270 setItem: function(key, value, options) {
13271 // RFC6265 compliant encoding
13272 key = encodeURIComponent(key)
13273 .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
13274 .replace(/[()]/g, escape);
13275 value = encodeURIComponent(value)
13276 .replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent);
13277
13278 var cookieOptions = '';
13279 for (var option in options) {
13280 if (options.hasOwnProperty(option)) {
13281 cookieOptions += '; ' + option;
13282 if (typeof options[option] === 'string') {
13283 cookieOptions += '=' + options[option].split(';')[0];
13284 }
13285 }
13286 }
13287 document.cookie = key + '=' + value + cookieOptions;
13288 },
13289 getItem: function(key) {
13290 var cookies = document.cookie.split('; ');
13291 for (var i = 0, il = cookies.length; i < il; i++) {
13292 var parts = cookies[i].split('='),
13293 foundKey = parts[0].replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
13294 if (key === foundKey) {
13295 return parts[1] || '';
13296 }
13297 }
13298 },
13299 removeItem: function(key, options) {
13300 storage.setItem(key,'',options);
13301 }
13302 };
13303 } else {
13304 module.error(error.noStorage);
13305 }
13306 },
13307 storageOptions: function() {
13308 var
13309 options = {}
13310 ;
13311 if(settings.expires) {
13312 options.expires = module.get.expirationDate(settings.expires);
13313 }
13314 if(settings.domain) {
13315 options.domain = settings.domain;
13316 }
13317 if(settings.path) {
13318 options.path = settings.path;
13319 }
13320 if(settings.secure) {
13321 options.secure = settings.secure;
13322 }
13323 if(settings.samesite) {
13324 options.samesite = settings.samesite;
13325 }
13326 return options;
13327 }
13328 },
13329
13330 clear: function() {
13331 module.storage.remove(settings.key);
13332 },
13333
13334 storage: {
13335 set: function(key, value) {
13336 var
13337 options = module.get.storageOptions()
13338 ;
13339 if(storage === window.localStorage && options.expires) {
13340 module.debug('Storing expiration value in localStorage', key, options.expires);
13341 storage.setItem(key + settings.expirationKey, options.expires );
13342 }
13343 module.debug('Value stored', key, value);
13344 try {
13345 storage.setItem(key, value, options);
13346 }
13347 catch(e) {
13348 module.error(error.setItem, e);
13349 }
13350 },
13351 get: function(key) {
13352 var
13353 storedValue
13354 ;
13355 storedValue = storage.getItem(key);
13356 if(storage === window.localStorage) {
13357 var expiration = storage.getItem(key + settings.expirationKey);
13358 if(expiration !== null && expiration !== undefined && new Date(expiration) < new Date()) {
13359 module.debug('Value in localStorage has expired. Deleting key', key);
13360 module.storage.remove(key);
13361 storedValue = null;
13362 }
13363 }
13364 if(storedValue == 'undefined' || storedValue == 'null' || storedValue === undefined || storedValue === null) {
13365 storedValue = undefined;
13366 }
13367 return storedValue;
13368 },
13369 remove: function(key) {
13370 var
13371 options = module.get.storageOptions()
13372 ;
13373 options.expires = module.get.expirationDate(-1);
13374 if(storage === window.localStorage) {
13375 storage.removeItem(key + settings.expirationKey);
13376 }
13377 storage.removeItem(key, options);
13378 }
13379 },
13380
13381 setting: function(name, value) {
13382 module.debug('Changing setting', name, value);
13383 if( $.isPlainObject(name) ) {
13384 $.extend(true, settings, name);
13385 }
13386 else if(value !== undefined) {
13387 if($.isPlainObject(settings[name])) {
13388 $.extend(true, settings[name], value);
13389 }
13390 else {
13391 settings[name] = value;
13392 }
13393 }
13394 else {
13395 return settings[name];
13396 }
13397 },
13398 internal: function(name, value) {
13399 if( $.isPlainObject(name) ) {
13400 $.extend(true, module, name);
13401 }
13402 else if(value !== undefined) {
13403 module[name] = value;
13404 }
13405 else {
13406 return module[name];
13407 }
13408 },
13409 debug: function() {
13410 if(!settings.silent && settings.debug) {
13411 if(settings.performance) {
13412 module.performance.log(arguments);
13413 }
13414 else {
13415 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
13416 module.debug.apply(console, arguments);
13417 }
13418 }
13419 },
13420 verbose: function() {
13421 if(!settings.silent && settings.verbose && settings.debug) {
13422 if(settings.performance) {
13423 module.performance.log(arguments);
13424 }
13425 else {
13426 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
13427 module.verbose.apply(console, arguments);
13428 }
13429 }
13430 },
13431 error: function() {
13432 if(!settings.silent) {
13433 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
13434 module.error.apply(console, arguments);
13435 }
13436 },
13437 performance: {
13438 log: function(message) {
13439 var
13440 currentTime,
13441 executionTime,
13442 previousTime
13443 ;
13444 if(settings.performance) {
13445 currentTime = new Date().getTime();
13446 previousTime = time || currentTime;
13447 executionTime = currentTime - previousTime;
13448 time = currentTime;
13449 performance.push({
13450 'Name' : message[0],
13451 'Arguments' : [].slice.call(message, 1) || '',
13452 'Element' : element,
13453 'Execution Time' : executionTime
13454 });
13455 }
13456 clearTimeout(module.performance.timer);
13457 module.performance.timer = setTimeout(module.performance.display, 500);
13458 },
13459 display: function() {
13460 var
13461 title = settings.name + ':',
13462 totalTime = 0
13463 ;
13464 time = false;
13465 clearTimeout(module.performance.timer);
13466 $.each(performance, function(index, data) {
13467 totalTime += data['Execution Time'];
13468 });
13469 title += ' ' + totalTime + 'ms';
13470 if(moduleSelector) {
13471 title += ' \'' + moduleSelector + '\'';
13472 }
13473 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
13474 console.groupCollapsed(title);
13475 if(console.table) {
13476 console.table(performance);
13477 }
13478 else {
13479 $.each(performance, function(index, data) {
13480 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
13481 });
13482 }
13483 console.groupEnd();
13484 }
13485 performance = [];
13486 }
13487 },
13488 invoke: function(query, passedArguments, context) {
13489 var
13490 object = instance,
13491 maxDepth,
13492 found,
13493 response
13494 ;
13495 passedArguments = passedArguments || queryArguments;
13496 context = element || context;
13497 if(typeof query == 'string' && object !== undefined) {
13498 query = query.split(/[\. ]/);
13499 maxDepth = query.length - 1;
13500 $.each(query, function(depth, value) {
13501 var camelCaseValue = (depth != maxDepth)
13502 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
13503 : query
13504 ;
13505 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
13506 object = object[camelCaseValue];
13507 }
13508 else if( object[camelCaseValue] !== undefined ) {
13509 found = object[camelCaseValue];
13510 return false;
13511 }
13512 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
13513 object = object[value];
13514 }
13515 else if( object[value] !== undefined ) {
13516 found = object[value];
13517 return false;
13518 }
13519 else {
13520 module.error(error.method, query);
13521 return false;
13522 }
13523 });
13524 }
13525 if ( $.isFunction( found ) ) {
13526 response = found.apply(context, passedArguments);
13527 }
13528 else if(found !== undefined) {
13529 response = found;
13530 }
13531 if(Array.isArray(returnedValue)) {
13532 returnedValue.push(response);
13533 }
13534 else if(returnedValue !== undefined) {
13535 returnedValue = [returnedValue, response];
13536 }
13537 else if(response !== undefined) {
13538 returnedValue = response;
13539 }
13540 return found;
13541 }
13542 };
13543
13544 if(methodInvoked) {
13545 if(instance === undefined) {
13546 module.initialize();
13547 }
13548 module.invoke(query);
13549 }
13550 else {
13551 if(instance !== undefined) {
13552 instance.invoke('destroy');
13553 }
13554 module.initialize();
13555 }
13556 })
13557 ;
13558
13559 return (returnedValue !== undefined)
13560 ? returnedValue
13561 : this
13562 ;
13563};
13564
13565$.fn.nag.settings = {
13566
13567 name : 'Nag',
13568
13569 silent : false,
13570 debug : false,
13571 verbose : false,
13572 performance : true,
13573
13574 namespace : 'Nag',
13575
13576 // allows cookie to be overridden
13577 persist : false,
13578
13579 // set to zero to require manually dismissal, otherwise hides on its own
13580 displayTime : 0,
13581
13582 animation : {
13583 show : 'slide',
13584 hide : 'slide'
13585 },
13586
13587 context : false,
13588 detachable : false,
13589
13590 expires : 30,
13591
13592// cookie storage only options
13593 domain : false,
13594 path : '/',
13595 secure : false,
13596 samesite : false,
13597
13598 // type of storage to use
13599 storageMethod : 'cookie',
13600
13601 // value to store in dismissed localstorage/cookie
13602 key : 'nag',
13603 value : 'dismiss',
13604
13605// Key suffix to support expiration in localstorage
13606 expirationKey : 'ExpirationDate',
13607
13608 error: {
13609 noStorage : 'Unsupported storage method',
13610 method : 'The method you called is not defined.',
13611 setItem : 'Unexpected error while setting value',
13612 expiresFormat : '"expires" must be a number of days or a Date Object'
13613 },
13614
13615 className : {
13616 bottom : 'bottom',
13617 fixed : 'fixed'
13618 },
13619
13620 selector : {
13621 close : '> .close.icon'
13622 },
13623
13624 duration : 500,
13625 easing : 'easeOutQuad',
13626
13627 // callback before show animation, return false to prevent show
13628 onShow : function() {},
13629
13630 // called after show animation
13631 onVisible : function() {},
13632
13633 // callback before hide animation, return false to prevent hide
13634 onHide : function() {},
13635
13636 // callback after hide animation
13637 onHidden : function() {}
13638
13639};
13640
13641// Adds easing
13642$.extend( $.easing, {
13643 easeOutQuad: function (x, t, b, c, d) {
13644 return -c *(t/=d)*(t-2) + b;
13645 }
13646});
13647
13648})( jQuery, window, document );
13649
13650/*!
13651 * # Fomantic-UI 2.8.8 - Popup
13652 * http://github.com/fomantic/Fomantic-UI/
13653 *
13654 *
13655 * Released under the MIT license
13656 * http://opensource.org/licenses/MIT
13657 *
13658 */
13659
13660;(function ($, window, document, undefined) {
13661
13662'use strict';
13663
13664$.isFunction = $.isFunction || function(obj) {
13665 return typeof obj === "function" && typeof obj.nodeType !== "number";
13666};
13667
13668window = (typeof window != 'undefined' && window.Math == Math)
13669 ? window
13670 : (typeof self != 'undefined' && self.Math == Math)
13671 ? self
13672 : Function('return this')()
13673;
13674
13675$.fn.popup = function(parameters) {
13676 var
13677 $allModules = $(this),
13678 $document = $(document),
13679 $window = $(window),
13680 $body = $('body'),
13681
13682 moduleSelector = $allModules.selector || '',
13683
13684 clickEvent = ('ontouchstart' in document.documentElement)
13685 ? 'touchstart'
13686 : 'click',
13687
13688 time = new Date().getTime(),
13689 performance = [],
13690
13691 query = arguments[0],
13692 methodInvoked = (typeof query == 'string'),
13693 queryArguments = [].slice.call(arguments, 1),
13694
13695 returnedValue
13696 ;
13697 $allModules
13698 .each(function() {
13699 var
13700 settings = ( $.isPlainObject(parameters) )
13701 ? $.extend(true, {}, $.fn.popup.settings, parameters)
13702 : $.extend({}, $.fn.popup.settings),
13703
13704 selector = settings.selector,
13705 className = settings.className,
13706 error = settings.error,
13707 metadata = settings.metadata,
13708 namespace = settings.namespace,
13709
13710 eventNamespace = '.' + settings.namespace,
13711 moduleNamespace = 'module-' + namespace,
13712
13713 $module = $(this),
13714 $context = $(settings.context),
13715 $scrollContext = $(settings.scrollContext),
13716 $boundary = $(settings.boundary),
13717 $target = (settings.target)
13718 ? $(settings.target)
13719 : $module,
13720
13721 $popup,
13722 $offsetParent,
13723
13724 searchDepth = 0,
13725 triedPositions = false,
13726 openedWithTouch = false,
13727
13728 element = this,
13729 instance = $module.data(moduleNamespace),
13730
13731 documentObserver,
13732 elementNamespace,
13733 id,
13734 module
13735 ;
13736
13737 module = {
13738
13739 // binds events
13740 initialize: function() {
13741 module.debug('Initializing', $module);
13742 module.createID();
13743 module.bind.events();
13744 if(!module.exists() && settings.preserve) {
13745 module.create();
13746 }
13747 if(settings.observeChanges) {
13748 module.observeChanges();
13749 }
13750 module.instantiate();
13751 },
13752
13753 instantiate: function() {
13754 module.verbose('Storing instance', module);
13755 instance = module;
13756 $module
13757 .data(moduleNamespace, instance)
13758 ;
13759 },
13760
13761 observeChanges: function() {
13762 if('MutationObserver' in window) {
13763 documentObserver = new MutationObserver(module.event.documentChanged);
13764 documentObserver.observe(document, {
13765 childList : true,
13766 subtree : true
13767 });
13768 module.debug('Setting up mutation observer', documentObserver);
13769 }
13770 },
13771
13772 refresh: function() {
13773 if(settings.popup) {
13774 $popup = $(settings.popup).eq(0);
13775 }
13776 else {
13777 if(settings.inline) {
13778 $popup = $target.nextAll(selector.popup).eq(0);
13779 settings.popup = $popup;
13780 }
13781 }
13782 if(settings.popup) {
13783 $popup.addClass(className.loading);
13784 $offsetParent = module.get.offsetParent();
13785 $popup.removeClass(className.loading);
13786 if(settings.movePopup && module.has.popup() && module.get.offsetParent($popup)[0] !== $offsetParent[0]) {
13787 module.debug('Moving popup to the same offset parent as target');
13788 $popup
13789 .detach()
13790 .appendTo($offsetParent)
13791 ;
13792 }
13793 }
13794 else {
13795 $offsetParent = (settings.inline)
13796 ? module.get.offsetParent($target)
13797 : module.has.popup()
13798 ? module.get.offsetParent($popup)
13799 : $body
13800 ;
13801 }
13802 if( $offsetParent.is('html') && $offsetParent[0] !== $body[0] ) {
13803 module.debug('Setting page as offset parent');
13804 $offsetParent = $body;
13805 }
13806 if( module.get.variation() ) {
13807 module.set.variation();
13808 }
13809 },
13810
13811 reposition: function() {
13812 module.refresh();
13813 module.set.position();
13814 },
13815
13816 destroy: function() {
13817 module.debug('Destroying previous module');
13818 if(documentObserver) {
13819 documentObserver.disconnect();
13820 }
13821 // remove element only if was created dynamically
13822 if($popup && !settings.preserve) {
13823 module.removePopup();
13824 }
13825 // clear all timeouts
13826 clearTimeout(module.hideTimer);
13827 clearTimeout(module.showTimer);
13828 // remove events
13829 module.unbind.close();
13830 module.unbind.events();
13831 $module
13832 .removeData(moduleNamespace)
13833 ;
13834 },
13835
13836 event: {
13837 start: function(event) {
13838 var
13839 delay = ($.isPlainObject(settings.delay))
13840 ? settings.delay.show
13841 : settings.delay
13842 ;
13843 clearTimeout(module.hideTimer);
13844 if(!openedWithTouch || (openedWithTouch && settings.addTouchEvents) ) {
13845 module.showTimer = setTimeout(module.show, delay);
13846 }
13847 },
13848 end: function() {
13849 var
13850 delay = ($.isPlainObject(settings.delay))
13851 ? settings.delay.hide
13852 : settings.delay
13853 ;
13854 clearTimeout(module.showTimer);
13855 module.hideTimer = setTimeout(module.hide, delay);
13856 },
13857 touchstart: function(event) {
13858 openedWithTouch = true;
13859 if(settings.addTouchEvents) {
13860 module.show();
13861 }
13862 },
13863 resize: function() {
13864 if( module.is.visible() ) {
13865 module.set.position();
13866 }
13867 },
13868 documentChanged: function(mutations) {
13869 [].forEach.call(mutations, function(mutation) {
13870 if(mutation.removedNodes) {
13871 [].forEach.call(mutation.removedNodes, function(node) {
13872 if(node == element || $(node).find(element).length > 0) {
13873 module.debug('Element removed from DOM, tearing down events');
13874 module.destroy();
13875 }
13876 });
13877 }
13878 });
13879 },
13880 hideGracefully: function(event) {
13881 var
13882 $target = $(event.target),
13883 isInDOM = $.contains(document.documentElement, event.target),
13884 inPopup = ($target.closest(selector.popup).length > 0)
13885 ;
13886 // don't close on clicks inside popup
13887 if(event && !inPopup && isInDOM) {
13888 module.debug('Click occurred outside popup hiding popup');
13889 module.hide();
13890 }
13891 else {
13892 module.debug('Click was inside popup, keeping popup open');
13893 }
13894 }
13895 },
13896
13897 // generates popup html from metadata
13898 create: function() {
13899 var
13900 html = module.get.html(),
13901 title = module.get.title(),
13902 content = module.get.content()
13903 ;
13904
13905 if(html || content || title) {
13906 module.debug('Creating pop-up html');
13907 if(!html) {
13908 html = settings.templates.popup({
13909 title : title,
13910 content : content
13911 });
13912 }
13913 $popup = $('<div/>')
13914 .addClass(className.popup)
13915 .data(metadata.activator, $module)
13916 .html(html)
13917 ;
13918 if(settings.inline) {
13919 module.verbose('Inserting popup element inline', $popup);
13920 $popup
13921 .insertAfter($module)
13922 ;
13923 }
13924 else {
13925 module.verbose('Appending popup element to body', $popup);
13926 $popup
13927 .appendTo( $context )
13928 ;
13929 }
13930 module.refresh();
13931 module.set.variation();
13932
13933 if(settings.hoverable) {
13934 module.bind.popup();
13935 }
13936 settings.onCreate.call($popup, element);
13937 }
13938 else if(settings.popup) {
13939 $(settings.popup).data(metadata.activator, $module);
13940 module.verbose('Used popup specified in settings');
13941 module.refresh();
13942 if(settings.hoverable) {
13943 module.bind.popup();
13944 }
13945 }
13946 else if($target.next(selector.popup).length !== 0) {
13947 module.verbose('Pre-existing popup found');
13948 settings.inline = true;
13949 settings.popup = $target.next(selector.popup).data(metadata.activator, $module);
13950 module.refresh();
13951 if(settings.hoverable) {
13952 module.bind.popup();
13953 }
13954 }
13955 else {
13956 module.debug('No content specified skipping display', element);
13957 }
13958 },
13959
13960 createID: function() {
13961 id = (Math.random().toString(16) + '000000000').substr(2, 8);
13962 elementNamespace = '.' + id;
13963 module.verbose('Creating unique id for element', id);
13964 },
13965
13966 // determines popup state
13967 toggle: function() {
13968 module.debug('Toggling pop-up');
13969 if( module.is.hidden() ) {
13970 module.debug('Popup is hidden, showing pop-up');
13971 module.unbind.close();
13972 module.show();
13973 }
13974 else {
13975 module.debug('Popup is visible, hiding pop-up');
13976 module.hide();
13977 }
13978 },
13979
13980 show: function(callback) {
13981 callback = callback || function(){};
13982 module.debug('Showing pop-up', settings.transition);
13983 if(module.is.hidden() && !( module.is.active() && module.is.dropdown()) ) {
13984 if( !module.exists() ) {
13985 module.create();
13986 }
13987 if(settings.onShow.call($popup, element) === false) {
13988 module.debug('onShow callback returned false, cancelling popup animation');
13989 return;
13990 }
13991 else if(!settings.preserve && !settings.popup) {
13992 module.refresh();
13993 }
13994 if( $popup && module.set.position() ) {
13995 module.save.conditions();
13996 if(settings.exclusive) {
13997 module.hideAll();
13998 }
13999 module.animate.show(callback);
14000 }
14001 }
14002 },
14003
14004
14005 hide: function(callback) {
14006 callback = callback || function(){};
14007 if( module.is.visible() || module.is.animating() ) {
14008 if(settings.onHide.call($popup, element) === false) {
14009 module.debug('onHide callback returned false, cancelling popup animation');
14010 return;
14011 }
14012 module.remove.visible();
14013 module.unbind.close();
14014 module.restore.conditions();
14015 module.animate.hide(callback);
14016 }
14017 },
14018
14019 hideAll: function() {
14020 $(selector.popup)
14021 .filter('.' + className.popupVisible)
14022 .each(function() {
14023 $(this)
14024 .data(metadata.activator)
14025 .popup('hide')
14026 ;
14027 })
14028 ;
14029 },
14030 exists: function() {
14031 if(!$popup) {
14032 return false;
14033 }
14034 if(settings.inline || settings.popup) {
14035 return ( module.has.popup() );
14036 }
14037 else {
14038 return ( $popup.closest($context).length >= 1 )
14039 ? true
14040 : false
14041 ;
14042 }
14043 },
14044
14045 removePopup: function() {
14046 if( module.has.popup() && !settings.popup) {
14047 module.debug('Removing popup', $popup);
14048 $popup.remove();
14049 $popup = undefined;
14050 settings.onRemove.call($popup, element);
14051 }
14052 },
14053
14054 save: {
14055 conditions: function() {
14056 module.cache = {
14057 title: $module.attr('title')
14058 };
14059 if (module.cache.title) {
14060 $module.removeAttr('title');
14061 }
14062 module.verbose('Saving original attributes', module.cache.title);
14063 }
14064 },
14065 restore: {
14066 conditions: function() {
14067 if(module.cache && module.cache.title) {
14068 $module.attr('title', module.cache.title);
14069 module.verbose('Restoring original attributes', module.cache.title);
14070 }
14071 return true;
14072 }
14073 },
14074 supports: {
14075 svg: function() {
14076 return (typeof SVGGraphicsElement !== 'undefined');
14077 }
14078 },
14079 animate: {
14080 show: function(callback) {
14081 callback = $.isFunction(callback) ? callback : function(){};
14082 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
14083 module.set.visible();
14084 $popup
14085 .transition({
14086 animation : (settings.transition.showMethod || settings.transition) + ' in',
14087 queue : false,
14088 debug : settings.debug,
14089 verbose : settings.verbose,
14090 duration : settings.transition.showDuration || settings.duration,
14091 onComplete : function() {
14092 module.bind.close();
14093 callback.call($popup, element);
14094 settings.onVisible.call($popup, element);
14095 }
14096 })
14097 ;
14098 }
14099 else {
14100 module.error(error.noTransition);
14101 }
14102 },
14103 hide: function(callback) {
14104 callback = $.isFunction(callback) ? callback : function(){};
14105 module.debug('Hiding pop-up');
14106 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
14107 $popup
14108 .transition({
14109 animation : (settings.transition.hideMethod || settings.transition) + ' out',
14110 queue : false,
14111 duration : settings.transition.hideDuration || settings.duration,
14112 debug : settings.debug,
14113 verbose : settings.verbose,
14114 onComplete : function() {
14115 module.reset();
14116 callback.call($popup, element);
14117 settings.onHidden.call($popup, element);
14118 }
14119 })
14120 ;
14121 }
14122 else {
14123 module.error(error.noTransition);
14124 }
14125 }
14126 },
14127
14128 change: {
14129 content: function(html) {
14130 $popup.html(html);
14131 }
14132 },
14133
14134 get: {
14135 html: function() {
14136 $module.removeData(metadata.html);
14137 return $module.data(metadata.html) || settings.html;
14138 },
14139 title: function() {
14140 $module.removeData(metadata.title);
14141 return $module.data(metadata.title) || settings.title;
14142 },
14143 content: function() {
14144 $module.removeData(metadata.content);
14145 return $module.data(metadata.content) || settings.content || $module.attr('title');
14146 },
14147 variation: function() {
14148 $module.removeData(metadata.variation);
14149 return $module.data(metadata.variation) || settings.variation;
14150 },
14151 popup: function() {
14152 return $popup;
14153 },
14154 popupOffset: function() {
14155 return $popup.offset();
14156 },
14157 calculations: function() {
14158 var
14159 $popupOffsetParent = module.get.offsetParent($popup),
14160 targetElement = $target[0],
14161 isWindow = ($boundary[0] == window),
14162 targetOffset = $target.offset(),
14163 parentOffset = settings.inline || (settings.popup && settings.movePopup)
14164 ? $target.offsetParent().offset()
14165 : { top: 0, left: 0 },
14166 screenPosition = (isWindow)
14167 ? { top: 0, left: 0 }
14168 : $boundary.offset(),
14169 calculations = {},
14170 scroll = (isWindow)
14171 ? { top: $window.scrollTop(), left: $window.scrollLeft() }
14172 : { top: 0, left: 0},
14173 screen
14174 ;
14175 calculations = {
14176 // element which is launching popup
14177 target : {
14178 element : $target[0],
14179 width : $target.outerWidth(),
14180 height : $target.outerHeight(),
14181 top : targetOffset.top - parentOffset.top,
14182 left : targetOffset.left - parentOffset.left,
14183 margin : {}
14184 },
14185 // popup itself
14186 popup : {
14187 width : $popup.outerWidth(),
14188 height : $popup.outerHeight()
14189 },
14190 // offset container (or 3d context)
14191 parent : {
14192 width : $offsetParent.outerWidth(),
14193 height : $offsetParent.outerHeight()
14194 },
14195 // screen boundaries
14196 screen : {
14197 top : screenPosition.top,
14198 left : screenPosition.left,
14199 scroll: {
14200 top : scroll.top,
14201 left : scroll.left
14202 },
14203 width : $boundary.width(),
14204 height : $boundary.height()
14205 }
14206 };
14207
14208 // if popup offset context is not same as target, then adjust calculations
14209 if($popupOffsetParent.get(0) !== $offsetParent.get(0)) {
14210 var
14211 popupOffset = $popupOffsetParent.offset()
14212 ;
14213 calculations.target.top -= popupOffset.top;
14214 calculations.target.left -= popupOffset.left;
14215 calculations.parent.width = $popupOffsetParent.outerWidth();
14216 calculations.parent.height = $popupOffsetParent.outerHeight();
14217 }
14218
14219 // add in container calcs if fluid
14220 if( settings.setFluidWidth && module.is.fluid() ) {
14221 calculations.container = {
14222 width: $popup.parent().outerWidth()
14223 };
14224 calculations.popup.width = calculations.container.width;
14225 }
14226
14227 // add in margins if inline
14228 calculations.target.margin.top = (settings.inline)
14229 ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10)
14230 : 0
14231 ;
14232 calculations.target.margin.left = (settings.inline)
14233 ? module.is.rtl()
14234 ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-right'), 10)
14235 : parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-left'), 10)
14236 : 0
14237 ;
14238 // calculate screen boundaries
14239 screen = calculations.screen;
14240 calculations.boundary = {
14241 top : screen.top + screen.scroll.top,
14242 bottom : screen.top + screen.scroll.top + screen.height,
14243 left : screen.left + screen.scroll.left,
14244 right : screen.left + screen.scroll.left + screen.width
14245 };
14246 return calculations;
14247 },
14248 id: function() {
14249 return id;
14250 },
14251 startEvent: function() {
14252 if(settings.on == 'hover') {
14253 return 'mouseenter';
14254 }
14255 else if(settings.on == 'focus') {
14256 return 'focus';
14257 }
14258 return false;
14259 },
14260 scrollEvent: function() {
14261 return 'scroll';
14262 },
14263 endEvent: function() {
14264 if(settings.on == 'hover') {
14265 return 'mouseleave';
14266 }
14267 else if(settings.on == 'focus') {
14268 return 'blur';
14269 }
14270 return false;
14271 },
14272 distanceFromBoundary: function(offset, calculations) {
14273 var
14274 distanceFromBoundary = {},
14275 popup,
14276 boundary
14277 ;
14278 calculations = calculations || module.get.calculations();
14279
14280 // shorthand
14281 popup = calculations.popup;
14282 boundary = calculations.boundary;
14283
14284 if(offset) {
14285 distanceFromBoundary = {
14286 top : (offset.top - boundary.top),
14287 left : (offset.left - boundary.left),
14288 right : (boundary.right - (offset.left + popup.width) ),
14289 bottom : (boundary.bottom - (offset.top + popup.height) )
14290 };
14291 module.verbose('Distance from boundaries determined', offset, distanceFromBoundary);
14292 }
14293 return distanceFromBoundary;
14294 },
14295 offsetParent: function($element) {
14296 var
14297 element = ($element !== undefined)
14298 ? $element[0]
14299 : $target[0],
14300 parentNode = element.parentNode,
14301 $node = $(parentNode)
14302 ;
14303 if(parentNode) {
14304 var
14305 is2D = ($node.css('transform') === 'none'),
14306 isStatic = ($node.css('position') === 'static'),
14307 isBody = $node.is('body')
14308 ;
14309 while(parentNode && !isBody && isStatic && is2D) {
14310 parentNode = parentNode.parentNode;
14311 $node = $(parentNode);
14312 is2D = ($node.css('transform') === 'none');
14313 isStatic = ($node.css('position') === 'static');
14314 isBody = $node.is('body');
14315 }
14316 }
14317 return ($node && $node.length > 0)
14318 ? $node
14319 : $()
14320 ;
14321 },
14322 positions: function() {
14323 return {
14324 'top left' : false,
14325 'top center' : false,
14326 'top right' : false,
14327 'bottom left' : false,
14328 'bottom center' : false,
14329 'bottom right' : false,
14330 'left center' : false,
14331 'right center' : false
14332 };
14333 },
14334 nextPosition: function(position) {
14335 var
14336 positions = position.split(' '),
14337 verticalPosition = positions[0],
14338 horizontalPosition = positions[1],
14339 opposite = {
14340 top : 'bottom',
14341 bottom : 'top',
14342 left : 'right',
14343 right : 'left'
14344 },
14345 adjacent = {
14346 left : 'center',
14347 center : 'right',
14348 right : 'left'
14349 },
14350 backup = {
14351 'top left' : 'top center',
14352 'top center' : 'top right',
14353 'top right' : 'right center',
14354 'right center' : 'bottom right',
14355 'bottom right' : 'bottom center',
14356 'bottom center' : 'bottom left',
14357 'bottom left' : 'left center',
14358 'left center' : 'top left'
14359 },
14360 adjacentsAvailable = (verticalPosition == 'top' || verticalPosition == 'bottom'),
14361 oppositeTried = false,
14362 adjacentTried = false,
14363 nextPosition = false
14364 ;
14365 if(!triedPositions) {
14366 module.verbose('All available positions available');
14367 triedPositions = module.get.positions();
14368 }
14369
14370 module.debug('Recording last position tried', position);
14371 triedPositions[position] = true;
14372
14373 if(settings.prefer === 'opposite') {
14374 nextPosition = [opposite[verticalPosition], horizontalPosition];
14375 nextPosition = nextPosition.join(' ');
14376 oppositeTried = (triedPositions[nextPosition] === true);
14377 module.debug('Trying opposite strategy', nextPosition);
14378 }
14379 if((settings.prefer === 'adjacent') && adjacentsAvailable ) {
14380 nextPosition = [verticalPosition, adjacent[horizontalPosition]];
14381 nextPosition = nextPosition.join(' ');
14382 adjacentTried = (triedPositions[nextPosition] === true);
14383 module.debug('Trying adjacent strategy', nextPosition);
14384 }
14385 if(adjacentTried || oppositeTried) {
14386 module.debug('Using backup position', nextPosition);
14387 nextPosition = backup[position];
14388 }
14389 return nextPosition;
14390 }
14391 },
14392
14393 set: {
14394 position: function(position, calculations) {
14395
14396 // exit conditions
14397 if($target.length === 0 || $popup.length === 0) {
14398 module.error(error.notFound);
14399 return;
14400 }
14401 var
14402 offset,
14403 distanceAway,
14404 target,
14405 popup,
14406 parent,
14407 positioning,
14408 popupOffset,
14409 distanceFromBoundary
14410 ;
14411
14412 calculations = calculations || module.get.calculations();
14413 position = position || $module.data(metadata.position) || settings.position;
14414
14415 offset = $module.data(metadata.offset) || settings.offset;
14416 distanceAway = settings.distanceAway;
14417
14418 // shorthand
14419 target = calculations.target;
14420 popup = calculations.popup;
14421 parent = calculations.parent;
14422
14423 if(module.should.centerArrow(calculations)) {
14424 module.verbose('Adjusting offset to center arrow on small target element');
14425 if(position == 'top left' || position == 'bottom left') {
14426 offset += (target.width / 2);
14427 offset -= settings.arrowPixelsFromEdge;
14428 }
14429 if(position == 'top right' || position == 'bottom right') {
14430 offset -= (target.width / 2);
14431 offset += settings.arrowPixelsFromEdge;
14432 }
14433 }
14434
14435 if(target.width === 0 && target.height === 0 && !module.is.svg(target.element)) {
14436 module.debug('Popup target is hidden, no action taken');
14437 return false;
14438 }
14439
14440 if(settings.inline) {
14441 module.debug('Adding margin to calculation', target.margin);
14442 if(position == 'left center' || position == 'right center') {
14443 offset += target.margin.top;
14444 distanceAway += -target.margin.left;
14445 }
14446 else if (position == 'top left' || position == 'top center' || position == 'top right') {
14447 offset += target.margin.left;
14448 distanceAway -= target.margin.top;
14449 }
14450 else {
14451 offset += target.margin.left;
14452 distanceAway += target.margin.top;
14453 }
14454 }
14455
14456 module.debug('Determining popup position from calculations', position, calculations);
14457
14458 if (module.is.rtl()) {
14459 position = position.replace(/left|right/g, function (match) {
14460 return (match == 'left')
14461 ? 'right'
14462 : 'left'
14463 ;
14464 });
14465 module.debug('RTL: Popup position updated', position);
14466 }
14467
14468 // if last attempt use specified last resort position
14469 if(searchDepth == settings.maxSearchDepth && typeof settings.lastResort === 'string') {
14470 position = settings.lastResort;
14471 }
14472
14473 switch (position) {
14474 case 'top left':
14475 positioning = {
14476 top : 'auto',
14477 bottom : parent.height - target.top + distanceAway,
14478 left : target.left + offset,
14479 right : 'auto'
14480 };
14481 break;
14482 case 'top center':
14483 positioning = {
14484 bottom : parent.height - target.top + distanceAway,
14485 left : target.left + (target.width / 2) - (popup.width / 2) + offset,
14486 top : 'auto',
14487 right : 'auto'
14488 };
14489 break;
14490 case 'top right':
14491 positioning = {
14492 bottom : parent.height - target.top + distanceAway,
14493 right : parent.width - target.left - target.width - offset,
14494 top : 'auto',
14495 left : 'auto'
14496 };
14497 break;
14498 case 'left center':
14499 positioning = {
14500 top : target.top + (target.height / 2) - (popup.height / 2) + offset,
14501 right : parent.width - target.left + distanceAway,
14502 left : 'auto',
14503 bottom : 'auto'
14504 };
14505 break;
14506 case 'right center':
14507 positioning = {
14508 top : target.top + (target.height / 2) - (popup.height / 2) + offset,
14509 left : target.left + target.width + distanceAway,
14510 bottom : 'auto',
14511 right : 'auto'
14512 };
14513 break;
14514 case 'bottom left':
14515 positioning = {
14516 top : target.top + target.height + distanceAway,
14517 left : target.left + offset,
14518 bottom : 'auto',
14519 right : 'auto'
14520 };
14521 break;
14522 case 'bottom center':
14523 positioning = {
14524 top : target.top + target.height + distanceAway,
14525 left : target.left + (target.width / 2) - (popup.width / 2) + offset,
14526 bottom : 'auto',
14527 right : 'auto'
14528 };
14529 break;
14530 case 'bottom right':
14531 positioning = {
14532 top : target.top + target.height + distanceAway,
14533 right : parent.width - target.left - target.width - offset,
14534 left : 'auto',
14535 bottom : 'auto'
14536 };
14537 break;
14538 }
14539 if(positioning === undefined) {
14540 module.error(error.invalidPosition, position);
14541 }
14542
14543 module.debug('Calculated popup positioning values', positioning);
14544
14545 // tentatively place on stage
14546 $popup
14547 .css(positioning)
14548 .removeClass(className.position)
14549 .addClass(position)
14550 .addClass(className.loading)
14551 ;
14552
14553 popupOffset = module.get.popupOffset();
14554
14555 // see if any boundaries are surpassed with this tentative position
14556 distanceFromBoundary = module.get.distanceFromBoundary(popupOffset, calculations);
14557
14558 if(!settings.forcePosition && module.is.offstage(distanceFromBoundary, position) ) {
14559 module.debug('Position is outside viewport', position);
14560 if(searchDepth < settings.maxSearchDepth) {
14561 searchDepth++;
14562 position = module.get.nextPosition(position);
14563 module.debug('Trying new position', position);
14564 return ($popup)
14565 ? module.set.position(position, calculations)
14566 : false
14567 ;
14568 }
14569 else {
14570 if(settings.lastResort) {
14571 module.debug('No position found, showing with last position');
14572 }
14573 else {
14574 module.debug('Popup could not find a position to display', $popup);
14575 module.error(error.cannotPlace, element);
14576 module.remove.attempts();
14577 module.remove.loading();
14578 module.reset();
14579 settings.onUnplaceable.call($popup, element);
14580 return false;
14581 }
14582 }
14583 }
14584 module.debug('Position is on stage', position);
14585 module.remove.attempts();
14586 module.remove.loading();
14587 if( settings.setFluidWidth && module.is.fluid() ) {
14588 module.set.fluidWidth(calculations);
14589 }
14590 return true;
14591 },
14592
14593 fluidWidth: function(calculations) {
14594 calculations = calculations || module.get.calculations();
14595 module.debug('Automatically setting element width to parent width', calculations.parent.width);
14596 $popup.css('width', calculations.container.width);
14597 },
14598
14599 variation: function(variation) {
14600 variation = variation || module.get.variation();
14601 if(variation && module.has.popup() ) {
14602 module.verbose('Adding variation to popup', variation);
14603 $popup.addClass(variation);
14604 }
14605 },
14606
14607 visible: function() {
14608 $module.addClass(className.visible);
14609 }
14610 },
14611
14612 remove: {
14613 loading: function() {
14614 $popup.removeClass(className.loading);
14615 },
14616 variation: function(variation) {
14617 variation = variation || module.get.variation();
14618 if(variation) {
14619 module.verbose('Removing variation', variation);
14620 $popup.removeClass(variation);
14621 }
14622 },
14623 visible: function() {
14624 $module.removeClass(className.visible);
14625 },
14626 attempts: function() {
14627 module.verbose('Resetting all searched positions');
14628 searchDepth = 0;
14629 triedPositions = false;
14630 }
14631 },
14632
14633 bind: {
14634 events: function() {
14635 module.debug('Binding popup events to module');
14636 if(settings.on == 'click') {
14637 $module
14638 .on(clickEvent + eventNamespace, module.toggle)
14639 ;
14640 }
14641 if(settings.on == 'hover') {
14642 $module
14643 .on('touchstart' + eventNamespace, module.event.touchstart)
14644 ;
14645 }
14646 if( module.get.startEvent() ) {
14647 $module
14648 .on(module.get.startEvent() + eventNamespace, module.event.start)
14649 .on(module.get.endEvent() + eventNamespace, module.event.end)
14650 ;
14651 }
14652 if(settings.target) {
14653 module.debug('Target set to element', $target);
14654 }
14655 $window.on('resize' + elementNamespace, module.event.resize);
14656 },
14657 popup: function() {
14658 module.verbose('Allowing hover events on popup to prevent closing');
14659 if( $popup && module.has.popup() ) {
14660 $popup
14661 .on('mouseenter' + eventNamespace, module.event.start)
14662 .on('mouseleave' + eventNamespace, module.event.end)
14663 ;
14664 }
14665 },
14666 close: function() {
14667 if(settings.hideOnScroll === true || (settings.hideOnScroll == 'auto' && settings.on != 'click')) {
14668 module.bind.closeOnScroll();
14669 }
14670 if(module.is.closable()) {
14671 module.bind.clickaway();
14672 }
14673 else if(settings.on == 'hover' && openedWithTouch) {
14674 module.bind.touchClose();
14675 }
14676 },
14677 closeOnScroll: function() {
14678 module.verbose('Binding scroll close event to document');
14679 $scrollContext
14680 .one(module.get.scrollEvent() + elementNamespace, module.event.hideGracefully)
14681 ;
14682 },
14683 touchClose: function() {
14684 module.verbose('Binding popup touchclose event to document');
14685 $document
14686 .on('touchstart' + elementNamespace, function(event) {
14687 module.verbose('Touched away from popup');
14688 module.event.hideGracefully.call(element, event);
14689 })
14690 ;
14691 },
14692 clickaway: function() {
14693 module.verbose('Binding popup close event to document');
14694 $document
14695 .on(clickEvent + elementNamespace, function(event) {
14696 module.verbose('Clicked away from popup');
14697 module.event.hideGracefully.call(element, event);
14698 })
14699 ;
14700 }
14701 },
14702
14703 unbind: {
14704 events: function() {
14705 $window
14706 .off(elementNamespace)
14707 ;
14708 $module
14709 .off(eventNamespace)
14710 ;
14711 },
14712 close: function() {
14713 $document
14714 .off(elementNamespace)
14715 ;
14716 $scrollContext
14717 .off(elementNamespace)
14718 ;
14719 },
14720 },
14721
14722 has: {
14723 popup: function() {
14724 return ($popup && $popup.length > 0);
14725 }
14726 },
14727
14728 should: {
14729 centerArrow: function(calculations) {
14730 return !module.is.basic() && calculations.target.width <= (settings.arrowPixelsFromEdge * 2);
14731 },
14732 },
14733
14734 is: {
14735 closable: function() {
14736 if(settings.closable == 'auto') {
14737 if(settings.on == 'hover') {
14738 return false;
14739 }
14740 return true;
14741 }
14742 return settings.closable;
14743 },
14744 offstage: function(distanceFromBoundary, position) {
14745 var
14746 offstage = []
14747 ;
14748 // return boundaries that have been surpassed
14749 $.each(distanceFromBoundary, function(direction, distance) {
14750 if(distance < -settings.jitter) {
14751 module.debug('Position exceeds allowable distance from edge', direction, distance, position);
14752 offstage.push(direction);
14753 }
14754 });
14755 if(offstage.length > 0) {
14756 return true;
14757 }
14758 else {
14759 return false;
14760 }
14761 },
14762 svg: function(element) {
14763 return module.supports.svg() && (element instanceof SVGGraphicsElement);
14764 },
14765 basic: function() {
14766 return $module.hasClass(className.basic);
14767 },
14768 active: function() {
14769 return $module.hasClass(className.active);
14770 },
14771 animating: function() {
14772 return ($popup !== undefined && $popup.hasClass(className.animating) );
14773 },
14774 fluid: function() {
14775 return ($popup !== undefined && $popup.hasClass(className.fluid));
14776 },
14777 visible: function() {
14778 return ($popup !== undefined && $popup.hasClass(className.popupVisible));
14779 },
14780 dropdown: function() {
14781 return $module.hasClass(className.dropdown);
14782 },
14783 hidden: function() {
14784 return !module.is.visible();
14785 },
14786 rtl: function () {
14787 return $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl';
14788 }
14789 },
14790
14791 reset: function() {
14792 module.remove.visible();
14793 if(settings.preserve) {
14794 if($.fn.transition !== undefined) {
14795 $popup
14796 .transition('remove transition')
14797 ;
14798 }
14799 }
14800 else {
14801 module.removePopup();
14802 }
14803 },
14804
14805 setting: function(name, value) {
14806 if( $.isPlainObject(name) ) {
14807 $.extend(true, settings, name);
14808 }
14809 else if(value !== undefined) {
14810 settings[name] = value;
14811 }
14812 else {
14813 return settings[name];
14814 }
14815 },
14816 internal: function(name, value) {
14817 if( $.isPlainObject(name) ) {
14818 $.extend(true, module, name);
14819 }
14820 else if(value !== undefined) {
14821 module[name] = value;
14822 }
14823 else {
14824 return module[name];
14825 }
14826 },
14827 debug: function() {
14828 if(!settings.silent && settings.debug) {
14829 if(settings.performance) {
14830 module.performance.log(arguments);
14831 }
14832 else {
14833 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
14834 module.debug.apply(console, arguments);
14835 }
14836 }
14837 },
14838 verbose: function() {
14839 if(!settings.silent && settings.verbose && settings.debug) {
14840 if(settings.performance) {
14841 module.performance.log(arguments);
14842 }
14843 else {
14844 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
14845 module.verbose.apply(console, arguments);
14846 }
14847 }
14848 },
14849 error: function() {
14850 if(!settings.silent) {
14851 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
14852 module.error.apply(console, arguments);
14853 }
14854 },
14855 performance: {
14856 log: function(message) {
14857 var
14858 currentTime,
14859 executionTime,
14860 previousTime
14861 ;
14862 if(settings.performance) {
14863 currentTime = new Date().getTime();
14864 previousTime = time || currentTime;
14865 executionTime = currentTime - previousTime;
14866 time = currentTime;
14867 performance.push({
14868 'Name' : message[0],
14869 'Arguments' : [].slice.call(message, 1) || '',
14870 'Element' : element,
14871 'Execution Time' : executionTime
14872 });
14873 }
14874 clearTimeout(module.performance.timer);
14875 module.performance.timer = setTimeout(module.performance.display, 500);
14876 },
14877 display: function() {
14878 var
14879 title = settings.name + ':',
14880 totalTime = 0
14881 ;
14882 time = false;
14883 clearTimeout(module.performance.timer);
14884 $.each(performance, function(index, data) {
14885 totalTime += data['Execution Time'];
14886 });
14887 title += ' ' + totalTime + 'ms';
14888 if(moduleSelector) {
14889 title += ' \'' + moduleSelector + '\'';
14890 }
14891 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
14892 console.groupCollapsed(title);
14893 if(console.table) {
14894 console.table(performance);
14895 }
14896 else {
14897 $.each(performance, function(index, data) {
14898 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
14899 });
14900 }
14901 console.groupEnd();
14902 }
14903 performance = [];
14904 }
14905 },
14906 invoke: function(query, passedArguments, context) {
14907 var
14908 object = instance,
14909 maxDepth,
14910 found,
14911 response
14912 ;
14913 passedArguments = passedArguments || queryArguments;
14914 context = element || context;
14915 if(typeof query == 'string' && object !== undefined) {
14916 query = query.split(/[\. ]/);
14917 maxDepth = query.length - 1;
14918 $.each(query, function(depth, value) {
14919 var camelCaseValue = (depth != maxDepth)
14920 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
14921 : query
14922 ;
14923 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
14924 object = object[camelCaseValue];
14925 }
14926 else if( object[camelCaseValue] !== undefined ) {
14927 found = object[camelCaseValue];
14928 return false;
14929 }
14930 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
14931 object = object[value];
14932 }
14933 else if( object[value] !== undefined ) {
14934 found = object[value];
14935 return false;
14936 }
14937 else {
14938 return false;
14939 }
14940 });
14941 }
14942 if ( $.isFunction( found ) ) {
14943 response = found.apply(context, passedArguments);
14944 }
14945 else if(found !== undefined) {
14946 response = found;
14947 }
14948 if(Array.isArray(returnedValue)) {
14949 returnedValue.push(response);
14950 }
14951 else if(returnedValue !== undefined) {
14952 returnedValue = [returnedValue, response];
14953 }
14954 else if(response !== undefined) {
14955 returnedValue = response;
14956 }
14957 return found;
14958 }
14959 };
14960
14961 if(methodInvoked) {
14962 if(instance === undefined) {
14963 module.initialize();
14964 }
14965 module.invoke(query);
14966 }
14967 else {
14968 if(instance !== undefined) {
14969 instance.invoke('destroy');
14970 }
14971 module.initialize();
14972 }
14973 })
14974 ;
14975
14976 return (returnedValue !== undefined)
14977 ? returnedValue
14978 : this
14979 ;
14980};
14981
14982$.fn.popup.settings = {
14983
14984 name : 'Popup',
14985
14986 // module settings
14987 silent : false,
14988 debug : false,
14989 verbose : false,
14990 performance : true,
14991 namespace : 'popup',
14992
14993 // whether it should use dom mutation observers
14994 observeChanges : true,
14995
14996 // callback only when element added to dom
14997 onCreate : function(){},
14998
14999 // callback before element removed from dom
15000 onRemove : function(){},
15001
15002 // callback before show animation
15003 onShow : function(){},
15004
15005 // callback after show animation
15006 onVisible : function(){},
15007
15008 // callback before hide animation
15009 onHide : function(){},
15010
15011 // callback when popup cannot be positioned in visible screen
15012 onUnplaceable : function(){},
15013
15014 // callback after hide animation
15015 onHidden : function(){},
15016
15017 // when to show popup
15018 on : 'hover',
15019
15020 // element to use to determine if popup is out of boundary
15021 boundary : window,
15022
15023 // whether to add touchstart events when using hover
15024 addTouchEvents : true,
15025
15026 // default position relative to element
15027 position : 'top left',
15028
15029 // if given position should be used regardless if popup fits
15030 forcePosition : false,
15031
15032 // name of variation to use
15033 variation : '',
15034
15035 // whether popup should be moved to context
15036 movePopup : true,
15037
15038 // element which popup should be relative to
15039 target : false,
15040
15041 // jq selector or element that should be used as popup
15042 popup : false,
15043
15044 // popup should remain inline next to activator
15045 inline : false,
15046
15047 // popup should be removed from page on hide
15048 preserve : false,
15049
15050 // popup should not close when being hovered on
15051 hoverable : false,
15052
15053 // explicitly set content
15054 content : false,
15055
15056 // explicitly set html
15057 html : false,
15058
15059 // explicitly set title
15060 title : false,
15061
15062 // whether automatically close on clickaway when on click
15063 closable : true,
15064
15065 // automatically hide on scroll
15066 hideOnScroll : 'auto',
15067
15068 // hide other popups on show
15069 exclusive : false,
15070
15071 // context to attach popups
15072 context : 'body',
15073
15074 // context for binding scroll events
15075 scrollContext : window,
15076
15077 // position to prefer when calculating new position
15078 prefer : 'opposite',
15079
15080 // specify position to appear even if it doesn't fit
15081 lastResort : false,
15082
15083 // number of pixels from edge of popup to pointing arrow center (used from centering)
15084 arrowPixelsFromEdge: 20,
15085
15086 // delay used to prevent accidental refiring of animations due to user error
15087 delay : {
15088 show : 50,
15089 hide : 70
15090 },
15091
15092 // whether fluid variation should assign width explicitly
15093 setFluidWidth : true,
15094
15095 // transition settings
15096 duration : 200,
15097 transition : 'scale',
15098
15099 // distance away from activating element in px
15100 distanceAway : 0,
15101
15102 // number of pixels an element is allowed to be "offstage" for a position to be chosen (allows for rounding)
15103 jitter : 2,
15104
15105 // offset on aligning axis from calculated position
15106 offset : 0,
15107
15108 // maximum times to look for a position before failing (9 positions total)
15109 maxSearchDepth : 15,
15110
15111 error: {
15112 invalidPosition : 'The position you specified is not a valid position',
15113 cannotPlace : 'Popup does not fit within the boundaries of the viewport',
15114 method : 'The method you called is not defined.',
15115 noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
15116 notFound : 'The target or popup you specified does not exist on the page'
15117 },
15118
15119 metadata: {
15120 activator : 'activator',
15121 content : 'content',
15122 html : 'html',
15123 offset : 'offset',
15124 position : 'position',
15125 title : 'title',
15126 variation : 'variation'
15127 },
15128
15129 className : {
15130 active : 'active',
15131 basic : 'basic',
15132 animating : 'animating',
15133 dropdown : 'dropdown',
15134 fluid : 'fluid',
15135 loading : 'loading',
15136 popup : 'ui popup',
15137 position : 'top left center bottom right',
15138 visible : 'visible',
15139 popupVisible : 'visible'
15140 },
15141
15142 selector : {
15143 popup : '.ui.popup'
15144 },
15145
15146 templates: {
15147 escape: function(string) {
15148 var
15149 badChars = /[<>"'`]/g,
15150 shouldEscape = /[&<>"'`]/,
15151 escape = {
15152 "<": "&lt;",
15153 ">": "&gt;",
15154 '"': "&quot;",
15155 "'": "&#x27;",
15156 "`": "&#x60;"
15157 },
15158 escapedChar = function(chr) {
15159 return escape[chr];
15160 }
15161 ;
15162 if(shouldEscape.test(string)) {
15163 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
15164 return string.replace(badChars, escapedChar);
15165 }
15166 return string;
15167 },
15168 popup: function(text) {
15169 var
15170 html = '',
15171 escape = $.fn.popup.settings.templates.escape
15172 ;
15173 if(typeof text !== undefined) {
15174 if(typeof text.title !== undefined && text.title) {
15175 text.title = escape(text.title);
15176 html += '<div class="header">' + text.title + '</div>';
15177 }
15178 if(typeof text.content !== undefined && text.content) {
15179 text.content = escape(text.content);
15180 html += '<div class="content">' + text.content + '</div>';
15181 }
15182 }
15183 return html;
15184 }
15185 }
15186
15187};
15188
15189
15190})( jQuery, window, document );
15191
15192/*!
15193 * # Fomantic-UI 2.8.8 - Progress
15194 * http://github.com/fomantic/Fomantic-UI/
15195 *
15196 *
15197 * Released under the MIT license
15198 * http://opensource.org/licenses/MIT
15199 *
15200 */
15201
15202;(function ($, window, document, undefined) {
15203
15204'use strict';
15205
15206$.isFunction = $.isFunction || function(obj) {
15207 return typeof obj === "function" && typeof obj.nodeType !== "number";
15208};
15209
15210window = (typeof window != 'undefined' && window.Math == Math)
15211 ? window
15212 : (typeof self != 'undefined' && self.Math == Math)
15213 ? self
15214 : Function('return this')()
15215;
15216
15217$.fn.progress = function(parameters) {
15218 var
15219 $allModules = $(this),
15220
15221 moduleSelector = $allModules.selector || '',
15222
15223 time = new Date().getTime(),
15224 performance = [],
15225
15226 query = arguments[0],
15227 methodInvoked = (typeof query == 'string'),
15228 queryArguments = [].slice.call(arguments, 1),
15229
15230 returnedValue
15231 ;
15232
15233 $allModules
15234 .each(function() {
15235 var
15236 settings = ( $.isPlainObject(parameters) )
15237 ? $.extend(true, {}, $.fn.progress.settings, parameters)
15238 : $.extend({}, $.fn.progress.settings),
15239
15240 className = settings.className,
15241 metadata = settings.metadata,
15242 namespace = settings.namespace,
15243 selector = settings.selector,
15244 error = settings.error,
15245
15246 eventNamespace = '.' + namespace,
15247 moduleNamespace = 'module-' + namespace,
15248
15249 $module = $(this),
15250 $bars = $(this).find(selector.bar),
15251 $progresses = $(this).find(selector.progress),
15252 $label = $(this).find(selector.label),
15253
15254 element = this,
15255 instance = $module.data(moduleNamespace),
15256
15257 animating = false,
15258 transitionEnd,
15259 module
15260 ;
15261 module = {
15262 helper: {
15263 sum: function (nums) {
15264 return Array.isArray(nums) ? nums.reduce(function (left, right) {
15265 return left + Number(right);
15266 }, 0) : 0;
15267 },
15268 /**
15269 * Derive precision for multiple progress with total and values.
15270 *
15271 * This helper dervices a precision that is sufficiently large to show minimum value of multiple progress.
15272 *
15273 * Example1
15274 * - total: 1122
15275 * - values: [325, 111, 74, 612]
15276 * - min ratio: 74/1122 = 0.0659...
15277 * - required precision: 100
15278 *
15279 * Example2
15280 * - total: 10541
15281 * - values: [3235, 1111, 74, 6121]
15282 * - min ratio: 74/10541 = 0.0070...
15283 * - required precision: 1000
15284 *
15285 * @param min A minimum value within multiple values
15286 * @param total A total amount of multiple values
15287 * @returns {number} A precison. Could be 1, 10, 100, ... 1e+10.
15288 */
15289 derivePrecision: function(min, total) {
15290 var precisionPower = 0
15291 var precision = 1;
15292 var ratio = min / total;
15293 while (precisionPower < 10) {
15294 ratio = ratio * precision;
15295 if (ratio > 1) {
15296 break;
15297 }
15298 precision = Math.pow(10, precisionPower++);
15299 }
15300 return precision;
15301 },
15302 forceArray: function (element) {
15303 return Array.isArray(element)
15304 ? element
15305 : !isNaN(element)
15306 ? [element]
15307 : typeof element == 'string'
15308 ? element.split(',')
15309 : []
15310 ;
15311 }
15312 },
15313
15314 initialize: function() {
15315 module.set.duration();
15316 module.set.transitionEvent();
15317 module.debug(element);
15318
15319 module.read.metadata();
15320 module.read.settings();
15321
15322 module.instantiate();
15323 },
15324
15325 instantiate: function() {
15326 module.verbose('Storing instance of progress', module);
15327 instance = module;
15328 $module
15329 .data(moduleNamespace, module)
15330 ;
15331 },
15332 destroy: function() {
15333 module.verbose('Destroying previous progress for', $module);
15334 clearInterval(instance.interval);
15335 module.remove.state();
15336 $module.removeData(moduleNamespace);
15337 instance = undefined;
15338 },
15339
15340 reset: function() {
15341 module.remove.nextValue();
15342 module.update.progress(0);
15343 },
15344
15345 complete: function(keepState) {
15346 if(module.percent === undefined || module.percent < 100) {
15347 module.remove.progressPoll();
15348 if(keepState !== true){
15349 module.set.percent(100);
15350 }
15351 }
15352 },
15353
15354 read: {
15355 metadata: function() {
15356 var
15357 data = {
15358 percent : module.helper.forceArray($module.data(metadata.percent)),
15359 total : $module.data(metadata.total),
15360 value : module.helper.forceArray($module.data(metadata.value))
15361 }
15362 ;
15363 if(data.total !== undefined) {
15364 module.debug('Total value set from metadata', data.total);
15365 module.set.total(data.total);
15366 }
15367 if(data.value.length > 0) {
15368 module.debug('Current value set from metadata', data.value);
15369 module.set.value(data.value);
15370 module.set.progress(data.value);
15371 }
15372 if(data.percent.length > 0) {
15373 module.debug('Current percent value set from metadata', data.percent);
15374 module.set.percent(data.percent);
15375 }
15376 },
15377 settings: function() {
15378 if(settings.total !== false) {
15379 module.debug('Current total set in settings', settings.total);
15380 module.set.total(settings.total);
15381 }
15382 if(settings.value !== false) {
15383 module.debug('Current value set in settings', settings.value);
15384 module.set.value(settings.value);
15385 module.set.progress(module.value);
15386 }
15387 if(settings.percent !== false) {
15388 module.debug('Current percent set in settings', settings.percent);
15389 module.set.percent(settings.percent);
15390 }
15391 }
15392 },
15393
15394 bind: {
15395 transitionEnd: function(callback) {
15396 var
15397 transitionEnd = module.get.transitionEnd()
15398 ;
15399 $bars
15400 .one(transitionEnd + eventNamespace, function(event) {
15401 clearTimeout(module.failSafeTimer);
15402 callback.call(this, event);
15403 })
15404 ;
15405 module.failSafeTimer = setTimeout(function() {
15406 $bars.triggerHandler(transitionEnd);
15407 }, settings.duration + settings.failSafeDelay);
15408 module.verbose('Adding fail safe timer', module.timer);
15409 }
15410 },
15411
15412 increment: function(incrementValue) {
15413 var
15414 startValue,
15415 newValue
15416 ;
15417 if( module.has.total() ) {
15418 startValue = module.get.value();
15419 incrementValue = incrementValue || 1;
15420 }
15421 else {
15422 startValue = module.get.percent();
15423 incrementValue = incrementValue || module.get.randomValue();
15424 }
15425 newValue = startValue + incrementValue;
15426 module.debug('Incrementing percentage by', startValue, newValue, incrementValue);
15427 newValue = module.get.normalizedValue(newValue);
15428 module.set.progress(newValue);
15429 },
15430 decrement: function(decrementValue) {
15431 var
15432 total = module.get.total(),
15433 startValue,
15434 newValue
15435 ;
15436 if(total) {
15437 startValue = module.get.value();
15438 decrementValue = decrementValue || 1;
15439 newValue = startValue - decrementValue;
15440 module.debug('Decrementing value by', decrementValue, startValue);
15441 }
15442 else {
15443 startValue = module.get.percent();
15444 decrementValue = decrementValue || module.get.randomValue();
15445 newValue = startValue - decrementValue;
15446 module.debug('Decrementing percentage by', decrementValue, startValue);
15447 }
15448 newValue = module.get.normalizedValue(newValue);
15449 module.set.progress(newValue);
15450 },
15451
15452 has: {
15453 progressPoll: function() {
15454 return module.progressPoll;
15455 },
15456 total: function() {
15457 return (module.get.total() !== false);
15458 }
15459 },
15460
15461 get: {
15462 text: function(templateText, index) {
15463 var
15464 index_ = index || 0,
15465 value = module.get.value(index_),
15466 total = module.get.total(),
15467 percent = (animating)
15468 ? module.get.displayPercent(index_)
15469 : module.get.percent(index_),
15470 left = (total !== false)
15471 ? Math.max(0,total - value)
15472 : (100 - percent)
15473 ;
15474 templateText = templateText || '';
15475 templateText = templateText
15476 .replace('{value}', value)
15477 .replace('{total}', total || 0)
15478 .replace('{left}', left)
15479 .replace('{percent}', percent)
15480 .replace('{bar}', settings.text.bars[index_] || '')
15481 ;
15482 module.verbose('Adding variables to progress bar text', templateText);
15483 return templateText;
15484 },
15485
15486 normalizedValue: function(value) {
15487 if(value < 0) {
15488 module.debug('Value cannot decrement below 0');
15489 return 0;
15490 }
15491 if(module.has.total()) {
15492 if(value > module.total) {
15493 module.debug('Value cannot increment above total', module.total);
15494 return module.total;
15495 }
15496 }
15497 else if(value > 100 ) {
15498 module.debug('Value cannot increment above 100 percent');
15499 return 100;
15500 }
15501 return value;
15502 },
15503
15504 updateInterval: function() {
15505 if(settings.updateInterval == 'auto') {
15506 return settings.duration;
15507 }
15508 return settings.updateInterval;
15509 },
15510
15511 randomValue: function() {
15512 module.debug('Generating random increment percentage');
15513 return Math.floor((Math.random() * settings.random.max) + settings.random.min);
15514 },
15515
15516 numericValue: function(value) {
15517 return (typeof value === 'string')
15518 ? (value.replace(/[^\d.]/g, '') !== '')
15519 ? +(value.replace(/[^\d.]/g, ''))
15520 : false
15521 : value
15522 ;
15523 },
15524
15525 transitionEnd: function() {
15526 var
15527 element = document.createElement('element'),
15528 transitions = {
15529 'transition' :'transitionend',
15530 'OTransition' :'oTransitionEnd',
15531 'MozTransition' :'transitionend',
15532 'WebkitTransition' :'webkitTransitionEnd'
15533 },
15534 transition
15535 ;
15536 for(transition in transitions){
15537 if( element.style[transition] !== undefined ){
15538 return transitions[transition];
15539 }
15540 }
15541 },
15542
15543 // gets current displayed percentage (if animating values this is the intermediary value)
15544 displayPercent: function(index) {
15545 var
15546 $bar = $($bars[index]),
15547 barWidth = $bar.width(),
15548 totalWidth = $module.width(),
15549 minDisplay = parseInt($bar.css('min-width'), 10),
15550 displayPercent = (barWidth > minDisplay)
15551 ? (barWidth / totalWidth * 100)
15552 : module.percent
15553 ;
15554 return (settings.precision > 0)
15555 ? Math.round(displayPercent * (10 * settings.precision)) / (10 * settings.precision)
15556 : Math.round(displayPercent)
15557 ;
15558 },
15559
15560 percent: function(index) {
15561 return module.percent && module.percent[index || 0] || 0;
15562 },
15563 value: function(index) {
15564 return module.nextValue || module.value && module.value[index || 0] || 0;
15565 },
15566 total: function() {
15567 return module.total !== undefined ? module.total : false;
15568 }
15569 },
15570
15571 create: {
15572 progressPoll: function() {
15573 module.progressPoll = setTimeout(function() {
15574 module.update.toNextValue();
15575 module.remove.progressPoll();
15576 }, module.get.updateInterval());
15577 },
15578 },
15579
15580 is: {
15581 complete: function() {
15582 return module.is.success() || module.is.warning() || module.is.error();
15583 },
15584 success: function() {
15585 return $module.hasClass(className.success);
15586 },
15587 warning: function() {
15588 return $module.hasClass(className.warning);
15589 },
15590 error: function() {
15591 return $module.hasClass(className.error);
15592 },
15593 active: function() {
15594 return $module.hasClass(className.active);
15595 },
15596 visible: function() {
15597 return $module.is(':visible');
15598 }
15599 },
15600
15601 remove: {
15602 progressPoll: function() {
15603 module.verbose('Removing progress poll timer');
15604 if(module.progressPoll) {
15605 clearTimeout(module.progressPoll);
15606 delete module.progressPoll;
15607 }
15608 },
15609 nextValue: function() {
15610 module.verbose('Removing progress value stored for next update');
15611 delete module.nextValue;
15612 },
15613 state: function() {
15614 module.verbose('Removing stored state');
15615 delete module.total;
15616 delete module.percent;
15617 delete module.value;
15618 },
15619 active: function() {
15620 module.verbose('Removing active state');
15621 $module.removeClass(className.active);
15622 },
15623 success: function() {
15624 module.verbose('Removing success state');
15625 $module.removeClass(className.success);
15626 },
15627 warning: function() {
15628 module.verbose('Removing warning state');
15629 $module.removeClass(className.warning);
15630 },
15631 error: function() {
15632 module.verbose('Removing error state');
15633 $module.removeClass(className.error);
15634 }
15635 },
15636
15637 set: {
15638 barWidth: function(values) {
15639 module.debug("set bar width with ", values);
15640 values = module.helper.forceArray(values);
15641 var firstNonZeroIndex = -1;
15642 var lastNonZeroIndex = -1;
15643 var valuesSum = module.helper.sum(values);
15644 var barCounts = $bars.length;
15645 var isMultiple = barCounts > 1;
15646 var percents = values.map(function(value, index) {
15647 var allZero = (index === barCounts - 1 && valuesSum === 0);
15648 var $bar = $($bars[index]);
15649 if (value === 0 && isMultiple && !allZero) {
15650 $bar.css('display', 'none');
15651 } else {
15652 if (isMultiple && allZero) {
15653 $bar.css('background', 'transparent');
15654 }
15655 if (firstNonZeroIndex == -1) {
15656 firstNonZeroIndex = index;
15657 }
15658 lastNonZeroIndex = index;
15659 $bar.css({
15660 display: 'block',
15661 width: value + '%'
15662 });
15663 }
15664 return parseFloat(value);
15665 });
15666 values.forEach(function(_, index) {
15667 var $bar = $($bars[index]);
15668 $bar.css({
15669 borderTopLeftRadius: index == firstNonZeroIndex ? '' : 0,
15670 borderBottomLeftRadius: index == firstNonZeroIndex ? '' : 0,
15671 borderTopRightRadius: index == lastNonZeroIndex ? '' : 0,
15672 borderBottomRightRadius: index == lastNonZeroIndex ? '' : 0
15673 });
15674 });
15675 $module
15676 .attr('data-percent', percents)
15677 ;
15678 },
15679 duration: function(duration) {
15680 duration = duration || settings.duration;
15681 duration = (typeof duration == 'number')
15682 ? duration + 'ms'
15683 : duration
15684 ;
15685 module.verbose('Setting progress bar transition duration', duration);
15686 $bars
15687 .css({
15688 'transition-duration': duration
15689 })
15690 ;
15691 },
15692 percent: function(percents) {
15693 percents = module.helper.forceArray(percents).map(function(percent) {
15694 percent = (typeof percent == 'string')
15695 ? +(percent.replace('%', ''))
15696 : percent
15697 ;
15698 return (settings.limitValues)
15699 ? Math.max(0, Math.min(100, percent))
15700 : percent
15701 ;
15702 });
15703 var hasTotal = module.has.total();
15704 var totalPercent = module.helper.sum(percents);
15705 var isMultipleValues = percents.length > 1 && hasTotal;
15706 var sumTotal = module.helper.sum(module.helper.forceArray(module.value));
15707 if (isMultipleValues && sumTotal > module.total) {
15708 // Sum values instead of pecents to avoid precision issues when summing floats
15709 module.error(error.sumExceedsTotal, sumTotal, module.total);
15710 } else if (!isMultipleValues && totalPercent > 100) {
15711 // Sum before rounding since sum of rounded may have error though sum of actual is fine
15712 module.error(error.tooHigh, totalPercent);
15713 } else if (totalPercent < 0) {
15714 module.error(error.tooLow, totalPercent);
15715 } else {
15716 var autoPrecision = settings.precision > 0
15717 ? settings.precision
15718 : isMultipleValues
15719 ? module.helper.derivePrecision(Math.min.apply(null, module.value), module.total)
15720 : 0;
15721
15722 // round display percentage
15723 var roundedPercents = percents.map(function (percent) {
15724 return (autoPrecision > 0)
15725 ? Math.round(percent * (10 * autoPrecision)) / (10 * autoPrecision)
15726 : Math.round(percent)
15727 ;
15728 });
15729 module.percent = roundedPercents;
15730 if (hasTotal) {
15731 module.value = percents.map(function (percent) {
15732 return (autoPrecision > 0)
15733 ? Math.round((percent / 100) * module.total * (10 * autoPrecision)) / (10 * autoPrecision)
15734 : Math.round((percent / 100) * module.total * 10) / 10
15735 ;
15736 });
15737 }
15738 module.set.barWidth(percents);
15739 module.set.labelInterval();
15740 }
15741 settings.onChange.call(element, percents, module.value, module.total);
15742 },
15743 labelInterval: function() {
15744 var
15745 animationCallback = function() {
15746 module.verbose('Bar finished animating, removing continuous label updates');
15747 clearInterval(module.interval);
15748 animating = false;
15749 module.set.labels();
15750 }
15751 ;
15752 clearInterval(module.interval);
15753 module.bind.transitionEnd(animationCallback);
15754 animating = true;
15755 module.interval = setInterval(function() {
15756 var
15757 isInDOM = $.contains(document.documentElement, element)
15758 ;
15759 if(!isInDOM) {
15760 clearInterval(module.interval);
15761 animating = false;
15762 }
15763 module.set.labels();
15764 }, settings.framerate);
15765 },
15766 labels: function() {
15767 module.verbose('Setting both bar progress and outer label text');
15768 module.set.barLabel();
15769 module.set.state();
15770 },
15771 label: function(text) {
15772 text = text || '';
15773 if(text) {
15774 text = module.get.text(text);
15775 module.verbose('Setting label to text', text);
15776 $label.text(text);
15777 }
15778 },
15779 state: function(percent) {
15780 percent = (percent !== undefined)
15781 ? percent
15782 : module.helper.sum(module.percent)
15783 ;
15784 if(percent === 100) {
15785 if(settings.autoSuccess && $bars.length === 1 && !(module.is.warning() || module.is.error() || module.is.success())) {
15786 module.set.success();
15787 module.debug('Automatically triggering success at 100%');
15788 }
15789 else {
15790 module.verbose('Reached 100% removing active state');
15791 module.remove.active();
15792 module.remove.progressPoll();
15793 }
15794 }
15795 else if(percent > 0) {
15796 module.verbose('Adjusting active progress bar label', percent);
15797 module.set.active();
15798 }
15799 else {
15800 module.remove.active();
15801 module.set.label(settings.text.active);
15802 }
15803 },
15804 barLabel: function(text) {
15805 $progresses.map(function(index, element){
15806 var $progress = $(element);
15807 if (text !== undefined) {
15808 $progress.text( module.get.text(text, index) );
15809 }
15810 else if (settings.label == 'ratio' && module.has.total()) {
15811 module.verbose('Adding ratio to bar label');
15812 $progress.text( module.get.text(settings.text.ratio, index) );
15813 }
15814 else if (settings.label == 'percent') {
15815 module.verbose('Adding percentage to bar label');
15816 $progress.text( module.get.text(settings.text.percent, index) );
15817 }
15818 });
15819 },
15820 active: function(text) {
15821 text = text || settings.text.active;
15822 module.debug('Setting active state');
15823 if(settings.showActivity && !module.is.active() ) {
15824 $module.addClass(className.active);
15825 }
15826 module.remove.warning();
15827 module.remove.error();
15828 module.remove.success();
15829 text = settings.onLabelUpdate('active', text, module.value, module.total);
15830 if(text) {
15831 module.set.label(text);
15832 }
15833 module.bind.transitionEnd(function() {
15834 settings.onActive.call(element, module.value, module.total);
15835 });
15836 },
15837 success : function(text, keepState) {
15838 text = text || settings.text.success || settings.text.active;
15839 module.debug('Setting success state');
15840 $module.addClass(className.success);
15841 module.remove.active();
15842 module.remove.warning();
15843 module.remove.error();
15844 module.complete(keepState);
15845 if(settings.text.success) {
15846 text = settings.onLabelUpdate('success', text, module.value, module.total);
15847 module.set.label(text);
15848 }
15849 else {
15850 text = settings.onLabelUpdate('active', text, module.value, module.total);
15851 module.set.label(text);
15852 }
15853 module.bind.transitionEnd(function() {
15854 settings.onSuccess.call(element, module.total);
15855 });
15856 },
15857 warning : function(text, keepState) {
15858 text = text || settings.text.warning;
15859 module.debug('Setting warning state');
15860 $module.addClass(className.warning);
15861 module.remove.active();
15862 module.remove.success();
15863 module.remove.error();
15864 module.complete(keepState);
15865 text = settings.onLabelUpdate('warning', text, module.value, module.total);
15866 if(text) {
15867 module.set.label(text);
15868 }
15869 module.bind.transitionEnd(function() {
15870 settings.onWarning.call(element, module.value, module.total);
15871 });
15872 },
15873 error : function(text, keepState) {
15874 text = text || settings.text.error;
15875 module.debug('Setting error state');
15876 $module.addClass(className.error);
15877 module.remove.active();
15878 module.remove.success();
15879 module.remove.warning();
15880 module.complete(keepState);
15881 text = settings.onLabelUpdate('error', text, module.value, module.total);
15882 if(text) {
15883 module.set.label(text);
15884 }
15885 module.bind.transitionEnd(function() {
15886 settings.onError.call(element, module.value, module.total);
15887 });
15888 },
15889 transitionEvent: function() {
15890 transitionEnd = module.get.transitionEnd();
15891 },
15892 total: function(totalValue) {
15893 module.total = totalValue;
15894 },
15895 value: function(value) {
15896 module.value = module.helper.forceArray(value);
15897 },
15898 progress: function(value) {
15899 if(!module.has.progressPoll()) {
15900 module.debug('First update in progress update interval, immediately updating', value);
15901 module.update.progress(value);
15902 module.create.progressPoll();
15903 }
15904 else {
15905 module.debug('Updated within interval, setting next update to use new value', value);
15906 module.set.nextValue(value);
15907 }
15908 },
15909 nextValue: function(value) {
15910 module.nextValue = value;
15911 }
15912 },
15913
15914 update: {
15915 toNextValue: function() {
15916 var
15917 nextValue = module.nextValue
15918 ;
15919 if(nextValue) {
15920 module.debug('Update interval complete using last updated value', nextValue);
15921 module.update.progress(nextValue);
15922 module.remove.nextValue();
15923 }
15924 },
15925 progress: function(values) {
15926 var hasTotal = module.has.total();
15927 if (hasTotal) {
15928 module.set.value(values);
15929 }
15930 var percentCompletes = module.helper.forceArray(values).map(function(value) {
15931 var
15932 percentComplete
15933 ;
15934 value = module.get.numericValue(value);
15935 if (value === false) {
15936 module.error(error.nonNumeric, value);
15937 }
15938 value = module.get.normalizedValue(value);
15939 if (hasTotal) {
15940 percentComplete = module.total > 0 ? (value / module.total) * 100 : 100;
15941 module.debug('Calculating percent complete from total', percentComplete);
15942 }
15943 else {
15944 percentComplete = value;
15945 module.debug('Setting value to exact percentage value', percentComplete);
15946 }
15947 return percentComplete;
15948 });
15949 module.set.percent( percentCompletes );
15950 }
15951 },
15952
15953 setting: function(name, value) {
15954 module.debug('Changing setting', name, value);
15955 if( $.isPlainObject(name) ) {
15956 $.extend(true, settings, name);
15957 }
15958 else if(value !== undefined) {
15959 if($.isPlainObject(settings[name])) {
15960 $.extend(true, settings[name], value);
15961 }
15962 else {
15963 settings[name] = value;
15964 }
15965 }
15966 else {
15967 return settings[name];
15968 }
15969 },
15970 internal: function(name, value) {
15971 if( $.isPlainObject(name) ) {
15972 $.extend(true, module, name);
15973 }
15974 else if(value !== undefined) {
15975 module[name] = value;
15976 }
15977 else {
15978 return module[name];
15979 }
15980 },
15981 debug: function() {
15982 if(!settings.silent && settings.debug) {
15983 if(settings.performance) {
15984 module.performance.log(arguments);
15985 }
15986 else {
15987 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
15988 module.debug.apply(console, arguments);
15989 }
15990 }
15991 },
15992 verbose: function() {
15993 if(!settings.silent && settings.verbose && settings.debug) {
15994 if(settings.performance) {
15995 module.performance.log(arguments);
15996 }
15997 else {
15998 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
15999 module.verbose.apply(console, arguments);
16000 }
16001 }
16002 },
16003 error: function() {
16004 if(!settings.silent) {
16005 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
16006 module.error.apply(console, arguments);
16007 }
16008 },
16009 performance: {
16010 log: function(message) {
16011 var
16012 currentTime,
16013 executionTime,
16014 previousTime
16015 ;
16016 if(settings.performance) {
16017 currentTime = new Date().getTime();
16018 previousTime = time || currentTime;
16019 executionTime = currentTime - previousTime;
16020 time = currentTime;
16021 performance.push({
16022 'Name' : message[0],
16023 'Arguments' : [].slice.call(message, 1) || '',
16024 'Element' : element,
16025 'Execution Time' : executionTime
16026 });
16027 }
16028 clearTimeout(module.performance.timer);
16029 module.performance.timer = setTimeout(module.performance.display, 500);
16030 },
16031 display: function() {
16032 var
16033 title = settings.name + ':',
16034 totalTime = 0
16035 ;
16036 time = false;
16037 clearTimeout(module.performance.timer);
16038 $.each(performance, function(index, data) {
16039 totalTime += data['Execution Time'];
16040 });
16041 title += ' ' + totalTime + 'ms';
16042 if(moduleSelector) {
16043 title += ' \'' + moduleSelector + '\'';
16044 }
16045 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
16046 console.groupCollapsed(title);
16047 if(console.table) {
16048 console.table(performance);
16049 }
16050 else {
16051 $.each(performance, function(index, data) {
16052 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
16053 });
16054 }
16055 console.groupEnd();
16056 }
16057 performance = [];
16058 }
16059 },
16060 invoke: function(query, passedArguments, context) {
16061 var
16062 object = instance,
16063 maxDepth,
16064 found,
16065 response
16066 ;
16067 passedArguments = passedArguments || queryArguments;
16068 context = element || context;
16069 if(typeof query == 'string' && object !== undefined) {
16070 query = query.split(/[\. ]/);
16071 maxDepth = query.length - 1;
16072 $.each(query, function(depth, value) {
16073 var camelCaseValue = (depth != maxDepth)
16074 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
16075 : query
16076 ;
16077 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
16078 object = object[camelCaseValue];
16079 }
16080 else if( object[camelCaseValue] !== undefined ) {
16081 found = object[camelCaseValue];
16082 return false;
16083 }
16084 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
16085 object = object[value];
16086 }
16087 else if( object[value] !== undefined ) {
16088 found = object[value];
16089 return false;
16090 }
16091 else {
16092 module.error(error.method, query);
16093 return false;
16094 }
16095 });
16096 }
16097 if ( $.isFunction( found ) ) {
16098 response = found.apply(context, passedArguments);
16099 }
16100 else if(found !== undefined) {
16101 response = found;
16102 }
16103 if(Array.isArray(returnedValue)) {
16104 returnedValue.push(response);
16105 }
16106 else if(returnedValue !== undefined) {
16107 returnedValue = [returnedValue, response];
16108 }
16109 else if(response !== undefined) {
16110 returnedValue = response;
16111 }
16112 return found;
16113 }
16114 };
16115
16116 if(methodInvoked) {
16117 if(instance === undefined) {
16118 module.initialize();
16119 }
16120 module.invoke(query);
16121 }
16122 else {
16123 if(instance !== undefined) {
16124 instance.invoke('destroy');
16125 }
16126 module.initialize();
16127 }
16128 })
16129 ;
16130
16131 return (returnedValue !== undefined)
16132 ? returnedValue
16133 : this
16134 ;
16135};
16136
16137$.fn.progress.settings = {
16138
16139 name : 'Progress',
16140 namespace : 'progress',
16141
16142 silent : false,
16143 debug : false,
16144 verbose : false,
16145 performance : true,
16146
16147 random : {
16148 min : 2,
16149 max : 5
16150 },
16151
16152 duration : 300,
16153
16154 updateInterval : 'auto',
16155
16156 autoSuccess : true,
16157 showActivity : true,
16158 limitValues : true,
16159
16160 label : 'percent',
16161 precision : 0,
16162 framerate : (1000 / 30), /// 30 fps
16163
16164 percent : false,
16165 total : false,
16166 value : false,
16167
16168 // delay in ms for fail safe animation callback
16169 failSafeDelay : 100,
16170
16171 onLabelUpdate : function(state, text, value, total){
16172 return text;
16173 },
16174 onChange : function(percent, value, total){},
16175 onSuccess : function(total){},
16176 onActive : function(value, total){},
16177 onError : function(value, total){},
16178 onWarning : function(value, total){},
16179
16180 error : {
16181 method : 'The method you called is not defined.',
16182 nonNumeric : 'Progress value is non numeric',
16183 tooHigh : 'Value specified is above 100%',
16184 tooLow : 'Value specified is below 0%',
16185 sumExceedsTotal : 'Sum of multple values exceed total',
16186 },
16187
16188 regExp: {
16189 variable: /\{\$*[A-z0-9]+\}/g
16190 },
16191
16192 metadata: {
16193 percent : 'percent',
16194 total : 'total',
16195 value : 'value'
16196 },
16197
16198 selector : {
16199 bar : '> .bar',
16200 label : '> .label',
16201 progress : '.bar > .progress'
16202 },
16203
16204 text : {
16205 active : false,
16206 error : false,
16207 success : false,
16208 warning : false,
16209 percent : '{percent}%',
16210 ratio : '{value} of {total}',
16211 bars : ['']
16212 },
16213
16214 className : {
16215 active : 'active',
16216 error : 'error',
16217 success : 'success',
16218 warning : 'warning'
16219 }
16220
16221};
16222
16223
16224})( jQuery, window, document );
16225
16226/*!
16227 * # Fomantic-UI 2.8.8 - Slider
16228 * http://github.com/fomantic/Fomantic-UI/
16229 *
16230 *
16231 * Released under the MIT license
16232 * http://opensource.org/licenses/MIT
16233 *
16234 */
16235
16236;(function ( $, window, document, undefined ) {
16237
16238"use strict";
16239
16240window = (typeof window != 'undefined' && window.Math == Math)
16241 ? window
16242 : (typeof self != 'undefined' && self.Math == Math)
16243 ? self
16244 : Function('return this')()
16245;
16246
16247$.fn.slider = function(parameters) {
16248
16249 var
16250 $allModules = $(this),
16251 $window = $(window),
16252
16253 moduleSelector = $allModules.selector || '',
16254
16255 time = new Date().getTime(),
16256 performance = [],
16257
16258 query = arguments[0],
16259 methodInvoked = (typeof query == 'string'),
16260 queryArguments = [].slice.call(arguments, 1),
16261
16262 alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
16263
16264 SINGLE_STEP = 1,
16265 BIG_STEP = 2,
16266 NO_STEP = 0,
16267 SINGLE_BACKSTEP = -1,
16268 BIG_BACKSTEP = -2,
16269
16270 // Used to manage document bound events.
16271 // Use this so that we can distinguish between which document events are bound to which range.
16272 currentRange = 0,
16273
16274 returnedValue
16275 ;
16276
16277 $allModules
16278 .each(function() {
16279
16280 var
16281 settings = ( $.isPlainObject(parameters) )
16282 ? $.extend(true, {}, $.fn.slider.settings, parameters)
16283 : $.extend({}, $.fn.slider.settings),
16284
16285 className = settings.className,
16286 metadata = settings.metadata,
16287 namespace = settings.namespace,
16288 error = settings.error,
16289 keys = settings.keys,
16290 interpretLabel = settings.interpretLabel,
16291
16292 isHover = false,
16293 eventNamespace = '.' + namespace,
16294 moduleNamespace = 'module-' + namespace,
16295
16296 $module = $(this),
16297 $currThumb,
16298 $thumb,
16299 $secondThumb,
16300 $track,
16301 $trackFill,
16302 $labels,
16303
16304 element = this,
16305 instance = $module.data(moduleNamespace),
16306
16307 documentEventID,
16308
16309 value,
16310 position,
16311 secondPos,
16312 offset,
16313 precision,
16314 isTouch,
16315 gapRatio = 1,
16316 previousValue,
16317
16318 initialPosition,
16319 initialLoad,
16320 module
16321 ;
16322
16323 module = {
16324
16325 initialize: function() {
16326 module.debug('Initializing slider', settings);
16327 initialLoad = true;
16328
16329 currentRange += 1;
16330 documentEventID = currentRange;
16331
16332 isTouch = module.setup.testOutTouch();
16333 module.setup.layout();
16334 module.setup.labels();
16335
16336 if(!module.is.disabled()) {
16337 module.bind.events();
16338 }
16339
16340 module.read.metadata();
16341 module.read.settings();
16342
16343 initialLoad = false;
16344 module.instantiate();
16345 },
16346
16347 instantiate: function() {
16348 module.verbose('Storing instance of slider', module);
16349 instance = module;
16350 $module
16351 .data(moduleNamespace, module)
16352 ;
16353 },
16354
16355 destroy: function() {
16356 module.verbose('Destroying previous slider for', $module);
16357 clearInterval(instance.interval);
16358 module.unbind.events();
16359 module.unbind.slidingEvents();
16360 $module.removeData(moduleNamespace);
16361 instance = undefined;
16362 },
16363
16364 setup: {
16365 layout: function() {
16366 if( $module.attr('tabindex') === undefined) {
16367 $module.attr('tabindex', 0);
16368 }
16369 if($module.find('.inner').length == 0) {
16370 $module.append("<div class='inner'>"
16371 + "<div class='track'></div>"
16372 + "<div class='track-fill'></div>"
16373 + "<div class='thumb'></div>"
16374 + "</div>");
16375 }
16376 precision = module.get.precision();
16377 $thumb = $module.find('.thumb:not(.second)');
16378 $currThumb = $thumb;
16379 if(module.is.range()) {
16380 if($module.find('.thumb.second').length == 0) {
16381 $module.find('.inner').append("<div class='thumb second'></div>");
16382 }
16383 $secondThumb = $module.find('.thumb.second');
16384 }
16385 $track = $module.find('.track');
16386 $trackFill = $module.find('.track-fill');
16387 offset = $thumb.width() / 2;
16388 },
16389 labels: function() {
16390 if(module.is.labeled()) {
16391 $labels = $module.find('.labels:not(.auto)');
16392 if($labels.length != 0) {
16393 module.setup.customLabel();
16394 } else {
16395 module.setup.autoLabel();
16396 }
16397
16398 if (settings.showLabelTicks) {
16399 $module.addClass(className.ticked)
16400 }
16401 }
16402 },
16403 testOutTouch: function() {
16404 try {
16405 document.createEvent('TouchEvent');
16406 return true;
16407 } catch (e) {
16408 return false;
16409 }
16410 },
16411 customLabel: function() {
16412 var
16413 $children = $labels.find('.label'),
16414 numChildren = $children.length,
16415 min = module.get.min(),
16416 max = module.get.max(),
16417 ratio
16418 ;
16419 $children.each(function(index) {
16420 var
16421 $child = $(this),
16422 attrValue = $child.attr('data-value')
16423 ;
16424 if(attrValue) {
16425 attrValue = attrValue > max ? max : attrValue < min ? min : attrValue;
16426 ratio = (attrValue - min) / (max - min);
16427 } else {
16428 ratio = (index + 1) / (numChildren + 1);
16429 }
16430 module.update.labelPosition(ratio, $(this));
16431 });
16432 },
16433 autoLabel: function() {
16434 $labels = $module.find('.labels');
16435 if($labels.length != 0) {
16436 $labels.empty();
16437 }
16438 else {
16439 $labels = $module.append('<ul class="auto labels"></ul>').find('.labels');
16440 }
16441 for(var i = 0, len = module.get.numLabels(); i <= len; i++) {
16442 var
16443 labelText = module.get.label(i),
16444 $label = (labelText !== "")
16445 ? !(i % module.get.gapRatio())
16446 ? $('<li class="label">' + labelText + '</li>')
16447 : $('<li class="halftick label"></li>')
16448 : null,
16449 ratio = i / len
16450 ;
16451 if($label) {
16452 module.update.labelPosition(ratio, $label);
16453 $labels.append($label);
16454 }
16455 }
16456 }
16457 },
16458
16459 bind: {
16460 events: function() {
16461 module.bind.globalKeyboardEvents();
16462 module.bind.keyboardEvents();
16463 module.bind.mouseEvents();
16464 if(module.is.touch()) {
16465 module.bind.touchEvents();
16466 }
16467 if (settings.autoAdjustLabels) {
16468 module.bind.windowEvents();
16469 }
16470 },
16471 keyboardEvents: function() {
16472 module.verbose('Binding keyboard events');
16473 $module.on('keydown' + eventNamespace, module.event.keydown);
16474 },
16475 globalKeyboardEvents: function() {
16476 $(document).on('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
16477 },
16478 mouseEvents: function() {
16479 module.verbose('Binding mouse events');
16480 $module.find('.track, .thumb, .inner').on('mousedown' + eventNamespace, function(event) {
16481 event.stopImmediatePropagation();
16482 event.preventDefault();
16483 module.event.down(event);
16484 });
16485 $module.on('mousedown' + eventNamespace, module.event.down);
16486 $module.on('mouseenter' + eventNamespace, function(event) {
16487 isHover = true;
16488 });
16489 $module.on('mouseleave' + eventNamespace, function(event) {
16490 isHover = false;
16491 });
16492 },
16493 touchEvents: function() {
16494 module.verbose('Binding touch events');
16495 $module.find('.track, .thumb, .inner').on('touchstart' + eventNamespace, function(event) {
16496 event.stopImmediatePropagation();
16497 event.preventDefault();
16498 module.event.down(event);
16499 });
16500 $module.on('touchstart' + eventNamespace, module.event.down);
16501 },
16502 slidingEvents: function() {
16503 // these don't need the identifier because we only ever want one of them to be registered with document
16504 module.verbose('Binding page wide events while handle is being draged');
16505 if(module.is.touch()) {
16506 $(document).on('touchmove' + eventNamespace, module.event.move);
16507 $(document).on('touchend' + eventNamespace, module.event.up);
16508 }
16509 else {
16510 $(document).on('mousemove' + eventNamespace, module.event.move);
16511 $(document).on('mouseup' + eventNamespace, module.event.up);
16512 }
16513 },
16514 windowEvents: function() {
16515 $window.on('resize' + eventNamespace, module.event.resize);
16516 }
16517 },
16518
16519 unbind: {
16520 events: function() {
16521 $module.find('.track, .thumb, .inner').off('mousedown' + eventNamespace);
16522 $module.find('.track, .thumb, .inner').off('touchstart' + eventNamespace);
16523 $module.off('mousedown' + eventNamespace);
16524 $module.off('mouseenter' + eventNamespace);
16525 $module.off('mouseleave' + eventNamespace);
16526 $module.off('touchstart' + eventNamespace);
16527 $module.off('keydown' + eventNamespace);
16528 $module.off('focusout' + eventNamespace);
16529 $(document).off('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
16530 $window.off('resize' + eventNamespace);
16531 },
16532 slidingEvents: function() {
16533 if(module.is.touch()) {
16534 $(document).off('touchmove' + eventNamespace);
16535 $(document).off('touchend' + eventNamespace);
16536 } else {
16537 $(document).off('mousemove' + eventNamespace);
16538 $(document).off('mouseup' + eventNamespace);
16539 }
16540 },
16541 },
16542
16543 event: {
16544 down: function(event) {
16545 event.preventDefault();
16546 if(module.is.range()) {
16547 var
16548 eventPos = module.determine.eventPos(event),
16549 newPos = module.determine.pos(eventPos)
16550 ;
16551 // Special handling if range mode and both thumbs have the same value
16552 if(settings.preventCrossover && module.is.range() && module.thumbVal === module.secondThumbVal) {
16553 initialPosition = newPos;
16554 $currThumb = undefined;
16555 } else {
16556 $currThumb = module.determine.closestThumb(newPos);
16557 }
16558 if (previousValue === undefined) {
16559 previousValue = module.get.currentThumbValue();
16560 }
16561 } else if (previousValue === undefined) {
16562 previousValue = module.get.value();
16563 }
16564
16565 if(!module.is.disabled()) {
16566 module.bind.slidingEvents();
16567 }
16568 },
16569 move: function(event) {
16570 event.preventDefault();
16571 var value = module.determine.valueFromEvent(event);
16572 if($currThumb === undefined) {
16573 var
16574 eventPos = module.determine.eventPos(event),
16575 newPos = module.determine.pos(eventPos)
16576 ;
16577 $currThumb = initialPosition > newPos ? $thumb : $secondThumb;
16578 }
16579 if(module.get.step() == 0 || module.is.smooth()) {
16580 var
16581 thumbVal = module.thumbVal,
16582 secondThumbVal = module.secondThumbVal,
16583 thumbSmoothVal = module.determine.smoothValueFromEvent(event)
16584 ;
16585 if(!$currThumb.hasClass('second')) {
16586 if(settings.preventCrossover && module.is.range()) {
16587 value = Math.min(secondThumbVal, value);
16588 thumbSmoothVal = Math.min(secondThumbVal, thumbSmoothVal);
16589 }
16590 thumbVal = value;
16591 } else {
16592 if(settings.preventCrossover && module.is.range()) {
16593 value = Math.max(thumbVal, value);
16594 thumbSmoothVal = Math.max(thumbVal, thumbSmoothVal);
16595 }
16596 secondThumbVal = value;
16597 }
16598 value = Math.abs(thumbVal - (secondThumbVal || 0));
16599 module.update.position(thumbSmoothVal);
16600 settings.onMove.call(element, value, thumbVal, secondThumbVal);
16601 } else {
16602 module.update.value(value, function(value, thumbVal, secondThumbVal) {
16603 settings.onMove.call(element, value, thumbVal, secondThumbVal);
16604 });
16605 }
16606 },
16607 up: function(event) {
16608 event.preventDefault();
16609 var value = module.determine.valueFromEvent(event);
16610 module.set.value(value);
16611 module.unbind.slidingEvents();
16612 if (previousValue !== undefined) {
16613 previousValue = undefined;
16614 }
16615 },
16616 keydown: function(event, first) {
16617 if(settings.preventCrossover && module.is.range() && module.thumbVal === module.secondThumbVal) {
16618 $currThumb = undefined;
16619 }
16620 if(module.is.focused()) {
16621 $(document).trigger(event);
16622 }
16623 if(first || module.is.focused()) {
16624 var step = module.determine.keyMovement(event);
16625 if(step != NO_STEP) {
16626 event.preventDefault();
16627 switch(step) {
16628 case SINGLE_STEP:
16629 module.takeStep();
16630 break;
16631 case BIG_STEP:
16632 module.takeStep(module.get.multiplier());
16633 break;
16634 case SINGLE_BACKSTEP:
16635 module.backStep();
16636 break;
16637 case BIG_BACKSTEP:
16638 module.backStep(module.get.multiplier());
16639 break;
16640 }
16641 }
16642 }
16643 },
16644 activateFocus: function(event) {
16645 if(!module.is.focused() && module.is.hover() && module.determine.keyMovement(event) != NO_STEP) {
16646 event.preventDefault();
16647 module.event.keydown(event, true);
16648 $module.focus();
16649 }
16650 },
16651 resize: function(_event) {
16652 // To avoid a useless performance cost, we only call the label refresh when its necessary
16653 if (gapRatio != module.get.gapRatio()) {
16654 module.setup.labels();
16655 gapRatio = module.get.gapRatio();
16656 }
16657 }
16658 },
16659
16660 resync: function() {
16661 module.verbose('Resyncing thumb position based on value');
16662 if(module.is.range()) {
16663 module.update.position(module.secondThumbVal, $secondThumb);
16664 }
16665 module.update.position(module.thumbVal, $thumb);
16666 module.setup.labels();
16667 },
16668 takeStep: function(multiplier) {
16669 var
16670 multiplier = multiplier != undefined ? multiplier : 1,
16671 step = module.get.step(),
16672 currValue = module.get.currentThumbValue()
16673 ;
16674 module.verbose('Taking a step');
16675 if(step > 0) {
16676 module.set.value(currValue + step * multiplier);
16677 } else if (step == 0){
16678 var
16679 precision = module.get.precision(),
16680 newValue = currValue + (multiplier/precision)
16681 ;
16682 module.set.value(Math.round(newValue * precision) / precision);
16683 }
16684 },
16685
16686 backStep: function(multiplier) {
16687 var
16688 multiplier = multiplier != undefined ? multiplier : 1,
16689 step = module.get.step(),
16690 currValue = module.get.currentThumbValue()
16691 ;
16692 module.verbose('Going back a step');
16693 if(step > 0) {
16694 module.set.value(currValue - step * multiplier);
16695 } else if (step == 0) {
16696 var
16697 precision = module.get.precision(),
16698 newValue = currValue - (multiplier/precision)
16699 ;
16700 module.set.value(Math.round(newValue * precision) / precision);
16701 }
16702 },
16703
16704 is: {
16705 range: function() {
16706 return $module.hasClass(settings.className.range);
16707 },
16708 hover: function() {
16709 return isHover;
16710 },
16711 focused: function() {
16712 return $module.is(':focus');
16713 },
16714 disabled: function() {
16715 return $module.hasClass(settings.className.disabled);
16716 },
16717 labeled: function() {
16718 return $module.hasClass(settings.className.labeled);
16719 },
16720 reversed: function() {
16721 return $module.hasClass(settings.className.reversed);
16722 },
16723 vertical: function() {
16724 return $module.hasClass(settings.className.vertical);
16725 },
16726 smooth: function() {
16727 return settings.smooth || $module.hasClass(settings.className.smooth);
16728 },
16729 touch: function() {
16730 return isTouch;
16731 }
16732 },
16733
16734 get: {
16735 trackOffset: function() {
16736 if (module.is.vertical()) {
16737 return $track.offset().top;
16738 } else {
16739 return $track.offset().left;
16740 }
16741 },
16742 trackLength: function() {
16743 if (module.is.vertical()) {
16744 return $track.height();
16745 } else {
16746 return $track.width();
16747 }
16748 },
16749 trackLeft: function() {
16750 if (module.is.vertical()) {
16751 return $track.position().top;
16752 } else {
16753 return $track.position().left;
16754 }
16755 },
16756 trackStartPos: function() {
16757 return module.is.reversed() ? module.get.trackLeft() + module.get.trackLength() : module.get.trackLeft();
16758 },
16759 trackEndPos: function() {
16760 return module.is.reversed() ? module.get.trackLeft() : module.get.trackLeft() + module.get.trackLength();
16761 },
16762 trackStartMargin: function () {
16763 var margin;
16764 if (module.is.vertical()) {
16765 margin = module.is.reversed() ? $module.css('padding-bottom') : $module.css('padding-top');
16766 } else {
16767 margin = module.is.reversed() ? $module.css('padding-right') : $module.css('padding-left');
16768 }
16769 return margin || '0px';
16770 },
16771 trackEndMargin: function () {
16772 var margin;
16773 if (module.is.vertical()) {
16774 margin = module.is.reversed() ? $module.css('padding-top') : $module.css('padding-bottom');
16775 } else {
16776 margin = module.is.reversed() ? $module.css('padding-left') : $module.css('padding-right');
16777 }
16778 return margin || '0px';
16779 },
16780 precision: function() {
16781 var
16782 decimalPlaces,
16783 step = module.get.step()
16784 ;
16785 if(step != 0) {
16786 var split = String(step).split('.');
16787 if(split.length == 2) {
16788 decimalPlaces = split[1].length;
16789 } else {
16790 decimalPlaces = 0;
16791 }
16792 } else {
16793 decimalPlaces = settings.decimalPlaces;
16794 }
16795 var precision = Math.pow(10, decimalPlaces);
16796 module.debug('Precision determined', precision);
16797 return precision;
16798 },
16799 min: function() {
16800 return settings.min;
16801 },
16802 max: function() {
16803 var step = module.get.step(),
16804 min = module.get.min(),
16805 quotient = step === 0 ? 0 : Math.floor((settings.max - min) / step),
16806 remainder = step === 0 ? 0 : (settings.max - min) % step;
16807 return remainder === 0 ? settings.max : min + quotient * step;
16808 },
16809 step: function() {
16810 return settings.step;
16811 },
16812 numLabels: function() {
16813 var value = Math.round((module.get.max() - module.get.min()) / (module.get.step() === 0 ? 1 : module.get.step()));
16814 module.debug('Determined that there should be ' + value + ' labels');
16815 return value;
16816 },
16817 labelType: function() {
16818 return settings.labelType;
16819 },
16820 label: function(value) {
16821 if(interpretLabel) {
16822 return interpretLabel(value);
16823 }
16824
16825 switch (settings.labelType) {
16826 case settings.labelTypes.number:
16827 return Math.round(((value * (module.get.step() === 0 ? 1 : module.get.step())) + module.get.min()) * precision ) / precision;
16828 case settings.labelTypes.letter:
16829 return alphabet[(value) % 26];
16830 default:
16831 return value;
16832 }
16833 },
16834 value: function() {
16835 return value;
16836 },
16837 currentThumbValue: function() {
16838 return $currThumb !== undefined && $currThumb.hasClass('second') ? module.secondThumbVal : module.thumbVal;
16839 },
16840 thumbValue: function(which) {
16841 switch(which) {
16842 case 'second':
16843 if(module.is.range()) {
16844 return module.secondThumbVal;
16845 }
16846 else {
16847 module.error(error.notrange);
16848 break;
16849 }
16850 case 'first':
16851 default:
16852 return module.thumbVal;
16853 }
16854 },
16855 multiplier: function() {
16856 return settings.pageMultiplier;
16857 },
16858 thumbPosition: function(which) {
16859 switch(which) {
16860 case 'second':
16861 if(module.is.range()) {
16862 return secondPos;
16863 }
16864 else {
16865 module.error(error.notrange);
16866 break;
16867 }
16868 case 'first':
16869 default:
16870 return position;
16871 }
16872 },
16873 gapRatio: function() {
16874 var gapRatio = 1;
16875
16876 if( settings.autoAdjustLabels ) {
16877 var
16878 numLabels = module.get.numLabels(),
16879 trackLength = module.get.trackLength(),
16880 gapCounter = 1
16881 ;
16882
16883 // While the distance between two labels is too short,
16884 // we divide the number of labels at each iteration
16885 // and apply only if the modulo of the operation is an odd number.
16886 if(trackLength>0){
16887 while ((trackLength / numLabels) * gapCounter < settings.labelDistance) {
16888 if( !(numLabels % gapCounter) ) {
16889 gapRatio = gapCounter;
16890 }
16891 gapCounter += 1;
16892 }
16893 }
16894 }
16895 return gapRatio;
16896 }
16897 },
16898
16899 determine: {
16900 pos: function(pagePos) {
16901 return module.is.reversed()
16902 ?
16903 module.get.trackStartPos() - pagePos + module.get.trackOffset()
16904 :
16905 pagePos - module.get.trackOffset() - module.get.trackStartPos()
16906 ;
16907 },
16908 closestThumb: function(eventPos) {
16909 var
16910 thumbPos = parseFloat(module.determine.thumbPos($thumb)),
16911 thumbDelta = Math.abs(eventPos - thumbPos),
16912 secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
16913 secondThumbDelta = Math.abs(eventPos - secondThumbPos)
16914 ;
16915 if(thumbDelta === secondThumbDelta && module.get.thumbValue() === module.get.min()) {
16916 return $secondThumb;
16917 }
16918 return thumbDelta <= secondThumbDelta ? $thumb : $secondThumb;
16919 },
16920 closestThumbPos: function(eventPos) {
16921 var
16922 thumbPos = parseFloat(module.determine.thumbPos($thumb)),
16923 thumbDelta = Math.abs(eventPos - thumbPos),
16924 secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
16925 secondThumbDelta = Math.abs(eventPos - secondThumbPos)
16926 ;
16927 return thumbDelta <= secondThumbDelta ? thumbPos : secondThumbPos;
16928 },
16929 thumbPos: function($element) {
16930 var pos =
16931 module.is.vertical()
16932 ?
16933 module.is.reversed() ? $element.css('bottom') : $element.css('top')
16934 :
16935 module.is.reversed() ? $element.css('right') : $element.css('left')
16936 ;
16937 return pos;
16938 },
16939 positionFromValue: function(value) {
16940 var
16941 min = module.get.min(),
16942 max = module.get.max(),
16943 value = value > max ? max : value < min ? min : value,
16944 trackLength = module.get.trackLength(),
16945 ratio = (value - min) / (max - min),
16946 position = Math.round(ratio * trackLength)
16947 ;
16948 module.verbose('Determined position: ' + position + ' from value: ' + value);
16949 return position;
16950 },
16951 positionFromRatio: function(ratio) {
16952 var
16953 trackLength = module.get.trackLength(),
16954 step = module.get.step(),
16955 position = Math.round(ratio * trackLength),
16956 adjustedPos = (step == 0) ? position : Math.round(position / step) * step
16957 ;
16958 return adjustedPos;
16959 },
16960 valueFromEvent: function(event) {
16961 var
16962 eventPos = module.determine.eventPos(event),
16963 newPos = module.determine.pos(eventPos),
16964 value
16965 ;
16966 if(eventPos < module.get.trackOffset()) {
16967 value = module.is.reversed() ? module.get.max() : module.get.min();
16968 } else if(eventPos > module.get.trackOffset() + module.get.trackLength()) {
16969 value = module.is.reversed() ? module.get.min() : module.get.max();
16970 } else {
16971 value = module.determine.value(newPos);
16972 }
16973 return value;
16974 },
16975 smoothValueFromEvent: function(event) {
16976 var
16977 min = module.get.min(),
16978 max = module.get.max(),
16979 trackLength = module.get.trackLength(),
16980 eventPos = module.determine.eventPos(event),
16981 newPos = eventPos - module.get.trackOffset(),
16982 ratio,
16983 value
16984 ;
16985 newPos = newPos < 0 ? 0 : newPos > trackLength ? trackLength : newPos;
16986 ratio = newPos / trackLength;
16987 if (module.is.reversed()) {
16988 ratio = 1 - ratio;
16989 }
16990 value = ratio * (max - min) + min;
16991 return value;
16992 },
16993 eventPos: function(event) {
16994 if(module.is.touch()) {
16995 var
16996 touchEvent = event.changedTouches ? event : event.originalEvent,
16997 touches = touchEvent.changedTouches[0] ? touchEvent.changedTouches : touchEvent.touches,
16998 touchY = touches[0].pageY,
16999 touchX = touches[0].pageX
17000 ;
17001 return module.is.vertical() ? touchY : touchX;
17002 }
17003 var
17004 clickY = event.pageY || event.originalEvent.pageY,
17005 clickX = event.pageX || event.originalEvent.pageX
17006 ;
17007 return module.is.vertical() ? clickY : clickX;
17008 },
17009 value: function(position) {
17010 var
17011 startPos = module.is.reversed() ? module.get.trackEndPos() : module.get.trackStartPos(),
17012 endPos = module.is.reversed() ? module.get.trackStartPos() : module.get.trackEndPos(),
17013 ratio = (position - startPos) / (endPos - startPos),
17014 range = module.get.max() - module.get.min(),
17015 step = module.get.step(),
17016 value = (ratio * range),
17017 difference = (step == 0) ? value : Math.round(value / step) * step
17018 ;
17019 module.verbose('Determined value based upon position: ' + position + ' as: ' + value);
17020 if(value != difference) {
17021 module.verbose('Rounding value to closest step: ' + difference);
17022 }
17023 // Use precision to avoid ugly Javascript floating point rounding issues
17024 // (like 35 * .01 = 0.35000000000000003)
17025 module.verbose('Cutting off additional decimal places');
17026 return Math.round((difference + module.get.min()) * precision) / precision;
17027 },
17028 keyMovement: function(event) {
17029 var
17030 key = event.which,
17031 downArrow =
17032 module.is.vertical()
17033 ?
17034 module.is.reversed() ? keys.downArrow : keys.upArrow
17035 :
17036 keys.downArrow
17037 ,
17038 upArrow =
17039 module.is.vertical()
17040 ?
17041 module.is.reversed() ? keys.upArrow : keys.downArrow
17042 :
17043 keys.upArrow
17044 ,
17045 leftArrow =
17046 !module.is.vertical()
17047 ?
17048 module.is.reversed() ? keys.rightArrow : keys.leftArrow
17049 :
17050 keys.leftArrow
17051 ,
17052 rightArrow =
17053 !module.is.vertical()
17054 ?
17055 module.is.reversed() ? keys.leftArrow : keys.rightArrow
17056 :
17057 keys.rightArrow
17058 ;
17059 if(key == downArrow || key == leftArrow) {
17060 return SINGLE_BACKSTEP;
17061 } else if(key == upArrow || key == rightArrow) {
17062 return SINGLE_STEP;
17063 } else if (key == keys.pageDown) {
17064 return BIG_BACKSTEP;
17065 } else if (key == keys.pageUp) {
17066 return BIG_STEP;
17067 } else {
17068 return NO_STEP;
17069 }
17070 }
17071 },
17072
17073 handleNewValuePosition: function(val) {
17074 var
17075 min = module.get.min(),
17076 max = module.get.max(),
17077 newPos
17078 ;
17079 if (val <= min) {
17080 val = min;
17081 } else if (val >= max) {
17082 val = max;
17083 }
17084 newPos = module.determine.positionFromValue(val);
17085 return newPos;
17086 },
17087
17088 set: {
17089 value: function(newValue, fireChange) {
17090 fireChange = fireChange !== false;
17091 var toReset = previousValue === undefined;
17092 previousValue = previousValue === undefined ? module.get.value() : previousValue;
17093 module.update.value(newValue, function(value, thumbVal, secondThumbVal) {
17094 if ((!initialLoad || settings.fireOnInit) && fireChange){
17095 if (newValue !== previousValue) {
17096 settings.onChange.call(element, value, thumbVal, secondThumbVal);
17097 }
17098 settings.onMove.call(element, value, thumbVal, secondThumbVal);
17099 }
17100 if (toReset) {
17101 previousValue = undefined;
17102 }
17103 });
17104 },
17105 rangeValue: function(first, second, fireChange) {
17106 fireChange = fireChange !== false;
17107 if(module.is.range()) {
17108 var
17109 min = module.get.min(),
17110 max = module.get.max(),
17111 toReset = previousValue === undefined
17112 ;
17113 previousValue = previousValue === undefined ? module.get.value() : previousValue;
17114 if (first <= min) {
17115 first = min;
17116 } else if(first >= max){
17117 first = max;
17118 }
17119 if (second <= min) {
17120 second = min;
17121 } else if(second >= max){
17122 second = max;
17123 }
17124 module.thumbVal = first;
17125 module.secondThumbVal = second;
17126 value = Math.abs(module.thumbVal - module.secondThumbVal);
17127 module.update.position(module.thumbVal, $thumb);
17128 module.update.position(module.secondThumbVal, $secondThumb);
17129 if ((!initialLoad || settings.fireOnInit) && fireChange) {
17130 if (value !== previousValue) {
17131 settings.onChange.call(element, value, module.thumbVal, module.secondThumbVal);
17132 }
17133 settings.onMove.call(element, value, module.thumbVal, module.secondThumbVal);
17134 }
17135 if (toReset) {
17136 previousValue = undefined;
17137 }
17138 } else {
17139 module.error(error.notrange);
17140 }
17141 },
17142 position: function(position, which) {
17143 var thumbVal = module.determine.value(position);
17144 switch (which) {
17145 case 'second':
17146 module.secondThumbVal = thumbVal;
17147 module.update.position(thumbVal, $secondThumb);
17148 break;
17149 default:
17150 module.thumbVal = thumbVal;
17151 module.update.position(thumbVal, $thumb);
17152 }
17153 value = Math.abs(module.thumbVal - (module.secondThumbVal || 0));
17154 module.set.value(value);
17155 }
17156 },
17157
17158 update: {
17159 value: function(newValue, callback) {
17160 var
17161 min = module.get.min(),
17162 max = module.get.max()
17163 ;
17164 if (newValue <= min) {
17165 newValue = min;
17166 } else if(newValue >= max){
17167 newValue = max;
17168 }
17169 if(!module.is.range()) {
17170 value = newValue;
17171 module.thumbVal = value;
17172 } else {
17173 if($currThumb === undefined) {
17174 $currThumb = newValue <= module.get.currentThumbValue() ? $thumb : $secondThumb;
17175 }
17176 if(!$currThumb.hasClass('second')) {
17177 if(settings.preventCrossover && module.is.range()) {
17178 newValue = Math.min(module.secondThumbVal, newValue);
17179 }
17180 module.thumbVal = newValue;
17181 } else {
17182 if(settings.preventCrossover && module.is.range()) {
17183 newValue = Math.max(module.thumbVal, newValue);
17184 }
17185 module.secondThumbVal = newValue;
17186 }
17187 value = Math.abs(module.thumbVal - module.secondThumbVal);
17188 }
17189 module.update.position(newValue);
17190 module.debug('Setting slider value to ' + value);
17191 if(typeof callback === 'function') {
17192 callback(value, module.thumbVal, module.secondThumbVal);
17193 }
17194 },
17195 position: function(newValue, $element) {
17196 var
17197 newPos = module.handleNewValuePosition(newValue),
17198 $targetThumb = $element != undefined ? $element : $currThumb,
17199 thumbVal = module.thumbVal || module.get.min(),
17200 secondThumbVal = module.secondThumbVal || module.get.min()
17201 ;
17202 if(module.is.range()) {
17203 if(!$targetThumb.hasClass('second')) {
17204 position = newPos;
17205 thumbVal = newValue;
17206 } else {
17207 secondPos = newPos;
17208 secondThumbVal = newValue;
17209 }
17210 } else {
17211 position = newPos;
17212 thumbVal = newValue;
17213 }
17214 var
17215 trackPosValue,
17216 thumbPosValue,
17217 min = module.get.min(),
17218 max = module.get.max(),
17219 thumbPosPercent = 100 * (newValue - min) / (max - min),
17220 trackStartPosPercent = 100 * (Math.min(thumbVal, secondThumbVal) - min) / (max - min),
17221 trackEndPosPercent = 100 * (1 - (Math.max(thumbVal, secondThumbVal) - min) / (max - min))
17222 ;
17223 if (module.is.vertical()) {
17224 if (module.is.reversed()) {
17225 thumbPosValue = {bottom: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', top: 'auto'};
17226 trackPosValue = {bottom: trackStartPosPercent + '%', top: trackEndPosPercent + '%'};
17227 }
17228 else {
17229 thumbPosValue = {top: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', bottom: 'auto'};
17230 trackPosValue = {top: trackStartPosPercent + '%', bottom: trackEndPosPercent + '%'};
17231 }
17232 } else {
17233 if (module.is.reversed()) {
17234 thumbPosValue = {right: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', left: 'auto'};
17235 trackPosValue = {right: trackStartPosPercent + '%', left: trackEndPosPercent + '%'};
17236 }
17237 else {
17238 thumbPosValue = {left: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', right: 'auto'};
17239 trackPosValue = {left: trackStartPosPercent + '%', right: trackEndPosPercent + '%'};
17240 }
17241 }
17242 $targetThumb.css(thumbPosValue);
17243 $trackFill.css(trackPosValue);
17244 module.debug('Setting slider position to ' + newPos);
17245 },
17246 labelPosition: function (ratio, $label) {
17247 var
17248 startMargin = module.get.trackStartMargin(),
17249 endMargin = module.get.trackEndMargin(),
17250 posDir =
17251 module.is.vertical()
17252 ?
17253 module.is.reversed() ? 'bottom' : 'top'
17254 :
17255 module.is.reversed() ? 'right' : 'left',
17256 startMarginMod = module.is.reversed() && !module.is.vertical() ? ' - ' : ' + '
17257 ;
17258 var position = '(100% - ' + startMargin + ' - ' + endMargin + ') * ' + ratio;
17259 $label.css(posDir, 'calc(' + position + startMarginMod + startMargin + ')');
17260 }
17261 },
17262
17263 goto: {
17264 max: function() {
17265 module.set.value(module.get.max());
17266 },
17267 min: function() {
17268 module.set.value(module.get.min());
17269 },
17270 },
17271
17272 read: {
17273 metadata: function() {
17274 var
17275 data = {
17276 thumbVal : $module.data(metadata.thumbVal),
17277 secondThumbVal : $module.data(metadata.secondThumbVal)
17278 }
17279 ;
17280 if(data.thumbVal) {
17281 if(module.is.range() && data.secondThumbVal) {
17282 module.debug('Current value set from metadata', data.thumbVal, data.secondThumbVal);
17283 module.set.rangeValue(data.thumbVal, data.secondThumbVal);
17284 } else {
17285 module.debug('Current value set from metadata', data.thumbVal);
17286 module.set.value(data.thumbVal);
17287 }
17288 }
17289 },
17290 settings: function() {
17291 if(settings.start !== false) {
17292 if(module.is.range()) {
17293 module.debug('Start position set from settings', settings.start, settings.end);
17294 module.set.rangeValue(settings.start, settings.end);
17295 } else {
17296 module.debug('Start position set from settings', settings.start);
17297 module.set.value(settings.start);
17298 }
17299 }
17300 }
17301 },
17302
17303 setting: function(name, value) {
17304 module.debug('Changing setting', name, value);
17305 if( $.isPlainObject(name) ) {
17306 $.extend(true, settings, name);
17307 }
17308 else if(value !== undefined) {
17309 if($.isPlainObject(settings[name])) {
17310 $.extend(true, settings[name], value);
17311 }
17312 else {
17313 settings[name] = value;
17314 }
17315 }
17316 else {
17317 return settings[name];
17318 }
17319 },
17320 internal: function(name, value) {
17321 if( $.isPlainObject(name) ) {
17322 $.extend(true, module, name);
17323 }
17324 else if(value !== undefined) {
17325 module[name] = value;
17326 }
17327 else {
17328 return module[name];
17329 }
17330 },
17331 debug: function() {
17332 if(!settings.silent && settings.debug) {
17333 if(settings.performance) {
17334 module.performance.log(arguments);
17335 }
17336 else {
17337 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
17338 module.debug.apply(console, arguments);
17339 }
17340 }
17341 },
17342 verbose: function() {
17343 if(!settings.silent && settings.verbose && settings.debug) {
17344 if(settings.performance) {
17345 module.performance.log(arguments);
17346 }
17347 else {
17348 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
17349 module.verbose.apply(console, arguments);
17350 }
17351 }
17352 },
17353 error: function() {
17354 if(!settings.silent) {
17355 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
17356 module.error.apply(console, arguments);
17357 }
17358 },
17359
17360 performance: {
17361 log: function(message) {
17362 var
17363 currentTime,
17364 executionTime,
17365 previousTime
17366 ;
17367 if(settings.performance) {
17368 currentTime = new Date().getTime();
17369 previousTime = time || currentTime;
17370 executionTime = currentTime - previousTime;
17371 time = currentTime;
17372 performance.push({
17373 'Name' : message[0],
17374 'Arguments' : [].slice.call(message, 1) || '',
17375 'Element' : element,
17376 'Execution Time' : executionTime
17377 });
17378 }
17379 clearTimeout(module.performance.timer);
17380 module.performance.timer = setTimeout(module.performance.display, 500);
17381 },
17382 display: function() {
17383 var
17384 title = settings.name + ':',
17385 totalTime = 0
17386 ;
17387 time = false;
17388 clearTimeout(module.performance.timer);
17389 $.each(performance, function(index, data) {
17390 totalTime += data['Execution Time'];
17391 });
17392 title += ' ' + totalTime + 'ms';
17393 if(moduleSelector) {
17394 title += ' \'' + moduleSelector + '\'';
17395 }
17396 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
17397 console.groupCollapsed(title);
17398 if(console.table) {
17399 console.table(performance);
17400 }
17401 else {
17402 $.each(performance, function(index, data) {
17403 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
17404 });
17405 }
17406 console.groupEnd();
17407 }
17408 performance = [];
17409 }
17410 },
17411
17412 invoke: function(query, passedArguments, context) {
17413 var
17414 object = instance,
17415 maxDepth,
17416 found,
17417 response
17418 ;
17419 passedArguments = passedArguments || queryArguments;
17420 context = element || context;
17421 if(typeof query == 'string' && object !== undefined) {
17422 query = query.split(/[\. ]/);
17423 maxDepth = query.length - 1;
17424 $.each(query, function(depth, value) {
17425 var camelCaseValue = (depth != maxDepth)
17426 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
17427 : query
17428 ;
17429 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
17430 object = object[camelCaseValue];
17431 }
17432 else if( object[camelCaseValue] !== undefined ) {
17433 found = object[camelCaseValue];
17434 return false;
17435 }
17436 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
17437 object = object[value];
17438 }
17439 else if( object[value] !== undefined ) {
17440 found = object[value];
17441 return false;
17442 }
17443 else {
17444 module.error(error.method, query);
17445 return false;
17446 }
17447 });
17448 }
17449 if ( $.isFunction( found ) ) {
17450 response = found.apply(context, passedArguments);
17451 }
17452 else if(found !== undefined) {
17453 response = found;
17454 }
17455 if($.isArray(returnedValue)) {
17456 returnedValue.push(response);
17457 }
17458 else if(returnedValue !== undefined) {
17459 returnedValue = [returnedValue, response];
17460 }
17461 else if(response !== undefined) {
17462 returnedValue = response;
17463 }
17464 return found;
17465 }
17466 };
17467
17468 if(methodInvoked) {
17469 if(instance === undefined) {
17470 module.initialize();
17471 }
17472 module.invoke(query);
17473 }
17474 else {
17475 if(instance !== undefined) {
17476 instance.invoke('destroy');
17477 }
17478 module.initialize();
17479 }
17480 })
17481 ;
17482
17483 return (returnedValue !== undefined)
17484 ? returnedValue
17485 : this
17486 ;
17487
17488};
17489
17490$.fn.slider.settings = {
17491
17492 silent : false,
17493 debug : false,
17494 verbose : false,
17495 performance : true,
17496
17497 name : 'Slider',
17498 namespace : 'slider',
17499
17500 error : {
17501 method : 'The method you called is not defined.',
17502 notrange : 'This slider is not a range slider'
17503 },
17504
17505 metadata: {
17506 thumbVal : 'thumbVal',
17507 secondThumbVal : 'secondThumbVal'
17508 },
17509
17510 min : 0,
17511 max : 20,
17512 step : 1,
17513 start : 0,
17514 end : 20,
17515 labelType : 'number',
17516 showLabelTicks : false,
17517 smooth : false,
17518 autoAdjustLabels : true,
17519 labelDistance : 100,
17520 preventCrossover : true,
17521 fireOnInit : false,
17522 interpretLabel : false,
17523
17524 //the decimal place to round to if step is undefined
17525 decimalPlaces : 2,
17526
17527 // page up/down multiplier. How many more times the steps to take on page up/down press
17528 pageMultiplier : 2,
17529
17530 selector: {
17531
17532 },
17533
17534 className : {
17535 reversed : 'reversed',
17536 disabled : 'disabled',
17537 labeled : 'labeled',
17538 ticked : 'ticked',
17539 vertical : 'vertical',
17540 range : 'range',
17541 smooth : 'smooth'
17542 },
17543
17544 keys : {
17545 pageUp : 33,
17546 pageDown : 34,
17547 leftArrow : 37,
17548 upArrow : 38,
17549 rightArrow : 39,
17550 downArrow : 40
17551 },
17552
17553 labelTypes : {
17554 number : 'number',
17555 letter : 'letter'
17556 },
17557
17558 onChange : function(value, thumbVal, secondThumbVal){},
17559 onMove : function(value, thumbVal, secondThumbVal){},
17560};
17561
17562
17563})( jQuery, window, document );
17564
17565/*!
17566 * # Fomantic-UI 2.8.8 - Rating
17567 * http://github.com/fomantic/Fomantic-UI/
17568 *
17569 *
17570 * Released under the MIT license
17571 * http://opensource.org/licenses/MIT
17572 *
17573 */
17574
17575;(function ($, window, document, undefined) {
17576
17577'use strict';
17578
17579$.isFunction = $.isFunction || function(obj) {
17580 return typeof obj === "function" && typeof obj.nodeType !== "number";
17581};
17582
17583window = (typeof window != 'undefined' && window.Math == Math)
17584 ? window
17585 : (typeof self != 'undefined' && self.Math == Math)
17586 ? self
17587 : Function('return this')()
17588;
17589
17590$.fn.rating = function(parameters) {
17591 var
17592 $allModules = $(this),
17593 moduleSelector = $allModules.selector || '',
17594
17595 time = new Date().getTime(),
17596 performance = [],
17597
17598 query = arguments[0],
17599 methodInvoked = (typeof query == 'string'),
17600 queryArguments = [].slice.call(arguments, 1),
17601 returnedValue
17602 ;
17603 $allModules
17604 .each(function() {
17605 var
17606 settings = ( $.isPlainObject(parameters) )
17607 ? $.extend(true, {}, $.fn.rating.settings, parameters)
17608 : $.extend({}, $.fn.rating.settings),
17609
17610 namespace = settings.namespace,
17611 className = settings.className,
17612 metadata = settings.metadata,
17613 selector = settings.selector,
17614 cssVars = settings.cssVars,
17615
17616 eventNamespace = '.' + namespace,
17617 moduleNamespace = 'module-' + namespace,
17618
17619 element = this,
17620 instance = $(this).data(moduleNamespace),
17621
17622 $module = $(this),
17623 $icon = $module.find(selector.icon),
17624
17625 initialLoad,
17626 module
17627 ;
17628
17629 module = {
17630
17631 initialize: function() {
17632 module.verbose('Initializing rating module', settings);
17633
17634 if($icon.length === 0) {
17635 module.setup.layout();
17636 }
17637
17638 if(settings.interactive && !module.is.disabled()) {
17639 module.enable();
17640 }
17641 else {
17642 module.disable();
17643 }
17644 module.set.initialLoad();
17645 module.set.rating( module.get.initialRating() );
17646 module.remove.initialLoad();
17647 module.instantiate();
17648 },
17649
17650 instantiate: function() {
17651 module.verbose('Instantiating module', settings);
17652 instance = module;
17653 $module
17654 .data(moduleNamespace, module)
17655 ;
17656 },
17657
17658 destroy: function() {
17659 module.verbose('Destroying previous instance', instance);
17660 module.remove.events();
17661 $module
17662 .removeData(moduleNamespace)
17663 ;
17664 },
17665
17666 refresh: function() {
17667 $icon = $module.find(selector.icon);
17668 },
17669
17670 setup: {
17671 layout: function() {
17672 var
17673 maxRating = module.get.maxRating(),
17674 icon = module.get.icon(),
17675 html = $.fn.rating.settings.templates.icon(maxRating, icon)
17676 ;
17677 module.debug('Generating icon html dynamically');
17678 $module
17679 .html(html)
17680 ;
17681 module.refresh();
17682 }
17683 },
17684
17685 event: {
17686 mouseenter: function() {
17687 var
17688 $activeIcon = $(this)
17689 ;
17690 $activeIcon
17691 .nextAll()
17692 .removeClass(className.selected)
17693 ;
17694 $module
17695 .addClass(className.selected)
17696 ;
17697 $activeIcon
17698 .addClass(className.selected)
17699 .prevAll()
17700 .addClass(className.selected)
17701 ;
17702 },
17703 mouseleave: function() {
17704 $module
17705 .removeClass(className.selected)
17706 ;
17707 $icon
17708 .removeClass(className.selected)
17709 ;
17710 },
17711 click: function() {
17712 var
17713 $activeIcon = $(this),
17714 currentRating = module.get.rating(),
17715 rating = $icon.index($activeIcon) + 1,
17716 canClear = (settings.clearable == 'auto')
17717 ? ($icon.length === 1)
17718 : settings.clearable
17719 ;
17720 if(canClear && currentRating == rating) {
17721 module.clearRating();
17722 }
17723 else {
17724 module.set.rating( rating );
17725 }
17726 }
17727 },
17728
17729 clearRating: function() {
17730 module.debug('Clearing current rating');
17731 module.set.rating(0);
17732 },
17733
17734 bind: {
17735 events: function() {
17736 module.verbose('Binding events');
17737 $module
17738 .on('mouseenter' + eventNamespace, selector.icon, module.event.mouseenter)
17739 .on('mouseleave' + eventNamespace, selector.icon, module.event.mouseleave)
17740 .on('click' + eventNamespace, selector.icon, module.event.click)
17741 ;
17742 }
17743 },
17744
17745 remove: {
17746 events: function() {
17747 module.verbose('Removing events');
17748 $module
17749 .off(eventNamespace)
17750 ;
17751 },
17752 initialLoad: function() {
17753 initialLoad = false;
17754 }
17755 },
17756
17757 enable: function() {
17758 module.debug('Setting rating to interactive mode');
17759 module.bind.events();
17760 $module
17761 .removeClass(className.disabled)
17762 ;
17763 },
17764
17765 disable: function() {
17766 module.debug('Setting rating to read-only mode');
17767 module.remove.events();
17768 $module
17769 .addClass(className.disabled)
17770 ;
17771 },
17772
17773 is: {
17774 initialLoad: function() {
17775 return initialLoad;
17776 },
17777 disabled: function() {
17778 return $module.hasClass(className.disabled);
17779 }
17780 },
17781
17782 get: {
17783 icon: function(){
17784 var icon = $module.data(metadata.icon);
17785 if (icon) {
17786 $module.removeData(metadata.icon);
17787 }
17788 return icon || settings.icon;
17789 },
17790 initialRating: function() {
17791 if($module.data(metadata.rating) !== undefined) {
17792 $module.removeData(metadata.rating);
17793 return $module.data(metadata.rating);
17794 }
17795 return settings.initialRating;
17796 },
17797 maxRating: function() {
17798 if($module.data(metadata.maxRating) !== undefined) {
17799 $module.removeData(metadata.maxRating);
17800 return $module.data(metadata.maxRating);
17801 }
17802 return settings.maxRating;
17803 },
17804 rating: function() {
17805 var
17806 currentRating = $icon.filter('.' + className.active).length
17807 ;
17808 module.verbose('Current rating retrieved', currentRating);
17809 return currentRating;
17810 }
17811 },
17812
17813 set: {
17814 rating: function(rating) {
17815 var
17816 ratingIndex = Math.floor(
17817 (rating - 1 >= 0)
17818 ? (rating - 1)
17819 : 0
17820 ),
17821 $activeIcon = $icon.eq(ratingIndex),
17822 $partialActiveIcon = rating <= 1
17823 ? $activeIcon
17824 : $activeIcon.next()
17825 ,
17826 filledPercentage = (rating % 1) * 100
17827 ;
17828 $module
17829 .removeClass(className.selected)
17830 ;
17831 $icon
17832 .removeClass(className.selected)
17833 .removeClass(className.active)
17834 .removeClass(className.partiallyActive)
17835 ;
17836 if(rating > 0) {
17837 module.verbose('Setting current rating to', rating);
17838 $activeIcon
17839 .prevAll()
17840 .addBack()
17841 .addClass(className.active)
17842 ;
17843 if($activeIcon.next() && rating % 1 !== 0) {
17844 $partialActiveIcon
17845 .addClass(className.partiallyActive)
17846 .addClass(className.active)
17847 ;
17848 $partialActiveIcon
17849 .css(cssVars.filledCustomPropName, filledPercentage + '%')
17850 ;
17851 if($partialActiveIcon.css('backgroundColor') === 'transparent') {
17852 $partialActiveIcon
17853 .removeClass(className.partiallyActive)
17854 .removeClass(className.active)
17855 ;
17856 }
17857 }
17858 }
17859 if(!module.is.initialLoad()) {
17860 settings.onRate.call(element, rating);
17861 }
17862 },
17863 initialLoad: function() {
17864 initialLoad = true;
17865 }
17866 },
17867
17868 setting: function(name, value) {
17869 module.debug('Changing setting', name, value);
17870 if( $.isPlainObject(name) ) {
17871 $.extend(true, settings, name);
17872 }
17873 else if(value !== undefined) {
17874 if($.isPlainObject(settings[name])) {
17875 $.extend(true, settings[name], value);
17876 }
17877 else {
17878 settings[name] = value;
17879 }
17880 }
17881 else {
17882 return settings[name];
17883 }
17884 },
17885 internal: function(name, value) {
17886 if( $.isPlainObject(name) ) {
17887 $.extend(true, module, name);
17888 }
17889 else if(value !== undefined) {
17890 module[name] = value;
17891 }
17892 else {
17893 return module[name];
17894 }
17895 },
17896 debug: function() {
17897 if(!settings.silent && settings.debug) {
17898 if(settings.performance) {
17899 module.performance.log(arguments);
17900 }
17901 else {
17902 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
17903 module.debug.apply(console, arguments);
17904 }
17905 }
17906 },
17907 verbose: function() {
17908 if(!settings.silent && settings.verbose && settings.debug) {
17909 if(settings.performance) {
17910 module.performance.log(arguments);
17911 }
17912 else {
17913 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
17914 module.verbose.apply(console, arguments);
17915 }
17916 }
17917 },
17918 error: function() {
17919 if(!settings.silent) {
17920 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
17921 module.error.apply(console, arguments);
17922 }
17923 },
17924 performance: {
17925 log: function(message) {
17926 var
17927 currentTime,
17928 executionTime,
17929 previousTime
17930 ;
17931 if(settings.performance) {
17932 currentTime = new Date().getTime();
17933 previousTime = time || currentTime;
17934 executionTime = currentTime - previousTime;
17935 time = currentTime;
17936 performance.push({
17937 'Name' : message[0],
17938 'Arguments' : [].slice.call(message, 1) || '',
17939 'Element' : element,
17940 'Execution Time' : executionTime
17941 });
17942 }
17943 clearTimeout(module.performance.timer);
17944 module.performance.timer = setTimeout(module.performance.display, 500);
17945 },
17946 display: function() {
17947 var
17948 title = settings.name + ':',
17949 totalTime = 0
17950 ;
17951 time = false;
17952 clearTimeout(module.performance.timer);
17953 $.each(performance, function(index, data) {
17954 totalTime += data['Execution Time'];
17955 });
17956 title += ' ' + totalTime + 'ms';
17957 if(moduleSelector) {
17958 title += ' \'' + moduleSelector + '\'';
17959 }
17960 if($allModules.length > 1) {
17961 title += ' ' + '(' + $allModules.length + ')';
17962 }
17963 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
17964 console.groupCollapsed(title);
17965 if(console.table) {
17966 console.table(performance);
17967 }
17968 else {
17969 $.each(performance, function(index, data) {
17970 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
17971 });
17972 }
17973 console.groupEnd();
17974 }
17975 performance = [];
17976 }
17977 },
17978 invoke: function(query, passedArguments, context) {
17979 var
17980 object = instance,
17981 maxDepth,
17982 found,
17983 response
17984 ;
17985 passedArguments = passedArguments || queryArguments;
17986 context = element || context;
17987 if(typeof query == 'string' && object !== undefined) {
17988 query = query.split(/[\. ]/);
17989 maxDepth = query.length - 1;
17990 $.each(query, function(depth, value) {
17991 var camelCaseValue = (depth != maxDepth)
17992 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
17993 : query
17994 ;
17995 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
17996 object = object[camelCaseValue];
17997 }
17998 else if( object[camelCaseValue] !== undefined ) {
17999 found = object[camelCaseValue];
18000 return false;
18001 }
18002 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
18003 object = object[value];
18004 }
18005 else if( object[value] !== undefined ) {
18006 found = object[value];
18007 return false;
18008 }
18009 else {
18010 return false;
18011 }
18012 });
18013 }
18014 if ( $.isFunction( found ) ) {
18015 response = found.apply(context, passedArguments);
18016 }
18017 else if(found !== undefined) {
18018 response = found;
18019 }
18020 if(Array.isArray(returnedValue)) {
18021 returnedValue.push(response);
18022 }
18023 else if(returnedValue !== undefined) {
18024 returnedValue = [returnedValue, response];
18025 }
18026 else if(response !== undefined) {
18027 returnedValue = response;
18028 }
18029 return found;
18030 }
18031 };
18032 if(methodInvoked) {
18033 if(instance === undefined) {
18034 module.initialize();
18035 }
18036 module.invoke(query);
18037 }
18038 else {
18039 if(instance !== undefined) {
18040 instance.invoke('destroy');
18041 }
18042 module.initialize();
18043 }
18044 })
18045 ;
18046
18047 return (returnedValue !== undefined)
18048 ? returnedValue
18049 : this
18050 ;
18051};
18052
18053$.fn.rating.settings = {
18054
18055 name : 'Rating',
18056 namespace : 'rating',
18057
18058 icon : 'star',
18059
18060 silent : false,
18061 debug : false,
18062 verbose : false,
18063 performance : true,
18064
18065 initialRating : 0,
18066 interactive : true,
18067 maxRating : 4,
18068 clearable : 'auto',
18069
18070 fireOnInit : false,
18071
18072 onRate : function(rating){},
18073
18074 error : {
18075 method : 'The method you called is not defined',
18076 noMaximum : 'No maximum rating specified. Cannot generate HTML automatically'
18077 },
18078
18079
18080 metadata: {
18081 rating : 'rating',
18082 maxRating : 'maxRating',
18083 icon : 'icon'
18084 },
18085
18086 className : {
18087 active : 'active',
18088 disabled : 'disabled',
18089 selected : 'selected',
18090 loading : 'loading',
18091 partiallyActive : 'partial'
18092 },
18093
18094 cssVars : {
18095 filledCustomPropName : '--full'
18096 },
18097
18098 selector : {
18099 icon : '.icon'
18100 },
18101
18102 templates: {
18103 icon: function(maxRating, iconClass) {
18104 var
18105 icon = 1,
18106 html = ''
18107 ;
18108 while(icon <= maxRating) {
18109 html += '<i class="'+iconClass+' icon"></i>';
18110 icon++;
18111 }
18112 return html;
18113 }
18114 }
18115
18116};
18117
18118})( jQuery, window, document );
18119
18120/*!
18121 * # Fomantic-UI 2.8.8 - Search
18122 * http://github.com/fomantic/Fomantic-UI/
18123 *
18124 *
18125 * Released under the MIT license
18126 * http://opensource.org/licenses/MIT
18127 *
18128 */
18129
18130;(function ($, window, document, undefined) {
18131
18132'use strict';
18133
18134$.isFunction = $.isFunction || function(obj) {
18135 return typeof obj === "function" && typeof obj.nodeType !== "number";
18136};
18137
18138window = (typeof window != 'undefined' && window.Math == Math)
18139 ? window
18140 : (typeof self != 'undefined' && self.Math == Math)
18141 ? self
18142 : Function('return this')()
18143;
18144
18145$.fn.search = function(parameters) {
18146 var
18147 $allModules = $(this),
18148 moduleSelector = $allModules.selector || '',
18149
18150 time = new Date().getTime(),
18151 performance = [],
18152
18153 query = arguments[0],
18154 methodInvoked = (typeof query == 'string'),
18155 queryArguments = [].slice.call(arguments, 1),
18156 returnedValue
18157 ;
18158 $(this)
18159 .each(function() {
18160 var
18161 settings = ( $.isPlainObject(parameters) )
18162 ? $.extend(true, {}, $.fn.search.settings, parameters)
18163 : $.extend({}, $.fn.search.settings),
18164
18165 className = settings.className,
18166 metadata = settings.metadata,
18167 regExp = settings.regExp,
18168 fields = settings.fields,
18169 selector = settings.selector,
18170 error = settings.error,
18171 namespace = settings.namespace,
18172
18173 eventNamespace = '.' + namespace,
18174 moduleNamespace = namespace + '-module',
18175
18176 $module = $(this),
18177 $prompt = $module.find(selector.prompt),
18178 $searchButton = $module.find(selector.searchButton),
18179 $results = $module.find(selector.results),
18180 $result = $module.find(selector.result),
18181 $category = $module.find(selector.category),
18182
18183 element = this,
18184 instance = $module.data(moduleNamespace),
18185
18186 disabledBubbled = false,
18187 resultsDismissed = false,
18188
18189 module
18190 ;
18191
18192 module = {
18193
18194 initialize: function() {
18195 module.verbose('Initializing module');
18196 module.get.settings();
18197 module.determine.searchFields();
18198 module.bind.events();
18199 module.set.type();
18200 module.create.results();
18201 module.instantiate();
18202 },
18203 instantiate: function() {
18204 module.verbose('Storing instance of module', module);
18205 instance = module;
18206 $module
18207 .data(moduleNamespace, module)
18208 ;
18209 },
18210 destroy: function() {
18211 module.verbose('Destroying instance');
18212 $module
18213 .off(eventNamespace)
18214 .removeData(moduleNamespace)
18215 ;
18216 },
18217
18218 refresh: function() {
18219 module.debug('Refreshing selector cache');
18220 $prompt = $module.find(selector.prompt);
18221 $searchButton = $module.find(selector.searchButton);
18222 $category = $module.find(selector.category);
18223 $results = $module.find(selector.results);
18224 $result = $module.find(selector.result);
18225 },
18226
18227 refreshResults: function() {
18228 $results = $module.find(selector.results);
18229 $result = $module.find(selector.result);
18230 },
18231
18232 bind: {
18233 events: function() {
18234 module.verbose('Binding events to search');
18235 if(settings.automatic) {
18236 $module
18237 .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
18238 ;
18239 $prompt
18240 .attr('autocomplete', module.is.chrome() ? 'fomantic-search' : 'off')
18241 ;
18242 }
18243 $module
18244 // prompt
18245 .on('focus' + eventNamespace, selector.prompt, module.event.focus)
18246 .on('blur' + eventNamespace, selector.prompt, module.event.blur)
18247 .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
18248 // search button
18249 .on('click' + eventNamespace, selector.searchButton, module.query)
18250 // results
18251 .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
18252 .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
18253 .on('click' + eventNamespace, selector.result, module.event.result.click)
18254 ;
18255 }
18256 },
18257
18258 determine: {
18259 searchFields: function() {
18260 // this makes sure $.extend does not add specified search fields to default fields
18261 // this is the only setting which should not extend defaults
18262 if(parameters && parameters.searchFields !== undefined) {
18263 settings.searchFields = parameters.searchFields;
18264 }
18265 }
18266 },
18267
18268 event: {
18269 input: function() {
18270 if(settings.searchDelay) {
18271 clearTimeout(module.timer);
18272 module.timer = setTimeout(function() {
18273 if(module.is.focused()) {
18274 module.query();
18275 }
18276 }, settings.searchDelay);
18277 }
18278 else {
18279 module.query();
18280 }
18281 },
18282 focus: function() {
18283 module.set.focus();
18284 if(settings.searchOnFocus && module.has.minimumCharacters() ) {
18285 module.query(function() {
18286 if(module.can.show() ) {
18287 module.showResults();
18288 }
18289 });
18290 }
18291 },
18292 blur: function(event) {
18293 var
18294 pageLostFocus = (document.activeElement === this),
18295 callback = function() {
18296 module.cancel.query();
18297 module.remove.focus();
18298 module.timer = setTimeout(module.hideResults, settings.hideDelay);
18299 }
18300 ;
18301 if(pageLostFocus) {
18302 return;
18303 }
18304 resultsDismissed = false;
18305 if(module.resultsClicked) {
18306 module.debug('Determining if user action caused search to close');
18307 $module
18308 .one('click.close' + eventNamespace, selector.results, function(event) {
18309 if(module.is.inMessage(event) || disabledBubbled) {
18310 $prompt.focus();
18311 return;
18312 }
18313 disabledBubbled = false;
18314 if( !module.is.animating() && !module.is.hidden()) {
18315 callback();
18316 }
18317 })
18318 ;
18319 }
18320 else {
18321 module.debug('Input blurred without user action, closing results');
18322 callback();
18323 }
18324 },
18325 result: {
18326 mousedown: function() {
18327 module.resultsClicked = true;
18328 },
18329 mouseup: function() {
18330 module.resultsClicked = false;
18331 },
18332 click: function(event) {
18333 module.debug('Search result selected');
18334 var
18335 $result = $(this),
18336 $title = $result.find(selector.title).eq(0),
18337 $link = $result.is('a[href]')
18338 ? $result
18339 : $result.find('a[href]').eq(0),
18340 href = $link.attr('href') || false,
18341 target = $link.attr('target') || false,
18342 // title is used for result lookup
18343 value = ($title.length > 0)
18344 ? $title.text()
18345 : false,
18346 results = module.get.results(),
18347 result = $result.data(metadata.result) || module.get.result(value, results)
18348 ;
18349 var oldValue = module.get.value();
18350 if( $.isFunction(settings.onSelect) ) {
18351 if(settings.onSelect.call(element, result, results) === false) {
18352 module.debug('Custom onSelect callback cancelled default select action');
18353 disabledBubbled = true;
18354 return;
18355 }
18356 }
18357 module.hideResults();
18358 if(value && module.get.value() === oldValue) {
18359 module.set.value(value);
18360 }
18361 if(href) {
18362 event.preventDefault();
18363 module.verbose('Opening search link found in result', $link);
18364 if(target == '_blank' || event.ctrlKey) {
18365 window.open(href);
18366 }
18367 else {
18368 window.location.href = (href);
18369 }
18370 }
18371 }
18372 }
18373 },
18374 ensureVisible: function($el) {
18375 var elTop, elBottom, resultsScrollTop, resultsHeight;
18376 if($el.length === 0) {
18377 return;
18378 }
18379 elTop = $el.position().top;
18380 elBottom = elTop + $el.outerHeight(true);
18381
18382 resultsScrollTop = $results.scrollTop();
18383 resultsHeight = $results.height();
18384
18385 if (elTop < 0) {
18386 $results.scrollTop(resultsScrollTop + elTop);
18387 }
18388
18389 else if (resultsHeight < elBottom) {
18390 $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
18391 }
18392 },
18393 handleKeyboard: function(event) {
18394 var
18395 // force selector refresh
18396 $result = $module.find(selector.result),
18397 $category = $module.find(selector.category),
18398 $activeResult = $result.filter('.' + className.active),
18399 currentIndex = $result.index( $activeResult ),
18400 resultSize = $result.length,
18401 hasActiveResult = $activeResult.length > 0,
18402
18403 keyCode = event.which,
18404 keys = {
18405 backspace : 8,
18406 enter : 13,
18407 escape : 27,
18408 upArrow : 38,
18409 downArrow : 40
18410 },
18411 newIndex
18412 ;
18413 // search shortcuts
18414 if(keyCode == keys.escape) {
18415 module.verbose('Escape key pressed, blurring search field');
18416 module.hideResults();
18417 resultsDismissed = true;
18418 }
18419 if( module.is.visible() ) {
18420 if(keyCode == keys.enter) {
18421 module.verbose('Enter key pressed, selecting active result');
18422 if( $result.filter('.' + className.active).length > 0 ) {
18423 module.event.result.click.call($result.filter('.' + className.active), event);
18424 event.preventDefault();
18425 return false;
18426 }
18427 }
18428 else if(keyCode == keys.upArrow && hasActiveResult) {
18429 module.verbose('Up key pressed, changing active result');
18430 newIndex = (currentIndex - 1 < 0)
18431 ? currentIndex
18432 : currentIndex - 1
18433 ;
18434 $category
18435 .removeClass(className.active)
18436 ;
18437 $result
18438 .removeClass(className.active)
18439 .eq(newIndex)
18440 .addClass(className.active)
18441 .closest($category)
18442 .addClass(className.active)
18443 ;
18444 module.ensureVisible($result.eq(newIndex));
18445 event.preventDefault();
18446 }
18447 else if(keyCode == keys.downArrow) {
18448 module.verbose('Down key pressed, changing active result');
18449 newIndex = (currentIndex + 1 >= resultSize)
18450 ? currentIndex
18451 : currentIndex + 1
18452 ;
18453 $category
18454 .removeClass(className.active)
18455 ;
18456 $result
18457 .removeClass(className.active)
18458 .eq(newIndex)
18459 .addClass(className.active)
18460 .closest($category)
18461 .addClass(className.active)
18462 ;
18463 module.ensureVisible($result.eq(newIndex));
18464 event.preventDefault();
18465 }
18466 }
18467 else {
18468 // query shortcuts
18469 if(keyCode == keys.enter) {
18470 module.verbose('Enter key pressed, executing query');
18471 module.query();
18472 module.set.buttonPressed();
18473 $prompt.one('keyup', module.remove.buttonFocus);
18474 }
18475 }
18476 },
18477
18478 setup: {
18479 api: function(searchTerm, callback) {
18480 var
18481 apiSettings = {
18482 debug : settings.debug,
18483 on : false,
18484 cache : settings.cache,
18485 action : 'search',
18486 urlData : {
18487 query : searchTerm
18488 },
18489 onSuccess : function(response) {
18490 module.parse.response.call(element, response, searchTerm);
18491 callback();
18492 },
18493 onFailure : function() {
18494 module.displayMessage(error.serverError);
18495 callback();
18496 },
18497 onAbort : function(response) {
18498 },
18499 onError : module.error
18500 }
18501 ;
18502 $.extend(true, apiSettings, settings.apiSettings);
18503 module.verbose('Setting up API request', apiSettings);
18504 $module.api(apiSettings);
18505 }
18506 },
18507
18508 can: {
18509 useAPI: function() {
18510 return $.fn.api !== undefined;
18511 },
18512 show: function() {
18513 return module.is.focused() && !module.is.visible() && !module.is.empty();
18514 },
18515 transition: function() {
18516 return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
18517 }
18518 },
18519
18520 is: {
18521 animating: function() {
18522 return $results.hasClass(className.animating);
18523 },
18524 chrome: function() {
18525 return !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
18526 },
18527 hidden: function() {
18528 return $results.hasClass(className.hidden);
18529 },
18530 inMessage: function(event) {
18531 if(!event.target) {
18532 return;
18533 }
18534 var
18535 $target = $(event.target),
18536 isInDOM = $.contains(document.documentElement, event.target)
18537 ;
18538 return (isInDOM && $target.closest(selector.message).length > 0);
18539 },
18540 empty: function() {
18541 return ($results.html() === '');
18542 },
18543 visible: function() {
18544 return ($results.filter(':visible').length > 0);
18545 },
18546 focused: function() {
18547 return ($prompt.filter(':focus').length > 0);
18548 }
18549 },
18550
18551 get: {
18552 settings: function() {
18553 if($.isPlainObject(parameters) && parameters.searchFullText) {
18554 settings.fullTextSearch = parameters.searchFullText;
18555 module.error(settings.error.oldSearchSyntax, element);
18556 }
18557 if (settings.ignoreDiacritics && !String.prototype.normalize) {
18558 settings.ignoreDiacritics = false;
18559 module.error(error.noNormalize, element);
18560 }
18561 },
18562 inputEvent: function() {
18563 var
18564 prompt = $prompt[0],
18565 inputEvent = (prompt !== undefined && prompt.oninput !== undefined)
18566 ? 'input'
18567 : (prompt !== undefined && prompt.onpropertychange !== undefined)
18568 ? 'propertychange'
18569 : 'keyup'
18570 ;
18571 return inputEvent;
18572 },
18573 value: function() {
18574 return $prompt.val();
18575 },
18576 results: function() {
18577 var
18578 results = $module.data(metadata.results)
18579 ;
18580 return results;
18581 },
18582 result: function(value, results) {
18583 var
18584 result = false
18585 ;
18586 value = (value !== undefined)
18587 ? value
18588 : module.get.value()
18589 ;
18590 results = (results !== undefined)
18591 ? results
18592 : module.get.results()
18593 ;
18594 if(settings.type === 'category') {
18595 module.debug('Finding result that matches', value);
18596 $.each(results, function(index, category) {
18597 if(Array.isArray(category.results)) {
18598 result = module.search.object(value, category.results)[0];
18599 // don't continue searching if a result is found
18600 if(result) {
18601 return false;
18602 }
18603 }
18604 });
18605 }
18606 else {
18607 module.debug('Finding result in results object', value);
18608 result = module.search.object(value, results)[0];
18609 }
18610 return result || false;
18611 },
18612 },
18613
18614 select: {
18615 firstResult: function() {
18616 module.verbose('Selecting first result');
18617 $result.first().addClass(className.active);
18618 }
18619 },
18620
18621 set: {
18622 focus: function() {
18623 $module.addClass(className.focus);
18624 },
18625 loading: function() {
18626 $module.addClass(className.loading);
18627 },
18628 value: function(value) {
18629 module.verbose('Setting search input value', value);
18630 $prompt
18631 .val(value)
18632 ;
18633 },
18634 type: function(type) {
18635 type = type || settings.type;
18636 if(settings.type == 'category') {
18637 $module.addClass(settings.type);
18638 }
18639 },
18640 buttonPressed: function() {
18641 $searchButton.addClass(className.pressed);
18642 }
18643 },
18644
18645 remove: {
18646 loading: function() {
18647 $module.removeClass(className.loading);
18648 },
18649 focus: function() {
18650 $module.removeClass(className.focus);
18651 },
18652 buttonPressed: function() {
18653 $searchButton.removeClass(className.pressed);
18654 },
18655 diacritics: function(text) {
18656 return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
18657 }
18658 },
18659
18660 query: function(callback) {
18661 callback = $.isFunction(callback)
18662 ? callback
18663 : function(){}
18664 ;
18665 var
18666 searchTerm = module.get.value(),
18667 cache = module.read.cache(searchTerm)
18668 ;
18669 callback = callback || function() {};
18670 if( module.has.minimumCharacters() ) {
18671 if(cache) {
18672 module.debug('Reading result from cache', searchTerm);
18673 module.save.results(cache.results);
18674 module.addResults(cache.html);
18675 module.inject.id(cache.results);
18676 callback();
18677 }
18678 else {
18679 module.debug('Querying for', searchTerm);
18680 if($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
18681 module.search.local(searchTerm);
18682 callback();
18683 }
18684 else if( module.can.useAPI() ) {
18685 module.search.remote(searchTerm, callback);
18686 }
18687 else {
18688 module.error(error.source);
18689 callback();
18690 }
18691 }
18692 settings.onSearchQuery.call(element, searchTerm);
18693 }
18694 else {
18695 module.hideResults();
18696 }
18697 },
18698
18699 search: {
18700 local: function(searchTerm) {
18701 var
18702 results = module.search.object(searchTerm, settings.source),
18703 searchHTML
18704 ;
18705 module.set.loading();
18706 module.save.results(results);
18707 module.debug('Returned full local search results', results);
18708 if(settings.maxResults > 0) {
18709 module.debug('Using specified max results', results);
18710 results = results.slice(0, settings.maxResults);
18711 }
18712 if(settings.type == 'category') {
18713 results = module.create.categoryResults(results);
18714 }
18715 searchHTML = module.generateResults({
18716 results: results
18717 });
18718 module.remove.loading();
18719 module.addResults(searchHTML);
18720 module.inject.id(results);
18721 module.write.cache(searchTerm, {
18722 html : searchHTML,
18723 results : results
18724 });
18725 },
18726 remote: function(searchTerm, callback) {
18727 callback = $.isFunction(callback)
18728 ? callback
18729 : function(){}
18730 ;
18731 if($module.api('is loading')) {
18732 $module.api('abort');
18733 }
18734 module.setup.api(searchTerm, callback);
18735 $module
18736 .api('query')
18737 ;
18738 },
18739 object: function(searchTerm, source, searchFields) {
18740 searchTerm = module.remove.diacritics(String(searchTerm));
18741 var
18742 results = [],
18743 exactResults = [],
18744 fuzzyResults = [],
18745 searchExp = searchTerm.replace(regExp.escape, '\\$&'),
18746 matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'),
18747
18748 // avoid duplicates when pushing results
18749 addResult = function(array, result) {
18750 var
18751 notResult = ($.inArray(result, results) == -1),
18752 notFuzzyResult = ($.inArray(result, fuzzyResults) == -1),
18753 notExactResults = ($.inArray(result, exactResults) == -1)
18754 ;
18755 if(notResult && notFuzzyResult && notExactResults) {
18756 array.push(result);
18757 }
18758 }
18759 ;
18760 source = source || settings.source;
18761 searchFields = (searchFields !== undefined)
18762 ? searchFields
18763 : settings.searchFields
18764 ;
18765
18766 // search fields should be array to loop correctly
18767 if(!Array.isArray(searchFields)) {
18768 searchFields = [searchFields];
18769 }
18770
18771 // exit conditions if no source
18772 if(source === undefined || source === false) {
18773 module.error(error.source);
18774 return [];
18775 }
18776 // iterate through search fields looking for matches
18777 $.each(searchFields, function(index, field) {
18778 $.each(source, function(label, content) {
18779 var
18780 fieldExists = (typeof content[field] == 'string') || (typeof content[field] == 'number')
18781 ;
18782 if(fieldExists) {
18783 var text;
18784 if (typeof content[field] === 'string'){
18785 text = module.remove.diacritics(content[field]);
18786 } else {
18787 text = content[field].toString();
18788 }
18789 if( text.search(matchRegExp) !== -1) {
18790 // content starts with value (first in results)
18791 addResult(results, content);
18792 }
18793 else if(settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text) ) {
18794 // content fuzzy matches (last in results)
18795 addResult(exactResults, content);
18796 }
18797 else if(settings.fullTextSearch == true && module.fuzzySearch(searchTerm, text) ) {
18798 // content fuzzy matches (last in results)
18799 addResult(fuzzyResults, content);
18800 }
18801 }
18802 });
18803 });
18804 $.merge(exactResults, fuzzyResults);
18805 $.merge(results, exactResults);
18806 return results;
18807 }
18808 },
18809 exactSearch: function (query, term) {
18810 query = query.toLowerCase();
18811 term = term.toLowerCase();
18812 return term.indexOf(query) > -1;
18813 },
18814 fuzzySearch: function(query, term) {
18815 var
18816 termLength = term.length,
18817 queryLength = query.length
18818 ;
18819 if(typeof query !== 'string') {
18820 return false;
18821 }
18822 query = query.toLowerCase();
18823 term = term.toLowerCase();
18824 if(queryLength > termLength) {
18825 return false;
18826 }
18827 if(queryLength === termLength) {
18828 return (query === term);
18829 }
18830 search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
18831 var
18832 queryCharacter = query.charCodeAt(characterIndex)
18833 ;
18834 while(nextCharacterIndex < termLength) {
18835 if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
18836 continue search;
18837 }
18838 }
18839 return false;
18840 }
18841 return true;
18842 },
18843
18844 parse: {
18845 response: function(response, searchTerm) {
18846 if(Array.isArray(response)){
18847 var o={};
18848 o[fields.results]=response;
18849 response = o;
18850 }
18851 var
18852 searchHTML = module.generateResults(response)
18853 ;
18854 module.verbose('Parsing server response', response);
18855 if(response !== undefined) {
18856 if(searchTerm !== undefined && response[fields.results] !== undefined) {
18857 module.addResults(searchHTML);
18858 module.inject.id(response[fields.results]);
18859 module.write.cache(searchTerm, {
18860 html : searchHTML,
18861 results : response[fields.results]
18862 });
18863 module.save.results(response[fields.results]);
18864 }
18865 }
18866 }
18867 },
18868
18869 cancel: {
18870 query: function() {
18871 if( module.can.useAPI() ) {
18872 $module.api('abort');
18873 }
18874 }
18875 },
18876
18877 has: {
18878 minimumCharacters: function() {
18879 var
18880 searchTerm = module.get.value(),
18881 numCharacters = searchTerm.length
18882 ;
18883 return (numCharacters >= settings.minCharacters);
18884 },
18885 results: function() {
18886 if($results.length === 0) {
18887 return false;
18888 }
18889 var
18890 html = $results.html()
18891 ;
18892 return html != '';
18893 }
18894 },
18895
18896 clear: {
18897 cache: function(value) {
18898 var
18899 cache = $module.data(metadata.cache)
18900 ;
18901 if(!value) {
18902 module.debug('Clearing cache', value);
18903 $module.removeData(metadata.cache);
18904 }
18905 else if(value && cache && cache[value]) {
18906 module.debug('Removing value from cache', value);
18907 delete cache[value];
18908 $module.data(metadata.cache, cache);
18909 }
18910 }
18911 },
18912
18913 read: {
18914 cache: function(name) {
18915 var
18916 cache = $module.data(metadata.cache)
18917 ;
18918 if(settings.cache) {
18919 module.verbose('Checking cache for generated html for query', name);
18920 return (typeof cache == 'object') && (cache[name] !== undefined)
18921 ? cache[name]
18922 : false
18923 ;
18924 }
18925 return false;
18926 }
18927 },
18928
18929 create: {
18930 categoryResults: function(results) {
18931 var
18932 categoryResults = {}
18933 ;
18934 $.each(results, function(index, result) {
18935 if(!result.category) {
18936 return;
18937 }
18938 if(categoryResults[result.category] === undefined) {
18939 module.verbose('Creating new category of results', result.category);
18940 categoryResults[result.category] = {
18941 name : result.category,
18942 results : [result]
18943 };
18944 }
18945 else {
18946 categoryResults[result.category].results.push(result);
18947 }
18948 });
18949 return categoryResults;
18950 },
18951 id: function(resultIndex, categoryIndex) {
18952 var
18953 resultID = (resultIndex + 1), // not zero indexed
18954 letterID,
18955 id
18956 ;
18957 if(categoryIndex !== undefined) {
18958 // start char code for "A"
18959 letterID = String.fromCharCode(97 + categoryIndex);
18960 id = letterID + resultID;
18961 module.verbose('Creating category result id', id);
18962 }
18963 else {
18964 id = resultID;
18965 module.verbose('Creating result id', id);
18966 }
18967 return id;
18968 },
18969 results: function() {
18970 if($results.length === 0) {
18971 $results = $('<div />')
18972 .addClass(className.results)
18973 .appendTo($module)
18974 ;
18975 }
18976 }
18977 },
18978
18979 inject: {
18980 result: function(result, resultIndex, categoryIndex) {
18981 module.verbose('Injecting result into results');
18982 var
18983 $selectedResult = (categoryIndex !== undefined)
18984 ? $results
18985 .children().eq(categoryIndex)
18986 .children(selector.results)
18987 .first()
18988 .children(selector.result)
18989 .eq(resultIndex)
18990 : $results
18991 .children(selector.result).eq(resultIndex)
18992 ;
18993 module.verbose('Injecting results metadata', $selectedResult);
18994 $selectedResult
18995 .data(metadata.result, result)
18996 ;
18997 },
18998 id: function(results) {
18999 module.debug('Injecting unique ids into results');
19000 var
19001 // since results may be object, we must use counters
19002 categoryIndex = 0,
19003 resultIndex = 0
19004 ;
19005 if(settings.type === 'category') {
19006 // iterate through each category result
19007 $.each(results, function(index, category) {
19008 if(category.results.length > 0){
19009 resultIndex = 0;
19010 $.each(category.results, function(index, result) {
19011 if(result.id === undefined) {
19012 result.id = module.create.id(resultIndex, categoryIndex);
19013 }
19014 module.inject.result(result, resultIndex, categoryIndex);
19015 resultIndex++;
19016 });
19017 categoryIndex++;
19018 }
19019 });
19020 }
19021 else {
19022 // top level
19023 $.each(results, function(index, result) {
19024 if(result.id === undefined) {
19025 result.id = module.create.id(resultIndex);
19026 }
19027 module.inject.result(result, resultIndex);
19028 resultIndex++;
19029 });
19030 }
19031 return results;
19032 }
19033 },
19034
19035 save: {
19036 results: function(results) {
19037 module.verbose('Saving current search results to metadata', results);
19038 $module.data(metadata.results, results);
19039 }
19040 },
19041
19042 write: {
19043 cache: function(name, value) {
19044 var
19045 cache = ($module.data(metadata.cache) !== undefined)
19046 ? $module.data(metadata.cache)
19047 : {}
19048 ;
19049 if(settings.cache) {
19050 module.verbose('Writing generated html to cache', name, value);
19051 cache[name] = value;
19052 $module
19053 .data(metadata.cache, cache)
19054 ;
19055 }
19056 }
19057 },
19058
19059 addResults: function(html) {
19060 if( $.isFunction(settings.onResultsAdd) ) {
19061 if( settings.onResultsAdd.call($results, html) === false ) {
19062 module.debug('onResultsAdd callback cancelled default action');
19063 return false;
19064 }
19065 }
19066 if(html) {
19067 $results
19068 .html(html)
19069 ;
19070 module.refreshResults();
19071 if(settings.selectFirstResult) {
19072 module.select.firstResult();
19073 }
19074 module.showResults();
19075 }
19076 else {
19077 module.hideResults(function() {
19078 $results.empty();
19079 });
19080 }
19081 },
19082
19083 showResults: function(callback) {
19084 callback = $.isFunction(callback)
19085 ? callback
19086 : function(){}
19087 ;
19088 if(resultsDismissed) {
19089 return;
19090 }
19091 if(!module.is.visible() && module.has.results()) {
19092 if( module.can.transition() ) {
19093 module.debug('Showing results with css animations');
19094 $results
19095 .transition({
19096 animation : settings.transition + ' in',
19097 debug : settings.debug,
19098 verbose : settings.verbose,
19099 duration : settings.duration,
19100 onShow : function() {
19101 var $firstResult = $module.find(selector.result).eq(0);
19102 module.ensureVisible($firstResult);
19103 },
19104 onComplete : function() {
19105 callback();
19106 },
19107 queue : true
19108 })
19109 ;
19110 }
19111 else {
19112 module.debug('Showing results with javascript');
19113 $results
19114 .stop()
19115 .fadeIn(settings.duration, settings.easing)
19116 ;
19117 }
19118 settings.onResultsOpen.call($results);
19119 }
19120 },
19121 hideResults: function(callback) {
19122 callback = $.isFunction(callback)
19123 ? callback
19124 : function(){}
19125 ;
19126 if( module.is.visible() ) {
19127 if( module.can.transition() ) {
19128 module.debug('Hiding results with css animations');
19129 $results
19130 .transition({
19131 animation : settings.transition + ' out',
19132 debug : settings.debug,
19133 verbose : settings.verbose,
19134 duration : settings.duration,
19135 onComplete : function() {
19136 callback();
19137 },
19138 queue : true
19139 })
19140 ;
19141 }
19142 else {
19143 module.debug('Hiding results with javascript');
19144 $results
19145 .stop()
19146 .fadeOut(settings.duration, settings.easing)
19147 ;
19148 }
19149 settings.onResultsClose.call($results);
19150 }
19151 },
19152
19153 generateResults: function(response) {
19154 module.debug('Generating html from response', response);
19155 var
19156 template = settings.templates[settings.type],
19157 isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
19158 isProperArray = (Array.isArray(response[fields.results]) && response[fields.results].length > 0),
19159 html = ''
19160 ;
19161 if(isProperObject || isProperArray ) {
19162 if(settings.maxResults > 0) {
19163 if(isProperObject) {
19164 if(settings.type == 'standard') {
19165 module.error(error.maxResults);
19166 }
19167 }
19168 else {
19169 response[fields.results] = response[fields.results].slice(0, settings.maxResults);
19170 }
19171 }
19172 if($.isFunction(template)) {
19173 html = template(response, fields, settings.preserveHTML);
19174 }
19175 else {
19176 module.error(error.noTemplate, false);
19177 }
19178 }
19179 else if(settings.showNoResults) {
19180 html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
19181 }
19182 settings.onResults.call(element, response);
19183 return html;
19184 },
19185
19186 displayMessage: function(text, type, header) {
19187 type = type || 'standard';
19188 module.debug('Displaying message', text, type, header);
19189 module.addResults( settings.templates.message(text, type, header) );
19190 return settings.templates.message(text, type, header);
19191 },
19192
19193 setting: function(name, value) {
19194 if( $.isPlainObject(name) ) {
19195 $.extend(true, settings, name);
19196 }
19197 else if(value !== undefined) {
19198 settings[name] = value;
19199 }
19200 else {
19201 return settings[name];
19202 }
19203 },
19204 internal: function(name, value) {
19205 if( $.isPlainObject(name) ) {
19206 $.extend(true, module, name);
19207 }
19208 else if(value !== undefined) {
19209 module[name] = value;
19210 }
19211 else {
19212 return module[name];
19213 }
19214 },
19215 debug: function() {
19216 if(!settings.silent && settings.debug) {
19217 if(settings.performance) {
19218 module.performance.log(arguments);
19219 }
19220 else {
19221 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
19222 module.debug.apply(console, arguments);
19223 }
19224 }
19225 },
19226 verbose: function() {
19227 if(!settings.silent && settings.verbose && settings.debug) {
19228 if(settings.performance) {
19229 module.performance.log(arguments);
19230 }
19231 else {
19232 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
19233 module.verbose.apply(console, arguments);
19234 }
19235 }
19236 },
19237 error: function() {
19238 if(!settings.silent) {
19239 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
19240 module.error.apply(console, arguments);
19241 }
19242 },
19243 performance: {
19244 log: function(message) {
19245 var
19246 currentTime,
19247 executionTime,
19248 previousTime
19249 ;
19250 if(settings.performance) {
19251 currentTime = new Date().getTime();
19252 previousTime = time || currentTime;
19253 executionTime = currentTime - previousTime;
19254 time = currentTime;
19255 performance.push({
19256 'Name' : message[0],
19257 'Arguments' : [].slice.call(message, 1) || '',
19258 'Element' : element,
19259 'Execution Time' : executionTime
19260 });
19261 }
19262 clearTimeout(module.performance.timer);
19263 module.performance.timer = setTimeout(module.performance.display, 500);
19264 },
19265 display: function() {
19266 var
19267 title = settings.name + ':',
19268 totalTime = 0
19269 ;
19270 time = false;
19271 clearTimeout(module.performance.timer);
19272 $.each(performance, function(index, data) {
19273 totalTime += data['Execution Time'];
19274 });
19275 title += ' ' + totalTime + 'ms';
19276 if(moduleSelector) {
19277 title += ' \'' + moduleSelector + '\'';
19278 }
19279 if($allModules.length > 1) {
19280 title += ' ' + '(' + $allModules.length + ')';
19281 }
19282 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
19283 console.groupCollapsed(title);
19284 if(console.table) {
19285 console.table(performance);
19286 }
19287 else {
19288 $.each(performance, function(index, data) {
19289 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
19290 });
19291 }
19292 console.groupEnd();
19293 }
19294 performance = [];
19295 }
19296 },
19297 invoke: function(query, passedArguments, context) {
19298 var
19299 object = instance,
19300 maxDepth,
19301 found,
19302 response
19303 ;
19304 passedArguments = passedArguments || queryArguments;
19305 context = element || context;
19306 if(typeof query == 'string' && object !== undefined) {
19307 query = query.split(/[\. ]/);
19308 maxDepth = query.length - 1;
19309 $.each(query, function(depth, value) {
19310 var camelCaseValue = (depth != maxDepth)
19311 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
19312 : query
19313 ;
19314 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
19315 object = object[camelCaseValue];
19316 }
19317 else if( object[camelCaseValue] !== undefined ) {
19318 found = object[camelCaseValue];
19319 return false;
19320 }
19321 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
19322 object = object[value];
19323 }
19324 else if( object[value] !== undefined ) {
19325 found = object[value];
19326 return false;
19327 }
19328 else {
19329 return false;
19330 }
19331 });
19332 }
19333 if( $.isFunction( found ) ) {
19334 response = found.apply(context, passedArguments);
19335 }
19336 else if(found !== undefined) {
19337 response = found;
19338 }
19339 if(Array.isArray(returnedValue)) {
19340 returnedValue.push(response);
19341 }
19342 else if(returnedValue !== undefined) {
19343 returnedValue = [returnedValue, response];
19344 }
19345 else if(response !== undefined) {
19346 returnedValue = response;
19347 }
19348 return found;
19349 }
19350 };
19351 if(methodInvoked) {
19352 if(instance === undefined) {
19353 module.initialize();
19354 }
19355 module.invoke(query);
19356 }
19357 else {
19358 if(instance !== undefined) {
19359 instance.invoke('destroy');
19360 }
19361 module.initialize();
19362 }
19363
19364 })
19365 ;
19366
19367 return (returnedValue !== undefined)
19368 ? returnedValue
19369 : this
19370 ;
19371};
19372
19373$.fn.search.settings = {
19374
19375 name : 'Search',
19376 namespace : 'search',
19377
19378 silent : false,
19379 debug : false,
19380 verbose : false,
19381 performance : true,
19382
19383 // template to use (specified in settings.templates)
19384 type : 'standard',
19385
19386 // minimum characters required to search
19387 minCharacters : 1,
19388
19389 // whether to select first result after searching automatically
19390 selectFirstResult : false,
19391
19392 // API config
19393 apiSettings : false,
19394
19395 // object to search
19396 source : false,
19397
19398 // Whether search should query current term on focus
19399 searchOnFocus : true,
19400
19401 // fields to search
19402 searchFields : [
19403 'id',
19404 'title',
19405 'description'
19406 ],
19407
19408 // field to display in standard results template
19409 displayField : '',
19410
19411 // search anywhere in value (set to 'exact' to require exact matches
19412 fullTextSearch : 'exact',
19413
19414 // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
19415 ignoreDiacritics : false,
19416
19417 // whether to add events to prompt automatically
19418 automatic : true,
19419
19420 // delay before hiding menu after blur
19421 hideDelay : 0,
19422
19423 // delay before searching
19424 searchDelay : 200,
19425
19426 // maximum results returned from search
19427 maxResults : 7,
19428
19429 // whether to store lookups in local cache
19430 cache : true,
19431
19432 // whether no results errors should be shown
19433 showNoResults : true,
19434
19435 // preserve possible html of resultset values
19436 preserveHTML : true,
19437
19438 // transition settings
19439 transition : 'scale',
19440 duration : 200,
19441 easing : 'easeOutExpo',
19442
19443 // callbacks
19444 onSelect : false,
19445 onResultsAdd : false,
19446
19447 onSearchQuery : function(query){},
19448 onResults : function(response){},
19449
19450 onResultsOpen : function(){},
19451 onResultsClose : function(){},
19452
19453 className: {
19454 animating : 'animating',
19455 active : 'active',
19456 empty : 'empty',
19457 focus : 'focus',
19458 hidden : 'hidden',
19459 loading : 'loading',
19460 results : 'results',
19461 pressed : 'down'
19462 },
19463
19464 error : {
19465 source : 'Cannot search. No source used, and Semantic API module was not included',
19466 noResultsHeader : 'No Results',
19467 noResults : 'Your search returned no results',
19468 logging : 'Error in debug logging, exiting.',
19469 noEndpoint : 'No search endpoint was specified',
19470 noTemplate : 'A valid template name was not specified.',
19471 oldSearchSyntax : 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
19472 serverError : 'There was an issue querying the server.',
19473 maxResults : 'Results must be an array to use maxResults setting',
19474 method : 'The method you called is not defined.',
19475 noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
19476 },
19477
19478 metadata: {
19479 cache : 'cache',
19480 results : 'results',
19481 result : 'result'
19482 },
19483
19484 regExp: {
19485 escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
19486 beginsWith : '(?:\s|^)'
19487 },
19488
19489 // maps api response attributes to internal representation
19490 fields: {
19491 categories : 'results', // array of categories (category view)
19492 categoryName : 'name', // name of category (category view)
19493 categoryResults : 'results', // array of results (category view)
19494 description : 'description', // result description
19495 image : 'image', // result image
19496 price : 'price', // result price
19497 results : 'results', // array of results (standard)
19498 title : 'title', // result title
19499 url : 'url', // result url
19500 action : 'action', // "view more" object name
19501 actionText : 'text', // "view more" text
19502 actionURL : 'url' // "view more" url
19503 },
19504
19505 selector : {
19506 prompt : '.prompt',
19507 searchButton : '.search.button',
19508 results : '.results',
19509 message : '.results > .message',
19510 category : '.category',
19511 result : '.result',
19512 title : '.title, .name'
19513 },
19514
19515 templates: {
19516 escape: function(string, preserveHTML) {
19517 if (preserveHTML){
19518 return string;
19519 }
19520 var
19521 badChars = /[<>"'`]/g,
19522 shouldEscape = /[&<>"'`]/,
19523 escape = {
19524 "<": "&lt;",
19525 ">": "&gt;",
19526 '"': "&quot;",
19527 "'": "&#x27;",
19528 "`": "&#x60;"
19529 },
19530 escapedChar = function(chr) {
19531 return escape[chr];
19532 }
19533 ;
19534 if(shouldEscape.test(string)) {
19535 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
19536 return string.replace(badChars, escapedChar);
19537 }
19538 return string;
19539 },
19540 message: function(message, type, header) {
19541 var
19542 html = ''
19543 ;
19544 if(message !== undefined && type !== undefined) {
19545 html += ''
19546 + '<div class="message ' + type + '">'
19547 ;
19548 if(header) {
19549 html += ''
19550 + '<div class="header">' + header + '</div>'
19551 ;
19552 }
19553 html += ' <div class="description">' + message + '</div>';
19554 html += '</div>';
19555 }
19556 return html;
19557 },
19558 category: function(response, fields, preserveHTML) {
19559 var
19560 html = '',
19561 escape = $.fn.search.settings.templates.escape
19562 ;
19563 if(response[fields.categoryResults] !== undefined) {
19564
19565 // each category
19566 $.each(response[fields.categoryResults], function(index, category) {
19567 if(category[fields.results] !== undefined && category.results.length > 0) {
19568
19569 html += '<div class="category">';
19570
19571 if(category[fields.categoryName] !== undefined) {
19572 html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
19573 }
19574
19575 // each item inside category
19576 html += '<div class="results">';
19577 $.each(category.results, function(index, result) {
19578 if(result[fields.url]) {
19579 html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
19580 }
19581 else {
19582 html += '<a class="result">';
19583 }
19584 if(result[fields.image] !== undefined) {
19585 html += ''
19586 + '<div class="image">'
19587 + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
19588 + '</div>'
19589 ;
19590 }
19591 html += '<div class="content">';
19592 if(result[fields.price] !== undefined) {
19593 html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
19594 }
19595 if(result[fields.title] !== undefined) {
19596 html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
19597 }
19598 if(result[fields.description] !== undefined) {
19599 html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
19600 }
19601 html += ''
19602 + '</div>'
19603 ;
19604 html += '</a>';
19605 });
19606 html += '</div>';
19607 html += ''
19608 + '</div>'
19609 ;
19610 }
19611 });
19612 if(response[fields.action]) {
19613 if(fields.actionURL === false) {
19614 html += ''
19615 + '<div class="action">'
19616 + escape(response[fields.action][fields.actionText], preserveHTML)
19617 + '</div>';
19618 } else {
19619 html += ''
19620 + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
19621 + escape(response[fields.action][fields.actionText], preserveHTML)
19622 + '</a>';
19623 }
19624 }
19625 return html;
19626 }
19627 return false;
19628 },
19629 standard: function(response, fields, preserveHTML) {
19630 var
19631 html = '',
19632 escape = $.fn.search.settings.templates.escape
19633 ;
19634 if(response[fields.results] !== undefined) {
19635
19636 // each result
19637 $.each(response[fields.results], function(index, result) {
19638 if(result[fields.url]) {
19639 html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
19640 }
19641 else {
19642 html += '<a class="result">';
19643 }
19644 if(result[fields.image] !== undefined) {
19645 html += ''
19646 + '<div class="image">'
19647 + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
19648 + '</div>'
19649 ;
19650 }
19651 html += '<div class="content">';
19652 if(result[fields.price] !== undefined) {
19653 html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
19654 }
19655 if(result[fields.title] !== undefined) {
19656 html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
19657 }
19658 if(result[fields.description] !== undefined) {
19659 html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
19660 }
19661 html += ''
19662 + '</div>'
19663 ;
19664 html += '</a>';
19665 });
19666 if(response[fields.action]) {
19667 if(fields.actionURL === false) {
19668 html += ''
19669 + '<div class="action">'
19670 + escape(response[fields.action][fields.actionText], preserveHTML)
19671 + '</div>';
19672 } else {
19673 html += ''
19674 + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
19675 + escape(response[fields.action][fields.actionText], preserveHTML)
19676 + '</a>';
19677 }
19678 }
19679 return html;
19680 }
19681 return false;
19682 }
19683 }
19684};
19685
19686})( jQuery, window, document );
19687
19688/*!
19689 * # Fomantic-UI 2.8.8 - Shape
19690 * http://github.com/fomantic/Fomantic-UI/
19691 *
19692 *
19693 * Released under the MIT license
19694 * http://opensource.org/licenses/MIT
19695 *
19696 */
19697
19698;(function ($, window, document, undefined) {
19699
19700'use strict';
19701
19702$.isFunction = $.isFunction || function(obj) {
19703 return typeof obj === "function" && typeof obj.nodeType !== "number";
19704};
19705
19706window = (typeof window != 'undefined' && window.Math == Math)
19707 ? window
19708 : (typeof self != 'undefined' && self.Math == Math)
19709 ? self
19710 : Function('return this')()
19711;
19712
19713$.fn.shape = function(parameters) {
19714 var
19715 $allModules = $(this),
19716
19717 time = new Date().getTime(),
19718 performance = [],
19719
19720 query = arguments[0],
19721 methodInvoked = (typeof query == 'string'),
19722 queryArguments = [].slice.call(arguments, 1),
19723
19724 requestAnimationFrame = window.requestAnimationFrame
19725 || window.mozRequestAnimationFrame
19726 || window.webkitRequestAnimationFrame
19727 || window.msRequestAnimationFrame
19728 || function(callback) { setTimeout(callback, 0); },
19729
19730 returnedValue
19731 ;
19732
19733 $allModules
19734 .each(function() {
19735 var
19736 moduleSelector = $allModules.selector || '',
19737 settings = ( $.isPlainObject(parameters) )
19738 ? $.extend(true, {}, $.fn.shape.settings, parameters)
19739 : $.extend({}, $.fn.shape.settings),
19740
19741 // internal aliases
19742 namespace = settings.namespace,
19743 selector = settings.selector,
19744 error = settings.error,
19745 className = settings.className,
19746
19747 // define namespaces for modules
19748 eventNamespace = '.' + namespace,
19749 moduleNamespace = 'module-' + namespace,
19750
19751 // selector cache
19752 $module = $(this),
19753 $sides = $module.find('>' + selector.sides),
19754 $side = $sides.find('>' + selector.side),
19755
19756 // private variables
19757 nextIndex = false,
19758 $activeSide,
19759 $nextSide,
19760
19761 // standard module
19762 element = this,
19763 instance = $module.data(moduleNamespace),
19764 module
19765 ;
19766
19767 module = {
19768
19769 initialize: function() {
19770 module.verbose('Initializing module for', element);
19771 module.set.defaultSide();
19772 module.instantiate();
19773 },
19774
19775 instantiate: function() {
19776 module.verbose('Storing instance of module', module);
19777 instance = module;
19778 $module
19779 .data(moduleNamespace, instance)
19780 ;
19781 },
19782
19783 destroy: function() {
19784 module.verbose('Destroying previous module for', element);
19785 $module
19786 .removeData(moduleNamespace)
19787 .off(eventNamespace)
19788 ;
19789 },
19790
19791 refresh: function() {
19792 module.verbose('Refreshing selector cache for', element);
19793 $module = $(element);
19794 $sides = $(this).find(selector.sides);
19795 $side = $(this).find(selector.side);
19796 },
19797
19798 repaint: function() {
19799 module.verbose('Forcing repaint event');
19800 var
19801 shape = $sides[0] || document.createElement('div'),
19802 fakeAssignment = shape.offsetWidth
19803 ;
19804 },
19805
19806 animate: function(propertyObject, callback) {
19807 module.verbose('Animating box with properties', propertyObject);
19808 callback = callback || function(event) {
19809 module.verbose('Executing animation callback');
19810 if(event !== undefined) {
19811 event.stopPropagation();
19812 }
19813 module.reset();
19814 module.set.active();
19815 };
19816 settings.beforeChange.call($nextSide[0]);
19817 if(module.get.transitionEvent()) {
19818 module.verbose('Starting CSS animation');
19819 $module
19820 .addClass(className.animating)
19821 ;
19822 $sides
19823 .css(propertyObject)
19824 .one(module.get.transitionEvent(), callback)
19825 ;
19826 module.set.duration(settings.duration);
19827 requestAnimationFrame(function() {
19828 $module
19829 .addClass(className.animating)
19830 ;
19831 $activeSide
19832 .addClass(className.hidden)
19833 ;
19834 });
19835 }
19836 else {
19837 callback();
19838 }
19839 },
19840
19841 queue: function(method) {
19842 module.debug('Queueing animation of', method);
19843 $sides
19844 .one(module.get.transitionEvent(), function() {
19845 module.debug('Executing queued animation');
19846 setTimeout(function(){
19847 $module.shape(method);
19848 }, 0);
19849 })
19850 ;
19851 },
19852
19853 reset: function() {
19854 module.verbose('Animating states reset');
19855 $module
19856 .removeClass(className.animating)
19857 .attr('style', '')
19858 .removeAttr('style')
19859 ;
19860 // removeAttr style does not consistently work in safari
19861 $sides
19862 .attr('style', '')
19863 .removeAttr('style')
19864 ;
19865 $side
19866 .attr('style', '')
19867 .removeAttr('style')
19868 .removeClass(className.hidden)
19869 ;
19870 $nextSide
19871 .removeClass(className.animating)
19872 .attr('style', '')
19873 .removeAttr('style')
19874 ;
19875 },
19876
19877 is: {
19878 complete: function() {
19879 return ($side.filter('.' + className.active)[0] == $nextSide[0]);
19880 },
19881 animating: function() {
19882 return $module.hasClass(className.animating);
19883 },
19884 hidden: function() {
19885 return $module.closest(':hidden').length > 0;
19886 }
19887 },
19888
19889 set: {
19890
19891 defaultSide: function() {
19892 $activeSide = $side.filter('.' + settings.className.active);
19893 $nextSide = ( $activeSide.next(selector.side).length > 0 )
19894 ? $activeSide.next(selector.side)
19895 : $side.first()
19896 ;
19897 nextIndex = false;
19898 module.verbose('Active side set to', $activeSide);
19899 module.verbose('Next side set to', $nextSide);
19900 },
19901
19902 duration: function(duration) {
19903 duration = duration || settings.duration;
19904 duration = (typeof duration == 'number')
19905 ? duration + 'ms'
19906 : duration
19907 ;
19908 module.verbose('Setting animation duration', duration);
19909 if(settings.duration || settings.duration === 0) {
19910 $sides.add($side)
19911 .css({
19912 '-webkit-transition-duration': duration,
19913 '-moz-transition-duration': duration,
19914 '-ms-transition-duration': duration,
19915 '-o-transition-duration': duration,
19916 'transition-duration': duration
19917 })
19918 ;
19919 }
19920 },
19921
19922 currentStageSize: function() {
19923 var
19924 $activeSide = $side.filter('.' + settings.className.active),
19925 width = $activeSide.outerWidth(true),
19926 height = $activeSide.outerHeight(true)
19927 ;
19928 $module
19929 .css({
19930 width: width,
19931 height: height
19932 })
19933 ;
19934 },
19935
19936 stageSize: function() {
19937 var
19938 $clone = $module.clone().addClass(className.loading),
19939 $side = $clone.find('>' + selector.sides + '>' + selector.side),
19940 $activeSide = $side.filter('.' + settings.className.active),
19941 $nextSide = (nextIndex)
19942 ? $side.eq(nextIndex)
19943 : ( $activeSide.next(selector.side).length > 0 )
19944 ? $activeSide.next(selector.side)
19945 : $side.first(),
19946 newWidth = (settings.width === 'next')
19947 ? $nextSide.outerWidth(true)
19948 : (settings.width === 'initial')
19949 ? $module.width()
19950 : settings.width,
19951 newHeight = (settings.height === 'next')
19952 ? $nextSide.outerHeight(true)
19953 : (settings.height === 'initial')
19954 ? $module.height()
19955 : settings.height
19956 ;
19957 $activeSide.removeClass(className.active);
19958 $nextSide.addClass(className.active);
19959 $clone.insertAfter($module);
19960 $clone.remove();
19961 if(settings.width !== 'auto') {
19962 $module.css('width', newWidth + settings.jitter);
19963 module.verbose('Specifying width during animation', newWidth);
19964 }
19965 if(settings.height !== 'auto') {
19966 $module.css('height', newHeight + settings.jitter);
19967 module.verbose('Specifying height during animation', newHeight);
19968 }
19969 },
19970
19971 nextSide: function(selector) {
19972 nextIndex = selector;
19973 $nextSide = $side.filter(selector);
19974 nextIndex = $side.index($nextSide);
19975 if($nextSide.length === 0) {
19976 module.set.defaultSide();
19977 module.error(error.side);
19978 }
19979 module.verbose('Next side manually set to', $nextSide);
19980 },
19981
19982 active: function() {
19983 module.verbose('Setting new side to active', $nextSide);
19984 $side
19985 .removeClass(className.active)
19986 ;
19987 $nextSide
19988 .addClass(className.active)
19989 ;
19990 settings.onChange.call($nextSide[0]);
19991 module.set.defaultSide();
19992 }
19993 },
19994
19995 flip: {
19996 to: function(type,stage){
19997 if(module.is.hidden()) {
19998 module.debug('Module not visible', $nextSide);
19999 return;
20000 }
20001 if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
20002 module.debug('Side already visible', $nextSide);
20003 return;
20004 }
20005 var
20006 transform = module.get.transform[type]()
20007 ;
20008 if( !module.is.animating()) {
20009 module.debug('Flipping '+type, $nextSide);
20010 module.set.stageSize();
20011 module.stage[stage]();
20012 module.animate(transform);
20013 }
20014 else {
20015 module.queue('flip '+type);
20016 }
20017 },
20018
20019 up: function() {
20020 module.flip.to('up','above');
20021 },
20022
20023 down: function() {
20024 module.flip.to('down','below');
20025 },
20026
20027 left: function() {
20028 module.flip.to('left','left');
20029 },
20030
20031 right: function() {
20032 module.flip.to('right','right');
20033 },
20034
20035 over: function() {
20036 module.flip.to('over','behind');
20037 },
20038
20039 back: function() {
20040 module.flip.to('back','behind');
20041 }
20042
20043 },
20044
20045 get: {
20046
20047 transform: {
20048 up: function() {
20049 var
20050 translateZ = $activeSide.outerHeight(true) / 2,
20051 translateY = $nextSide.outerHeight(true) - translateZ
20052 ;
20053 return {
20054 transform: 'translateY(' + translateY + 'px) translateZ(-'+ translateZ + 'px) rotateX(-90deg)'
20055 };
20056 },
20057
20058 down: function() {
20059 var
20060 translate = {
20061 z: $activeSide.outerHeight(true) / 2
20062 }
20063 ;
20064 return {
20065 transform: 'translateY(-' + translate.z + 'px) translateZ(-'+ translate.z + 'px) rotateX(90deg)'
20066 };
20067 },
20068
20069 left: function() {
20070 var
20071 translateZ = $activeSide.outerWidth(true) / 2,
20072 translateX = $nextSide.outerWidth(true) - translateZ
20073 ;
20074 return {
20075 transform: 'translateX(' + translateX + 'px) translateZ(-' + translateZ + 'px) rotateY(90deg)'
20076 };
20077 },
20078
20079 right: function() {
20080 var
20081 translate = {
20082 z : $activeSide.outerWidth(true) / 2
20083 }
20084 ;
20085 return {
20086 transform: 'translateX(-' + translate.z + 'px) translateZ(-' + translate.z + 'px) rotateY(-90deg)'
20087 };
20088 },
20089
20090 over: function() {
20091 var
20092 translate = {
20093 x : -(($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) / 2)
20094 }
20095 ;
20096 return {
20097 transform: 'translateX(' + translate.x + 'px) rotateY(180deg)'
20098 };
20099 },
20100
20101 back: function() {
20102 var
20103 translate = {
20104 x : -(($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) / 2)
20105 }
20106 ;
20107 return {
20108 transform: 'translateX(' + translate.x + 'px) rotateY(-180deg)'
20109 };
20110 }
20111 },
20112
20113 transitionEvent: function() {
20114 var
20115 element = document.createElement('element'),
20116 transitions = {
20117 'transition' :'transitionend',
20118 'OTransition' :'oTransitionEnd',
20119 'MozTransition' :'transitionend',
20120 'WebkitTransition' :'webkitTransitionEnd'
20121 },
20122 transition
20123 ;
20124 for(transition in transitions){
20125 if( element.style[transition] !== undefined ){
20126 return transitions[transition];
20127 }
20128 }
20129 },
20130
20131 nextSide: function() {
20132 return ( $activeSide.next(selector.side).length > 0 )
20133 ? $activeSide.next(selector.side)
20134 : $side.first()
20135 ;
20136 }
20137
20138 },
20139
20140 stage: {
20141
20142 above: function() {
20143 var
20144 box = {
20145 origin : (($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) / 2),
20146 depth : {
20147 active : ($nextSide.outerHeight(true) / 2),
20148 next : ($activeSide.outerHeight(true) / 2)
20149 }
20150 }
20151 ;
20152 module.verbose('Setting the initial animation position as above', $nextSide, box);
20153 $activeSide
20154 .css({
20155 'transform' : 'rotateX(0deg)'
20156 })
20157 ;
20158 $nextSide
20159 .addClass(className.animating)
20160 .css({
20161 'top' : box.origin + 'px',
20162 'transform' : 'rotateX(90deg) translateZ(' + box.depth.next + 'px) translateY(-' + box.depth.active + 'px)'
20163 })
20164 ;
20165 },
20166
20167 below: function() {
20168 var
20169 box = {
20170 origin : (($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) / 2),
20171 depth : {
20172 active : ($nextSide.outerHeight(true) / 2),
20173 next : ($activeSide.outerHeight(true) / 2)
20174 }
20175 }
20176 ;
20177 module.verbose('Setting the initial animation position as below', $nextSide, box);
20178 $activeSide
20179 .css({
20180 'transform' : 'rotateX(0deg)'
20181 })
20182 ;
20183 $nextSide
20184 .addClass(className.animating)
20185 .css({
20186 'top' : box.origin + 'px',
20187 'transform' : 'rotateX(-90deg) translateZ(' + box.depth.next + 'px) translateY(' + box.depth.active + 'px)'
20188 })
20189 ;
20190 },
20191
20192 left: function() {
20193 var
20194 height = {
20195 active : $activeSide.outerWidth(true),
20196 next : $nextSide.outerWidth(true)
20197 },
20198 box = {
20199 origin : ( ( height.active - height.next ) / 2),
20200 depth : {
20201 active : (height.next / 2),
20202 next : (height.active / 2)
20203 }
20204 }
20205 ;
20206 module.verbose('Setting the initial animation position as left', $nextSide, box);
20207 $activeSide
20208 .css({
20209 'transform' : 'rotateY(0deg)'
20210 })
20211 ;
20212 $nextSide
20213 .addClass(className.animating)
20214 .css({
20215 'left' : box.origin + 'px',
20216 'transform' : 'rotateY(-90deg) translateZ(' + box.depth.next + 'px) translateX(-' + box.depth.active + 'px)'
20217 })
20218 ;
20219 },
20220
20221 right: function() {
20222 var
20223 height = {
20224 active : $activeSide.outerWidth(true),
20225 next : $nextSide.outerWidth(true)
20226 },
20227 box = {
20228 origin : ( ( height.active - height.next ) / 2),
20229 depth : {
20230 active : (height.next / 2),
20231 next : (height.active / 2)
20232 }
20233 }
20234 ;
20235 module.verbose('Setting the initial animation position as right', $nextSide, box);
20236 $activeSide
20237 .css({
20238 'transform' : 'rotateY(0deg)'
20239 })
20240 ;
20241 $nextSide
20242 .addClass(className.animating)
20243 .css({
20244 'left' : box.origin + 'px',
20245 'transform' : 'rotateY(90deg) translateZ(' + box.depth.next + 'px) translateX(' + box.depth.active + 'px)'
20246 })
20247 ;
20248 },
20249
20250 behind: function() {
20251 var
20252 height = {
20253 active : $activeSide.outerWidth(true),
20254 next : $nextSide.outerWidth(true)
20255 },
20256 box = {
20257 origin : ( ( height.active - height.next ) / 2),
20258 depth : {
20259 active : (height.next / 2),
20260 next : (height.active / 2)
20261 }
20262 }
20263 ;
20264 module.verbose('Setting the initial animation position as behind', $nextSide, box);
20265 $activeSide
20266 .css({
20267 'transform' : 'rotateY(0deg)'
20268 })
20269 ;
20270 $nextSide
20271 .addClass(className.animating)
20272 .css({
20273 'left' : box.origin + 'px',
20274 'transform' : 'rotateY(-180deg)'
20275 })
20276 ;
20277 }
20278 },
20279 setting: function(name, value) {
20280 module.debug('Changing setting', name, value);
20281 if( $.isPlainObject(name) ) {
20282 $.extend(true, settings, name);
20283 }
20284 else if(value !== undefined) {
20285 if($.isPlainObject(settings[name])) {
20286 $.extend(true, settings[name], value);
20287 }
20288 else {
20289 settings[name] = value;
20290 }
20291 }
20292 else {
20293 return settings[name];
20294 }
20295 },
20296 internal: function(name, value) {
20297 if( $.isPlainObject(name) ) {
20298 $.extend(true, module, name);
20299 }
20300 else if(value !== undefined) {
20301 module[name] = value;
20302 }
20303 else {
20304 return module[name];
20305 }
20306 },
20307 debug: function() {
20308 if(!settings.silent && settings.debug) {
20309 if(settings.performance) {
20310 module.performance.log(arguments);
20311 }
20312 else {
20313 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
20314 module.debug.apply(console, arguments);
20315 }
20316 }
20317 },
20318 verbose: function() {
20319 if(!settings.silent && settings.verbose && settings.debug) {
20320 if(settings.performance) {
20321 module.performance.log(arguments);
20322 }
20323 else {
20324 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
20325 module.verbose.apply(console, arguments);
20326 }
20327 }
20328 },
20329 error: function() {
20330 if(!settings.silent) {
20331 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
20332 module.error.apply(console, arguments);
20333 }
20334 },
20335 performance: {
20336 log: function(message) {
20337 var
20338 currentTime,
20339 executionTime,
20340 previousTime
20341 ;
20342 if(settings.performance) {
20343 currentTime = new Date().getTime();
20344 previousTime = time || currentTime;
20345 executionTime = currentTime - previousTime;
20346 time = currentTime;
20347 performance.push({
20348 'Name' : message[0],
20349 'Arguments' : [].slice.call(message, 1) || '',
20350 'Element' : element,
20351 'Execution Time' : executionTime
20352 });
20353 }
20354 clearTimeout(module.performance.timer);
20355 module.performance.timer = setTimeout(module.performance.display, 500);
20356 },
20357 display: function() {
20358 var
20359 title = settings.name + ':',
20360 totalTime = 0
20361 ;
20362 time = false;
20363 clearTimeout(module.performance.timer);
20364 $.each(performance, function(index, data) {
20365 totalTime += data['Execution Time'];
20366 });
20367 title += ' ' + totalTime + 'ms';
20368 if(moduleSelector) {
20369 title += ' \'' + moduleSelector + '\'';
20370 }
20371 if($allModules.length > 1) {
20372 title += ' ' + '(' + $allModules.length + ')';
20373 }
20374 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
20375 console.groupCollapsed(title);
20376 if(console.table) {
20377 console.table(performance);
20378 }
20379 else {
20380 $.each(performance, function(index, data) {
20381 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
20382 });
20383 }
20384 console.groupEnd();
20385 }
20386 performance = [];
20387 }
20388 },
20389 invoke: function(query, passedArguments, context) {
20390 var
20391 object = instance,
20392 maxDepth,
20393 found,
20394 response
20395 ;
20396 passedArguments = passedArguments || queryArguments;
20397 context = element || context;
20398 if(typeof query == 'string' && object !== undefined) {
20399 query = query.split(/[\. ]/);
20400 maxDepth = query.length - 1;
20401 $.each(query, function(depth, value) {
20402 var camelCaseValue = (depth != maxDepth)
20403 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
20404 : query
20405 ;
20406 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
20407 object = object[camelCaseValue];
20408 }
20409 else if( object[camelCaseValue] !== undefined ) {
20410 found = object[camelCaseValue];
20411 return false;
20412 }
20413 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
20414 object = object[value];
20415 }
20416 else if( object[value] !== undefined ) {
20417 found = object[value];
20418 return false;
20419 }
20420 else {
20421 return false;
20422 }
20423 });
20424 }
20425 if ( $.isFunction( found ) ) {
20426 response = found.apply(context, passedArguments);
20427 }
20428 else if(found !== undefined) {
20429 response = found;
20430 }
20431 if(Array.isArray(returnedValue)) {
20432 returnedValue.push(response);
20433 }
20434 else if(returnedValue !== undefined) {
20435 returnedValue = [returnedValue, response];
20436 }
20437 else if(response !== undefined) {
20438 returnedValue = response;
20439 }
20440 return found;
20441 }
20442 };
20443
20444 if(methodInvoked) {
20445 if(instance === undefined) {
20446 module.initialize();
20447 }
20448 var $inputs = $module.find('input');
20449 if( $inputs.length > 0) {
20450 $inputs.blur();
20451 setTimeout(function(){
20452 module.invoke(query);
20453 }, 150);
20454 } else {
20455 module.invoke(query);
20456 }
20457 }
20458 else {
20459 if(instance !== undefined) {
20460 instance.invoke('destroy');
20461 }
20462 module.initialize();
20463 }
20464 })
20465 ;
20466
20467 return (returnedValue !== undefined)
20468 ? returnedValue
20469 : this
20470 ;
20471};
20472
20473$.fn.shape.settings = {
20474
20475 // module info
20476 name : 'Shape',
20477
20478 // hide all debug content
20479 silent : false,
20480
20481 // debug content outputted to console
20482 debug : false,
20483
20484 // verbose debug output
20485 verbose : false,
20486
20487 // fudge factor in pixels when swapping from 2d to 3d (can be useful to correct rounding errors)
20488 jitter : 0,
20489
20490 // performance data output
20491 performance: true,
20492
20493 // event namespace
20494 namespace : 'shape',
20495
20496 // width during animation, can be set to 'auto', initial', 'next' or pixel amount
20497 width: 'initial',
20498
20499 // height during animation, can be set to 'auto', 'initial', 'next' or pixel amount
20500 height: 'initial',
20501
20502 // callback occurs on side change
20503 beforeChange : function() {},
20504 onChange : function() {},
20505
20506 // allow animation to same side
20507 allowRepeats: false,
20508
20509 // animation duration
20510 duration : false,
20511
20512 // possible errors
20513 error: {
20514 side : 'You tried to switch to a side that does not exist.',
20515 method : 'The method you called is not defined'
20516 },
20517
20518 // classnames used
20519 className : {
20520 animating : 'animating',
20521 hidden : 'hidden',
20522 loading : 'loading',
20523 active : 'active'
20524 },
20525
20526 // selectors used
20527 selector : {
20528 sides : '.sides',
20529 side : '.side'
20530 }
20531
20532};
20533
20534
20535})( jQuery, window, document );
20536
20537/*!
20538 * # Fomantic-UI 2.8.8 - Sidebar
20539 * http://github.com/fomantic/Fomantic-UI/
20540 *
20541 *
20542 * Released under the MIT license
20543 * http://opensource.org/licenses/MIT
20544 *
20545 */
20546
20547;(function ($, window, document, undefined) {
20548
20549'use strict';
20550
20551$.isFunction = $.isFunction || function(obj) {
20552 return typeof obj === "function" && typeof obj.nodeType !== "number";
20553};
20554
20555window = (typeof window != 'undefined' && window.Math == Math)
20556 ? window
20557 : (typeof self != 'undefined' && self.Math == Math)
20558 ? self
20559 : Function('return this')()
20560;
20561
20562$.fn.sidebar = function(parameters) {
20563 var
20564 $allModules = $(this),
20565 $window = $(window),
20566 $document = $(document),
20567 $html = $('html'),
20568 $head = $('head'),
20569
20570 moduleSelector = $allModules.selector || '',
20571
20572 time = new Date().getTime(),
20573 performance = [],
20574
20575 query = arguments[0],
20576 methodInvoked = (typeof query == 'string'),
20577 queryArguments = [].slice.call(arguments, 1),
20578
20579 requestAnimationFrame = window.requestAnimationFrame
20580 || window.mozRequestAnimationFrame
20581 || window.webkitRequestAnimationFrame
20582 || window.msRequestAnimationFrame
20583 || function(callback) { setTimeout(callback, 0); },
20584
20585 returnedValue
20586 ;
20587
20588 $allModules
20589 .each(function() {
20590 var
20591 settings = ( $.isPlainObject(parameters) )
20592 ? $.extend(true, {}, $.fn.sidebar.settings, parameters)
20593 : $.extend({}, $.fn.sidebar.settings),
20594
20595 selector = settings.selector,
20596 className = settings.className,
20597 namespace = settings.namespace,
20598 regExp = settings.regExp,
20599 error = settings.error,
20600
20601 eventNamespace = '.' + namespace,
20602 moduleNamespace = 'module-' + namespace,
20603
20604 $module = $(this),
20605 $context = $(settings.context),
20606
20607 $sidebars = $module.children(selector.sidebar),
20608 $fixed = $context.children(selector.fixed),
20609 $pusher = $context.children(selector.pusher),
20610 $style,
20611
20612 element = this,
20613 instance = $module.data(moduleNamespace),
20614
20615 elementNamespace,
20616 id,
20617 currentScroll,
20618 transitionEvent,
20619
20620 module
20621 ;
20622
20623 module = {
20624
20625 initialize: function() {
20626 module.debug('Initializing sidebar', parameters);
20627
20628 module.create.id();
20629
20630 transitionEvent = module.get.transitionEvent();
20631
20632 // avoids locking rendering if initialized in onReady
20633 if(settings.delaySetup) {
20634 requestAnimationFrame(module.setup.layout);
20635 }
20636 else {
20637 module.setup.layout();
20638 }
20639
20640 requestAnimationFrame(function() {
20641 module.setup.cache();
20642 });
20643
20644 module.instantiate();
20645 },
20646
20647 instantiate: function() {
20648 module.verbose('Storing instance of module', module);
20649 instance = module;
20650 $module
20651 .data(moduleNamespace, module)
20652 ;
20653 },
20654
20655 create: {
20656 id: function() {
20657 id = (Math.random().toString(16) + '000000000').substr(2,8);
20658 elementNamespace = '.' + id;
20659 module.verbose('Creating unique id for element', id);
20660 }
20661 },
20662
20663 destroy: function() {
20664 module.verbose('Destroying previous module for', $module);
20665 $module
20666 .off(eventNamespace)
20667 .removeData(moduleNamespace)
20668 ;
20669 if(module.is.ios()) {
20670 module.remove.ios();
20671 }
20672 // bound by uuid
20673 $context.off(elementNamespace);
20674 $window.off(elementNamespace);
20675 $document.off(elementNamespace);
20676 },
20677
20678 event: {
20679 clickaway: function(event) {
20680 if(settings.closable){
20681 var
20682 clickedInPusher = ($pusher.find(event.target).length > 0 || $pusher.is(event.target)),
20683 clickedContext = ($context.is(event.target))
20684 ;
20685 if(clickedInPusher) {
20686 module.verbose('User clicked on dimmed page');
20687 module.hide();
20688 }
20689 if(clickedContext) {
20690 module.verbose('User clicked on dimmable context (scaled out page)');
20691 module.hide();
20692 }
20693 }
20694 },
20695 touch: function(event) {
20696 //event.stopPropagation();
20697 },
20698 containScroll: function(event) {
20699 if(element.scrollTop <= 0) {
20700 element.scrollTop = 1;
20701 }
20702 if((element.scrollTop + element.offsetHeight) >= element.scrollHeight) {
20703 element.scrollTop = element.scrollHeight - element.offsetHeight - 1;
20704 }
20705 },
20706 scroll: function(event) {
20707 if( $(event.target).closest(selector.sidebar).length === 0 ) {
20708 event.preventDefault();
20709 }
20710 }
20711 },
20712
20713 bind: {
20714 clickaway: function() {
20715 module.verbose('Adding clickaway events to context', $context);
20716 $context
20717 .on('click' + elementNamespace, module.event.clickaway)
20718 .on('touchend' + elementNamespace, module.event.clickaway)
20719 ;
20720 },
20721 scrollLock: function() {
20722 if(settings.scrollLock) {
20723 module.debug('Disabling page scroll');
20724 $window
20725 .on('DOMMouseScroll' + elementNamespace, module.event.scroll)
20726 ;
20727 }
20728 module.verbose('Adding events to contain sidebar scroll');
20729 $document
20730 .on('touchmove' + elementNamespace, module.event.touch)
20731 ;
20732 $module
20733 .on('scroll' + eventNamespace, module.event.containScroll)
20734 ;
20735 }
20736 },
20737 unbind: {
20738 clickaway: function() {
20739 module.verbose('Removing clickaway events from context', $context);
20740 $context.off(elementNamespace);
20741 },
20742 scrollLock: function() {
20743 module.verbose('Removing scroll lock from page');
20744 $document.off(elementNamespace);
20745 $window.off(elementNamespace);
20746 $module.off('scroll' + eventNamespace);
20747 }
20748 },
20749
20750 add: {
20751 inlineCSS: function() {
20752 var
20753 width = module.cache.width || $module.outerWidth(),
20754 height = module.cache.height || $module.outerHeight(),
20755 isRTL = module.is.rtl(),
20756 direction = module.get.direction(),
20757 distance = {
20758 left : width,
20759 right : -width,
20760 top : height,
20761 bottom : -height
20762 },
20763 style
20764 ;
20765
20766 if(isRTL){
20767 module.verbose('RTL detected, flipping widths');
20768 distance.left = -width;
20769 distance.right = width;
20770 }
20771
20772 style = '<style>';
20773
20774 if(direction === 'left' || direction === 'right') {
20775 module.debug('Adding CSS rules for animation distance', width);
20776 style += ''
20777 + ' .ui.visible.' + direction + '.sidebar ~ .fixed,'
20778 + ' .ui.visible.' + direction + '.sidebar ~ .pusher {'
20779 + ' -webkit-transform: translate3d('+ distance[direction] + 'px, 0, 0);'
20780 + ' transform: translate3d('+ distance[direction] + 'px, 0, 0);'
20781 + ' }'
20782 ;
20783 }
20784 else if(direction === 'top' || direction == 'bottom') {
20785 style += ''
20786 + ' .ui.visible.' + direction + '.sidebar ~ .fixed,'
20787 + ' .ui.visible.' + direction + '.sidebar ~ .pusher {'
20788 + ' -webkit-transform: translate3d(0, ' + distance[direction] + 'px, 0);'
20789 + ' transform: translate3d(0, ' + distance[direction] + 'px, 0);'
20790 + ' }'
20791 ;
20792 }
20793
20794 /* IE is only browser not to create context with transforms */
20795 /* https://www.w3.org/Bugs/Public/show_bug.cgi?id=16328 */
20796 if( module.is.ie() ) {
20797 if(direction === 'left' || direction === 'right') {
20798 module.debug('Adding CSS rules for animation distance', width);
20799 style += ''
20800 + ' body.pushable > .ui.visible.' + direction + '.sidebar ~ .pusher:after {'
20801 + ' -webkit-transform: translate3d('+ distance[direction] + 'px, 0, 0);'
20802 + ' transform: translate3d('+ distance[direction] + 'px, 0, 0);'
20803 + ' }'
20804 ;
20805 }
20806 else if(direction === 'top' || direction == 'bottom') {
20807 style += ''
20808 + ' body.pushable > .ui.visible.' + direction + '.sidebar ~ .pusher:after {'
20809 + ' -webkit-transform: translate3d(0, ' + distance[direction] + 'px, 0);'
20810 + ' transform: translate3d(0, ' + distance[direction] + 'px, 0);'
20811 + ' }'
20812 ;
20813 }
20814 /* opposite sides visible forces content overlay */
20815 style += ''
20816 + ' body.pushable > .ui.visible.left.sidebar ~ .ui.visible.right.sidebar ~ .pusher:after,'
20817 + ' body.pushable > .ui.visible.right.sidebar ~ .ui.visible.left.sidebar ~ .pusher:after {'
20818 + ' -webkit-transform: translate3d(0, 0, 0);'
20819 + ' transform: translate3d(0, 0, 0);'
20820 + ' }'
20821 ;
20822 }
20823 style += '</style>';
20824 $style = $(style)
20825 .appendTo($head)
20826 ;
20827 module.debug('Adding sizing css to head', $style);
20828 }
20829 },
20830
20831 refresh: function() {
20832 module.verbose('Refreshing selector cache');
20833 $context = $(settings.context);
20834 $sidebars = $context.children(selector.sidebar);
20835 $pusher = $context.children(selector.pusher);
20836 $fixed = $context.children(selector.fixed);
20837 module.clear.cache();
20838 },
20839
20840 refreshSidebars: function() {
20841 module.verbose('Refreshing other sidebars');
20842 $sidebars = $context.children(selector.sidebar);
20843 },
20844
20845 repaint: function() {
20846 module.verbose('Forcing repaint event');
20847 element.style.display = 'none';
20848 var ignored = element.offsetHeight;
20849 element.scrollTop = element.scrollTop;
20850 element.style.display = '';
20851 },
20852
20853 setup: {
20854 cache: function() {
20855 module.cache = {
20856 width : $module.outerWidth(),
20857 height : $module.outerHeight()
20858 };
20859 },
20860 layout: function() {
20861 if( $context.children(selector.pusher).length === 0 ) {
20862 module.debug('Adding wrapper element for sidebar');
20863 module.error(error.pusher);
20864 $pusher = $('<div class="pusher" />');
20865 $context
20866 .children()
20867 .not(selector.omitted)
20868 .not($sidebars)
20869 .wrapAll($pusher)
20870 ;
20871 module.refresh();
20872 }
20873 if($module.nextAll(selector.pusher).length === 0 || $module.nextAll(selector.pusher)[0] !== $pusher[0]) {
20874 module.debug('Moved sidebar to correct parent element');
20875 module.error(error.movedSidebar, element);
20876 $module.detach().prependTo($context);
20877 module.refresh();
20878 }
20879 module.clear.cache();
20880 module.set.pushable();
20881 module.set.direction();
20882 }
20883 },
20884
20885 attachEvents: function(selector, event) {
20886 var
20887 $toggle = $(selector)
20888 ;
20889 event = $.isFunction(module[event])
20890 ? module[event]
20891 : module.toggle
20892 ;
20893 if($toggle.length > 0) {
20894 module.debug('Attaching sidebar events to element', selector, event);
20895 $toggle
20896 .on('click' + eventNamespace, event)
20897 ;
20898 }
20899 else {
20900 module.error(error.notFound, selector);
20901 }
20902 },
20903
20904 show: function(callback) {
20905 callback = $.isFunction(callback)
20906 ? callback
20907 : function(){}
20908 ;
20909 if(module.is.hidden()) {
20910 module.refreshSidebars();
20911 if(settings.overlay) {
20912 module.error(error.overlay);
20913 settings.transition = 'overlay';
20914 }
20915 module.refresh();
20916 if(module.othersActive()) {
20917 module.debug('Other sidebars currently visible');
20918 if(settings.exclusive) {
20919 // if not overlay queue animation after hide
20920 if(settings.transition != 'overlay') {
20921 module.hideOthers(module.show);
20922 return;
20923 }
20924 else {
20925 module.hideOthers();
20926 }
20927 }
20928 else {
20929 settings.transition = 'overlay';
20930 }
20931 }
20932 module.pushPage(function() {
20933 callback.call(element);
20934 settings.onShow.call(element);
20935 });
20936 settings.onChange.call(element);
20937 settings.onVisible.call(element);
20938 }
20939 else {
20940 module.debug('Sidebar is already visible');
20941 }
20942 },
20943
20944 hide: function(callback) {
20945 callback = $.isFunction(callback)
20946 ? callback
20947 : function(){}
20948 ;
20949 if(module.is.visible() || module.is.animating()) {
20950 module.debug('Hiding sidebar', callback);
20951 module.refreshSidebars();
20952 module.pullPage(function() {
20953 callback.call(element);
20954 settings.onHidden.call(element);
20955 });
20956 settings.onChange.call(element);
20957 settings.onHide.call(element);
20958 }
20959 },
20960
20961 othersAnimating: function() {
20962 return ($sidebars.not($module).filter('.' + className.animating).length > 0);
20963 },
20964 othersVisible: function() {
20965 return ($sidebars.not($module).filter('.' + className.visible).length > 0);
20966 },
20967 othersActive: function() {
20968 return(module.othersVisible() || module.othersAnimating());
20969 },
20970
20971 hideOthers: function(callback) {
20972 var
20973 $otherSidebars = $sidebars.not($module).filter('.' + className.visible),
20974 sidebarCount = $otherSidebars.length,
20975 callbackCount = 0
20976 ;
20977 callback = callback || function(){};
20978 $otherSidebars
20979 .sidebar('hide', function() {
20980 callbackCount++;
20981 if(callbackCount == sidebarCount) {
20982 callback();
20983 }
20984 })
20985 ;
20986 },
20987
20988 toggle: function() {
20989 module.verbose('Determining toggled direction');
20990 if(module.is.hidden()) {
20991 module.show();
20992 }
20993 else {
20994 module.hide();
20995 }
20996 },
20997
20998 pushPage: function(callback) {
20999 var
21000 transition = module.get.transition(),
21001 $transition = (transition === 'overlay' || module.othersActive())
21002 ? $module
21003 : $pusher,
21004 animate,
21005 dim,
21006 transitionEnd
21007 ;
21008 callback = $.isFunction(callback)
21009 ? callback
21010 : function(){}
21011 ;
21012 if(settings.transition == 'scale down') {
21013 module.scrollToTop();
21014 }
21015 module.set.transition(transition);
21016 module.repaint();
21017 animate = function() {
21018 module.bind.clickaway();
21019 module.add.inlineCSS();
21020 module.set.animating();
21021 module.set.visible();
21022 };
21023 dim = function() {
21024 module.set.dimmed();
21025 };
21026 transitionEnd = function(event) {
21027 if( event.target == $transition[0] ) {
21028 $transition.off(transitionEvent + elementNamespace, transitionEnd);
21029 module.remove.animating();
21030 module.bind.scrollLock();
21031 callback.call(element);
21032 }
21033 };
21034 $transition.off(transitionEvent + elementNamespace);
21035 $transition.on(transitionEvent + elementNamespace, transitionEnd);
21036 requestAnimationFrame(animate);
21037 if(settings.dimPage && !module.othersVisible()) {
21038 requestAnimationFrame(dim);
21039 }
21040 },
21041
21042 pullPage: function(callback) {
21043 var
21044 transition = module.get.transition(),
21045 $transition = (transition == 'overlay' || module.othersActive())
21046 ? $module
21047 : $pusher,
21048 animate,
21049 transitionEnd
21050 ;
21051 callback = $.isFunction(callback)
21052 ? callback
21053 : function(){}
21054 ;
21055 module.verbose('Removing context push state', module.get.direction());
21056
21057 module.unbind.clickaway();
21058 module.unbind.scrollLock();
21059
21060 animate = function() {
21061 module.set.transition(transition);
21062 module.set.animating();
21063 module.remove.visible();
21064 if(settings.dimPage && !module.othersVisible()) {
21065 $pusher.removeClass(className.dimmed);
21066 }
21067 };
21068 transitionEnd = function(event) {
21069 if( event.target == $transition[0] ) {
21070 $transition.off(transitionEvent + elementNamespace, transitionEnd);
21071 module.remove.animating();
21072 module.remove.transition();
21073 module.remove.inlineCSS();
21074 if(transition == 'scale down' || (settings.returnScroll && module.is.mobile()) ) {
21075 module.scrollBack();
21076 }
21077 callback.call(element);
21078 }
21079 };
21080 $transition.off(transitionEvent + elementNamespace);
21081 $transition.on(transitionEvent + elementNamespace, transitionEnd);
21082 requestAnimationFrame(animate);
21083 },
21084
21085 scrollToTop: function() {
21086 module.verbose('Scrolling to top of page to avoid animation issues');
21087 currentScroll = $(window).scrollTop();
21088 $module.scrollTop(0);
21089 window.scrollTo(0, 0);
21090 },
21091
21092 scrollBack: function() {
21093 module.verbose('Scrolling back to original page position');
21094 window.scrollTo(0, currentScroll);
21095 },
21096
21097 clear: {
21098 cache: function() {
21099 module.verbose('Clearing cached dimensions');
21100 module.cache = {};
21101 }
21102 },
21103
21104 set: {
21105
21106 // ios only (scroll on html not document). This prevent auto-resize canvas/scroll in ios
21107 // (This is no longer necessary in latest iOS)
21108 ios: function() {
21109 $html.addClass(className.ios);
21110 },
21111
21112 // container
21113 pushed: function() {
21114 $context.addClass(className.pushed);
21115 },
21116 pushable: function() {
21117 $context.addClass(className.pushable);
21118 },
21119
21120 // pusher
21121 dimmed: function() {
21122 $pusher.addClass(className.dimmed);
21123 },
21124
21125 // sidebar
21126 active: function() {
21127 $module.addClass(className.active);
21128 },
21129 animating: function() {
21130 $module.addClass(className.animating);
21131 },
21132 transition: function(transition) {
21133 transition = transition || module.get.transition();
21134 $module.addClass(transition);
21135 },
21136 direction: function(direction) {
21137 direction = direction || module.get.direction();
21138 $module.addClass(className[direction]);
21139 },
21140 visible: function() {
21141 $module.addClass(className.visible);
21142 },
21143 overlay: function() {
21144 $module.addClass(className.overlay);
21145 }
21146 },
21147 remove: {
21148
21149 inlineCSS: function() {
21150 module.debug('Removing inline css styles', $style);
21151 if($style && $style.length > 0) {
21152 $style.remove();
21153 }
21154 },
21155
21156 // ios scroll on html not document
21157 ios: function() {
21158 $html.removeClass(className.ios);
21159 },
21160
21161 // context
21162 pushed: function() {
21163 $context.removeClass(className.pushed);
21164 },
21165 pushable: function() {
21166 $context.removeClass(className.pushable);
21167 },
21168
21169 // sidebar
21170 active: function() {
21171 $module.removeClass(className.active);
21172 },
21173 animating: function() {
21174 $module.removeClass(className.animating);
21175 },
21176 transition: function(transition) {
21177 transition = transition || module.get.transition();
21178 $module.removeClass(transition);
21179 },
21180 direction: function(direction) {
21181 direction = direction || module.get.direction();
21182 $module.removeClass(className[direction]);
21183 },
21184 visible: function() {
21185 $module.removeClass(className.visible);
21186 },
21187 overlay: function() {
21188 $module.removeClass(className.overlay);
21189 }
21190 },
21191
21192 get: {
21193 direction: function() {
21194 if($module.hasClass(className.top)) {
21195 return className.top;
21196 }
21197 else if($module.hasClass(className.right)) {
21198 return className.right;
21199 }
21200 else if($module.hasClass(className.bottom)) {
21201 return className.bottom;
21202 }
21203 return className.left;
21204 },
21205 transition: function() {
21206 var
21207 direction = module.get.direction(),
21208 transition
21209 ;
21210 transition = ( module.is.mobile() )
21211 ? (settings.mobileTransition == 'auto')
21212 ? settings.defaultTransition.mobile[direction]
21213 : settings.mobileTransition
21214 : (settings.transition == 'auto')
21215 ? settings.defaultTransition.computer[direction]
21216 : settings.transition
21217 ;
21218 module.verbose('Determined transition', transition);
21219 return transition;
21220 },
21221 transitionEvent: function() {
21222 var
21223 element = document.createElement('element'),
21224 transitions = {
21225 'transition' :'transitionend',
21226 'OTransition' :'oTransitionEnd',
21227 'MozTransition' :'transitionend',
21228 'WebkitTransition' :'webkitTransitionEnd'
21229 },
21230 transition
21231 ;
21232 for(transition in transitions){
21233 if( element.style[transition] !== undefined ){
21234 return transitions[transition];
21235 }
21236 }
21237 }
21238 },
21239
21240 is: {
21241
21242 ie: function() {
21243 var
21244 isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
21245 isIE = ('ActiveXObject' in window)
21246 ;
21247 return (isIE11 || isIE);
21248 },
21249
21250 ios: function() {
21251 var
21252 userAgent = navigator.userAgent,
21253 isIOS = userAgent.match(regExp.ios),
21254 isMobileChrome = userAgent.match(regExp.mobileChrome)
21255 ;
21256 if(isIOS && !isMobileChrome) {
21257 module.verbose('Browser was found to be iOS', userAgent);
21258 return true;
21259 }
21260 else {
21261 return false;
21262 }
21263 },
21264 mobile: function() {
21265 var
21266 userAgent = navigator.userAgent,
21267 isMobile = userAgent.match(regExp.mobile)
21268 ;
21269 if(isMobile) {
21270 module.verbose('Browser was found to be mobile', userAgent);
21271 return true;
21272 }
21273 else {
21274 module.verbose('Browser is not mobile, using regular transition', userAgent);
21275 return false;
21276 }
21277 },
21278 hidden: function() {
21279 return !module.is.visible();
21280 },
21281 visible: function() {
21282 return $module.hasClass(className.visible);
21283 },
21284 // alias
21285 open: function() {
21286 return module.is.visible();
21287 },
21288 closed: function() {
21289 return module.is.hidden();
21290 },
21291 vertical: function() {
21292 return $module.hasClass(className.top);
21293 },
21294 animating: function() {
21295 return $context.hasClass(className.animating);
21296 },
21297 rtl: function () {
21298 if(module.cache.rtl === undefined) {
21299 module.cache.rtl = $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl';
21300 }
21301 return module.cache.rtl;
21302 }
21303 },
21304
21305 setting: function(name, value) {
21306 module.debug('Changing setting', name, value);
21307 if( $.isPlainObject(name) ) {
21308 $.extend(true, settings, name);
21309 }
21310 else if(value !== undefined) {
21311 if($.isPlainObject(settings[name])) {
21312 $.extend(true, settings[name], value);
21313 }
21314 else {
21315 settings[name] = value;
21316 }
21317 }
21318 else {
21319 return settings[name];
21320 }
21321 },
21322 internal: function(name, value) {
21323 if( $.isPlainObject(name) ) {
21324 $.extend(true, module, name);
21325 }
21326 else if(value !== undefined) {
21327 module[name] = value;
21328 }
21329 else {
21330 return module[name];
21331 }
21332 },
21333 debug: function() {
21334 if(!settings.silent && settings.debug) {
21335 if(settings.performance) {
21336 module.performance.log(arguments);
21337 }
21338 else {
21339 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
21340 module.debug.apply(console, arguments);
21341 }
21342 }
21343 },
21344 verbose: function() {
21345 if(!settings.silent && settings.verbose && settings.debug) {
21346 if(settings.performance) {
21347 module.performance.log(arguments);
21348 }
21349 else {
21350 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
21351 module.verbose.apply(console, arguments);
21352 }
21353 }
21354 },
21355 error: function() {
21356 if(!settings.silent) {
21357 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
21358 module.error.apply(console, arguments);
21359 }
21360 },
21361 performance: {
21362 log: function(message) {
21363 var
21364 currentTime,
21365 executionTime,
21366 previousTime
21367 ;
21368 if(settings.performance) {
21369 currentTime = new Date().getTime();
21370 previousTime = time || currentTime;
21371 executionTime = currentTime - previousTime;
21372 time = currentTime;
21373 performance.push({
21374 'Name' : message[0],
21375 'Arguments' : [].slice.call(message, 1) || '',
21376 'Element' : element,
21377 'Execution Time' : executionTime
21378 });
21379 }
21380 clearTimeout(module.performance.timer);
21381 module.performance.timer = setTimeout(module.performance.display, 500);
21382 },
21383 display: function() {
21384 var
21385 title = settings.name + ':',
21386 totalTime = 0
21387 ;
21388 time = false;
21389 clearTimeout(module.performance.timer);
21390 $.each(performance, function(index, data) {
21391 totalTime += data['Execution Time'];
21392 });
21393 title += ' ' + totalTime + 'ms';
21394 if(moduleSelector) {
21395 title += ' \'' + moduleSelector + '\'';
21396 }
21397 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
21398 console.groupCollapsed(title);
21399 if(console.table) {
21400 console.table(performance);
21401 }
21402 else {
21403 $.each(performance, function(index, data) {
21404 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
21405 });
21406 }
21407 console.groupEnd();
21408 }
21409 performance = [];
21410 }
21411 },
21412 invoke: function(query, passedArguments, context) {
21413 var
21414 object = instance,
21415 maxDepth,
21416 found,
21417 response
21418 ;
21419 passedArguments = passedArguments || queryArguments;
21420 context = element || context;
21421 if(typeof query == 'string' && object !== undefined) {
21422 query = query.split(/[\. ]/);
21423 maxDepth = query.length - 1;
21424 $.each(query, function(depth, value) {
21425 var camelCaseValue = (depth != maxDepth)
21426 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
21427 : query
21428 ;
21429 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
21430 object = object[camelCaseValue];
21431 }
21432 else if( object[camelCaseValue] !== undefined ) {
21433 found = object[camelCaseValue];
21434 return false;
21435 }
21436 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
21437 object = object[value];
21438 }
21439 else if( object[value] !== undefined ) {
21440 found = object[value];
21441 return false;
21442 }
21443 else {
21444 module.error(error.method, query);
21445 return false;
21446 }
21447 });
21448 }
21449 if ( $.isFunction( found ) ) {
21450 response = found.apply(context, passedArguments);
21451 }
21452 else if(found !== undefined) {
21453 response = found;
21454 }
21455 if(Array.isArray(returnedValue)) {
21456 returnedValue.push(response);
21457 }
21458 else if(returnedValue !== undefined) {
21459 returnedValue = [returnedValue, response];
21460 }
21461 else if(response !== undefined) {
21462 returnedValue = response;
21463 }
21464 return found;
21465 }
21466 }
21467 ;
21468
21469 if(methodInvoked) {
21470 if(instance === undefined) {
21471 module.initialize();
21472 }
21473 module.invoke(query);
21474 }
21475 else {
21476 if(instance !== undefined) {
21477 module.invoke('destroy');
21478 }
21479 module.initialize();
21480 }
21481 });
21482
21483 return (returnedValue !== undefined)
21484 ? returnedValue
21485 : this
21486 ;
21487};
21488
21489$.fn.sidebar.settings = {
21490
21491 name : 'Sidebar',
21492 namespace : 'sidebar',
21493
21494 silent : false,
21495 debug : false,
21496 verbose : false,
21497 performance : true,
21498
21499 transition : 'auto',
21500 mobileTransition : 'auto',
21501
21502 defaultTransition : {
21503 computer: {
21504 left : 'uncover',
21505 right : 'uncover',
21506 top : 'overlay',
21507 bottom : 'overlay'
21508 },
21509 mobile: {
21510 left : 'uncover',
21511 right : 'uncover',
21512 top : 'overlay',
21513 bottom : 'overlay'
21514 }
21515 },
21516
21517 context : 'body',
21518 exclusive : false,
21519 closable : true,
21520 dimPage : true,
21521 scrollLock : false,
21522 returnScroll : false,
21523 delaySetup : false,
21524
21525 duration : 500,
21526
21527 onChange : function(){},
21528 onShow : function(){},
21529 onHide : function(){},
21530
21531 onHidden : function(){},
21532 onVisible : function(){},
21533
21534 className : {
21535 active : 'active',
21536 animating : 'animating',
21537 dimmed : 'dimmed',
21538 ios : 'ios',
21539 pushable : 'pushable',
21540 pushed : 'pushed',
21541 right : 'right',
21542 top : 'top',
21543 left : 'left',
21544 bottom : 'bottom',
21545 visible : 'visible'
21546 },
21547
21548 selector: {
21549 fixed : '.fixed',
21550 omitted : 'script, link, style, .ui.modal, .ui.dimmer, .ui.nag, .ui.fixed',
21551 pusher : '.pusher',
21552 sidebar : '.ui.sidebar'
21553 },
21554
21555 regExp: {
21556 ios : /(iPad|iPhone|iPod)/g,
21557 mobileChrome : /(CriOS)/g,
21558 mobile : /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/g
21559 },
21560
21561 error : {
21562 method : 'The method you called is not defined.',
21563 pusher : 'Had to add pusher element. For optimal performance make sure body content is inside a pusher element',
21564 movedSidebar : 'Had to move sidebar. For optimal performance make sure sidebar and pusher are direct children of your body tag',
21565 overlay : 'The overlay setting is no longer supported, use animation: overlay',
21566 notFound : 'There were no elements that matched the specified selector'
21567 }
21568
21569};
21570
21571
21572})( jQuery, window, document );
21573
21574/*!
21575 * # Fomantic-UI 2.8.8 - Sticky
21576 * http://github.com/fomantic/Fomantic-UI/
21577 *
21578 *
21579 * Released under the MIT license
21580 * http://opensource.org/licenses/MIT
21581 *
21582 */
21583
21584;(function ($, window, document, undefined) {
21585
21586'use strict';
21587
21588$.isFunction = $.isFunction || function(obj) {
21589 return typeof obj === "function" && typeof obj.nodeType !== "number";
21590};
21591
21592window = (typeof window != 'undefined' && window.Math == Math)
21593 ? window
21594 : (typeof self != 'undefined' && self.Math == Math)
21595 ? self
21596 : Function('return this')()
21597;
21598
21599$.fn.sticky = function(parameters) {
21600 var
21601 $allModules = $(this),
21602 moduleSelector = $allModules.selector || '',
21603
21604 time = new Date().getTime(),
21605 performance = [],
21606
21607 query = arguments[0],
21608 methodInvoked = (typeof query == 'string'),
21609 queryArguments = [].slice.call(arguments, 1),
21610 returnedValue
21611 ;
21612
21613 $allModules
21614 .each(function() {
21615 var
21616 settings = ( $.isPlainObject(parameters) )
21617 ? $.extend(true, {}, $.fn.sticky.settings, parameters)
21618 : $.extend({}, $.fn.sticky.settings),
21619
21620 className = settings.className,
21621 namespace = settings.namespace,
21622 error = settings.error,
21623
21624 eventNamespace = '.' + namespace,
21625 moduleNamespace = 'module-' + namespace,
21626
21627 $module = $(this),
21628 $window = $(window),
21629 $scroll = $(settings.scrollContext),
21630 $container,
21631 $context,
21632
21633 instance = $module.data(moduleNamespace),
21634
21635 requestAnimationFrame = window.requestAnimationFrame
21636 || window.mozRequestAnimationFrame
21637 || window.webkitRequestAnimationFrame
21638 || window.msRequestAnimationFrame
21639 || function(callback) { setTimeout(callback, 0); },
21640
21641 element = this,
21642
21643 documentObserver,
21644 observer,
21645 module
21646 ;
21647
21648 module = {
21649
21650 initialize: function() {
21651
21652 module.determineContainer();
21653 module.determineContext();
21654 module.verbose('Initializing sticky', settings, $container);
21655
21656 module.save.positions();
21657 module.checkErrors();
21658 module.bind.events();
21659
21660 if(settings.observeChanges) {
21661 module.observeChanges();
21662 }
21663 module.instantiate();
21664 },
21665
21666 instantiate: function() {
21667 module.verbose('Storing instance of module', module);
21668 instance = module;
21669 $module
21670 .data(moduleNamespace, module)
21671 ;
21672 },
21673
21674 destroy: function() {
21675 module.verbose('Destroying previous instance');
21676 module.reset();
21677 if(documentObserver) {
21678 documentObserver.disconnect();
21679 }
21680 if(observer) {
21681 observer.disconnect();
21682 }
21683 $window
21684 .off('load' + eventNamespace, module.event.load)
21685 .off('resize' + eventNamespace, module.event.resize)
21686 ;
21687 $scroll
21688 .off('scrollchange' + eventNamespace, module.event.scrollchange)
21689 ;
21690 $module.removeData(moduleNamespace);
21691 },
21692
21693 observeChanges: function() {
21694 if('MutationObserver' in window) {
21695 documentObserver = new MutationObserver(module.event.documentChanged);
21696 observer = new MutationObserver(module.event.changed);
21697 documentObserver.observe(document, {
21698 childList : true,
21699 subtree : true
21700 });
21701 observer.observe(element, {
21702 childList : true,
21703 subtree : true
21704 });
21705 observer.observe($context[0], {
21706 childList : true,
21707 subtree : true
21708 });
21709 module.debug('Setting up mutation observer', observer);
21710 }
21711 },
21712
21713 determineContainer: function() {
21714 if(settings.container) {
21715 $container = $(settings.container);
21716 }
21717 else {
21718 $container = $module.offsetParent();
21719 }
21720 },
21721
21722 determineContext: function() {
21723 if(settings.context) {
21724 $context = $(settings.context);
21725 }
21726 else {
21727 $context = $container;
21728 }
21729 if($context.length === 0) {
21730 module.error(error.invalidContext, settings.context, $module);
21731 return;
21732 }
21733 },
21734
21735 checkErrors: function() {
21736 if( module.is.hidden() ) {
21737 module.error(error.visible, $module);
21738 }
21739 if(module.cache.element.height > module.cache.context.height) {
21740 module.reset();
21741 module.error(error.elementSize, $module);
21742 return;
21743 }
21744 },
21745
21746 bind: {
21747 events: function() {
21748 $window
21749 .on('load' + eventNamespace, module.event.load)
21750 .on('resize' + eventNamespace, module.event.resize)
21751 ;
21752 // pub/sub pattern
21753 $scroll
21754 .off('scroll' + eventNamespace)
21755 .on('scroll' + eventNamespace, module.event.scroll)
21756 .on('scrollchange' + eventNamespace, module.event.scrollchange)
21757 ;
21758 }
21759 },
21760
21761 event: {
21762 changed: function(mutations) {
21763 clearTimeout(module.timer);
21764 module.timer = setTimeout(function() {
21765 module.verbose('DOM tree modified, updating sticky menu', mutations);
21766 module.refresh();
21767 }, 100);
21768 },
21769 documentChanged: function(mutations) {
21770 [].forEach.call(mutations, function(mutation) {
21771 if(mutation.removedNodes) {
21772 [].forEach.call(mutation.removedNodes, function(node) {
21773 if(node == element || $(node).find(element).length > 0) {
21774 module.debug('Element removed from DOM, tearing down events');
21775 module.destroy();
21776 }
21777 });
21778 }
21779 });
21780 },
21781 load: function() {
21782 module.verbose('Page contents finished loading');
21783 requestAnimationFrame(module.refresh);
21784 },
21785 resize: function() {
21786 module.verbose('Window resized');
21787 requestAnimationFrame(module.refresh);
21788 },
21789 scroll: function() {
21790 requestAnimationFrame(function() {
21791 $scroll.triggerHandler('scrollchange' + eventNamespace, $scroll.scrollTop() );
21792 });
21793 },
21794 scrollchange: function(event, scrollPosition) {
21795 module.stick(scrollPosition);
21796 settings.onScroll.call(element);
21797 }
21798 },
21799
21800 refresh: function(hardRefresh) {
21801 module.reset();
21802 if(!settings.context) {
21803 module.determineContext();
21804 }
21805 if(hardRefresh) {
21806 module.determineContainer();
21807 }
21808 module.save.positions();
21809 module.stick();
21810 settings.onReposition.call(element);
21811 },
21812
21813 supports: {
21814 sticky: function() {
21815 var
21816 $element = $('<div/>')
21817 ;
21818 $element.addClass(className.supported);
21819 return($element.css('position').match('sticky'));
21820 }
21821 },
21822
21823 save: {
21824 lastScroll: function(scroll) {
21825 module.lastScroll = scroll;
21826 },
21827 elementScroll: function(scroll) {
21828 module.elementScroll = scroll;
21829 },
21830 positions: function() {
21831 var
21832 scrollContext = {
21833 height : $scroll.height()
21834 },
21835 element = {
21836 margin: {
21837 top : parseInt($module.css('margin-top'), 10),
21838 bottom : parseInt($module.css('margin-bottom'), 10),
21839 },
21840 offset : $module.offset(),
21841 width : $module.outerWidth(),
21842 height : $module.outerHeight()
21843 },
21844 context = {
21845 offset : $context.offset(),
21846 height : $context.outerHeight()
21847 }
21848 ;
21849 if( !module.is.standardScroll() ) {
21850 module.debug('Non-standard scroll. Removing scroll offset from element offset');
21851
21852 scrollContext.top = $scroll.scrollTop();
21853 scrollContext.left = $scroll.scrollLeft();
21854
21855 element.offset.top += scrollContext.top;
21856 context.offset.top += scrollContext.top;
21857 element.offset.left += scrollContext.left;
21858 context.offset.left += scrollContext.left;
21859 }
21860 module.cache = {
21861 fits : ( (element.height + settings.offset) <= scrollContext.height),
21862 sameHeight : (element.height == context.height),
21863 scrollContext : {
21864 height : scrollContext.height
21865 },
21866 element: {
21867 margin : element.margin,
21868 top : element.offset.top - element.margin.top,
21869 left : element.offset.left,
21870 width : element.width,
21871 height : element.height,
21872 bottom : element.offset.top + element.height
21873 },
21874 context: {
21875 top : context.offset.top,
21876 height : context.height,
21877 bottom : context.offset.top + context.height
21878 }
21879 };
21880 module.set.containerSize();
21881
21882 module.stick();
21883 module.debug('Caching element positions', module.cache);
21884 }
21885 },
21886
21887 get: {
21888 direction: function(scroll) {
21889 var
21890 direction = 'down'
21891 ;
21892 scroll = scroll || $scroll.scrollTop();
21893 if(module.lastScroll !== undefined) {
21894 if(module.lastScroll < scroll) {
21895 direction = 'down';
21896 }
21897 else if(module.lastScroll > scroll) {
21898 direction = 'up';
21899 }
21900 }
21901 return direction;
21902 },
21903 scrollChange: function(scroll) {
21904 scroll = scroll || $scroll.scrollTop();
21905 return (module.lastScroll)
21906 ? (scroll - module.lastScroll)
21907 : 0
21908 ;
21909 },
21910 currentElementScroll: function() {
21911 if(module.elementScroll) {
21912 return module.elementScroll;
21913 }
21914 return ( module.is.top() )
21915 ? Math.abs(parseInt($module.css('top'), 10)) || 0
21916 : Math.abs(parseInt($module.css('bottom'), 10)) || 0
21917 ;
21918 },
21919
21920 elementScroll: function(scroll) {
21921 scroll = scroll || $scroll.scrollTop();
21922 var
21923 element = module.cache.element,
21924 scrollContext = module.cache.scrollContext,
21925 delta = module.get.scrollChange(scroll),
21926 maxScroll = (element.height - scrollContext.height + settings.offset),
21927 elementScroll = module.get.currentElementScroll(),
21928 possibleScroll = (elementScroll + delta)
21929 ;
21930 if(module.cache.fits || possibleScroll < 0) {
21931 elementScroll = 0;
21932 }
21933 else if(possibleScroll > maxScroll ) {
21934 elementScroll = maxScroll;
21935 }
21936 else {
21937 elementScroll = possibleScroll;
21938 }
21939 return elementScroll;
21940 }
21941 },
21942
21943 remove: {
21944 lastScroll: function() {
21945 delete module.lastScroll;
21946 },
21947 elementScroll: function(scroll) {
21948 delete module.elementScroll;
21949 },
21950 minimumSize: function() {
21951 $container
21952 .css('min-height', '')
21953 ;
21954 },
21955 offset: function() {
21956 $module.css('margin-top', '');
21957 }
21958 },
21959
21960 set: {
21961 offset: function() {
21962 module.verbose('Setting offset on element', settings.offset);
21963 $module
21964 .css('margin-top', settings.offset)
21965 ;
21966 },
21967 containerSize: function() {
21968 var
21969 tagName = $container.get(0).tagName
21970 ;
21971 if(tagName === 'HTML' || tagName == 'body') {
21972 // this can trigger for too many reasons
21973 //module.error(error.container, tagName, $module);
21974 module.determineContainer();
21975 }
21976 else {
21977 if( Math.abs($container.outerHeight() - module.cache.context.height) > settings.jitter) {
21978 module.debug('Context has padding, specifying exact height for container', module.cache.context.height);
21979 $container.css({
21980 height: module.cache.context.height
21981 });
21982 }
21983 }
21984 },
21985 minimumSize: function() {
21986 var
21987 element = module.cache.element
21988 ;
21989 $container
21990 .css('min-height', element.height)
21991 ;
21992 },
21993 scroll: function(scroll) {
21994 module.debug('Setting scroll on element', scroll);
21995 if(module.elementScroll == scroll) {
21996 return;
21997 }
21998 if( module.is.top() ) {
21999 $module
22000 .css('bottom', '')
22001 .css('top', -scroll)
22002 ;
22003 }
22004 if( module.is.bottom() ) {
22005 $module
22006 .css('top', '')
22007 .css('bottom', scroll)
22008 ;
22009 }
22010 },
22011 size: function() {
22012 if(module.cache.element.height !== 0 && module.cache.element.width !== 0) {
22013 element.style.setProperty('width', module.cache.element.width + 'px', 'important');
22014 element.style.setProperty('height', module.cache.element.height + 'px', 'important');
22015 }
22016 }
22017 },
22018
22019 is: {
22020 standardScroll: function() {
22021 return ($scroll[0] == window);
22022 },
22023 top: function() {
22024 return $module.hasClass(className.top);
22025 },
22026 bottom: function() {
22027 return $module.hasClass(className.bottom);
22028 },
22029 initialPosition: function() {
22030 return (!module.is.fixed() && !module.is.bound());
22031 },
22032 hidden: function() {
22033 return (!$module.is(':visible'));
22034 },
22035 bound: function() {
22036 return $module.hasClass(className.bound);
22037 },
22038 fixed: function() {
22039 return $module.hasClass(className.fixed);
22040 }
22041 },
22042
22043 stick: function(scroll) {
22044 var
22045 cachedPosition = scroll || $scroll.scrollTop(),
22046 cache = module.cache,
22047 fits = cache.fits,
22048 sameHeight = cache.sameHeight,
22049 element = cache.element,
22050 scrollContext = cache.scrollContext,
22051 context = cache.context,
22052 offset = (module.is.bottom() && settings.pushing)
22053 ? settings.bottomOffset
22054 : settings.offset,
22055 scroll = {
22056 top : cachedPosition + offset,
22057 bottom : cachedPosition + offset + scrollContext.height
22058 },
22059 elementScroll = (fits)
22060 ? 0
22061 : module.get.elementScroll(scroll.top),
22062
22063 // shorthand
22064 doesntFit = !fits,
22065 elementVisible = (element.height !== 0)
22066 ;
22067 if(elementVisible && !sameHeight) {
22068
22069 if( module.is.initialPosition() ) {
22070 if(scroll.top >= context.bottom) {
22071 module.debug('Initial element position is bottom of container');
22072 module.bindBottom();
22073 }
22074 else if(scroll.top > element.top) {
22075 if( (element.height + scroll.top - elementScroll) >= context.bottom ) {
22076 module.debug('Initial element position is bottom of container');
22077 module.bindBottom();
22078 }
22079 else {
22080 module.debug('Initial element position is fixed');
22081 module.fixTop();
22082 }
22083 }
22084
22085 }
22086 else if( module.is.fixed() ) {
22087
22088 // currently fixed top
22089 if( module.is.top() ) {
22090 if( scroll.top <= element.top ) {
22091 module.debug('Fixed element reached top of container');
22092 module.setInitialPosition();
22093 }
22094 else if( (element.height + scroll.top - elementScroll) >= context.bottom ) {
22095 module.debug('Fixed element reached bottom of container');
22096 module.bindBottom();
22097 }
22098 // scroll element if larger than screen
22099 else if(doesntFit) {
22100 module.set.scroll(elementScroll);
22101 module.save.lastScroll(scroll.top);
22102 module.save.elementScroll(elementScroll);
22103 }
22104 }
22105
22106 // currently fixed bottom
22107 else if(module.is.bottom() ) {
22108
22109 // top edge
22110 if( (scroll.bottom - element.height) <= element.top) {
22111 module.debug('Bottom fixed rail has reached top of container');
22112 module.setInitialPosition();
22113 }
22114 // bottom edge
22115 else if(scroll.bottom >= context.bottom) {
22116 module.debug('Bottom fixed rail has reached bottom of container');
22117 module.bindBottom();
22118 }
22119 // scroll element if larger than screen
22120 else if(doesntFit) {
22121 module.set.scroll(elementScroll);
22122 module.save.lastScroll(scroll.top);
22123 module.save.elementScroll(elementScroll);
22124 }
22125
22126 }
22127 }
22128 else if( module.is.bottom() ) {
22129 if( scroll.top <= element.top ) {
22130 module.debug('Jumped from bottom fixed to top fixed, most likely used home/end button');
22131 module.setInitialPosition();
22132 }
22133 else {
22134 if(settings.pushing) {
22135 if(module.is.bound() && scroll.bottom <= context.bottom ) {
22136 module.debug('Fixing bottom attached element to bottom of browser.');
22137 module.fixBottom();
22138 }
22139 }
22140 else {
22141 if(module.is.bound() && (scroll.top <= context.bottom - element.height) ) {
22142 module.debug('Fixing bottom attached element to top of browser.');
22143 module.fixTop();
22144 }
22145 }
22146 }
22147 }
22148 }
22149 },
22150
22151 bindTop: function() {
22152 module.debug('Binding element to top of parent container');
22153 module.remove.offset();
22154 $module
22155 .css({
22156 left : '',
22157 top : '',
22158 marginBottom : ''
22159 })
22160 .removeClass(className.fixed)
22161 .removeClass(className.bottom)
22162 .addClass(className.bound)
22163 .addClass(className.top)
22164 ;
22165 settings.onTop.call(element);
22166 settings.onUnstick.call(element);
22167 },
22168 bindBottom: function() {
22169 module.debug('Binding element to bottom of parent container');
22170 module.remove.offset();
22171 $module
22172 .css({
22173 left : '',
22174 top : ''
22175 })
22176 .removeClass(className.fixed)
22177 .removeClass(className.top)
22178 .addClass(className.bound)
22179 .addClass(className.bottom)
22180 ;
22181 settings.onBottom.call(element);
22182 settings.onUnstick.call(element);
22183 },
22184
22185 setInitialPosition: function() {
22186 module.debug('Returning to initial position');
22187 module.unfix();
22188 module.unbind();
22189 },
22190
22191
22192 fixTop: function() {
22193 module.debug('Fixing element to top of page');
22194 if(settings.setSize) {
22195 module.set.size();
22196 }
22197 module.set.minimumSize();
22198 module.set.offset();
22199 $module
22200 .css({
22201 left : module.cache.element.left,
22202 bottom : '',
22203 marginBottom : ''
22204 })
22205 .removeClass(className.bound)
22206 .removeClass(className.bottom)
22207 .addClass(className.fixed)
22208 .addClass(className.top)
22209 ;
22210 settings.onStick.call(element);
22211 },
22212
22213 fixBottom: function() {
22214 module.debug('Sticking element to bottom of page');
22215 if(settings.setSize) {
22216 module.set.size();
22217 }
22218 module.set.minimumSize();
22219 module.set.offset();
22220 $module
22221 .css({
22222 left : module.cache.element.left,
22223 bottom : '',
22224 marginBottom : ''
22225 })
22226 .removeClass(className.bound)
22227 .removeClass(className.top)
22228 .addClass(className.fixed)
22229 .addClass(className.bottom)
22230 ;
22231 settings.onStick.call(element);
22232 },
22233
22234 unbind: function() {
22235 if( module.is.bound() ) {
22236 module.debug('Removing container bound position on element');
22237 module.remove.offset();
22238 $module
22239 .removeClass(className.bound)
22240 .removeClass(className.top)
22241 .removeClass(className.bottom)
22242 ;
22243 }
22244 },
22245
22246 unfix: function() {
22247 if( module.is.fixed() ) {
22248 module.debug('Removing fixed position on element');
22249 module.remove.minimumSize();
22250 module.remove.offset();
22251 $module
22252 .removeClass(className.fixed)
22253 .removeClass(className.top)
22254 .removeClass(className.bottom)
22255 ;
22256 settings.onUnstick.call(element);
22257 }
22258 },
22259
22260 reset: function() {
22261 module.debug('Resetting elements position');
22262 module.unbind();
22263 module.unfix();
22264 module.resetCSS();
22265 module.remove.offset();
22266 module.remove.lastScroll();
22267 },
22268
22269 resetCSS: function() {
22270 $module
22271 .css({
22272 width : '',
22273 height : ''
22274 })
22275 ;
22276 $container
22277 .css({
22278 height: ''
22279 })
22280 ;
22281 },
22282
22283 setting: function(name, value) {
22284 if( $.isPlainObject(name) ) {
22285 $.extend(true, settings, name);
22286 }
22287 else if(value !== undefined) {
22288 settings[name] = value;
22289 }
22290 else {
22291 return settings[name];
22292 }
22293 },
22294 internal: function(name, value) {
22295 if( $.isPlainObject(name) ) {
22296 $.extend(true, module, name);
22297 }
22298 else if(value !== undefined) {
22299 module[name] = value;
22300 }
22301 else {
22302 return module[name];
22303 }
22304 },
22305 debug: function() {
22306 if(!settings.silent && settings.debug) {
22307 if(settings.performance) {
22308 module.performance.log(arguments);
22309 }
22310 else {
22311 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
22312 module.debug.apply(console, arguments);
22313 }
22314 }
22315 },
22316 verbose: function() {
22317 if(!settings.silent && settings.verbose && settings.debug) {
22318 if(settings.performance) {
22319 module.performance.log(arguments);
22320 }
22321 else {
22322 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
22323 module.verbose.apply(console, arguments);
22324 }
22325 }
22326 },
22327 error: function() {
22328 if(!settings.silent) {
22329 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
22330 module.error.apply(console, arguments);
22331 }
22332 },
22333 performance: {
22334 log: function(message) {
22335 var
22336 currentTime,
22337 executionTime,
22338 previousTime
22339 ;
22340 if(settings.performance) {
22341 currentTime = new Date().getTime();
22342 previousTime = time || currentTime;
22343 executionTime = currentTime - previousTime;
22344 time = currentTime;
22345 performance.push({
22346 'Name' : message[0],
22347 'Arguments' : [].slice.call(message, 1) || '',
22348 'Element' : element,
22349 'Execution Time' : executionTime
22350 });
22351 }
22352 clearTimeout(module.performance.timer);
22353 module.performance.timer = setTimeout(module.performance.display, 0);
22354 },
22355 display: function() {
22356 var
22357 title = settings.name + ':',
22358 totalTime = 0
22359 ;
22360 time = false;
22361 clearTimeout(module.performance.timer);
22362 $.each(performance, function(index, data) {
22363 totalTime += data['Execution Time'];
22364 });
22365 title += ' ' + totalTime + 'ms';
22366 if(moduleSelector) {
22367 title += ' \'' + moduleSelector + '\'';
22368 }
22369 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
22370 console.groupCollapsed(title);
22371 if(console.table) {
22372 console.table(performance);
22373 }
22374 else {
22375 $.each(performance, function(index, data) {
22376 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
22377 });
22378 }
22379 console.groupEnd();
22380 }
22381 performance = [];
22382 }
22383 },
22384 invoke: function(query, passedArguments, context) {
22385 var
22386 object = instance,
22387 maxDepth,
22388 found,
22389 response
22390 ;
22391 passedArguments = passedArguments || queryArguments;
22392 context = element || context;
22393 if(typeof query == 'string' && object !== undefined) {
22394 query = query.split(/[\. ]/);
22395 maxDepth = query.length - 1;
22396 $.each(query, function(depth, value) {
22397 var camelCaseValue = (depth != maxDepth)
22398 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
22399 : query
22400 ;
22401 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
22402 object = object[camelCaseValue];
22403 }
22404 else if( object[camelCaseValue] !== undefined ) {
22405 found = object[camelCaseValue];
22406 return false;
22407 }
22408 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
22409 object = object[value];
22410 }
22411 else if( object[value] !== undefined ) {
22412 found = object[value];
22413 return false;
22414 }
22415 else {
22416 return false;
22417 }
22418 });
22419 }
22420 if ( $.isFunction( found ) ) {
22421 response = found.apply(context, passedArguments);
22422 }
22423 else if(found !== undefined) {
22424 response = found;
22425 }
22426 if(Array.isArray(returnedValue)) {
22427 returnedValue.push(response);
22428 }
22429 else if(returnedValue !== undefined) {
22430 returnedValue = [returnedValue, response];
22431 }
22432 else if(response !== undefined) {
22433 returnedValue = response;
22434 }
22435 return found;
22436 }
22437 };
22438
22439 if(methodInvoked) {
22440 if(instance === undefined) {
22441 module.initialize();
22442 }
22443 module.invoke(query);
22444 }
22445 else {
22446 if(instance !== undefined) {
22447 instance.invoke('destroy');
22448 }
22449 module.initialize();
22450 }
22451 })
22452 ;
22453
22454 return (returnedValue !== undefined)
22455 ? returnedValue
22456 : this
22457 ;
22458};
22459
22460$.fn.sticky.settings = {
22461
22462 name : 'Sticky',
22463 namespace : 'sticky',
22464
22465 silent : false,
22466 debug : false,
22467 verbose : true,
22468 performance : true,
22469
22470 // whether to stick in the opposite direction on scroll up
22471 pushing : false,
22472
22473 context : false,
22474 container : false,
22475
22476 // Context to watch scroll events
22477 scrollContext : window,
22478
22479 // Offset to adjust scroll
22480 offset : 0,
22481
22482 // Offset to adjust scroll when attached to bottom of screen
22483 bottomOffset : 0,
22484
22485 // will only set container height if difference between context and container is larger than this number
22486 jitter : 5,
22487
22488 // set width of sticky element when it is fixed to page (used to make sure 100% width is maintained if no fixed size set)
22489 setSize : true,
22490
22491 // Whether to automatically observe changes with Mutation Observers
22492 observeChanges : false,
22493
22494 // Called when position is recalculated
22495 onReposition : function(){},
22496
22497 // Called on each scroll
22498 onScroll : function(){},
22499
22500 // Called when element is stuck to viewport
22501 onStick : function(){},
22502
22503 // Called when element is unstuck from viewport
22504 onUnstick : function(){},
22505
22506 // Called when element reaches top of context
22507 onTop : function(){},
22508
22509 // Called when element reaches bottom of context
22510 onBottom : function(){},
22511
22512 error : {
22513 container : 'Sticky element must be inside a relative container',
22514 visible : 'Element is hidden, you must call refresh after element becomes visible. Use silent setting to surpress this warning in production.',
22515 method : 'The method you called is not defined.',
22516 invalidContext : 'Context specified does not exist',
22517 elementSize : 'Sticky element is larger than its container, cannot create sticky.'
22518 },
22519
22520 className : {
22521 bound : 'bound',
22522 fixed : 'fixed',
22523 supported : 'native',
22524 top : 'top',
22525 bottom : 'bottom'
22526 }
22527
22528};
22529
22530})( jQuery, window, document );
22531
22532/*!
22533 * # Fomantic-UI 2.8.8 - Tab
22534 * http://github.com/fomantic/Fomantic-UI/
22535 *
22536 *
22537 * Released under the MIT license
22538 * http://opensource.org/licenses/MIT
22539 *
22540 */
22541
22542;(function ($, window, document, undefined) {
22543
22544'use strict';
22545
22546$.isWindow = $.isWindow || function(obj) {
22547 return obj != null && obj === obj.window;
22548};
22549$.isFunction = $.isFunction || function(obj) {
22550 return typeof obj === "function" && typeof obj.nodeType !== "number";
22551};
22552
22553window = (typeof window != 'undefined' && window.Math == Math)
22554 ? window
22555 : (typeof self != 'undefined' && self.Math == Math)
22556 ? self
22557 : Function('return this')()
22558;
22559
22560$.fn.tab = function(parameters) {
22561
22562 var
22563 // use window context if none specified
22564 $allModules = $.isFunction(this)
22565 ? $(window)
22566 : $(this),
22567
22568 moduleSelector = $allModules.selector || '',
22569 time = new Date().getTime(),
22570 performance = [],
22571
22572 query = arguments[0],
22573 methodInvoked = (typeof query == 'string'),
22574 queryArguments = [].slice.call(arguments, 1),
22575
22576 initializedHistory = false,
22577 returnedValue
22578 ;
22579
22580 $allModules
22581 .each(function() {
22582 var
22583
22584 settings = ( $.isPlainObject(parameters) )
22585 ? $.extend(true, {}, $.fn.tab.settings, parameters)
22586 : $.extend({}, $.fn.tab.settings),
22587
22588 className = settings.className,
22589 metadata = settings.metadata,
22590 selector = settings.selector,
22591 error = settings.error,
22592 regExp = settings.regExp,
22593
22594 eventNamespace = '.' + settings.namespace,
22595 moduleNamespace = 'module-' + settings.namespace,
22596
22597 $module = $(this),
22598 $context,
22599 $tabs,
22600
22601 cache = {},
22602 firstLoad = true,
22603 recursionDepth = 0,
22604 element = this,
22605 instance = $module.data(moduleNamespace),
22606
22607 activeTabPath,
22608 parameterArray,
22609 module,
22610
22611 historyEvent
22612
22613 ;
22614
22615 module = {
22616
22617 initialize: function() {
22618 module.debug('Initializing tab menu item', $module);
22619 module.fix.callbacks();
22620 module.determineTabs();
22621
22622 module.debug('Determining tabs', settings.context, $tabs);
22623 // set up automatic routing
22624 if(settings.auto) {
22625 module.set.auto();
22626 }
22627 module.bind.events();
22628
22629 if(settings.history && !initializedHistory) {
22630 module.initializeHistory();
22631 initializedHistory = true;
22632 }
22633
22634 if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) {
22635 module.debug('No active tab detected, setting first tab active', module.get.initialPath());
22636 module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation);
22637 };
22638
22639 module.instantiate();
22640 },
22641
22642 instantiate: function () {
22643 module.verbose('Storing instance of module', module);
22644 instance = module;
22645 $module
22646 .data(moduleNamespace, module)
22647 ;
22648 },
22649
22650 destroy: function() {
22651 module.debug('Destroying tabs', $module);
22652 $module
22653 .removeData(moduleNamespace)
22654 .off(eventNamespace)
22655 ;
22656 },
22657
22658 bind: {
22659 events: function() {
22660 // if using $.tab don't add events
22661 if( !$.isWindow( element ) ) {
22662 module.debug('Attaching tab activation events to element', $module);
22663 $module
22664 .on('click' + eventNamespace, module.event.click)
22665 ;
22666 }
22667 }
22668 },
22669
22670 determineTabs: function() {
22671 var
22672 $reference
22673 ;
22674
22675 // determine tab context
22676 if(settings.context === 'parent') {
22677 if($module.closest(selector.ui).length > 0) {
22678 $reference = $module.closest(selector.ui);
22679 module.verbose('Using closest UI element as parent', $reference);
22680 }
22681 else {
22682 $reference = $module;
22683 }
22684 $context = $reference.parent();
22685 module.verbose('Determined parent element for creating context', $context);
22686 }
22687 else if(settings.context) {
22688 $context = $(settings.context);
22689 module.verbose('Using selector for tab context', settings.context, $context);
22690 }
22691 else {
22692 $context = $('body');
22693 }
22694 // find tabs
22695 if(settings.childrenOnly) {
22696 $tabs = $context.children(selector.tabs);
22697 module.debug('Searching tab context children for tabs', $context, $tabs);
22698 }
22699 else {
22700 $tabs = $context.find(selector.tabs);
22701 module.debug('Searching tab context for tabs', $context, $tabs);
22702 }
22703 },
22704
22705 fix: {
22706 callbacks: function() {
22707 if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
22708 if(parameters.onTabLoad) {
22709 parameters.onLoad = parameters.onTabLoad;
22710 delete parameters.onTabLoad;
22711 module.error(error.legacyLoad, parameters.onLoad);
22712 }
22713 if(parameters.onTabInit) {
22714 parameters.onFirstLoad = parameters.onTabInit;
22715 delete parameters.onTabInit;
22716 module.error(error.legacyInit, parameters.onFirstLoad);
22717 }
22718 settings = $.extend(true, {}, $.fn.tab.settings, parameters);
22719 }
22720 }
22721 },
22722
22723 initializeHistory: function() {
22724 module.debug('Initializing page state');
22725 if( $.address === undefined ) {
22726 module.error(error.state);
22727 return false;
22728 }
22729 else {
22730 if(settings.historyType == 'state') {
22731 module.debug('Using HTML5 to manage state');
22732 if(settings.path !== false) {
22733 $.address
22734 .history(true)
22735 .state(settings.path)
22736 ;
22737 }
22738 else {
22739 module.error(error.path);
22740 return false;
22741 }
22742 }
22743 $.address
22744 .bind('change', module.event.history.change)
22745 ;
22746 }
22747 },
22748
22749 event: {
22750 click: function(event) {
22751 var
22752 tabPath = $(this).data(metadata.tab)
22753 ;
22754 if(tabPath !== undefined) {
22755 if(settings.history) {
22756 module.verbose('Updating page state', event);
22757 $.address.value(tabPath);
22758 }
22759 else {
22760 module.verbose('Changing tab', event);
22761 module.changeTab(tabPath);
22762 }
22763 event.preventDefault();
22764 }
22765 else {
22766 module.debug('No tab specified');
22767 }
22768 },
22769 history: {
22770 change: function(event) {
22771 var
22772 tabPath = event.pathNames.join('/') || module.get.initialPath(),
22773 pageTitle = settings.templates.determineTitle(tabPath) || false
22774 ;
22775 module.performance.display();
22776 module.debug('History change event', tabPath, event);
22777 historyEvent = event;
22778 if(tabPath !== undefined) {
22779 module.changeTab(tabPath);
22780 }
22781 if(pageTitle) {
22782 $.address.title(pageTitle);
22783 }
22784 }
22785 }
22786 },
22787
22788 refresh: function() {
22789 if(activeTabPath) {
22790 module.debug('Refreshing tab', activeTabPath);
22791 module.changeTab(activeTabPath);
22792 }
22793 },
22794
22795 cache: {
22796
22797 read: function(cacheKey) {
22798 return (cacheKey !== undefined)
22799 ? cache[cacheKey]
22800 : false
22801 ;
22802 },
22803 add: function(cacheKey, content) {
22804 cacheKey = cacheKey || activeTabPath;
22805 module.debug('Adding cached content for', cacheKey);
22806 cache[cacheKey] = content;
22807 },
22808 remove: function(cacheKey) {
22809 cacheKey = cacheKey || activeTabPath;
22810 module.debug('Removing cached content for', cacheKey);
22811 delete cache[cacheKey];
22812 }
22813 },
22814
22815 escape: {
22816 string: function(text) {
22817 text = String(text);
22818 return text.replace(regExp.escape, '\\$&');
22819 }
22820 },
22821
22822 set: {
22823 auto: function() {
22824 var
22825 url = (typeof settings.path == 'string')
22826 ? settings.path.replace(/\/$/, '') + '/{$tab}'
22827 : '/{$tab}'
22828 ;
22829 module.verbose('Setting up automatic tab retrieval from server', url);
22830 if($.isPlainObject(settings.apiSettings)) {
22831 settings.apiSettings.url = url;
22832 }
22833 else {
22834 settings.apiSettings = {
22835 url: url
22836 };
22837 }
22838 },
22839 loading: function(tabPath) {
22840 var
22841 $tab = module.get.tabElement(tabPath),
22842 isLoading = $tab.hasClass(className.loading)
22843 ;
22844 if(!isLoading) {
22845 module.verbose('Setting loading state for', $tab);
22846 $tab
22847 .addClass(className.loading)
22848 .siblings($tabs)
22849 .removeClass(className.active + ' ' + className.loading)
22850 ;
22851 if($tab.length > 0) {
22852 settings.onRequest.call($tab[0], tabPath);
22853 }
22854 }
22855 },
22856 state: function(state) {
22857 $.address.value(state);
22858 }
22859 },
22860
22861 changeTab: function(tabPath) {
22862 var
22863 pushStateAvailable = (window.history && window.history.pushState),
22864 shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
22865 remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ),
22866 // only add default path if not remote content
22867 pathArray = (remoteContent && !shouldIgnoreLoad)
22868 ? module.utilities.pathToArray(tabPath)
22869 : module.get.defaultPathArray(tabPath)
22870 ;
22871 tabPath = module.utilities.arrayToPath(pathArray);
22872 $.each(pathArray, function(index, tab) {
22873 var
22874 currentPathArray = pathArray.slice(0, index + 1),
22875 currentPath = module.utilities.arrayToPath(currentPathArray),
22876
22877 isTab = module.is.tab(currentPath),
22878 isLastIndex = (index + 1 == pathArray.length),
22879
22880 $tab = module.get.tabElement(currentPath),
22881 $anchor,
22882 nextPathArray,
22883 nextPath,
22884 isLastTab
22885 ;
22886 module.verbose('Looking for tab', tab);
22887 if(isTab) {
22888 module.verbose('Tab was found', tab);
22889 // scope up
22890 activeTabPath = currentPath;
22891 parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
22892
22893 if(isLastIndex) {
22894 isLastTab = true;
22895 }
22896 else {
22897 nextPathArray = pathArray.slice(0, index + 2);
22898 nextPath = module.utilities.arrayToPath(nextPathArray);
22899 isLastTab = ( !module.is.tab(nextPath) );
22900 if(isLastTab) {
22901 module.verbose('Tab parameters found', nextPathArray);
22902 }
22903 }
22904 if(isLastTab && remoteContent) {
22905 if(!shouldIgnoreLoad) {
22906 module.activate.navigation(currentPath);
22907 module.fetch.content(currentPath, tabPath);
22908 }
22909 else {
22910 module.debug('Ignoring remote content on first tab load', currentPath);
22911 firstLoad = false;
22912 module.cache.add(tabPath, $tab.html());
22913 module.activate.all(currentPath);
22914 settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22915 settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22916 }
22917 return false;
22918 }
22919 else {
22920 module.debug('Opened local tab', currentPath);
22921 module.activate.all(currentPath);
22922 if( !module.cache.read(currentPath) ) {
22923 module.cache.add(currentPath, true);
22924 module.debug('First time tab loaded calling tab init');
22925 settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22926 }
22927 settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22928 }
22929
22930 }
22931 else if(tabPath.search('/') == -1 && tabPath !== '') {
22932 // look for in page anchor
22933 tabPath = module.escape.string(tabPath);
22934 $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
22935 currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
22936 $tab = module.get.tabElement(currentPath);
22937 // if anchor exists use parent tab
22938 if($anchor && $anchor.length > 0 && currentPath) {
22939 module.debug('Anchor link used, opening parent tab', $tab, $anchor);
22940 if( !$tab.hasClass(className.active) ) {
22941 setTimeout(function() {
22942 module.scrollTo($anchor);
22943 }, 0);
22944 }
22945 module.activate.all(currentPath);
22946 if( !module.cache.read(currentPath) ) {
22947 module.cache.add(currentPath, true);
22948 module.debug('First time tab loaded calling tab init');
22949 settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22950 }
22951 settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
22952 return false;
22953 }
22954 }
22955 else {
22956 module.error(error.missingTab, $module, $context, currentPath);
22957 return false;
22958 }
22959 });
22960 },
22961
22962 scrollTo: function($element) {
22963 var
22964 scrollOffset = ($element && $element.length > 0)
22965 ? $element.offset().top
22966 : false
22967 ;
22968 if(scrollOffset !== false) {
22969 module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
22970 $(document).scrollTop(scrollOffset);
22971 }
22972 },
22973
22974 update: {
22975 content: function(tabPath, html, evaluateScripts) {
22976 var
22977 $tab = module.get.tabElement(tabPath),
22978 tab = $tab[0]
22979 ;
22980 evaluateScripts = (evaluateScripts !== undefined)
22981 ? evaluateScripts
22982 : settings.evaluateScripts
22983 ;
22984 if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
22985 $tab
22986 .empty()
22987 .append($(html).clone(true))
22988 ;
22989 }
22990 else {
22991 if(evaluateScripts) {
22992 module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
22993 $tab.html(html);
22994 }
22995 else {
22996 module.debug('Updating HTML', tabPath, html);
22997 tab.innerHTML = html;
22998 }
22999 }
23000 }
23001 },
23002
23003 fetch: {
23004
23005 content: function(tabPath, fullTabPath) {
23006 var
23007 $tab = module.get.tabElement(tabPath),
23008 apiSettings = {
23009 dataType : 'html',
23010 encodeParameters : false,
23011 on : 'now',
23012 cache : settings.alwaysRefresh,
23013 headers : {
23014 'X-Remote': true
23015 },
23016 onSuccess : function(response) {
23017 if(settings.cacheType == 'response') {
23018 module.cache.add(fullTabPath, response);
23019 }
23020 module.update.content(tabPath, response);
23021 if(tabPath == activeTabPath) {
23022 module.debug('Content loaded', tabPath);
23023 module.activate.tab(tabPath);
23024 }
23025 else {
23026 module.debug('Content loaded in background', tabPath);
23027 }
23028 settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
23029 settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
23030
23031 if(settings.loadOnce) {
23032 module.cache.add(fullTabPath, true);
23033 }
23034 else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
23035 setTimeout(function() {
23036 var
23037 $clone = $tab.children().clone(true)
23038 ;
23039 $clone = $clone.not('script');
23040 module.cache.add(fullTabPath, $clone);
23041 }, 0);
23042 }
23043 else {
23044 module.cache.add(fullTabPath, $tab.html());
23045 }
23046 },
23047 urlData: {
23048 tab: fullTabPath
23049 }
23050 },
23051 request = $tab.api('get request') || false,
23052 existingRequest = ( request && request.state() === 'pending' ),
23053 requestSettings,
23054 cachedContent
23055 ;
23056
23057 fullTabPath = fullTabPath || tabPath;
23058 cachedContent = module.cache.read(fullTabPath);
23059
23060
23061 if(settings.cache && cachedContent) {
23062 module.activate.tab(tabPath);
23063 module.debug('Adding cached content', fullTabPath);
23064 if(!settings.loadOnce) {
23065 if(settings.evaluateScripts == 'once') {
23066 module.update.content(tabPath, cachedContent, false);
23067 }
23068 else {
23069 module.update.content(tabPath, cachedContent);
23070 }
23071 }
23072 settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
23073 }
23074 else if(existingRequest) {
23075 module.set.loading(tabPath);
23076 module.debug('Content is already loading', fullTabPath);
23077 }
23078 else if($.api !== undefined) {
23079 requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
23080 module.debug('Retrieving remote content', fullTabPath, requestSettings);
23081 module.set.loading(tabPath);
23082 $tab.api(requestSettings);
23083 }
23084 else {
23085 module.error(error.api);
23086 }
23087 }
23088 },
23089
23090 activate: {
23091 all: function(tabPath) {
23092 module.activate.tab(tabPath);
23093 module.activate.navigation(tabPath);
23094 },
23095 tab: function(tabPath) {
23096 var
23097 $tab = module.get.tabElement(tabPath),
23098 $deactiveTabs = (settings.deactivate == 'siblings')
23099 ? $tab.siblings($tabs)
23100 : $tabs.not($tab),
23101 isActive = $tab.hasClass(className.active)
23102 ;
23103 module.verbose('Showing tab content for', $tab);
23104 if(!isActive) {
23105 $tab
23106 .addClass(className.active)
23107 ;
23108 $deactiveTabs
23109 .removeClass(className.active + ' ' + className.loading)
23110 ;
23111 if($tab.length > 0) {
23112 settings.onVisible.call($tab[0], tabPath);
23113 }
23114 }
23115 },
23116 navigation: function(tabPath) {
23117 var
23118 $navigation = module.get.navElement(tabPath),
23119 $deactiveNavigation = (settings.deactivate == 'siblings')
23120 ? $navigation.siblings($allModules)
23121 : $allModules.not($navigation),
23122 isActive = $navigation.hasClass(className.active)
23123 ;
23124 module.verbose('Activating tab navigation for', $navigation, tabPath);
23125 if(!isActive) {
23126 $navigation
23127 .addClass(className.active)
23128 ;
23129 $deactiveNavigation
23130 .removeClass(className.active + ' ' + className.loading)
23131 ;
23132 }
23133 }
23134 },
23135
23136 deactivate: {
23137 all: function() {
23138 module.deactivate.navigation();
23139 module.deactivate.tabs();
23140 },
23141 navigation: function() {
23142 $allModules
23143 .removeClass(className.active)
23144 ;
23145 },
23146 tabs: function() {
23147 $tabs
23148 .removeClass(className.active + ' ' + className.loading)
23149 ;
23150 }
23151 },
23152
23153 is: {
23154 tab: function(tabName) {
23155 return (tabName !== undefined)
23156 ? ( module.get.tabElement(tabName).length > 0 )
23157 : false
23158 ;
23159 }
23160 },
23161
23162 get: {
23163 initialPath: function() {
23164 return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
23165 },
23166 path: function() {
23167 return $.address.value();
23168 },
23169 // adds default tabs to tab path
23170 defaultPathArray: function(tabPath) {
23171 return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
23172 },
23173 defaultPath: function(tabPath) {
23174 var
23175 $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
23176 defaultTab = $defaultNav.data(metadata.tab) || false
23177 ;
23178 if( defaultTab ) {
23179 module.debug('Found default tab', defaultTab);
23180 if(recursionDepth < settings.maxDepth) {
23181 recursionDepth++;
23182 return module.get.defaultPath(defaultTab);
23183 }
23184 module.error(error.recursion);
23185 }
23186 else {
23187 module.debug('No default tabs found for', tabPath, $tabs);
23188 }
23189 recursionDepth = 0;
23190 return tabPath;
23191 },
23192 navElement: function(tabPath) {
23193 tabPath = tabPath || activeTabPath;
23194 return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
23195 },
23196 tabElement: function(tabPath) {
23197 var
23198 $fullPathTab,
23199 $simplePathTab,
23200 tabPathArray,
23201 lastTab
23202 ;
23203 tabPath = tabPath || activeTabPath;
23204 tabPathArray = module.utilities.pathToArray(tabPath);
23205 lastTab = module.utilities.last(tabPathArray);
23206 $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
23207 $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');
23208 return ($fullPathTab.length > 0)
23209 ? $fullPathTab
23210 : $simplePathTab
23211 ;
23212 },
23213 tab: function() {
23214 return activeTabPath;
23215 }
23216 },
23217
23218 determine: {
23219 activeTab: function() {
23220 var activeTab = null;
23221
23222 $tabs.each(function(_index, tab) {
23223 var $tab = $(tab);
23224
23225 if( $tab.hasClass(className.active) ) {
23226 var
23227 tabPath = $(this).data(metadata.tab),
23228 $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
23229 ;
23230
23231 if( $anchor.hasClass(className.active) ) {
23232 activeTab = tabPath;
23233 }
23234 }
23235 });
23236
23237 return activeTab;
23238 }
23239 },
23240
23241 utilities: {
23242 filterArray: function(keepArray, removeArray) {
23243 return $.grep(keepArray, function(keepValue) {
23244 return ( $.inArray(keepValue, removeArray) == -1);
23245 });
23246 },
23247 last: function(array) {
23248 return Array.isArray(array)
23249 ? array[ array.length - 1]
23250 : false
23251 ;
23252 },
23253 pathToArray: function(pathName) {
23254 if(pathName === undefined) {
23255 pathName = activeTabPath;
23256 }
23257 return typeof pathName == 'string'
23258 ? pathName.split('/')
23259 : [pathName]
23260 ;
23261 },
23262 arrayToPath: function(pathArray) {
23263 return Array.isArray(pathArray)
23264 ? pathArray.join('/')
23265 : false
23266 ;
23267 }
23268 },
23269
23270 setting: function(name, value) {
23271 module.debug('Changing setting', name, value);
23272 if( $.isPlainObject(name) ) {
23273 $.extend(true, settings, name);
23274 }
23275 else if(value !== undefined) {
23276 if($.isPlainObject(settings[name])) {
23277 $.extend(true, settings[name], value);
23278 }
23279 else {
23280 settings[name] = value;
23281 }
23282 }
23283 else {
23284 return settings[name];
23285 }
23286 },
23287 internal: function(name, value) {
23288 if( $.isPlainObject(name) ) {
23289 $.extend(true, module, name);
23290 }
23291 else if(value !== undefined) {
23292 module[name] = value;
23293 }
23294 else {
23295 return module[name];
23296 }
23297 },
23298 debug: function() {
23299 if(!settings.silent && settings.debug) {
23300 if(settings.performance) {
23301 module.performance.log(arguments);
23302 }
23303 else {
23304 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
23305 module.debug.apply(console, arguments);
23306 }
23307 }
23308 },
23309 verbose: function() {
23310 if(!settings.silent && settings.verbose && settings.debug) {
23311 if(settings.performance) {
23312 module.performance.log(arguments);
23313 }
23314 else {
23315 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
23316 module.verbose.apply(console, arguments);
23317 }
23318 }
23319 },
23320 error: function() {
23321 if(!settings.silent) {
23322 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
23323 module.error.apply(console, arguments);
23324 }
23325 },
23326 performance: {
23327 log: function(message) {
23328 var
23329 currentTime,
23330 executionTime,
23331 previousTime
23332 ;
23333 if(settings.performance) {
23334 currentTime = new Date().getTime();
23335 previousTime = time || currentTime;
23336 executionTime = currentTime - previousTime;
23337 time = currentTime;
23338 performance.push({
23339 'Name' : message[0],
23340 'Arguments' : [].slice.call(message, 1) || '',
23341 'Element' : element,
23342 'Execution Time' : executionTime
23343 });
23344 }
23345 clearTimeout(module.performance.timer);
23346 module.performance.timer = setTimeout(module.performance.display, 500);
23347 },
23348 display: function() {
23349 var
23350 title = settings.name + ':',
23351 totalTime = 0
23352 ;
23353 time = false;
23354 clearTimeout(module.performance.timer);
23355 $.each(performance, function(index, data) {
23356 totalTime += data['Execution Time'];
23357 });
23358 title += ' ' + totalTime + 'ms';
23359 if(moduleSelector) {
23360 title += ' \'' + moduleSelector + '\'';
23361 }
23362 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
23363 console.groupCollapsed(title);
23364 if(console.table) {
23365 console.table(performance);
23366 }
23367 else {
23368 $.each(performance, function(index, data) {
23369 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
23370 });
23371 }
23372 console.groupEnd();
23373 }
23374 performance = [];
23375 }
23376 },
23377 invoke: function(query, passedArguments, context) {
23378 var
23379 object = instance,
23380 maxDepth,
23381 found,
23382 response
23383 ;
23384 passedArguments = passedArguments || queryArguments;
23385 context = element || context;
23386 if(typeof query == 'string' && object !== undefined) {
23387 query = query.split(/[\. ]/);
23388 maxDepth = query.length - 1;
23389 $.each(query, function(depth, value) {
23390 var camelCaseValue = (depth != maxDepth)
23391 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
23392 : query
23393 ;
23394 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
23395 object = object[camelCaseValue];
23396 }
23397 else if( object[camelCaseValue] !== undefined ) {
23398 found = object[camelCaseValue];
23399 return false;
23400 }
23401 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
23402 object = object[value];
23403 }
23404 else if( object[value] !== undefined ) {
23405 found = object[value];
23406 return false;
23407 }
23408 else {
23409 module.error(error.method, query);
23410 return false;
23411 }
23412 });
23413 }
23414 if ( $.isFunction( found ) ) {
23415 response = found.apply(context, passedArguments);
23416 }
23417 else if(found !== undefined) {
23418 response = found;
23419 }
23420 if(Array.isArray(returnedValue)) {
23421 returnedValue.push(response);
23422 }
23423 else if(returnedValue !== undefined) {
23424 returnedValue = [returnedValue, response];
23425 }
23426 else if(response !== undefined) {
23427 returnedValue = response;
23428 }
23429 return found;
23430 }
23431 };
23432 if(methodInvoked) {
23433 if(instance === undefined) {
23434 module.initialize();
23435 }
23436 module.invoke(query);
23437 }
23438 else {
23439 if(instance !== undefined) {
23440 instance.invoke('destroy');
23441 }
23442 module.initialize();
23443 }
23444 })
23445 ;
23446 return (returnedValue !== undefined)
23447 ? returnedValue
23448 : this
23449 ;
23450
23451};
23452
23453// shortcut for tabbed content with no defined navigation
23454$.tab = function() {
23455 $(window).tab.apply(this, arguments);
23456};
23457
23458$.fn.tab.settings = {
23459
23460 name : 'Tab',
23461 namespace : 'tab',
23462
23463 silent : false,
23464 debug : false,
23465 verbose : false,
23466 performance : true,
23467
23468 auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers
23469 history : false, // use browser history
23470 historyType : 'hash', // #/ or html5 state
23471 path : false, // base path of url
23472
23473 context : false, // specify a context that tabs must appear inside
23474 childrenOnly : false, // use only tabs that are children of context
23475 maxDepth : 25, // max depth a tab can be nested
23476
23477 deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
23478
23479 alwaysRefresh : false, // load tab content new every tab click
23480 cache : true, // cache the content requests to pull locally
23481 loadOnce : false, // Whether tab data should only be loaded once when using remote content
23482 cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
23483 ignoreFirstLoad : false, // don't load remote content on first load
23484
23485 apiSettings : false, // settings for api call
23486 evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
23487 autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab
23488
23489 onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
23490 onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load
23491 onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
23492 onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
23493
23494 templates : {
23495 determineTitle: function(tabArray) {} // returns page title for path
23496 },
23497
23498 error: {
23499 api : 'You attempted to load content without API module',
23500 method : 'The method you called is not defined',
23501 missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
23502 noContent : 'The tab you specified is missing a content url.',
23503 path : 'History enabled, but no path was specified',
23504 recursion : 'Max recursive depth reached',
23505 legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
23506 legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
23507 state : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>'
23508 },
23509
23510 regExp : {
23511 escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g
23512 },
23513
23514 metadata : {
23515 tab : 'tab',
23516 loaded : 'loaded',
23517 promise: 'promise'
23518 },
23519
23520 className : {
23521 loading : 'loading',
23522 active : 'active'
23523 },
23524
23525 selector : {
23526 tabs : '.ui.tab',
23527 ui : '.ui'
23528 }
23529
23530};
23531
23532})( jQuery, window, document );
23533
23534/*!
23535 * # Fomantic-UI 2.8.8 - Toast
23536 * http://github.com/fomantic/Fomantic-UI/
23537 *
23538 *
23539 * Released under the MIT license
23540 * http://opensource.org/licenses/MIT
23541 *
23542 */
23543
23544;(function ($, window, document, undefined) {
23545
23546'use strict';
23547
23548$.isFunction = $.isFunction || function(obj) {
23549 return typeof obj === "function" && typeof obj.nodeType !== "number";
23550};
23551
23552window = (typeof window != 'undefined' && window.Math == Math)
23553 ? window
23554 : (typeof self != 'undefined' && self.Math == Math)
23555 ? self
23556 : Function('return this')()
23557;
23558
23559$.fn.toast = function(parameters) {
23560 var
23561 $allModules = $(this),
23562 moduleSelector = $allModules.selector || '',
23563
23564 time = new Date().getTime(),
23565 performance = [],
23566
23567 query = arguments[0],
23568 methodInvoked = (typeof query == 'string'),
23569 queryArguments = [].slice.call(arguments, 1),
23570 returnedValue
23571 ;
23572 $allModules
23573 .each(function() {
23574 var
23575 settings = ( $.isPlainObject(parameters) )
23576 ? $.extend(true, {}, $.fn.toast.settings, parameters)
23577 : $.extend({}, $.fn.toast.settings),
23578
23579 className = settings.className,
23580 selector = settings.selector,
23581 error = settings.error,
23582 namespace = settings.namespace,
23583 fields = settings.fields,
23584
23585 eventNamespace = '.' + namespace,
23586 moduleNamespace = namespace + '-module',
23587
23588 $module = $(this),
23589 $toastBox,
23590 $toast,
23591 $actions,
23592 $progress,
23593 $progressBar,
23594 $animationObject,
23595 $close,
23596 $context = (settings.context)
23597 ? $(settings.context)
23598 : $('body'),
23599
23600 isToastComponent = $module.hasClass('toast') || $module.hasClass('message') || $module.hasClass('card'),
23601
23602 element = this,
23603 instance = isToastComponent ? $module.data(moduleNamespace) : undefined,
23604
23605 module
23606 ;
23607 module = {
23608
23609 initialize: function() {
23610 module.verbose('Initializing element');
23611 if (!module.has.container()) {
23612 module.create.container();
23613 }
23614 if(isToastComponent || settings.message !== '' || settings.title !== '' || module.get.iconClass() !== '' || settings.showImage || module.has.configActions()) {
23615 if(typeof settings.showProgress !== 'string' || [className.top,className.bottom].indexOf(settings.showProgress) === -1 ) {
23616 settings.showProgress = false;
23617 }
23618 module.create.toast();
23619 if(settings.closeOnClick && (settings.closeIcon || $($toast).find(selector.input).length > 0 || module.has.configActions())){
23620 settings.closeOnClick = false;
23621 }
23622 if(!settings.closeOnClick) {
23623 $toastBox.addClass(className.unclickable);
23624 }
23625 module.bind.events();
23626 }
23627 module.instantiate();
23628 if($toastBox) {
23629 module.show();
23630 }
23631 },
23632
23633 instantiate: function() {
23634 module.verbose('Storing instance of toast');
23635 instance = module;
23636 $module
23637 .data(moduleNamespace, instance)
23638 ;
23639 },
23640
23641 destroy: function() {
23642 if($toastBox) {
23643 module.debug('Removing toast', $toastBox);
23644 module.unbind.events();
23645 $toastBox.remove();
23646 $toastBox = undefined;
23647 $toast = undefined;
23648 $animationObject = undefined;
23649 settings.onRemove.call($toastBox, element);
23650 $progress = undefined;
23651 $progressBar = undefined;
23652 $close = undefined;
23653 }
23654 $module
23655 .removeData(moduleNamespace)
23656 ;
23657 },
23658
23659 show: function(callback) {
23660 callback = callback || function(){};
23661 module.debug('Showing toast');
23662 if(settings.onShow.call($toastBox, element) === false) {
23663 module.debug('onShow callback returned false, cancelling toast animation');
23664 return;
23665 }
23666 module.animate.show(callback);
23667 },
23668
23669 close: function(callback) {
23670 callback = callback || function(){};
23671 module.remove.visible();
23672 module.unbind.events();
23673 module.animate.close(callback);
23674
23675 },
23676
23677 create: {
23678 container: function() {
23679 module.verbose('Creating container');
23680 $context.append($('<div/>',{class: settings.position + ' ' + className.container + ' ' +(settings.horizontal ? className.horizontal : '')}));
23681 },
23682 toast: function() {
23683 $toastBox = $('<div/>', {class: className.box});
23684 var iconClass = module.get.iconClass();
23685 if (!isToastComponent) {
23686 module.verbose('Creating toast');
23687 $toast = $('<div/>');
23688 var $content = $('<div/>', {class: className.content});
23689 if (iconClass !== '') {
23690 $toast.append($('<i/>', {class: iconClass + ' ' + className.icon}));
23691 }
23692
23693 if (settings.showImage) {
23694 $toast.append($('<img>', {
23695 class: className.image + ' ' + settings.classImage,
23696 src: settings.showImage
23697 }));
23698 }
23699 if (settings.title !== '') {
23700 $content.append($('<div/>', {
23701 class: className.title,
23702 text: settings.title
23703 }));
23704 }
23705
23706 $content.append($('<div/>', {class: className.message, html: module.helpers.escape(settings.message, settings.preserveHTML)}));
23707
23708 $toast
23709 .addClass(settings.class + ' ' + className.toast)
23710 .append($content)
23711 ;
23712 $toast.css('opacity', settings.opacity);
23713 if (settings.closeIcon) {
23714 $close = $('<i/>', {class: className.close + ' ' + (typeof settings.closeIcon === 'string' ? settings.closeIcon : '')});
23715 if($close.hasClass(className.left)) {
23716 $toast.prepend($close);
23717 } else {
23718 $toast.append($close);
23719 }
23720 }
23721 } else {
23722 $toast = settings.cloneModule ? $module.clone().removeAttr('id') : $module;
23723 $close = $toast.find('> i'+module.helpers.toClass(className.close));
23724 settings.closeIcon = ($close.length > 0);
23725 if (iconClass !== '') {
23726 $toast.find(selector.icon).attr('class',iconClass + ' ' + className.icon);
23727 }
23728 if (settings.showImage) {
23729 $toast.find(selector.image).attr('src',settings.showImage);
23730 }
23731 if (settings.title !== '') {
23732 $toast.find(selector.title).html(module.helpers.escape(settings.title, settings.preserveHTML));
23733 }
23734 if (settings.message !== '') {
23735 $toast.find(selector.message).html(module.helpers.escape(settings.message, settings.preserveHTML));
23736 }
23737 }
23738 if ($toast.hasClass(className.compact)) {
23739 settings.compact = true;
23740 }
23741 if ($toast.hasClass('card')) {
23742 settings.compact = false;
23743 }
23744 $actions = $toast.find('.actions');
23745 if (module.has.configActions()) {
23746 if ($actions.length === 0) {
23747 $actions = $('<div/>', {class: className.actions + ' ' + (settings.classActions || '')}).appendTo($toast);
23748 }
23749 if($toast.hasClass('card') && !$actions.hasClass(className.attached)) {
23750 $actions.addClass(className.extraContent);
23751 if($actions.hasClass(className.vertical)) {
23752 $actions.removeClass(className.vertical);
23753 module.error(error.verticalCard);
23754 }
23755 }
23756 settings.actions.forEach(function (el) {
23757 var icon = el[fields.icon] ? '<i class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>' : '',
23758 text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
23759 cls = module.helpers.deQuote(el[fields.class] || ''),
23760 click = el[fields.click] && $.isFunction(el[fields.click]) ? el[fields.click] : function () {};
23761 $actions.append($('<button/>', {
23762 html: icon + text,
23763 class: className.button + ' ' + cls,
23764 click: function () {
23765 if (click.call(element, $module) === false) {
23766 return;
23767 }
23768 module.close();
23769 }
23770 }));
23771 });
23772 }
23773 if ($actions && $actions.hasClass(className.vertical)) {
23774 $toast.addClass(className.vertical);
23775 }
23776 if($actions.length > 0 && !$actions.hasClass(className.attached)) {
23777 if ($actions && (!$actions.hasClass(className.basic) || $actions.hasClass(className.left))) {
23778 $toast.addClass(className.actions);
23779 }
23780 }
23781 if(settings.displayTime === 'auto'){
23782 settings.displayTime = Math.max(settings.minDisplayTime, $toast.text().split(" ").length / settings.wordsPerMinute * 60000);
23783 }
23784 $toastBox.append($toast);
23785
23786 if($actions.length > 0 && $actions.hasClass(className.attached)) {
23787 $actions.addClass(className.buttons);
23788 $actions.detach();
23789 $toast.addClass(className.attached);
23790 if (!$actions.hasClass(className.vertical)) {
23791 if ($actions.hasClass(className.top)) {
23792 $toastBox.prepend($actions);
23793 $toast.addClass(className.bottom);
23794 } else {
23795 $toastBox.append($actions);
23796 $toast.addClass(className.top);
23797 }
23798 } else {
23799 $toast.wrap(
23800 $('<div/>',{
23801 class:className.vertical + ' ' +
23802 className.attached + ' ' +
23803 (settings.compact ? className.compact : '')
23804 })
23805 );
23806 if($actions.hasClass(className.left)) {
23807 $toast.addClass(className.left).parent().addClass(className.left).prepend($actions);
23808 } else {
23809 $toast.parent().append($actions);
23810 }
23811 }
23812 }
23813 if($module !== $toast) {
23814 $module = $toast;
23815 element = $toast[0];
23816 }
23817 if(settings.displayTime > 0) {
23818 var progressingClass = className.progressing+' '+(settings.pauseOnHover ? className.pausable:'');
23819 if (!!settings.showProgress) {
23820 $progress = $('<div/>', {
23821 class: className.progress + ' ' + (settings.classProgress || settings.class),
23822 'data-percent': ''
23823 });
23824 if(!settings.classProgress) {
23825 if ($toast.hasClass('toast') && !$toast.hasClass(className.inverted)) {
23826 $progress.addClass(className.inverted);
23827 } else {
23828 $progress.removeClass(className.inverted);
23829 }
23830 }
23831 $progressBar = $('<div/>', {class: 'bar '+(settings.progressUp ? 'up ' : 'down ')+progressingClass});
23832 $progress
23833 .addClass(settings.showProgress)
23834 .append($progressBar);
23835 if ($progress.hasClass(className.top)) {
23836 $toastBox.prepend($progress);
23837 } else {
23838 $toastBox.append($progress);
23839 }
23840 $progressBar.css('animation-duration', settings.displayTime / 1000 + 's');
23841 }
23842 $animationObject = $('<span/>',{class:'wait '+progressingClass});
23843 $animationObject.css('animation-duration', settings.displayTime / 1000 + 's');
23844 $animationObject.appendTo($toast);
23845 }
23846 if (settings.compact) {
23847 $toastBox.addClass(className.compact);
23848 $toast.addClass(className.compact);
23849 if($progress) {
23850 $progress.addClass(className.compact);
23851 }
23852 }
23853 if (settings.newestOnTop) {
23854 $toastBox.prependTo(module.get.container());
23855 }
23856 else {
23857 $toastBox.appendTo(module.get.container());
23858 }
23859 }
23860 },
23861
23862 bind: {
23863 events: function() {
23864 module.debug('Binding events to toast');
23865 if(settings.closeOnClick || settings.closeIcon) {
23866 (settings.closeIcon ? $close : $toast)
23867 .on('click' + eventNamespace, module.event.click)
23868 ;
23869 }
23870 if($animationObject) {
23871 $animationObject.on('animationend' + eventNamespace, module.close);
23872 }
23873 $toastBox
23874 .on('click' + eventNamespace, selector.approve, module.event.approve)
23875 .on('click' + eventNamespace, selector.deny, module.event.deny)
23876 ;
23877 }
23878 },
23879
23880 unbind: {
23881 events: function() {
23882 module.debug('Unbinding events to toast');
23883 if(settings.closeOnClick || settings.closeIcon) {
23884 (settings.closeIcon ? $close : $toast)
23885 .off('click' + eventNamespace)
23886 ;
23887 }
23888 if($animationObject) {
23889 $animationObject.off('animationend' + eventNamespace);
23890 }
23891 $toastBox
23892 .off('click' + eventNamespace)
23893 ;
23894 }
23895 },
23896
23897 animate: {
23898 show: function(callback) {
23899 callback = $.isFunction(callback) ? callback : function(){};
23900 if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
23901 module.set.visible();
23902 $toastBox
23903 .transition({
23904 animation : settings.transition.showMethod + ' in',
23905 queue : false,
23906 debug : settings.debug,
23907 verbose : settings.verbose,
23908 duration : settings.transition.showDuration,
23909 onComplete : function() {
23910 callback.call($toastBox, element);
23911 settings.onVisible.call($toastBox, element);
23912 }
23913 })
23914 ;
23915 }
23916 },
23917 close: function(callback) {
23918 callback = $.isFunction(callback) ? callback : function(){};
23919 module.debug('Closing toast');
23920 if(settings.onHide.call($toastBox, element) === false) {
23921 module.debug('onHide callback returned false, cancelling toast animation');
23922 return;
23923 }
23924 if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
23925 $toastBox
23926 .transition({
23927 animation : settings.transition.hideMethod + ' out',
23928 queue : false,
23929 duration : settings.transition.hideDuration,
23930 debug : settings.debug,
23931 verbose : settings.verbose,
23932 interval : 50,
23933
23934 onBeforeHide: function(callback){
23935 callback = $.isFunction(callback)?callback : function(){};
23936 if(settings.transition.closeEasing !== ''){
23937 if($toastBox) {
23938 $toastBox.css('opacity', 0);
23939 $toastBox.wrap('<div/>').parent().hide(settings.transition.closeDuration, settings.transition.closeEasing, function () {
23940 if ($toastBox) {
23941 $toastBox.parent().remove();
23942 callback.call($toastBox);
23943 }
23944 });
23945 }
23946 } else {
23947 callback.call($toastBox);
23948 }
23949 },
23950 onComplete : function() {
23951 callback.call($toastBox, element);
23952 settings.onHidden.call($toastBox, element);
23953 module.destroy();
23954 }
23955 })
23956 ;
23957 }
23958 else {
23959 module.error(error.noTransition);
23960 }
23961 },
23962 pause: function() {
23963 $animationObject.css('animationPlayState','paused');
23964 if($progressBar) {
23965 $progressBar.css('animationPlayState', 'paused');
23966 }
23967 },
23968 continue: function() {
23969 $animationObject.css('animationPlayState','running');
23970 if($progressBar) {
23971 $progressBar.css('animationPlayState', 'running');
23972 }
23973 }
23974 },
23975
23976 has: {
23977 container: function() {
23978 module.verbose('Determining if there is already a container');
23979 return ($context.find(module.helpers.toClass(settings.position) + selector.container + (settings.horizontal ? module.helpers.toClass(className.horizontal) : '')).length > 0);
23980 },
23981 toast: function(){
23982 return !!module.get.toast();
23983 },
23984 toasts: function(){
23985 return module.get.toasts().length > 0;
23986 },
23987 configActions: function () {
23988 return Array.isArray(settings.actions) && settings.actions.length > 0;
23989 }
23990 },
23991
23992 get: {
23993 container: function() {
23994 return ($context.find(module.helpers.toClass(settings.position) + selector.container)[0]);
23995 },
23996 toastBox: function() {
23997 return $toastBox || null;
23998 },
23999 toast: function() {
24000 return $toast || null;
24001 },
24002 toasts: function() {
24003 return $(module.get.container()).find(selector.box);
24004 },
24005 iconClass: function() {
24006 return typeof settings.showIcon === 'string' ? settings.showIcon : settings.showIcon && settings.icons[settings.class] ? settings.icons[settings.class] : '';
24007 },
24008 remainingTime: function() {
24009 return $animationObject ? $animationObject.css('opacity') * settings.displayTime : 0;
24010 }
24011 },
24012
24013 set: {
24014 visible: function() {
24015 $toast.addClass(className.visible);
24016 }
24017 },
24018
24019 remove: {
24020 visible: function() {
24021 $toast.removeClass(className.visible);
24022 }
24023 },
24024
24025 event: {
24026 click: function(event) {
24027 if($(event.target).closest('a').length === 0) {
24028 settings.onClick.call($toastBox, element);
24029 module.close();
24030 }
24031 },
24032 approve: function() {
24033 if(settings.onApprove.call(element, $module) === false) {
24034 module.verbose('Approve callback returned false cancelling close');
24035 return;
24036 }
24037 module.close();
24038 },
24039 deny: function() {
24040 if(settings.onDeny.call(element, $module) === false) {
24041 module.verbose('Deny callback returned false cancelling close');
24042 return;
24043 }
24044 module.close();
24045 }
24046 },
24047
24048 helpers: {
24049 toClass: function(selector) {
24050 var
24051 classes = selector.split(' '),
24052 result = ''
24053 ;
24054
24055 classes.forEach(function (element) {
24056 result += '.' + element;
24057 });
24058
24059 return result;
24060 },
24061 deQuote: function(string) {
24062 return String(string).replace(/"/g,"");
24063 },
24064 escape: function(string, preserveHTML) {
24065 if (preserveHTML){
24066 return string;
24067 }
24068 var
24069 badChars = /[<>"'`]/g,
24070 shouldEscape = /[&<>"'`]/,
24071 escape = {
24072 "<": "&lt;",
24073 ">": "&gt;",
24074 '"': "&quot;",
24075 "'": "&#x27;",
24076 "`": "&#x60;"
24077 },
24078 escapedChar = function(chr) {
24079 return escape[chr];
24080 }
24081 ;
24082 if(shouldEscape.test(string)) {
24083 string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
24084 return string.replace(badChars, escapedChar);
24085 }
24086 return string;
24087 }
24088 },
24089
24090 can: {
24091 useElement: function(element){
24092 if ($.fn[element] !== undefined) {
24093 return true;
24094 }
24095 module.error(error.noElement.replace('{element}',element));
24096 return false;
24097 }
24098 },
24099
24100 setting: function(name, value) {
24101 module.debug('Changing setting', name, value);
24102 if( $.isPlainObject(name) ) {
24103 $.extend(true, settings, name);
24104 }
24105 else if(value !== undefined) {
24106 if($.isPlainObject(settings[name])) {
24107 $.extend(true, settings[name], value);
24108 }
24109 else {
24110 settings[name] = value;
24111 }
24112 }
24113 else {
24114 return settings[name];
24115 }
24116 },
24117 internal: function(name, value) {
24118 if( $.isPlainObject(name) ) {
24119 $.extend(true, module, name);
24120 }
24121 else if(value !== undefined) {
24122 module[name] = value;
24123 }
24124 else {
24125 return module[name];
24126 }
24127 },
24128 debug: function() {
24129 if(!settings.silent && settings.debug) {
24130 if(settings.performance) {
24131 module.performance.log(arguments);
24132 }
24133 else {
24134 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
24135 module.debug.apply(console, arguments);
24136 }
24137 }
24138 },
24139 verbose: function() {
24140 if(!settings.silent && settings.verbose && settings.debug) {
24141 if(settings.performance) {
24142 module.performance.log(arguments);
24143 }
24144 else {
24145 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
24146 module.verbose.apply(console, arguments);
24147 }
24148 }
24149 },
24150 error: function() {
24151 if(!settings.silent) {
24152 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
24153 module.error.apply(console, arguments);
24154 }
24155 },
24156 performance: {
24157 log: function(message) {
24158 var
24159 currentTime,
24160 executionTime,
24161 previousTime
24162 ;
24163 if(settings.performance) {
24164 currentTime = new Date().getTime();
24165 previousTime = time || currentTime;
24166 executionTime = currentTime - previousTime;
24167 time = currentTime;
24168 performance.push({
24169 'Name' : message[0],
24170 'Arguments' : [].slice.call(message, 1) || '',
24171 'Element' : element,
24172 'Execution Time' : executionTime
24173 });
24174 }
24175 clearTimeout(module.performance.timer);
24176 module.performance.timer = setTimeout(module.performance.display, 500);
24177 },
24178 display: function() {
24179 var
24180 title = settings.name + ':',
24181 totalTime = 0
24182 ;
24183 time = false;
24184 clearTimeout(module.performance.timer);
24185 $.each(performance, function(index, data) {
24186 totalTime += data['Execution Time'];
24187 });
24188 title += ' ' + totalTime + 'ms';
24189 if(moduleSelector) {
24190 title += ' \'' + moduleSelector + '\'';
24191 }
24192 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
24193 console.groupCollapsed(title);
24194 if(console.table) {
24195 console.table(performance);
24196 }
24197 else {
24198 $.each(performance, function(index, data) {
24199 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
24200 });
24201 }
24202 console.groupEnd();
24203 }
24204 performance = [];
24205 }
24206 },
24207 invoke: function(query, passedArguments, context) {
24208 var
24209 object = instance,
24210 maxDepth,
24211 found,
24212 response
24213 ;
24214 passedArguments = passedArguments || queryArguments;
24215 context = element || context;
24216 if(typeof query == 'string' && object !== undefined) {
24217 query = query.split(/[\. ]/);
24218 maxDepth = query.length - 1;
24219 $.each(query, function(depth, value) {
24220 var camelCaseValue = (depth != maxDepth)
24221 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
24222 : query
24223 ;
24224 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
24225 object = object[camelCaseValue];
24226 }
24227 else if( object[camelCaseValue] !== undefined ) {
24228 found = object[camelCaseValue];
24229 return false;
24230 }
24231 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
24232 object = object[value];
24233 }
24234 else if( object[value] !== undefined ) {
24235 found = object[value];
24236 return false;
24237 }
24238 else {
24239 module.error(error.method, query);
24240 return false;
24241 }
24242 });
24243 }
24244 if ( $.isFunction( found ) ) {
24245 response = found.apply(context, passedArguments);
24246 }
24247 else if(found !== undefined) {
24248 response = found;
24249 }
24250 if(Array.isArray(returnedValue)) {
24251 returnedValue.push(response);
24252 }
24253 else if(returnedValue !== undefined) {
24254 returnedValue = [returnedValue, response];
24255 }
24256 else if(response !== undefined) {
24257 returnedValue = response;
24258 }
24259 return found;
24260 }
24261 };
24262
24263 if(methodInvoked) {
24264 if(instance === undefined) {
24265 module.initialize();
24266 }
24267 module.invoke(query);
24268 }
24269 else {
24270 if(instance !== undefined) {
24271 instance.invoke('destroy');
24272 }
24273 module.initialize();
24274 returnedValue = $module;
24275 }
24276 })
24277 ;
24278
24279 return (returnedValue !== undefined)
24280 ? returnedValue
24281 : this
24282 ;
24283};
24284
24285$.fn.toast.settings = {
24286
24287 name : 'Toast',
24288 namespace : 'toast',
24289
24290 silent : false,
24291 debug : false,
24292 verbose : false,
24293 performance : true,
24294
24295 context : 'body',
24296
24297 position : 'top right',
24298 horizontal : false,
24299 class : 'neutral',
24300 classProgress : false,
24301 classActions : false,
24302 classImage : 'mini',
24303
24304 title : '',
24305 message : '',
24306 displayTime : 3000, // set to zero to require manually dismissal, otherwise hides on its own
24307 minDisplayTime : 1000, // minimum displaytime in case displayTime is set to 'auto'
24308 wordsPerMinute : 120,
24309 showIcon : false,
24310 newestOnTop : false,
24311 showProgress : false,
24312 pauseOnHover : true,
24313 progressUp : false, //if true, the bar will start at 0% and increase to 100%
24314 opacity : 1,
24315 compact : true,
24316 closeIcon : false,
24317 closeOnClick : true,
24318 cloneModule : true,
24319 actions : false,
24320 preserveHTML : true,
24321 showImage : false,
24322
24323 // transition settings
24324 transition : {
24325 showMethod : 'scale',
24326 showDuration : 500,
24327 hideMethod : 'scale',
24328 hideDuration : 500,
24329 closeEasing : 'easeOutCubic', //Set to empty string to stack the closed toast area immediately (old behaviour)
24330 closeDuration: 500
24331 },
24332
24333 error: {
24334 method : 'The method you called is not defined.',
24335 noElement : 'This module requires ui {element}',
24336 verticalCard : 'Vertical but not attached actions are not supported for card layout'
24337 },
24338
24339 className : {
24340 container : 'ui toast-container',
24341 box : 'floating toast-box',
24342 progress : 'ui attached active progress',
24343 toast : 'ui toast',
24344 icon : 'centered icon',
24345 visible : 'visible',
24346 content : 'content',
24347 title : 'ui header',
24348 message : 'message',
24349 actions : 'actions',
24350 extraContent : 'extra content',
24351 button : 'ui button',
24352 buttons : 'ui buttons',
24353 close : 'close icon',
24354 image : 'ui image',
24355 vertical : 'vertical',
24356 horizontal : 'horizontal',
24357 attached : 'attached',
24358 inverted : 'inverted',
24359 compact : 'compact',
24360 pausable : 'pausable',
24361 progressing : 'progressing',
24362 top : 'top',
24363 bottom : 'bottom',
24364 left : 'left',
24365 basic : 'basic',
24366 unclickable : 'unclickable'
24367 },
24368
24369 icons : {
24370 info : 'info',
24371 success : 'checkmark',
24372 warning : 'warning',
24373 error : 'times'
24374 },
24375
24376 selector : {
24377 container : '.ui.toast-container',
24378 box : '.toast-box',
24379 toast : '.ui.toast',
24380 title : '.header',
24381 message : '.message:not(.ui)',
24382 image : '> img.image, > .image > img',
24383 icon : '> i.icon',
24384 input : 'input:not([type="hidden"]), textarea, select, button, .ui.button, ui.dropdown',
24385 approve : '.actions .positive, .actions .approve, .actions .ok',
24386 deny : '.actions .negative, .actions .deny, .actions .cancel'
24387 },
24388
24389 fields : {
24390 class : 'class',
24391 text : 'text',
24392 icon : 'icon',
24393 click : 'click'
24394 },
24395
24396 // callbacks
24397 onShow : function(){},
24398 onVisible : function(){},
24399 onClick : function(){},
24400 onHide : function(){},
24401 onHidden : function(){},
24402 onRemove : function(){},
24403 onApprove : function(){},
24404 onDeny : function(){}
24405};
24406
24407$.extend( $.easing, {
24408 easeOutBounce: function (x, t, b, c, d) {
24409 if ((t/=d) < (1/2.75)) {
24410 return c*(7.5625*t*t) + b;
24411 } else if (t < (2/2.75)) {
24412 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
24413 } else if (t < (2.5/2.75)) {
24414 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
24415 } else {
24416 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
24417 }
24418 },
24419 easeOutCubic: function (t) {
24420 return (--t)*t*t+1;
24421 }
24422});
24423
24424
24425})( jQuery, window, document );
24426
24427/*!
24428 * # Fomantic-UI 2.8.8 - Transition
24429 * http://github.com/fomantic/Fomantic-UI/
24430 *
24431 *
24432 * Released under the MIT license
24433 * http://opensource.org/licenses/MIT
24434 *
24435 */
24436
24437;(function ($, window, document, undefined) {
24438
24439'use strict';
24440
24441$.isFunction = $.isFunction || function(obj) {
24442 return typeof obj === "function" && typeof obj.nodeType !== "number";
24443};
24444
24445window = (typeof window != 'undefined' && window.Math == Math)
24446 ? window
24447 : (typeof self != 'undefined' && self.Math == Math)
24448 ? self
24449 : Function('return this')()
24450;
24451
24452$.fn.transition = function() {
24453 var
24454 $allModules = $(this),
24455 moduleSelector = $allModules.selector || '',
24456
24457 time = new Date().getTime(),
24458 performance = [],
24459
24460 moduleArguments = arguments,
24461 query = moduleArguments[0],
24462 queryArguments = [].slice.call(arguments, 1),
24463 methodInvoked = (typeof query === 'string'),
24464
24465 returnedValue
24466 ;
24467 $allModules
24468 .each(function(index) {
24469 var
24470 $module = $(this),
24471 element = this,
24472
24473 // set at run time
24474 settings,
24475 instance,
24476
24477 error,
24478 className,
24479 metadata,
24480 animationEnd,
24481
24482 moduleNamespace,
24483 eventNamespace,
24484 module
24485 ;
24486
24487 module = {
24488
24489 initialize: function() {
24490
24491 // get full settings
24492 settings = module.get.settings.apply(element, moduleArguments);
24493
24494 // shorthand
24495 className = settings.className;
24496 error = settings.error;
24497 metadata = settings.metadata;
24498
24499 // define namespace
24500 eventNamespace = '.' + settings.namespace;
24501 moduleNamespace = 'module-' + settings.namespace;
24502 instance = $module.data(moduleNamespace) || module;
24503
24504 // get vendor specific events
24505 animationEnd = module.get.animationEndEvent();
24506
24507 if(methodInvoked) {
24508 methodInvoked = module.invoke(query);
24509 }
24510
24511 // method not invoked, lets run an animation
24512 if(methodInvoked === false) {
24513 module.verbose('Converted arguments into settings object', settings);
24514 if(settings.interval) {
24515 module.delay(settings.animate);
24516 }
24517 else {
24518 module.animate();
24519 }
24520 module.instantiate();
24521 }
24522 },
24523
24524 instantiate: function() {
24525 module.verbose('Storing instance of module', module);
24526 instance = module;
24527 $module
24528 .data(moduleNamespace, instance)
24529 ;
24530 },
24531
24532 destroy: function() {
24533 module.verbose('Destroying previous module for', element);
24534 $module
24535 .removeData(moduleNamespace)
24536 ;
24537 },
24538
24539 refresh: function() {
24540 module.verbose('Refreshing display type on next animation');
24541 delete module.displayType;
24542 },
24543
24544 forceRepaint: function() {
24545 module.verbose('Forcing element repaint');
24546 var
24547 $parentElement = $module.parent(),
24548 $nextElement = $module.next()
24549 ;
24550 if($nextElement.length === 0) {
24551 $module.detach().appendTo($parentElement);
24552 }
24553 else {
24554 $module.detach().insertBefore($nextElement);
24555 }
24556 },
24557
24558 repaint: function() {
24559 module.verbose('Repainting element');
24560 var
24561 fakeAssignment = element.offsetWidth
24562 ;
24563 },
24564
24565 delay: function(interval) {
24566 var
24567 direction = module.get.animationDirection(),
24568 shouldReverse,
24569 delay
24570 ;
24571 if(!direction) {
24572 direction = module.can.transition()
24573 ? module.get.direction()
24574 : 'static'
24575 ;
24576 }
24577 interval = (interval !== undefined)
24578 ? interval
24579 : settings.interval
24580 ;
24581 shouldReverse = (settings.reverse == 'auto' && direction == className.outward);
24582 delay = (shouldReverse || settings.reverse == true)
24583 ? ($allModules.length - index) * settings.interval
24584 : index * settings.interval
24585 ;
24586 module.debug('Delaying animation by', delay);
24587 setTimeout(module.animate, delay);
24588 },
24589
24590 animate: function(overrideSettings) {
24591 settings = overrideSettings || settings;
24592 if(!module.is.supported()) {
24593 module.error(error.support);
24594 return false;
24595 }
24596 module.debug('Preparing animation', settings.animation);
24597 if(module.is.animating()) {
24598 if(settings.queue) {
24599 if(!settings.allowRepeats && module.has.direction() && module.is.occurring() && module.queuing !== true) {
24600 module.debug('Animation is currently occurring, preventing queueing same animation', settings.animation);
24601 }
24602 else {
24603 module.queue(settings.animation);
24604 }
24605 return false;
24606 }
24607 else if(!settings.allowRepeats && module.is.occurring()) {
24608 module.debug('Animation is already occurring, will not execute repeated animation', settings.animation);
24609 return false;
24610 }
24611 else {
24612 module.debug('New animation started, completing previous early', settings.animation);
24613 instance.complete();
24614 }
24615 }
24616 if( module.can.animate() ) {
24617 module.set.animating(settings.animation);
24618 }
24619 else {
24620 module.error(error.noAnimation, settings.animation, element);
24621 }
24622 },
24623
24624 reset: function() {
24625 module.debug('Resetting animation to beginning conditions');
24626 module.remove.animationCallbacks();
24627 module.restore.conditions();
24628 module.remove.animating();
24629 },
24630
24631 queue: function(animation) {
24632 module.debug('Queueing animation of', animation);
24633 module.queuing = true;
24634 $module
24635 .one(animationEnd + '.queue' + eventNamespace, function() {
24636 module.queuing = false;
24637 module.repaint();
24638 module.animate.apply(this, settings);
24639 })
24640 ;
24641 },
24642
24643 complete: function (event) {
24644 if(event && event.target === element) {
24645 event.stopPropagation();
24646 }
24647 module.debug('Animation complete', settings.animation);
24648 module.remove.completeCallback();
24649 module.remove.failSafe();
24650 if(!module.is.looping()) {
24651 if( module.is.outward() ) {
24652 module.verbose('Animation is outward, hiding element');
24653 module.restore.conditions();
24654 module.hide();
24655 }
24656 else if( module.is.inward() ) {
24657 module.verbose('Animation is outward, showing element');
24658 module.restore.conditions();
24659 module.show();
24660 }
24661 else {
24662 module.verbose('Static animation completed');
24663 module.restore.conditions();
24664 settings.onComplete.call(element);
24665 }
24666 }
24667 },
24668
24669 force: {
24670 visible: function() {
24671 var
24672 style = $module.attr('style'),
24673 userStyle = module.get.userStyle(style),
24674 displayType = module.get.displayType(),
24675 overrideStyle = userStyle + 'display: ' + displayType + ' !important;',
24676 inlineDisplay = $module[0].style.display,
24677 mustStayHidden = !displayType || (inlineDisplay === 'none' && settings.skipInlineHidden) || $module[0].tagName.match(/(script|link|style)/i)
24678 ;
24679 if (mustStayHidden){
24680 module.remove.transition();
24681 return false;
24682 }
24683 module.verbose('Overriding default display to show element', displayType);
24684 $module
24685 .attr('style', overrideStyle)
24686 ;
24687 return true;
24688 },
24689 hidden: function() {
24690 var
24691 style = $module.attr('style'),
24692 currentDisplay = $module.css('display'),
24693 emptyStyle = (style === undefined || style === '')
24694 ;
24695 if(currentDisplay !== 'none' && !module.is.hidden()) {
24696 module.verbose('Overriding default display to hide element');
24697 $module
24698 .css('display', 'none')
24699 ;
24700 }
24701 else if(emptyStyle) {
24702 $module
24703 .removeAttr('style')
24704 ;
24705 }
24706 }
24707 },
24708
24709 has: {
24710 direction: function(animation) {
24711 var
24712 hasDirection = false
24713 ;
24714 animation = animation || settings.animation;
24715 if(typeof animation === 'string') {
24716 animation = animation.split(' ');
24717 $.each(animation, function(index, word){
24718 if(word === className.inward || word === className.outward) {
24719 hasDirection = true;
24720 }
24721 });
24722 }
24723 return hasDirection;
24724 },
24725 inlineDisplay: function() {
24726 var
24727 style = $module.attr('style') || ''
24728 ;
24729 return Array.isArray(style.match(/display.*?;/, ''));
24730 }
24731 },
24732
24733 set: {
24734 animating: function(animation) {
24735 // remove previous callbacks
24736 module.remove.completeCallback();
24737
24738 // determine exact animation
24739 animation = animation || settings.animation;
24740 var animationClass = module.get.animationClass(animation);
24741
24742 // save animation class in cache to restore class names
24743 module.save.animation(animationClass);
24744
24745 if(module.force.visible()) {
24746 module.remove.hidden();
24747 module.remove.direction();
24748
24749 module.start.animation(animationClass);
24750 }
24751 },
24752 duration: function(animationName, duration) {
24753 duration = duration || settings.duration;
24754 duration = (typeof duration == 'number')
24755 ? duration + 'ms'
24756 : duration
24757 ;
24758 if(duration || duration === 0) {
24759 module.verbose('Setting animation duration', duration);
24760 $module
24761 .css({
24762 'animation-duration': duration
24763 })
24764 ;
24765 }
24766 },
24767 direction: function(direction) {
24768 direction = direction || module.get.direction();
24769 if(direction == className.inward) {
24770 module.set.inward();
24771 }
24772 else {
24773 module.set.outward();
24774 }
24775 },
24776 looping: function() {
24777 module.debug('Transition set to loop');
24778 $module
24779 .addClass(className.looping)
24780 ;
24781 },
24782 hidden: function() {
24783 $module
24784 .addClass(className.transition)
24785 .addClass(className.hidden)
24786 ;
24787 },
24788 inward: function() {
24789 module.debug('Setting direction to inward');
24790 $module
24791 .removeClass(className.outward)
24792 .addClass(className.inward)
24793 ;
24794 },
24795 outward: function() {
24796 module.debug('Setting direction to outward');
24797 $module
24798 .removeClass(className.inward)
24799 .addClass(className.outward)
24800 ;
24801 },
24802 visible: function() {
24803 $module
24804 .addClass(className.transition)
24805 .addClass(className.visible)
24806 ;
24807 }
24808 },
24809
24810 start: {
24811 animation: function(animationClass) {
24812 animationClass = animationClass || module.get.animationClass();
24813 module.debug('Starting tween', animationClass);
24814 $module
24815 .addClass(animationClass)
24816 .one(animationEnd + '.complete' + eventNamespace, module.complete)
24817 ;
24818 if(settings.useFailSafe) {
24819 module.add.failSafe();
24820 }
24821 module.set.duration(settings.duration);
24822 settings.onStart.call(element);
24823 }
24824 },
24825
24826 save: {
24827 animation: function(animation) {
24828 if(!module.cache) {
24829 module.cache = {};
24830 }
24831 module.cache.animation = animation;
24832 },
24833 displayType: function(displayType) {
24834 if(displayType !== 'none') {
24835 $module.data(metadata.displayType, displayType);
24836 }
24837 },
24838 transitionExists: function(animation, exists) {
24839 $.fn.transition.exists[animation] = exists;
24840 module.verbose('Saving existence of transition', animation, exists);
24841 }
24842 },
24843
24844 restore: {
24845 conditions: function() {
24846 var
24847 animation = module.get.currentAnimation()
24848 ;
24849 if(animation) {
24850 $module
24851 .removeClass(animation)
24852 ;
24853 module.verbose('Removing animation class', module.cache);
24854 }
24855 module.remove.duration();
24856 }
24857 },
24858
24859 add: {
24860 failSafe: function() {
24861 var
24862 duration = module.get.duration()
24863 ;
24864 module.timer = setTimeout(function() {
24865 $module.triggerHandler(animationEnd);
24866 }, duration + settings.failSafeDelay);
24867 module.verbose('Adding fail safe timer', module.timer);
24868 }
24869 },
24870
24871 remove: {
24872 animating: function() {
24873 $module.removeClass(className.animating);
24874 },
24875 animationCallbacks: function() {
24876 module.remove.queueCallback();
24877 module.remove.completeCallback();
24878 },
24879 queueCallback: function() {
24880 $module.off('.queue' + eventNamespace);
24881 },
24882 completeCallback: function() {
24883 $module.off('.complete' + eventNamespace);
24884 },
24885 display: function() {
24886 $module.css('display', '');
24887 },
24888 direction: function() {
24889 $module
24890 .removeClass(className.inward)
24891 .removeClass(className.outward)
24892 ;
24893 },
24894 duration: function() {
24895 $module
24896 .css('animation-duration', '')
24897 ;
24898 },
24899 failSafe: function() {
24900 module.verbose('Removing fail safe timer', module.timer);
24901 if(module.timer) {
24902 clearTimeout(module.timer);
24903 }
24904 },
24905 hidden: function() {
24906 $module.removeClass(className.hidden);
24907 },
24908 visible: function() {
24909 $module.removeClass(className.visible);
24910 },
24911 looping: function() {
24912 module.debug('Transitions are no longer looping');
24913 if( module.is.looping() ) {
24914 module.reset();
24915 $module
24916 .removeClass(className.looping)
24917 ;
24918 }
24919 },
24920 transition: function() {
24921 $module
24922 .removeClass(className.transition)
24923 .removeClass(className.visible)
24924 .removeClass(className.hidden)
24925 ;
24926 }
24927 },
24928 get: {
24929 settings: function(animation, duration, onComplete) {
24930 // single settings object
24931 if(typeof animation == 'object') {
24932 return $.extend(true, {}, $.fn.transition.settings, animation);
24933 }
24934 // all arguments provided
24935 else if(typeof onComplete == 'function') {
24936 return $.extend({}, $.fn.transition.settings, {
24937 animation : animation,
24938 onComplete : onComplete,
24939 duration : duration
24940 });
24941 }
24942 // only duration provided
24943 else if(typeof duration == 'string' || typeof duration == 'number') {
24944 return $.extend({}, $.fn.transition.settings, {
24945 animation : animation,
24946 duration : duration
24947 });
24948 }
24949 // duration is actually settings object
24950 else if(typeof duration == 'object') {
24951 return $.extend({}, $.fn.transition.settings, duration, {
24952 animation : animation
24953 });
24954 }
24955 // duration is actually callback
24956 else if(typeof duration == 'function') {
24957 return $.extend({}, $.fn.transition.settings, {
24958 animation : animation,
24959 onComplete : duration
24960 });
24961 }
24962 // only animation provided
24963 else {
24964 return $.extend({}, $.fn.transition.settings, {
24965 animation : animation
24966 });
24967 }
24968 },
24969 animationClass: function(animation) {
24970 var
24971 animationClass = animation || settings.animation,
24972 directionClass = (module.can.transition() && !module.has.direction())
24973 ? module.get.direction() + ' '
24974 : ''
24975 ;
24976 return className.animating + ' '
24977 + className.transition + ' '
24978 + directionClass
24979 + animationClass
24980 ;
24981 },
24982 currentAnimation: function() {
24983 return (module.cache && module.cache.animation !== undefined)
24984 ? module.cache.animation
24985 : false
24986 ;
24987 },
24988 currentDirection: function() {
24989 return module.is.inward()
24990 ? className.inward
24991 : className.outward
24992 ;
24993 },
24994 direction: function() {
24995 return module.is.hidden() || !module.is.visible()
24996 ? className.inward
24997 : className.outward
24998 ;
24999 },
25000 animationDirection: function(animation) {
25001 var
25002 direction
25003 ;
25004 animation = animation || settings.animation;
25005 if(typeof animation === 'string') {
25006 animation = animation.split(' ');
25007 // search animation name for out/in class
25008 $.each(animation, function(index, word){
25009 if(word === className.inward) {
25010 direction = className.inward;
25011 }
25012 else if(word === className.outward) {
25013 direction = className.outward;
25014 }
25015 });
25016 }
25017 // return found direction
25018 if(direction) {
25019 return direction;
25020 }
25021 return false;
25022 },
25023 duration: function(duration) {
25024 duration = duration || settings.duration;
25025 if(duration === false) {
25026 duration = $module.css('animation-duration') || 0;
25027 }
25028 return (typeof duration === 'string')
25029 ? (duration.indexOf('ms') > -1)
25030 ? parseFloat(duration)
25031 : parseFloat(duration) * 1000
25032 : duration
25033 ;
25034 },
25035 displayType: function(shouldDetermine) {
25036 shouldDetermine = (shouldDetermine !== undefined)
25037 ? shouldDetermine
25038 : true
25039 ;
25040 if(settings.displayType) {
25041 return settings.displayType;
25042 }
25043 if(shouldDetermine && $module.data(metadata.displayType) === undefined) {
25044 var currentDisplay = $module.css('display');
25045 if(currentDisplay === '' || currentDisplay === 'none'){
25046 // create fake element to determine display state
25047 module.can.transition(true);
25048 } else {
25049 module.save.displayType(currentDisplay);
25050 }
25051 }
25052 return $module.data(metadata.displayType);
25053 },
25054 userStyle: function(style) {
25055 style = style || $module.attr('style') || '';
25056 return style.replace(/display.*?;/, '');
25057 },
25058 transitionExists: function(animation) {
25059 return $.fn.transition.exists[animation];
25060 },
25061 animationStartEvent: function() {
25062 var
25063 element = document.createElement('div'),
25064 animations = {
25065 'animation' :'animationstart',
25066 'OAnimation' :'oAnimationStart',
25067 'MozAnimation' :'mozAnimationStart',
25068 'WebkitAnimation' :'webkitAnimationStart'
25069 },
25070 animation
25071 ;
25072 for(animation in animations){
25073 if( element.style[animation] !== undefined ){
25074 return animations[animation];
25075 }
25076 }
25077 return false;
25078 },
25079 animationEndEvent: function() {
25080 var
25081 element = document.createElement('div'),
25082 animations = {
25083 'animation' :'animationend',
25084 'OAnimation' :'oAnimationEnd',
25085 'MozAnimation' :'mozAnimationEnd',
25086 'WebkitAnimation' :'webkitAnimationEnd'
25087 },
25088 animation
25089 ;
25090 for(animation in animations){
25091 if( element.style[animation] !== undefined ){
25092 return animations[animation];
25093 }
25094 }
25095 return false;
25096 }
25097
25098 },
25099
25100 can: {
25101 transition: function(forced) {
25102 var
25103 animation = settings.animation,
25104 transitionExists = module.get.transitionExists(animation),
25105 displayType = module.get.displayType(false),
25106 elementClass,
25107 tagName,
25108 $clone,
25109 currentAnimation,
25110 inAnimation,
25111 directionExists
25112 ;
25113 if( transitionExists === undefined || forced) {
25114 module.verbose('Determining whether animation exists');
25115 elementClass = $module.attr('class');
25116 tagName = $module.prop('tagName');
25117
25118 $clone = $('<' + tagName + ' />').addClass( elementClass ).insertAfter($module);
25119 currentAnimation = $clone
25120 .addClass(animation)
25121 .removeClass(className.inward)
25122 .removeClass(className.outward)
25123 .addClass(className.animating)
25124 .addClass(className.transition)
25125 .css('animationName')
25126 ;
25127 inAnimation = $clone
25128 .addClass(className.inward)
25129 .css('animationName')
25130 ;
25131 if(!displayType) {
25132 displayType = $clone
25133 .attr('class', elementClass)
25134 .removeAttr('style')
25135 .removeClass(className.hidden)
25136 .removeClass(className.visible)
25137 .show()
25138 .css('display')
25139 ;
25140 module.verbose('Determining final display state', displayType);
25141 module.save.displayType(displayType);
25142 }
25143
25144 $clone.remove();
25145 if(currentAnimation != inAnimation) {
25146 module.debug('Direction exists for animation', animation);
25147 directionExists = true;
25148 }
25149 else if(currentAnimation == 'none' || !currentAnimation) {
25150 module.debug('No animation defined in css', animation);
25151 return;
25152 }
25153 else {
25154 module.debug('Static animation found', animation, displayType);
25155 directionExists = false;
25156 }
25157 module.save.transitionExists(animation, directionExists);
25158 }
25159 return (transitionExists !== undefined)
25160 ? transitionExists
25161 : directionExists
25162 ;
25163 },
25164 animate: function() {
25165 // can transition does not return a value if animation does not exist
25166 return (module.can.transition() !== undefined);
25167 }
25168 },
25169
25170 is: {
25171 animating: function() {
25172 return $module.hasClass(className.animating);
25173 },
25174 inward: function() {
25175 return $module.hasClass(className.inward);
25176 },
25177 outward: function() {
25178 return $module.hasClass(className.outward);
25179 },
25180 looping: function() {
25181 return $module.hasClass(className.looping);
25182 },
25183 occurring: function(animation) {
25184 animation = animation || settings.animation;
25185 animation = '.' + animation.replace(' ', '.');
25186 return ( $module.filter(animation).length > 0 );
25187 },
25188 visible: function() {
25189 return $module.is(':visible');
25190 },
25191 hidden: function() {
25192 return $module.css('visibility') === 'hidden';
25193 },
25194 supported: function() {
25195 return(animationEnd !== false);
25196 }
25197 },
25198
25199 hide: function() {
25200 module.verbose('Hiding element');
25201 if( module.is.animating() ) {
25202 module.reset();
25203 }
25204 element.blur(); // IE will trigger focus change if element is not blurred before hiding
25205 module.remove.display();
25206 module.remove.visible();
25207 if($.isFunction(settings.onBeforeHide)){
25208 settings.onBeforeHide.call(element,function(){
25209 module.hideNow();
25210 });
25211 } else {
25212 module.hideNow();
25213 }
25214
25215 },
25216
25217 hideNow: function() {
25218 module.set.hidden();
25219 module.force.hidden();
25220 settings.onHide.call(element);
25221 settings.onComplete.call(element);
25222 // module.repaint();
25223 },
25224
25225 show: function(display) {
25226 module.verbose('Showing element', display);
25227 if(module.force.visible()) {
25228 module.remove.hidden();
25229 module.set.visible();
25230 settings.onShow.call(element);
25231 settings.onComplete.call(element);
25232 // module.repaint();
25233 }
25234 },
25235
25236 toggle: function() {
25237 if( module.is.visible() ) {
25238 module.hide();
25239 }
25240 else {
25241 module.show();
25242 }
25243 },
25244
25245 stop: function() {
25246 module.debug('Stopping current animation');
25247 $module.triggerHandler(animationEnd);
25248 },
25249
25250 stopAll: function() {
25251 module.debug('Stopping all animation');
25252 module.remove.queueCallback();
25253 $module.triggerHandler(animationEnd);
25254 },
25255
25256 clear: {
25257 queue: function() {
25258 module.debug('Clearing animation queue');
25259 module.remove.queueCallback();
25260 }
25261 },
25262
25263 enable: function() {
25264 module.verbose('Starting animation');
25265 $module.removeClass(className.disabled);
25266 },
25267
25268 disable: function() {
25269 module.debug('Stopping animation');
25270 $module.addClass(className.disabled);
25271 },
25272
25273 setting: function(name, value) {
25274 module.debug('Changing setting', name, value);
25275 if( $.isPlainObject(name) ) {
25276 $.extend(true, settings, name);
25277 }
25278 else if(value !== undefined) {
25279 if($.isPlainObject(settings[name])) {
25280 $.extend(true, settings[name], value);
25281 }
25282 else {
25283 settings[name] = value;
25284 }
25285 }
25286 else {
25287 return settings[name];
25288 }
25289 },
25290 internal: function(name, value) {
25291 if( $.isPlainObject(name) ) {
25292 $.extend(true, module, name);
25293 }
25294 else if(value !== undefined) {
25295 module[name] = value;
25296 }
25297 else {
25298 return module[name];
25299 }
25300 },
25301 debug: function() {
25302 if(!settings.silent && settings.debug) {
25303 if(settings.performance) {
25304 module.performance.log(arguments);
25305 }
25306 else {
25307 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
25308 module.debug.apply(console, arguments);
25309 }
25310 }
25311 },
25312 verbose: function() {
25313 if(!settings.silent && settings.verbose && settings.debug) {
25314 if(settings.performance) {
25315 module.performance.log(arguments);
25316 }
25317 else {
25318 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
25319 module.verbose.apply(console, arguments);
25320 }
25321 }
25322 },
25323 error: function() {
25324 if(!settings.silent) {
25325 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
25326 module.error.apply(console, arguments);
25327 }
25328 },
25329 performance: {
25330 log: function(message) {
25331 var
25332 currentTime,
25333 executionTime,
25334 previousTime
25335 ;
25336 if(settings.performance) {
25337 currentTime = new Date().getTime();
25338 previousTime = time || currentTime;
25339 executionTime = currentTime - previousTime;
25340 time = currentTime;
25341 performance.push({
25342 'Name' : message[0],
25343 'Arguments' : [].slice.call(message, 1) || '',
25344 'Element' : element,
25345 'Execution Time' : executionTime
25346 });
25347 }
25348 clearTimeout(module.performance.timer);
25349 module.performance.timer = setTimeout(module.performance.display, 500);
25350 },
25351 display: function() {
25352 var
25353 title = settings.name + ':',
25354 totalTime = 0
25355 ;
25356 time = false;
25357 clearTimeout(module.performance.timer);
25358 $.each(performance, function(index, data) {
25359 totalTime += data['Execution Time'];
25360 });
25361 title += ' ' + totalTime + 'ms';
25362 if(moduleSelector) {
25363 title += ' \'' + moduleSelector + '\'';
25364 }
25365 if($allModules.length > 1) {
25366 title += ' ' + '(' + $allModules.length + ')';
25367 }
25368 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
25369 console.groupCollapsed(title);
25370 if(console.table) {
25371 console.table(performance);
25372 }
25373 else {
25374 $.each(performance, function(index, data) {
25375 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
25376 });
25377 }
25378 console.groupEnd();
25379 }
25380 performance = [];
25381 }
25382 },
25383 // modified for transition to return invoke success
25384 invoke: function(query, passedArguments, context) {
25385 var
25386 object = instance,
25387 maxDepth,
25388 found,
25389 response
25390 ;
25391 passedArguments = passedArguments || queryArguments;
25392 context = element || context;
25393 if(typeof query == 'string' && object !== undefined) {
25394 query = query.split(/[\. ]/);
25395 maxDepth = query.length - 1;
25396 $.each(query, function(depth, value) {
25397 var camelCaseValue = (depth != maxDepth)
25398 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
25399 : query
25400 ;
25401 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
25402 object = object[camelCaseValue];
25403 }
25404 else if( object[camelCaseValue] !== undefined ) {
25405 found = object[camelCaseValue];
25406 return false;
25407 }
25408 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
25409 object = object[value];
25410 }
25411 else if( object[value] !== undefined ) {
25412 found = object[value];
25413 return false;
25414 }
25415 else {
25416 return false;
25417 }
25418 });
25419 }
25420 if ( $.isFunction( found ) ) {
25421 response = found.apply(context, passedArguments);
25422 }
25423 else if(found !== undefined) {
25424 response = found;
25425 }
25426
25427 if(Array.isArray(returnedValue)) {
25428 returnedValue.push(response);
25429 }
25430 else if(returnedValue !== undefined) {
25431 returnedValue = [returnedValue, response];
25432 }
25433 else if(response !== undefined) {
25434 returnedValue = response;
25435 }
25436 return (found !== undefined)
25437 ? found
25438 : false
25439 ;
25440 }
25441 };
25442 module.initialize();
25443 })
25444 ;
25445 return (returnedValue !== undefined)
25446 ? returnedValue
25447 : this
25448 ;
25449};
25450
25451// Records if CSS transition is available
25452$.fn.transition.exists = {};
25453
25454$.fn.transition.settings = {
25455
25456 // module info
25457 name : 'Transition',
25458
25459 // hide all output from this component regardless of other settings
25460 silent : false,
25461
25462 // debug content outputted to console
25463 debug : false,
25464
25465 // verbose debug output
25466 verbose : false,
25467
25468 // performance data output
25469 performance : true,
25470
25471 // event namespace
25472 namespace : 'transition',
25473
25474 // delay between animations in group
25475 interval : 0,
25476
25477 // whether group animations should be reversed
25478 reverse : 'auto',
25479
25480 // animation callback event
25481 onStart : function() {},
25482 onComplete : function() {},
25483 onShow : function() {},
25484 onHide : function() {},
25485
25486 // whether timeout should be used to ensure callback fires in cases animationend does not
25487 useFailSafe : true,
25488
25489 // delay in ms for fail safe
25490 failSafeDelay : 100,
25491
25492 // whether EXACT animation can occur twice in a row
25493 allowRepeats : false,
25494
25495 // Override final display type on visible
25496 displayType : false,
25497
25498 // animation duration
25499 animation : 'fade',
25500 duration : false,
25501
25502 // new animations will occur after previous ones
25503 queue : true,
25504
25505// whether initially inline hidden objects should be skipped for transition
25506 skipInlineHidden: false,
25507
25508 metadata : {
25509 displayType: 'display'
25510 },
25511
25512 className : {
25513 animating : 'animating',
25514 disabled : 'disabled',
25515 hidden : 'hidden',
25516 inward : 'in',
25517 loading : 'loading',
25518 looping : 'looping',
25519 outward : 'out',
25520 transition : 'transition',
25521 visible : 'visible'
25522 },
25523
25524 // possible errors
25525 error: {
25526 noAnimation : 'Element is no longer attached to DOM. Unable to animate. Use silent setting to surpress this warning in production.',
25527 repeated : 'That animation is already occurring, cancelling repeated animation',
25528 method : 'The method you called is not defined',
25529 support : 'This browser does not support CSS animations'
25530 }
25531
25532};
25533
25534
25535})( jQuery, window, document );
25536
25537/*!
25538 * # Fomantic-UI 2.8.8 - API
25539 * http://github.com/fomantic/Fomantic-UI/
25540 *
25541 *
25542 * Released under the MIT license
25543 * http://opensource.org/licenses/MIT
25544 *
25545 */
25546
25547;(function ($, window, document, undefined) {
25548
25549'use strict';
25550
25551$.isWindow = $.isWindow || function(obj) {
25552 return obj != null && obj === obj.window;
25553};
25554
25555 window = (typeof window != 'undefined' && window.Math == Math)
25556 ? window
25557 : (typeof self != 'undefined' && self.Math == Math)
25558 ? self
25559 : Function('return this')()
25560;
25561
25562$.api = $.fn.api = function(parameters) {
25563
25564 var
25565 // use window context if none specified
25566 $allModules = $.isFunction(this)
25567 ? $(window)
25568 : $(this),
25569 moduleSelector = $allModules.selector || '',
25570 time = new Date().getTime(),
25571 performance = [],
25572
25573 query = arguments[0],
25574 methodInvoked = (typeof query == 'string'),
25575 queryArguments = [].slice.call(arguments, 1),
25576
25577 returnedValue
25578 ;
25579
25580 $allModules
25581 .each(function() {
25582 var
25583 settings = ( $.isPlainObject(parameters) )
25584 ? $.extend(true, {}, $.fn.api.settings, parameters)
25585 : $.extend({}, $.fn.api.settings),
25586
25587 // internal aliases
25588 namespace = settings.namespace,
25589 metadata = settings.metadata,
25590 selector = settings.selector,
25591 error = settings.error,
25592 className = settings.className,
25593
25594 // define namespaces for modules
25595 eventNamespace = '.' + namespace,
25596 moduleNamespace = 'module-' + namespace,
25597
25598 // element that creates request
25599 $module = $(this),
25600 $form = $module.closest(selector.form),
25601
25602 // context used for state
25603 $context = (settings.stateContext)
25604 ? $(settings.stateContext)
25605 : $module,
25606
25607 // request details
25608 ajaxSettings,
25609 requestSettings,
25610 url,
25611 data,
25612 requestStartTime,
25613
25614 // standard module
25615 element = this,
25616 context = $context[0],
25617 instance = $module.data(moduleNamespace),
25618 module
25619 ;
25620
25621 module = {
25622
25623 initialize: function() {
25624 if(!methodInvoked) {
25625 module.bind.events();
25626 }
25627 module.instantiate();
25628 },
25629
25630 instantiate: function() {
25631 module.verbose('Storing instance of module', module);
25632 instance = module;
25633 $module
25634 .data(moduleNamespace, instance)
25635 ;
25636 },
25637
25638 destroy: function() {
25639 module.verbose('Destroying previous module for', element);
25640 $module
25641 .removeData(moduleNamespace)
25642 .off(eventNamespace)
25643 ;
25644 },
25645
25646 bind: {
25647 events: function() {
25648 var
25649 triggerEvent = module.get.event()
25650 ;
25651 if( triggerEvent ) {
25652 module.verbose('Attaching API events to element', triggerEvent);
25653 $module
25654 .on(triggerEvent + eventNamespace, module.event.trigger)
25655 ;
25656 }
25657 else if(settings.on == 'now') {
25658 module.debug('Querying API endpoint immediately');
25659 module.query();
25660 }
25661 }
25662 },
25663
25664 decode: {
25665 json: function(response) {
25666 if(response !== undefined && typeof response == 'string') {
25667 try {
25668 response = JSON.parse(response);
25669 }
25670 catch(e) {
25671 // isnt json string
25672 }
25673 }
25674 return response;
25675 }
25676 },
25677
25678 read: {
25679 cachedResponse: function(url) {
25680 var
25681 response
25682 ;
25683 if(window.Storage === undefined) {
25684 module.error(error.noStorage);
25685 return;
25686 }
25687 response = sessionStorage.getItem(url);
25688 module.debug('Using cached response', url, response);
25689 response = module.decode.json(response);
25690 return response;
25691 }
25692 },
25693 write: {
25694 cachedResponse: function(url, response) {
25695 if(response && response === '') {
25696 module.debug('Response empty, not caching', response);
25697 return;
25698 }
25699 if(window.Storage === undefined) {
25700 module.error(error.noStorage);
25701 return;
25702 }
25703 if( $.isPlainObject(response) ) {
25704 response = JSON.stringify(response);
25705 }
25706 sessionStorage.setItem(url, response);
25707 module.verbose('Storing cached response for url', url, response);
25708 }
25709 },
25710
25711 query: function() {
25712
25713 if(module.is.disabled()) {
25714 module.debug('Element is disabled API request aborted');
25715 return;
25716 }
25717
25718 if(module.is.loading()) {
25719 if(settings.interruptRequests) {
25720 module.debug('Interrupting previous request');
25721 module.abort();
25722 }
25723 else {
25724 module.debug('Cancelling request, previous request is still pending');
25725 return;
25726 }
25727 }
25728
25729 // pass element metadata to url (value, text)
25730 if(settings.defaultData) {
25731 $.extend(true, settings.urlData, module.get.defaultData());
25732 }
25733
25734 // Add form content
25735 if(settings.serializeForm) {
25736 settings.data = module.add.formData(settings.data);
25737 }
25738
25739 // call beforesend and get any settings changes
25740 requestSettings = module.get.settings();
25741
25742 // check if before send cancelled request
25743 if(requestSettings === false) {
25744 module.cancelled = true;
25745 module.error(error.beforeSend);
25746 return;
25747 }
25748 else {
25749 module.cancelled = false;
25750 }
25751
25752 // get url
25753 url = module.get.templatedURL();
25754
25755 if(!url && !module.is.mocked()) {
25756 module.error(error.missingURL);
25757 return;
25758 }
25759
25760 // replace variables
25761 url = module.add.urlData( url );
25762 // missing url parameters
25763 if( !url && !module.is.mocked()) {
25764 return;
25765 }
25766
25767 requestSettings.url = settings.base + url;
25768
25769 // look for jQuery ajax parameters in settings
25770 ajaxSettings = $.extend(true, {}, settings, {
25771 type : settings.method || settings.type,
25772 data : data,
25773 url : settings.base + url,
25774 beforeSend : settings.beforeXHR,
25775 success : function() {},
25776 failure : function() {},
25777 complete : function() {}
25778 });
25779
25780 module.debug('Querying URL', ajaxSettings.url);
25781 module.verbose('Using AJAX settings', ajaxSettings);
25782 if(settings.cache === 'local' && module.read.cachedResponse(url)) {
25783 module.debug('Response returned from local cache');
25784 module.request = module.create.request();
25785 module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
25786 return;
25787 }
25788
25789 if( !settings.throttle ) {
25790 module.debug('Sending request', data, ajaxSettings.method);
25791 module.send.request();
25792 }
25793 else {
25794 if(!settings.throttleFirstRequest && !module.timer) {
25795 module.debug('Sending request', data, ajaxSettings.method);
25796 module.send.request();
25797 module.timer = setTimeout(function(){}, settings.throttle);
25798 }
25799 else {
25800 module.debug('Throttling request', settings.throttle);
25801 clearTimeout(module.timer);
25802 module.timer = setTimeout(function() {
25803 if(module.timer) {
25804 delete module.timer;
25805 }
25806 module.debug('Sending throttled request', data, ajaxSettings.method);
25807 module.send.request();
25808 }, settings.throttle);
25809 }
25810 }
25811
25812 },
25813
25814 should: {
25815 removeError: function() {
25816 return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
25817 }
25818 },
25819
25820 is: {
25821 disabled: function() {
25822 return ($module.filter(selector.disabled).length > 0);
25823 },
25824 expectingJSON: function() {
25825 return settings.dataType === 'json' || settings.dataType === 'jsonp';
25826 },
25827 form: function() {
25828 return $module.is('form') || $context.is('form');
25829 },
25830 mocked: function() {
25831 return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
25832 },
25833 input: function() {
25834 return $module.is('input');
25835 },
25836 loading: function() {
25837 return (module.request)
25838 ? (module.request.state() == 'pending')
25839 : false
25840 ;
25841 },
25842 abortedRequest: function(xhr) {
25843 if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
25844 module.verbose('XHR request determined to be aborted');
25845 return true;
25846 }
25847 else {
25848 module.verbose('XHR request was not aborted');
25849 return false;
25850 }
25851 },
25852 validResponse: function(response) {
25853 if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
25854 module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
25855 return true;
25856 }
25857 module.debug('Checking JSON returned success', settings.successTest, response);
25858 if( settings.successTest(response) ) {
25859 module.debug('Response passed success test', response);
25860 return true;
25861 }
25862 else {
25863 module.debug('Response failed success test', response);
25864 return false;
25865 }
25866 }
25867 },
25868
25869 was: {
25870 cancelled: function() {
25871 return (module.cancelled || false);
25872 },
25873 succesful: function() {
25874 module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
25875 return module.was.successful();
25876 },
25877 successful: function() {
25878 return (module.request && module.request.state() == 'resolved');
25879 },
25880 failure: function() {
25881 return (module.request && module.request.state() == 'rejected');
25882 },
25883 complete: function() {
25884 return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
25885 }
25886 },
25887
25888 add: {
25889 urlData: function(url, urlData) {
25890 var
25891 requiredVariables,
25892 optionalVariables
25893 ;
25894 if(url) {
25895 requiredVariables = url.match(settings.regExp.required);
25896 optionalVariables = url.match(settings.regExp.optional);
25897 urlData = urlData || settings.urlData;
25898 if(requiredVariables) {
25899 module.debug('Looking for required URL variables', requiredVariables);
25900 $.each(requiredVariables, function(index, templatedString) {
25901 var
25902 // allow legacy {$var} style
25903 variable = (templatedString.indexOf('$') !== -1)
25904 ? templatedString.substr(2, templatedString.length - 3)
25905 : templatedString.substr(1, templatedString.length - 2),
25906 value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
25907 ? urlData[variable]
25908 : ($module.data(variable) !== undefined)
25909 ? $module.data(variable)
25910 : ($context.data(variable) !== undefined)
25911 ? $context.data(variable)
25912 : urlData[variable]
25913 ;
25914 // remove value
25915 if(value === undefined) {
25916 module.error(error.requiredParameter, variable, url);
25917 url = false;
25918 return false;
25919 }
25920 else {
25921 module.verbose('Found required variable', variable, value);
25922 value = (settings.encodeParameters)
25923 ? module.get.urlEncodedValue(value)
25924 : value
25925 ;
25926 url = url.replace(templatedString, value);
25927 }
25928 });
25929 }
25930 if(optionalVariables) {
25931 module.debug('Looking for optional URL variables', requiredVariables);
25932 $.each(optionalVariables, function(index, templatedString) {
25933 var
25934 // allow legacy {/$var} style
25935 variable = (templatedString.indexOf('$') !== -1)
25936 ? templatedString.substr(3, templatedString.length - 4)
25937 : templatedString.substr(2, templatedString.length - 3),
25938 value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
25939 ? urlData[variable]
25940 : ($module.data(variable) !== undefined)
25941 ? $module.data(variable)
25942 : ($context.data(variable) !== undefined)
25943 ? $context.data(variable)
25944 : urlData[variable]
25945 ;
25946 // optional replacement
25947 if(value !== undefined) {
25948 module.verbose('Optional variable Found', variable, value);
25949 url = url.replace(templatedString, value);
25950 }
25951 else {
25952 module.verbose('Optional variable not found', variable);
25953 // remove preceding slash if set
25954 if(url.indexOf('/' + templatedString) !== -1) {
25955 url = url.replace('/' + templatedString, '');
25956 }
25957 else {
25958 url = url.replace(templatedString, '');
25959 }
25960 }
25961 });
25962 }
25963 }
25964 return url;
25965 },
25966 formData: function(data) {
25967 var
25968 canSerialize = ($.fn.serializeObject !== undefined),
25969 formData = (canSerialize)
25970 ? $form.serializeObject()
25971 : $form.serialize(),
25972 hasOtherData
25973 ;
25974 data = data || settings.data;
25975 hasOtherData = $.isPlainObject(data);
25976
25977 if(hasOtherData) {
25978 if(canSerialize) {
25979 module.debug('Extending existing data with form data', data, formData);
25980 data = $.extend(true, {}, data, formData);
25981 }
25982 else {
25983 module.error(error.missingSerialize);
25984 module.debug('Cant extend data. Replacing data with form data', data, formData);
25985 data = formData;
25986 }
25987 }
25988 else {
25989 module.debug('Adding form data', formData);
25990 data = formData;
25991 }
25992 return data;
25993 }
25994 },
25995
25996 send: {
25997 request: function() {
25998 module.set.loading();
25999 module.request = module.create.request();
26000 if( module.is.mocked() ) {
26001 module.mockedXHR = module.create.mockedXHR();
26002 }
26003 else {
26004 module.xhr = module.create.xhr();
26005 }
26006 settings.onRequest.call(context, module.request, module.xhr);
26007 }
26008 },
26009
26010 event: {
26011 trigger: function(event) {
26012 module.query();
26013 if(event.type == 'submit' || event.type == 'click') {
26014 event.preventDefault();
26015 }
26016 },
26017 xhr: {
26018 always: function() {
26019 // nothing special
26020 },
26021 done: function(response, textStatus, xhr) {
26022 var
26023 context = this,
26024 elapsedTime = (new Date().getTime() - requestStartTime),
26025 timeLeft = (settings.loadingDuration - elapsedTime),
26026 translatedResponse = ( $.isFunction(settings.onResponse) )
26027 ? module.is.expectingJSON() && !settings.rawResponse
26028 ? settings.onResponse.call(context, $.extend(true, {}, response))
26029 : settings.onResponse.call(context, response)
26030 : false
26031 ;
26032 timeLeft = (timeLeft > 0)
26033 ? timeLeft
26034 : 0
26035 ;
26036 if(translatedResponse) {
26037 module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
26038 response = translatedResponse;
26039 }
26040 if(timeLeft > 0) {
26041 module.debug('Response completed early delaying state change by', timeLeft);
26042 }
26043 setTimeout(function() {
26044 if( module.is.validResponse(response) ) {
26045 module.request.resolveWith(context, [response, xhr]);
26046 }
26047 else {
26048 module.request.rejectWith(context, [xhr, 'invalid']);
26049 }
26050 }, timeLeft);
26051 },
26052 fail: function(xhr, status, httpMessage) {
26053 var
26054 context = this,
26055 elapsedTime = (new Date().getTime() - requestStartTime),
26056 timeLeft = (settings.loadingDuration - elapsedTime)
26057 ;
26058 timeLeft = (timeLeft > 0)
26059 ? timeLeft
26060 : 0
26061 ;
26062 if(timeLeft > 0) {
26063 module.debug('Response completed early delaying state change by', timeLeft);
26064 }
26065 setTimeout(function() {
26066 if( module.is.abortedRequest(xhr) ) {
26067 module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
26068 }
26069 else {
26070 module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
26071 }
26072 }, timeLeft);
26073 }
26074 },
26075 request: {
26076 done: function(response, xhr) {
26077 module.debug('Successful API Response', response);
26078 if(settings.cache === 'local' && url) {
26079 module.write.cachedResponse(url, response);
26080 module.debug('Saving server response locally', module.cache);
26081 }
26082 settings.onSuccess.call(context, response, $module, xhr);
26083 },
26084 complete: function(firstParameter, secondParameter) {
26085 var
26086 xhr,
26087 response
26088 ;
26089 // have to guess callback parameters based on request success
26090 if( module.was.successful() ) {
26091 response = firstParameter;
26092 xhr = secondParameter;
26093 }
26094 else {
26095 xhr = firstParameter;
26096 response = module.get.responseFromXHR(xhr);
26097 }
26098 module.remove.loading();
26099 settings.onComplete.call(context, response, $module, xhr);
26100 },
26101 fail: function(xhr, status, httpMessage) {
26102 var
26103 // pull response from xhr if available
26104 response = module.get.responseFromXHR(xhr),
26105 errorMessage = module.get.errorFromRequest(response, status, httpMessage)
26106 ;
26107 if(status == 'aborted') {
26108 module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
26109 settings.onAbort.call(context, status, $module, xhr);
26110 return true;
26111 }
26112 else if(status == 'invalid') {
26113 module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
26114 }
26115 else if(status == 'error') {
26116 if(xhr !== undefined) {
26117 module.debug('XHR produced a server error', status, httpMessage);
26118 // make sure we have an error to display to console
26119 if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
26120 module.error(error.statusMessage + httpMessage, ajaxSettings.url);
26121 }
26122 settings.onError.call(context, errorMessage, $module, xhr);
26123 }
26124 }
26125
26126 if(settings.errorDuration && status !== 'aborted') {
26127 module.debug('Adding error state');
26128 module.set.error();
26129 if( module.should.removeError() ) {
26130 setTimeout(module.remove.error, settings.errorDuration);
26131 }
26132 }
26133 module.debug('API Request failed', errorMessage, xhr);
26134 settings.onFailure.call(context, response, $module, xhr);
26135 }
26136 }
26137 },
26138
26139 create: {
26140
26141 request: function() {
26142 // api request promise
26143 return $.Deferred()
26144 .always(module.event.request.complete)
26145 .done(module.event.request.done)
26146 .fail(module.event.request.fail)
26147 ;
26148 },
26149
26150 mockedXHR: function () {
26151 var
26152 // xhr does not simulate these properties of xhr but must return them
26153 textStatus = false,
26154 status = false,
26155 httpMessage = false,
26156 responder = settings.mockResponse || settings.response,
26157 asyncResponder = settings.mockResponseAsync || settings.responseAsync,
26158 asyncCallback,
26159 response,
26160 mockedXHR
26161 ;
26162
26163 mockedXHR = $.Deferred()
26164 .always(module.event.xhr.complete)
26165 .done(module.event.xhr.done)
26166 .fail(module.event.xhr.fail)
26167 ;
26168
26169 if(responder) {
26170 if( $.isFunction(responder) ) {
26171 module.debug('Using specified synchronous callback', responder);
26172 response = responder.call(context, requestSettings);
26173 }
26174 else {
26175 module.debug('Using settings specified response', responder);
26176 response = responder;
26177 }
26178 // simulating response
26179 mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
26180 }
26181 else if( $.isFunction(asyncResponder) ) {
26182 asyncCallback = function(response) {
26183 module.debug('Async callback returned response', response);
26184
26185 if(response) {
26186 mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
26187 }
26188 else {
26189 mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
26190 }
26191 };
26192 module.debug('Using specified async response callback', asyncResponder);
26193 asyncResponder.call(context, requestSettings, asyncCallback);
26194 }
26195 return mockedXHR;
26196 },
26197
26198 xhr: function() {
26199 var
26200 xhr
26201 ;
26202 // ajax request promise
26203 xhr = $.ajax(ajaxSettings)
26204 .always(module.event.xhr.always)
26205 .done(module.event.xhr.done)
26206 .fail(module.event.xhr.fail)
26207 ;
26208 module.verbose('Created server request', xhr, ajaxSettings);
26209 return xhr;
26210 }
26211 },
26212
26213 set: {
26214 error: function() {
26215 module.verbose('Adding error state to element', $context);
26216 $context.addClass(className.error);
26217 },
26218 loading: function() {
26219 module.verbose('Adding loading state to element', $context);
26220 $context.addClass(className.loading);
26221 requestStartTime = new Date().getTime();
26222 }
26223 },
26224
26225 remove: {
26226 error: function() {
26227 module.verbose('Removing error state from element', $context);
26228 $context.removeClass(className.error);
26229 },
26230 loading: function() {
26231 module.verbose('Removing loading state from element', $context);
26232 $context.removeClass(className.loading);
26233 }
26234 },
26235
26236 get: {
26237 responseFromXHR: function(xhr) {
26238 return $.isPlainObject(xhr)
26239 ? (module.is.expectingJSON())
26240 ? module.decode.json(xhr.responseText)
26241 : xhr.responseText
26242 : false
26243 ;
26244 },
26245 errorFromRequest: function(response, status, httpMessage) {
26246 return ($.isPlainObject(response) && response.error !== undefined)
26247 ? response.error // use json error message
26248 : (settings.error[status] !== undefined) // use server error message
26249 ? settings.error[status]
26250 : httpMessage
26251 ;
26252 },
26253 request: function() {
26254 return module.request || false;
26255 },
26256 xhr: function() {
26257 return module.xhr || false;
26258 },
26259 settings: function() {
26260 var
26261 runSettings
26262 ;
26263 runSettings = settings.beforeSend.call($module, settings);
26264 if(runSettings) {
26265 if(runSettings.success !== undefined) {
26266 module.debug('Legacy success callback detected', runSettings);
26267 module.error(error.legacyParameters, runSettings.success);
26268 runSettings.onSuccess = runSettings.success;
26269 }
26270 if(runSettings.failure !== undefined) {
26271 module.debug('Legacy failure callback detected', runSettings);
26272 module.error(error.legacyParameters, runSettings.failure);
26273 runSettings.onFailure = runSettings.failure;
26274 }
26275 if(runSettings.complete !== undefined) {
26276 module.debug('Legacy complete callback detected', runSettings);
26277 module.error(error.legacyParameters, runSettings.complete);
26278 runSettings.onComplete = runSettings.complete;
26279 }
26280 }
26281 if(runSettings === undefined) {
26282 module.error(error.noReturnedValue);
26283 }
26284 if(runSettings === false) {
26285 return runSettings;
26286 }
26287 return (runSettings !== undefined)
26288 ? $.extend(true, {}, runSettings)
26289 : $.extend(true, {}, settings)
26290 ;
26291 },
26292 urlEncodedValue: function(value) {
26293 var
26294 decodedValue = window.decodeURIComponent(value),
26295 encodedValue = window.encodeURIComponent(value),
26296 alreadyEncoded = (decodedValue !== value)
26297 ;
26298 if(alreadyEncoded) {
26299 module.debug('URL value is already encoded, avoiding double encoding', value);
26300 return value;
26301 }
26302 module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
26303 return encodedValue;
26304 },
26305 defaultData: function() {
26306 var
26307 data = {}
26308 ;
26309 if( !$.isWindow(element) ) {
26310 if( module.is.input() ) {
26311 data.value = $module.val();
26312 }
26313 else if( module.is.form() ) {
26314
26315 }
26316 else {
26317 data.text = $module.text();
26318 }
26319 }
26320 return data;
26321 },
26322 event: function() {
26323 if( $.isWindow(element) || settings.on == 'now' ) {
26324 module.debug('API called without element, no events attached');
26325 return false;
26326 }
26327 else if(settings.on == 'auto') {
26328 if( $module.is('input') ) {
26329 return (element.oninput !== undefined)
26330 ? 'input'
26331 : (element.onpropertychange !== undefined)
26332 ? 'propertychange'
26333 : 'keyup'
26334 ;
26335 }
26336 else if( $module.is('form') ) {
26337 return 'submit';
26338 }
26339 else {
26340 return 'click';
26341 }
26342 }
26343 else {
26344 return settings.on;
26345 }
26346 },
26347 templatedURL: function(action) {
26348 action = action || $module.data(metadata.action) || settings.action || false;
26349 url = $module.data(metadata.url) || settings.url || false;
26350 if(url) {
26351 module.debug('Using specified url', url);
26352 return url;
26353 }
26354 if(action) {
26355 module.debug('Looking up url for action', action, settings.api);
26356 if(settings.api[action] === undefined && !module.is.mocked()) {
26357 module.error(error.missingAction, settings.action, settings.api);
26358 return;
26359 }
26360 url = settings.api[action];
26361 }
26362 else if( module.is.form() ) {
26363 url = $module.attr('action') || $context.attr('action') || false;
26364 module.debug('No url or action specified, defaulting to form action', url);
26365 }
26366 return url;
26367 }
26368 },
26369
26370 abort: function() {
26371 var
26372 xhr = module.get.xhr()
26373 ;
26374 if( xhr && xhr.state() !== 'resolved') {
26375 module.debug('Cancelling API request');
26376 xhr.abort();
26377 }
26378 },
26379
26380 // reset state
26381 reset: function() {
26382 module.remove.error();
26383 module.remove.loading();
26384 },
26385
26386 setting: function(name, value) {
26387 module.debug('Changing setting', name, value);
26388 if( $.isPlainObject(name) ) {
26389 $.extend(true, settings, name);
26390 }
26391 else if(value !== undefined) {
26392 if($.isPlainObject(settings[name])) {
26393 $.extend(true, settings[name], value);
26394 }
26395 else {
26396 settings[name] = value;
26397 }
26398 }
26399 else {
26400 return settings[name];
26401 }
26402 },
26403 internal: function(name, value) {
26404 if( $.isPlainObject(name) ) {
26405 $.extend(true, module, name);
26406 }
26407 else if(value !== undefined) {
26408 module[name] = value;
26409 }
26410 else {
26411 return module[name];
26412 }
26413 },
26414 debug: function() {
26415 if(!settings.silent && settings.debug) {
26416 if(settings.performance) {
26417 module.performance.log(arguments);
26418 }
26419 else {
26420 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
26421 module.debug.apply(console, arguments);
26422 }
26423 }
26424 },
26425 verbose: function() {
26426 if(!settings.silent && settings.verbose && settings.debug) {
26427 if(settings.performance) {
26428 module.performance.log(arguments);
26429 }
26430 else {
26431 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
26432 module.verbose.apply(console, arguments);
26433 }
26434 }
26435 },
26436 error: function() {
26437 if(!settings.silent) {
26438 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
26439 module.error.apply(console, arguments);
26440 }
26441 },
26442 performance: {
26443 log: function(message) {
26444 var
26445 currentTime,
26446 executionTime,
26447 previousTime
26448 ;
26449 if(settings.performance) {
26450 currentTime = new Date().getTime();
26451 previousTime = time || currentTime;
26452 executionTime = currentTime - previousTime;
26453 time = currentTime;
26454 performance.push({
26455 'Name' : message[0],
26456 'Arguments' : [].slice.call(message, 1) || '',
26457 //'Element' : element,
26458 'Execution Time' : executionTime
26459 });
26460 }
26461 clearTimeout(module.performance.timer);
26462 module.performance.timer = setTimeout(module.performance.display, 500);
26463 },
26464 display: function() {
26465 var
26466 title = settings.name + ':',
26467 totalTime = 0
26468 ;
26469 time = false;
26470 clearTimeout(module.performance.timer);
26471 $.each(performance, function(index, data) {
26472 totalTime += data['Execution Time'];
26473 });
26474 title += ' ' + totalTime + 'ms';
26475 if(moduleSelector) {
26476 title += ' \'' + moduleSelector + '\'';
26477 }
26478 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
26479 console.groupCollapsed(title);
26480 if(console.table) {
26481 console.table(performance);
26482 }
26483 else {
26484 $.each(performance, function(index, data) {
26485 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
26486 });
26487 }
26488 console.groupEnd();
26489 }
26490 performance = [];
26491 }
26492 },
26493 invoke: function(query, passedArguments, context) {
26494 var
26495 object = instance,
26496 maxDepth,
26497 found,
26498 response
26499 ;
26500 passedArguments = passedArguments || queryArguments;
26501 context = element || context;
26502 if(typeof query == 'string' && object !== undefined) {
26503 query = query.split(/[\. ]/);
26504 maxDepth = query.length - 1;
26505 $.each(query, function(depth, value) {
26506 var camelCaseValue = (depth != maxDepth)
26507 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
26508 : query
26509 ;
26510 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
26511 object = object[camelCaseValue];
26512 }
26513 else if( object[camelCaseValue] !== undefined ) {
26514 found = object[camelCaseValue];
26515 return false;
26516 }
26517 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
26518 object = object[value];
26519 }
26520 else if( object[value] !== undefined ) {
26521 found = object[value];
26522 return false;
26523 }
26524 else {
26525 module.error(error.method, query);
26526 return false;
26527 }
26528 });
26529 }
26530 if ( $.isFunction( found ) ) {
26531 response = found.apply(context, passedArguments);
26532 }
26533 else if(found !== undefined) {
26534 response = found;
26535 }
26536 if(Array.isArray(returnedValue)) {
26537 returnedValue.push(response);
26538 }
26539 else if(returnedValue !== undefined) {
26540 returnedValue = [returnedValue, response];
26541 }
26542 else if(response !== undefined) {
26543 returnedValue = response;
26544 }
26545 return found;
26546 }
26547 };
26548
26549 if(methodInvoked) {
26550 if(instance === undefined) {
26551 module.initialize();
26552 }
26553 module.invoke(query);
26554 }
26555 else {
26556 if(instance !== undefined) {
26557 instance.invoke('destroy');
26558 }
26559 module.initialize();
26560 }
26561 })
26562 ;
26563
26564 return (returnedValue !== undefined)
26565 ? returnedValue
26566 : this
26567 ;
26568};
26569
26570$.api.settings = {
26571
26572 name : 'API',
26573 namespace : 'api',
26574
26575 debug : false,
26576 verbose : false,
26577 performance : true,
26578
26579 // object containing all templates endpoints
26580 api : {},
26581
26582 // whether to cache responses
26583 cache : true,
26584
26585 // whether new requests should abort previous requests
26586 interruptRequests : true,
26587
26588 // event binding
26589 on : 'auto',
26590
26591 // context for applying state classes
26592 stateContext : false,
26593
26594 // duration for loading state
26595 loadingDuration : 0,
26596
26597 // whether to hide errors after a period of time
26598 hideError : 'auto',
26599
26600 // duration for error state
26601 errorDuration : 2000,
26602
26603 // whether parameters should be encoded with encodeURIComponent
26604 encodeParameters : true,
26605
26606 // API action to use
26607 action : false,
26608
26609 // templated URL to use
26610 url : false,
26611
26612 // base URL to apply to all endpoints
26613 base : '',
26614
26615 // data that will
26616 urlData : {},
26617
26618 // whether to add default data to url data
26619 defaultData : true,
26620
26621 // whether to serialize closest form
26622 serializeForm : false,
26623
26624 // how long to wait before request should occur
26625 throttle : 0,
26626
26627 // whether to throttle first request or only repeated
26628 throttleFirstRequest : true,
26629
26630 // standard ajax settings
26631 method : 'get',
26632 data : {},
26633 dataType : 'json',
26634
26635 // mock response
26636 mockResponse : false,
26637 mockResponseAsync : false,
26638
26639 // aliases for mock
26640 response : false,
26641 responseAsync : false,
26642
26643// whether onResponse should work with response value without force converting into an object
26644 rawResponse : false,
26645
26646 // callbacks before request
26647 beforeSend : function(settings) { return settings; },
26648 beforeXHR : function(xhr) {},
26649 onRequest : function(promise, xhr) {},
26650
26651 // after request
26652 onResponse : false, // function(response) { },
26653
26654 // response was successful, if JSON passed validation
26655 onSuccess : function(response, $module) {},
26656
26657 // request finished without aborting
26658 onComplete : function(response, $module) {},
26659
26660 // failed JSON success test
26661 onFailure : function(response, $module) {},
26662
26663 // server error
26664 onError : function(errorMessage, $module) {},
26665
26666 // request aborted
26667 onAbort : function(errorMessage, $module) {},
26668
26669 successTest : false,
26670
26671 // errors
26672 error : {
26673 beforeSend : 'The before send function has aborted the request',
26674 error : 'There was an error with your request',
26675 exitConditions : 'API Request Aborted. Exit conditions met',
26676 JSONParse : 'JSON could not be parsed during error handling',
26677 legacyParameters : 'You are using legacy API success callback names',
26678 method : 'The method you called is not defined',
26679 missingAction : 'API action used but no url was defined',
26680 missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
26681 missingURL : 'No URL specified for api event',
26682 noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
26683 noStorage : 'Caching responses locally requires session storage',
26684 parseError : 'There was an error parsing your request',
26685 requiredParameter : 'Missing a required URL parameter: ',
26686 statusMessage : 'Server gave an error: ',
26687 timeout : 'Your request timed out'
26688 },
26689
26690 regExp : {
26691 required : /\{\$*[A-z0-9]+\}/g,
26692 optional : /\{\/\$*[A-z0-9]+\}/g,
26693 },
26694
26695 className: {
26696 loading : 'loading',
26697 error : 'error'
26698 },
26699
26700 selector: {
26701 disabled : '.disabled',
26702 form : 'form'
26703 },
26704
26705 metadata: {
26706 action : 'action',
26707 url : 'url'
26708 }
26709};
26710
26711
26712
26713})( jQuery, window, document );
26714
26715/*!
26716 * # Fomantic-UI 2.8.8 - State
26717 * http://github.com/fomantic/Fomantic-UI/
26718 *
26719 *
26720 * Released under the MIT license
26721 * http://opensource.org/licenses/MIT
26722 *
26723 */
26724
26725;(function ($, window, document, undefined) {
26726
26727"use strict";
26728
26729$.isFunction = $.isFunction || function(obj) {
26730 return typeof obj === "function" && typeof obj.nodeType !== "number";
26731};
26732
26733window = (typeof window != 'undefined' && window.Math == Math)
26734 ? window
26735 : (typeof self != 'undefined' && self.Math == Math)
26736 ? self
26737 : Function('return this')()
26738;
26739
26740$.fn.state = function(parameters) {
26741 var
26742 $allModules = $(this),
26743
26744 moduleSelector = $allModules.selector || '',
26745
26746 time = new Date().getTime(),
26747 performance = [],
26748
26749 query = arguments[0],
26750 methodInvoked = (typeof query == 'string'),
26751 queryArguments = [].slice.call(arguments, 1),
26752
26753 returnedValue
26754 ;
26755 $allModules
26756 .each(function() {
26757 var
26758 settings = ( $.isPlainObject(parameters) )
26759 ? $.extend(true, {}, $.fn.state.settings, parameters)
26760 : $.extend({}, $.fn.state.settings),
26761
26762 error = settings.error,
26763 metadata = settings.metadata,
26764 className = settings.className,
26765 namespace = settings.namespace,
26766 states = settings.states,
26767 text = settings.text,
26768
26769 eventNamespace = '.' + namespace,
26770 moduleNamespace = namespace + '-module',
26771
26772 $module = $(this),
26773
26774 element = this,
26775 instance = $module.data(moduleNamespace),
26776
26777 module
26778 ;
26779 module = {
26780
26781 initialize: function() {
26782 module.verbose('Initializing module');
26783
26784 // allow module to guess desired state based on element
26785 if(settings.automatic) {
26786 module.add.defaults();
26787 }
26788
26789 // bind events with delegated events
26790 if(settings.context && moduleSelector !== '') {
26791 $(settings.context)
26792 .on(moduleSelector, 'mouseenter' + eventNamespace, module.change.text)
26793 .on(moduleSelector, 'mouseleave' + eventNamespace, module.reset.text)
26794 .on(moduleSelector, 'click' + eventNamespace, module.toggle.state)
26795 ;
26796 }
26797 else {
26798 $module
26799 .on('mouseenter' + eventNamespace, module.change.text)
26800 .on('mouseleave' + eventNamespace, module.reset.text)
26801 .on('click' + eventNamespace, module.toggle.state)
26802 ;
26803 }
26804 module.instantiate();
26805 },
26806
26807 instantiate: function() {
26808 module.verbose('Storing instance of module', module);
26809 instance = module;
26810 $module
26811 .data(moduleNamespace, module)
26812 ;
26813 },
26814
26815 destroy: function() {
26816 module.verbose('Destroying previous module', instance);
26817 $module
26818 .off(eventNamespace)
26819 .removeData(moduleNamespace)
26820 ;
26821 },
26822
26823 refresh: function() {
26824 module.verbose('Refreshing selector cache');
26825 $module = $(element);
26826 },
26827
26828 add: {
26829 defaults: function() {
26830 var
26831 userStates = parameters && $.isPlainObject(parameters.states)
26832 ? parameters.states
26833 : {}
26834 ;
26835 $.each(settings.defaults, function(type, typeStates) {
26836 if( module.is[type] !== undefined && module.is[type]() ) {
26837 module.verbose('Adding default states', type, element);
26838 $.extend(settings.states, typeStates, userStates);
26839 }
26840 });
26841 }
26842 },
26843
26844 is: {
26845
26846 active: function() {
26847 return $module.hasClass(className.active);
26848 },
26849 loading: function() {
26850 return $module.hasClass(className.loading);
26851 },
26852 inactive: function() {
26853 return !( $module.hasClass(className.active) );
26854 },
26855 state: function(state) {
26856 if(className[state] === undefined) {
26857 return false;
26858 }
26859 return $module.hasClass( className[state] );
26860 },
26861
26862 enabled: function() {
26863 return !( $module.is(settings.filter.active) );
26864 },
26865 disabled: function() {
26866 return ( $module.is(settings.filter.active) );
26867 },
26868 textEnabled: function() {
26869 return !( $module.is(settings.filter.text) );
26870 },
26871
26872 // definitions for automatic type detection
26873 button: function() {
26874 return $module.is('.button:not(a, .submit)');
26875 },
26876 input: function() {
26877 return $module.is('input');
26878 },
26879 progress: function() {
26880 return $module.is('.ui.progress');
26881 }
26882 },
26883
26884 allow: function(state) {
26885 module.debug('Now allowing state', state);
26886 states[state] = true;
26887 },
26888 disallow: function(state) {
26889 module.debug('No longer allowing', state);
26890 states[state] = false;
26891 },
26892
26893 allows: function(state) {
26894 return states[state] || false;
26895 },
26896
26897 enable: function() {
26898 $module.removeClass(className.disabled);
26899 },
26900
26901 disable: function() {
26902 $module.addClass(className.disabled);
26903 },
26904
26905 setState: function(state) {
26906 if(module.allows(state)) {
26907 $module.addClass( className[state] );
26908 }
26909 },
26910
26911 removeState: function(state) {
26912 if(module.allows(state)) {
26913 $module.removeClass( className[state] );
26914 }
26915 },
26916
26917 toggle: {
26918 state: function() {
26919 var
26920 apiRequest,
26921 requestCancelled
26922 ;
26923 if( module.allows('active') && module.is.enabled() ) {
26924 module.refresh();
26925 if($.fn.api !== undefined) {
26926 apiRequest = $module.api('get request');
26927 requestCancelled = $module.api('was cancelled');
26928 if( requestCancelled ) {
26929 module.debug('API Request cancelled by beforesend');
26930 settings.activateTest = function(){ return false; };
26931 settings.deactivateTest = function(){ return false; };
26932 }
26933 else if(apiRequest) {
26934 module.listenTo(apiRequest);
26935 return;
26936 }
26937 }
26938 module.change.state();
26939 }
26940 }
26941 },
26942
26943 listenTo: function(apiRequest) {
26944 module.debug('API request detected, waiting for state signal', apiRequest);
26945 if(apiRequest) {
26946 if(text.loading) {
26947 module.update.text(text.loading);
26948 }
26949 $.when(apiRequest)
26950 .then(function() {
26951 if(apiRequest.state() == 'resolved') {
26952 module.debug('API request succeeded');
26953 settings.activateTest = function(){ return true; };
26954 settings.deactivateTest = function(){ return true; };
26955 }
26956 else {
26957 module.debug('API request failed');
26958 settings.activateTest = function(){ return false; };
26959 settings.deactivateTest = function(){ return false; };
26960 }
26961 module.change.state();
26962 })
26963 ;
26964 }
26965 },
26966
26967 // checks whether active/inactive state can be given
26968 change: {
26969
26970 state: function() {
26971 module.debug('Determining state change direction');
26972 // inactive to active change
26973 if( module.is.inactive() ) {
26974 module.activate();
26975 }
26976 else {
26977 module.deactivate();
26978 }
26979 if(settings.sync) {
26980 module.sync();
26981 }
26982 settings.onChange.call(element);
26983 },
26984
26985 text: function() {
26986 if( module.is.textEnabled() ) {
26987 if(module.is.disabled() ) {
26988 module.verbose('Changing text to disabled text', text.hover);
26989 module.update.text(text.disabled);
26990 }
26991 else if( module.is.active() ) {
26992 if(text.hover) {
26993 module.verbose('Changing text to hover text', text.hover);
26994 module.update.text(text.hover);
26995 }
26996 else if(text.deactivate) {
26997 module.verbose('Changing text to deactivating text', text.deactivate);
26998 module.update.text(text.deactivate);
26999 }
27000 }
27001 else {
27002 if(text.hover) {
27003 module.verbose('Changing text to hover text', text.hover);
27004 module.update.text(text.hover);
27005 }
27006 else if(text.activate){
27007 module.verbose('Changing text to activating text', text.activate);
27008 module.update.text(text.activate);
27009 }
27010 }
27011 }
27012 }
27013
27014 },
27015
27016 activate: function() {
27017 if( settings.activateTest.call(element) ) {
27018 module.debug('Setting state to active');
27019 $module
27020 .addClass(className.active)
27021 ;
27022 module.update.text(text.active);
27023 settings.onActivate.call(element);
27024 }
27025 },
27026
27027 deactivate: function() {
27028 if( settings.deactivateTest.call(element) ) {
27029 module.debug('Setting state to inactive');
27030 $module
27031 .removeClass(className.active)
27032 ;
27033 module.update.text(text.inactive);
27034 settings.onDeactivate.call(element);
27035 }
27036 },
27037
27038 sync: function() {
27039 module.verbose('Syncing other buttons to current state');
27040 if( module.is.active() ) {
27041 $allModules
27042 .not($module)
27043 .state('activate');
27044 }
27045 else {
27046 $allModules
27047 .not($module)
27048 .state('deactivate')
27049 ;
27050 }
27051 },
27052
27053 get: {
27054 text: function() {
27055 return (settings.selector.text)
27056 ? $module.find(settings.selector.text).text()
27057 : $module.html()
27058 ;
27059 },
27060 textFor: function(state) {
27061 return text[state] || false;
27062 }
27063 },
27064
27065 flash: {
27066 text: function(text, duration, callback) {
27067 var
27068 previousText = module.get.text()
27069 ;
27070 module.debug('Flashing text message', text, duration);
27071 text = text || settings.text.flash;
27072 duration = duration || settings.flashDuration;
27073 callback = callback || function() {};
27074 module.update.text(text);
27075 setTimeout(function(){
27076 module.update.text(previousText);
27077 callback.call(element);
27078 }, duration);
27079 }
27080 },
27081
27082 reset: {
27083 // on mouseout sets text to previous value
27084 text: function() {
27085 var
27086 activeText = text.active || $module.data(metadata.storedText),
27087 inactiveText = text.inactive || $module.data(metadata.storedText)
27088 ;
27089 if( module.is.textEnabled() ) {
27090 if( module.is.active() && activeText) {
27091 module.verbose('Resetting active text', activeText);
27092 module.update.text(activeText);
27093 }
27094 else if(inactiveText) {
27095 module.verbose('Resetting inactive text', activeText);
27096 module.update.text(inactiveText);
27097 }
27098 }
27099 }
27100 },
27101
27102 update: {
27103 text: function(text) {
27104 var
27105 currentText = module.get.text()
27106 ;
27107 if(text && text !== currentText) {
27108 module.debug('Updating text', text);
27109 if(settings.selector.text) {
27110 $module
27111 .data(metadata.storedText, text)
27112 .find(settings.selector.text)
27113 .text(text)
27114 ;
27115 }
27116 else {
27117 $module
27118 .data(metadata.storedText, text)
27119 .html(text)
27120 ;
27121 }
27122 }
27123 else {
27124 module.debug('Text is already set, ignoring update', text);
27125 }
27126 }
27127 },
27128
27129 setting: function(name, value) {
27130 module.debug('Changing setting', name, value);
27131 if( $.isPlainObject(name) ) {
27132 $.extend(true, settings, name);
27133 }
27134 else if(value !== undefined) {
27135 if($.isPlainObject(settings[name])) {
27136 $.extend(true, settings[name], value);
27137 }
27138 else {
27139 settings[name] = value;
27140 }
27141 }
27142 else {
27143 return settings[name];
27144 }
27145 },
27146 internal: function(name, value) {
27147 if( $.isPlainObject(name) ) {
27148 $.extend(true, module, name);
27149 }
27150 else if(value !== undefined) {
27151 module[name] = value;
27152 }
27153 else {
27154 return module[name];
27155 }
27156 },
27157 debug: function() {
27158 if(!settings.silent && settings.debug) {
27159 if(settings.performance) {
27160 module.performance.log(arguments);
27161 }
27162 else {
27163 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
27164 module.debug.apply(console, arguments);
27165 }
27166 }
27167 },
27168 verbose: function() {
27169 if(!settings.silent && settings.verbose && settings.debug) {
27170 if(settings.performance) {
27171 module.performance.log(arguments);
27172 }
27173 else {
27174 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
27175 module.verbose.apply(console, arguments);
27176 }
27177 }
27178 },
27179 error: function() {
27180 if(!settings.silent) {
27181 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
27182 module.error.apply(console, arguments);
27183 }
27184 },
27185 performance: {
27186 log: function(message) {
27187 var
27188 currentTime,
27189 executionTime,
27190 previousTime
27191 ;
27192 if(settings.performance) {
27193 currentTime = new Date().getTime();
27194 previousTime = time || currentTime;
27195 executionTime = currentTime - previousTime;
27196 time = currentTime;
27197 performance.push({
27198 'Name' : message[0],
27199 'Arguments' : [].slice.call(message, 1) || '',
27200 'Element' : element,
27201 'Execution Time' : executionTime
27202 });
27203 }
27204 clearTimeout(module.performance.timer);
27205 module.performance.timer = setTimeout(module.performance.display, 500);
27206 },
27207 display: function() {
27208 var
27209 title = settings.name + ':',
27210 totalTime = 0
27211 ;
27212 time = false;
27213 clearTimeout(module.performance.timer);
27214 $.each(performance, function(index, data) {
27215 totalTime += data['Execution Time'];
27216 });
27217 title += ' ' + totalTime + 'ms';
27218 if(moduleSelector) {
27219 title += ' \'' + moduleSelector + '\'';
27220 }
27221 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
27222 console.groupCollapsed(title);
27223 if(console.table) {
27224 console.table(performance);
27225 }
27226 else {
27227 $.each(performance, function(index, data) {
27228 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
27229 });
27230 }
27231 console.groupEnd();
27232 }
27233 performance = [];
27234 }
27235 },
27236 invoke: function(query, passedArguments, context) {
27237 var
27238 object = instance,
27239 maxDepth,
27240 found,
27241 response
27242 ;
27243 passedArguments = passedArguments || queryArguments;
27244 context = element || context;
27245 if(typeof query == 'string' && object !== undefined) {
27246 query = query.split(/[\. ]/);
27247 maxDepth = query.length - 1;
27248 $.each(query, function(depth, value) {
27249 var camelCaseValue = (depth != maxDepth)
27250 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
27251 : query
27252 ;
27253 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
27254 object = object[camelCaseValue];
27255 }
27256 else if( object[camelCaseValue] !== undefined ) {
27257 found = object[camelCaseValue];
27258 return false;
27259 }
27260 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
27261 object = object[value];
27262 }
27263 else if( object[value] !== undefined ) {
27264 found = object[value];
27265 return false;
27266 }
27267 else {
27268 module.error(error.method, query);
27269 return false;
27270 }
27271 });
27272 }
27273 if ( $.isFunction( found ) ) {
27274 response = found.apply(context, passedArguments);
27275 }
27276 else if(found !== undefined) {
27277 response = found;
27278 }
27279 if(Array.isArray(returnedValue)) {
27280 returnedValue.push(response);
27281 }
27282 else if(returnedValue !== undefined) {
27283 returnedValue = [returnedValue, response];
27284 }
27285 else if(response !== undefined) {
27286 returnedValue = response;
27287 }
27288 return found;
27289 }
27290 };
27291
27292 if(methodInvoked) {
27293 if(instance === undefined) {
27294 module.initialize();
27295 }
27296 module.invoke(query);
27297 }
27298 else {
27299 if(instance !== undefined) {
27300 instance.invoke('destroy');
27301 }
27302 module.initialize();
27303 }
27304 })
27305 ;
27306
27307 return (returnedValue !== undefined)
27308 ? returnedValue
27309 : this
27310 ;
27311};
27312
27313$.fn.state.settings = {
27314
27315 // module info
27316 name : 'State',
27317
27318 // debug output
27319 debug : false,
27320
27321 // verbose debug output
27322 verbose : false,
27323
27324 // namespace for events
27325 namespace : 'state',
27326
27327 // debug data includes performance
27328 performance : true,
27329
27330 // callback occurs on state change
27331 onActivate : function() {},
27332 onDeactivate : function() {},
27333 onChange : function() {},
27334
27335 // state test functions
27336 activateTest : function() { return true; },
27337 deactivateTest : function() { return true; },
27338
27339 // whether to automatically map default states
27340 automatic : true,
27341
27342 // activate / deactivate changes all elements instantiated at same time
27343 sync : false,
27344
27345 // default flash text duration, used for temporarily changing text of an element
27346 flashDuration : 1000,
27347
27348 // selector filter
27349 filter : {
27350 text : '.loading, .disabled',
27351 active : '.disabled'
27352 },
27353
27354 context : false,
27355
27356 // error
27357 error: {
27358 beforeSend : 'The before send function has cancelled state change',
27359 method : 'The method you called is not defined.'
27360 },
27361
27362 // metadata
27363 metadata: {
27364 promise : 'promise',
27365 storedText : 'stored-text'
27366 },
27367
27368 // change class on state
27369 className: {
27370 active : 'active',
27371 disabled : 'disabled',
27372 error : 'error',
27373 loading : 'loading',
27374 success : 'success',
27375 warning : 'warning'
27376 },
27377
27378 selector: {
27379 // selector for text node
27380 text: false
27381 },
27382
27383 defaults : {
27384 input: {
27385 disabled : true,
27386 loading : true,
27387 active : true
27388 },
27389 button: {
27390 disabled : true,
27391 loading : true,
27392 active : true,
27393 },
27394 progress: {
27395 active : true,
27396 success : true,
27397 warning : true,
27398 error : true
27399 }
27400 },
27401
27402 states : {
27403 active : true,
27404 disabled : true,
27405 error : true,
27406 loading : true,
27407 success : true,
27408 warning : true
27409 },
27410
27411 text : {
27412 disabled : false,
27413 flash : false,
27414 hover : false,
27415 active : false,
27416 inactive : false,
27417 activate : false,
27418 deactivate : false
27419 }
27420
27421};
27422
27423
27424
27425})( jQuery, window, document );
27426
27427/*!
27428 * # Fomantic-UI 2.8.8 - Visibility
27429 * http://github.com/fomantic/Fomantic-UI/
27430 *
27431 *
27432 * Released under the MIT license
27433 * http://opensource.org/licenses/MIT
27434 *
27435 */
27436
27437;(function ($, window, document, undefined) {
27438
27439'use strict';
27440
27441$.isFunction = $.isFunction || function(obj) {
27442 return typeof obj === "function" && typeof obj.nodeType !== "number";
27443};
27444
27445window = (typeof window != 'undefined' && window.Math == Math)
27446 ? window
27447 : (typeof self != 'undefined' && self.Math == Math)
27448 ? self
27449 : Function('return this')()
27450;
27451
27452$.fn.visibility = function(parameters) {
27453 var
27454 $allModules = $(this),
27455 moduleSelector = $allModules.selector || '',
27456
27457 time = new Date().getTime(),
27458 performance = [],
27459
27460 query = arguments[0],
27461 methodInvoked = (typeof query == 'string'),
27462 queryArguments = [].slice.call(arguments, 1),
27463 returnedValue,
27464
27465 moduleCount = $allModules.length,
27466 loadedCount = 0
27467 ;
27468
27469 $allModules
27470 .each(function() {
27471 var
27472 settings = ( $.isPlainObject(parameters) )
27473 ? $.extend(true, {}, $.fn.visibility.settings, parameters)
27474 : $.extend({}, $.fn.visibility.settings),
27475
27476 className = settings.className,
27477 namespace = settings.namespace,
27478 error = settings.error,
27479 metadata = settings.metadata,
27480
27481 eventNamespace = '.' + namespace,
27482 moduleNamespace = 'module-' + namespace,
27483
27484 $window = $(window),
27485
27486 $module = $(this),
27487 $context = $(settings.context),
27488
27489 $placeholder,
27490
27491 instance = $module.data(moduleNamespace),
27492
27493 requestAnimationFrame = window.requestAnimationFrame
27494 || window.mozRequestAnimationFrame
27495 || window.webkitRequestAnimationFrame
27496 || window.msRequestAnimationFrame
27497 || function(callback) { setTimeout(callback, 0); },
27498
27499 element = this,
27500 disabled = false,
27501
27502 contextObserver,
27503 observer,
27504 module
27505 ;
27506
27507 module = {
27508
27509 initialize: function() {
27510 module.debug('Initializing', settings);
27511
27512 module.setup.cache();
27513
27514 if( module.should.trackChanges() ) {
27515
27516 if(settings.type == 'image') {
27517 module.setup.image();
27518 }
27519 if(settings.type == 'fixed') {
27520 module.setup.fixed();
27521 }
27522
27523 if(settings.observeChanges) {
27524 module.observeChanges();
27525 }
27526 module.bind.events();
27527 }
27528
27529 module.save.position();
27530 if( !module.is.visible() ) {
27531 module.error(error.visible, $module);
27532 }
27533
27534 if(settings.initialCheck) {
27535 module.checkVisibility();
27536 }
27537 module.instantiate();
27538 },
27539
27540 instantiate: function() {
27541 module.debug('Storing instance', module);
27542 $module
27543 .data(moduleNamespace, module)
27544 ;
27545 instance = module;
27546 },
27547
27548 destroy: function() {
27549 module.verbose('Destroying previous module');
27550 if(observer) {
27551 observer.disconnect();
27552 }
27553 if(contextObserver) {
27554 contextObserver.disconnect();
27555 }
27556 $window
27557 .off('load' + eventNamespace, module.event.load)
27558 .off('resize' + eventNamespace, module.event.resize)
27559 ;
27560 $context
27561 .off('scroll' + eventNamespace, module.event.scroll)
27562 .off('scrollchange' + eventNamespace, module.event.scrollchange)
27563 ;
27564 if(settings.type == 'fixed') {
27565 module.resetFixed();
27566 module.remove.placeholder();
27567 }
27568 $module
27569 .off(eventNamespace)
27570 .removeData(moduleNamespace)
27571 ;
27572 },
27573
27574 observeChanges: function() {
27575 if('MutationObserver' in window) {
27576 contextObserver = new MutationObserver(module.event.contextChanged);
27577 observer = new MutationObserver(module.event.changed);
27578 contextObserver.observe(document, {
27579 childList : true,
27580 subtree : true
27581 });
27582 observer.observe(element, {
27583 childList : true,
27584 subtree : true
27585 });
27586 module.debug('Setting up mutation observer', observer);
27587 }
27588 },
27589
27590 bind: {
27591 events: function() {
27592 module.verbose('Binding visibility events to scroll and resize');
27593 if(settings.refreshOnLoad) {
27594 $window
27595 .on('load' + eventNamespace, module.event.load)
27596 ;
27597 }
27598 $window
27599 .on('resize' + eventNamespace, module.event.resize)
27600 ;
27601 // pub/sub pattern
27602 $context
27603 .off('scroll' + eventNamespace)
27604 .on('scroll' + eventNamespace, module.event.scroll)
27605 .on('scrollchange' + eventNamespace, module.event.scrollchange)
27606 ;
27607 }
27608 },
27609
27610 event: {
27611 changed: function(mutations) {
27612 module.verbose('DOM tree modified, updating visibility calculations');
27613 module.timer = setTimeout(function() {
27614 module.verbose('DOM tree modified, updating sticky menu');
27615 module.refresh();
27616 }, 100);
27617 },
27618 contextChanged: function(mutations) {
27619 [].forEach.call(mutations, function(mutation) {
27620 if(mutation.removedNodes) {
27621 [].forEach.call(mutation.removedNodes, function(node) {
27622 if(node == element || $(node).find(element).length > 0) {
27623 module.debug('Element removed from DOM, tearing down events');
27624 module.destroy();
27625 }
27626 });
27627 }
27628 });
27629 },
27630 resize: function() {
27631 module.debug('Window resized');
27632 if(settings.refreshOnResize) {
27633 requestAnimationFrame(module.refresh);
27634 }
27635 },
27636 load: function() {
27637 module.debug('Page finished loading');
27638 requestAnimationFrame(module.refresh);
27639 },
27640 // publishes scrollchange event on one scroll
27641 scroll: function() {
27642 if(settings.throttle) {
27643 clearTimeout(module.timer);
27644 module.timer = setTimeout(function() {
27645 $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
27646 }, settings.throttle);
27647 }
27648 else {
27649 requestAnimationFrame(function() {
27650 $context.triggerHandler('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
27651 });
27652 }
27653 },
27654 // subscribes to scrollchange
27655 scrollchange: function(event, scrollPosition) {
27656 module.checkVisibility(scrollPosition);
27657 },
27658 },
27659
27660 precache: function(images, callback) {
27661 if (!(images instanceof Array)) {
27662 images = [images];
27663 }
27664 var
27665 imagesLength = images.length,
27666 loadedCounter = 0,
27667 cache = [],
27668 cacheImage = document.createElement('img'),
27669 handleLoad = function() {
27670 loadedCounter++;
27671 if (loadedCounter >= images.length) {
27672 if ($.isFunction(callback)) {
27673 callback();
27674 }
27675 }
27676 }
27677 ;
27678 while (imagesLength--) {
27679 cacheImage = document.createElement('img');
27680 cacheImage.onload = handleLoad;
27681 cacheImage.onerror = handleLoad;
27682 cacheImage.src = images[imagesLength];
27683 cache.push(cacheImage);
27684 }
27685 },
27686
27687 enableCallbacks: function() {
27688 module.debug('Allowing callbacks to occur');
27689 disabled = false;
27690 },
27691
27692 disableCallbacks: function() {
27693 module.debug('Disabling all callbacks temporarily');
27694 disabled = true;
27695 },
27696
27697 should: {
27698 trackChanges: function() {
27699 if(methodInvoked) {
27700 module.debug('One time query, no need to bind events');
27701 return false;
27702 }
27703 module.debug('Callbacks being attached');
27704 return true;
27705 }
27706 },
27707
27708 setup: {
27709 cache: function() {
27710 module.cache = {
27711 occurred : {},
27712 screen : {},
27713 element : {},
27714 };
27715 },
27716 image: function() {
27717 var
27718 src = $module.data(metadata.src)
27719 ;
27720 if(src) {
27721 module.verbose('Lazy loading image', src);
27722 settings.once = true;
27723 settings.observeChanges = false;
27724
27725 // show when top visible
27726 settings.onOnScreen = function() {
27727 module.debug('Image on screen', element);
27728 module.precache(src, function() {
27729 module.set.image(src, function() {
27730 loadedCount++;
27731 if(loadedCount == moduleCount) {
27732 settings.onAllLoaded.call(this);
27733 }
27734 settings.onLoad.call(this);
27735 });
27736 });
27737 };
27738 }
27739 },
27740 fixed: function() {
27741 module.debug('Setting up fixed');
27742 settings.once = false;
27743 settings.observeChanges = false;
27744 settings.initialCheck = true;
27745 settings.refreshOnLoad = true;
27746 if(!parameters.transition) {
27747 settings.transition = false;
27748 }
27749 module.create.placeholder();
27750 module.debug('Added placeholder', $placeholder);
27751 settings.onTopPassed = function() {
27752 module.debug('Element passed, adding fixed position', $module);
27753 module.show.placeholder();
27754 module.set.fixed();
27755 if(settings.transition) {
27756 if($.fn.transition !== undefined) {
27757 $module.transition(settings.transition, settings.duration);
27758 }
27759 }
27760 };
27761 settings.onTopPassedReverse = function() {
27762 module.debug('Element returned to position, removing fixed', $module);
27763 module.hide.placeholder();
27764 module.remove.fixed();
27765 };
27766 }
27767 },
27768
27769 create: {
27770 placeholder: function() {
27771 module.verbose('Creating fixed position placeholder');
27772 $placeholder = $module
27773 .clone(false)
27774 .css('display', 'none')
27775 .addClass(className.placeholder)
27776 .insertAfter($module)
27777 ;
27778 }
27779 },
27780
27781 show: {
27782 placeholder: function() {
27783 module.verbose('Showing placeholder');
27784 $placeholder
27785 .css('display', 'block')
27786 .css('visibility', 'hidden')
27787 ;
27788 }
27789 },
27790 hide: {
27791 placeholder: function() {
27792 module.verbose('Hiding placeholder');
27793 $placeholder
27794 .css('display', 'none')
27795 .css('visibility', '')
27796 ;
27797 }
27798 },
27799
27800 set: {
27801 fixed: function() {
27802 module.verbose('Setting element to fixed position');
27803 $module
27804 .addClass(className.fixed)
27805 .css({
27806 position : 'fixed',
27807 top : settings.offset + 'px',
27808 left : 'auto',
27809 zIndex : settings.zIndex
27810 })
27811 ;
27812 settings.onFixed.call(element);
27813 },
27814 image: function(src, callback) {
27815 $module
27816 .attr('src', src)
27817 ;
27818 if(settings.transition) {
27819 if( $.fn.transition !== undefined) {
27820 if($module.hasClass(className.visible)) {
27821 module.debug('Transition already occurred on this image, skipping animation');
27822 return;
27823 }
27824 $module.transition(settings.transition, settings.duration, callback);
27825 }
27826 else {
27827 $module.fadeIn(settings.duration, callback);
27828 }
27829 }
27830 else {
27831 $module.show();
27832 }
27833 }
27834 },
27835
27836 is: {
27837 onScreen: function() {
27838 var
27839 calculations = module.get.elementCalculations()
27840 ;
27841 return calculations.onScreen;
27842 },
27843 offScreen: function() {
27844 var
27845 calculations = module.get.elementCalculations()
27846 ;
27847 return calculations.offScreen;
27848 },
27849 visible: function() {
27850 if(module.cache && module.cache.element) {
27851 return !(module.cache.element.width === 0 && module.cache.element.offset.top === 0);
27852 }
27853 return false;
27854 },
27855 verticallyScrollableContext: function() {
27856 var
27857 overflowY = ($context.get(0) !== window)
27858 ? $context.css('overflow-y')
27859 : false
27860 ;
27861 return (overflowY == 'auto' || overflowY == 'scroll');
27862 },
27863 horizontallyScrollableContext: function() {
27864 var
27865 overflowX = ($context.get(0) !== window)
27866 ? $context.css('overflow-x')
27867 : false
27868 ;
27869 return (overflowX == 'auto' || overflowX == 'scroll');
27870 }
27871 },
27872
27873 refresh: function() {
27874 module.debug('Refreshing constants (width/height)');
27875 if(settings.type == 'fixed') {
27876 module.resetFixed();
27877 }
27878 module.reset();
27879 module.save.position();
27880 if(settings.checkOnRefresh) {
27881 module.checkVisibility();
27882 }
27883 settings.onRefresh.call(element);
27884 },
27885
27886 resetFixed: function () {
27887 module.remove.fixed();
27888 module.remove.occurred();
27889 },
27890
27891 reset: function() {
27892 module.verbose('Resetting all cached values');
27893 if( $.isPlainObject(module.cache) ) {
27894 module.cache.screen = {};
27895 module.cache.element = {};
27896 }
27897 },
27898
27899 checkVisibility: function(scroll) {
27900 module.verbose('Checking visibility of element', module.cache.element);
27901
27902 if( !disabled && module.is.visible() ) {
27903
27904 // save scroll position
27905 module.save.scroll(scroll);
27906
27907 // update calculations derived from scroll
27908 module.save.calculations();
27909
27910 // percentage
27911 module.passed();
27912
27913 // reverse (must be first)
27914 module.passingReverse();
27915 module.topVisibleReverse();
27916 module.bottomVisibleReverse();
27917 module.topPassedReverse();
27918 module.bottomPassedReverse();
27919
27920 // one time
27921 module.onScreen();
27922 module.offScreen();
27923 module.passing();
27924 module.topVisible();
27925 module.bottomVisible();
27926 module.topPassed();
27927 module.bottomPassed();
27928
27929 // on update callback
27930 if(settings.onUpdate) {
27931 settings.onUpdate.call(element, module.get.elementCalculations());
27932 }
27933 }
27934 },
27935
27936 passed: function(amount, newCallback) {
27937 var
27938 calculations = module.get.elementCalculations()
27939 ;
27940 // assign callback
27941 if(amount && newCallback) {
27942 settings.onPassed[amount] = newCallback;
27943 }
27944 else if(amount !== undefined) {
27945 return (module.get.pixelsPassed(amount) > calculations.pixelsPassed);
27946 }
27947 else if(calculations.passing) {
27948 $.each(settings.onPassed, function(amount, callback) {
27949 if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
27950 module.execute(callback, amount);
27951 }
27952 else if(!settings.once) {
27953 module.remove.occurred(callback);
27954 }
27955 });
27956 }
27957 },
27958
27959 onScreen: function(newCallback) {
27960 var
27961 calculations = module.get.elementCalculations(),
27962 callback = newCallback || settings.onOnScreen,
27963 callbackName = 'onScreen'
27964 ;
27965 if(newCallback) {
27966 module.debug('Adding callback for onScreen', newCallback);
27967 settings.onOnScreen = newCallback;
27968 }
27969 if(calculations.onScreen) {
27970 module.execute(callback, callbackName);
27971 }
27972 else if(!settings.once) {
27973 module.remove.occurred(callbackName);
27974 }
27975 if(newCallback !== undefined) {
27976 return calculations.onOnScreen;
27977 }
27978 },
27979
27980 offScreen: function(newCallback) {
27981 var
27982 calculations = module.get.elementCalculations(),
27983 callback = newCallback || settings.onOffScreen,
27984 callbackName = 'offScreen'
27985 ;
27986 if(newCallback) {
27987 module.debug('Adding callback for offScreen', newCallback);
27988 settings.onOffScreen = newCallback;
27989 }
27990 if(calculations.offScreen) {
27991 module.execute(callback, callbackName);
27992 }
27993 else if(!settings.once) {
27994 module.remove.occurred(callbackName);
27995 }
27996 if(newCallback !== undefined) {
27997 return calculations.onOffScreen;
27998 }
27999 },
28000
28001 passing: function(newCallback) {
28002 var
28003 calculations = module.get.elementCalculations(),
28004 callback = newCallback || settings.onPassing,
28005 callbackName = 'passing'
28006 ;
28007 if(newCallback) {
28008 module.debug('Adding callback for passing', newCallback);
28009 settings.onPassing = newCallback;
28010 }
28011 if(calculations.passing) {
28012 module.execute(callback, callbackName);
28013 }
28014 else if(!settings.once) {
28015 module.remove.occurred(callbackName);
28016 }
28017 if(newCallback !== undefined) {
28018 return calculations.passing;
28019 }
28020 },
28021
28022
28023 topVisible: function(newCallback) {
28024 var
28025 calculations = module.get.elementCalculations(),
28026 callback = newCallback || settings.onTopVisible,
28027 callbackName = 'topVisible'
28028 ;
28029 if(newCallback) {
28030 module.debug('Adding callback for top visible', newCallback);
28031 settings.onTopVisible = newCallback;
28032 }
28033 if(calculations.topVisible) {
28034 module.execute(callback, callbackName);
28035 }
28036 else if(!settings.once) {
28037 module.remove.occurred(callbackName);
28038 }
28039 if(newCallback === undefined) {
28040 return calculations.topVisible;
28041 }
28042 },
28043
28044 bottomVisible: function(newCallback) {
28045 var
28046 calculations = module.get.elementCalculations(),
28047 callback = newCallback || settings.onBottomVisible,
28048 callbackName = 'bottomVisible'
28049 ;
28050 if(newCallback) {
28051 module.debug('Adding callback for bottom visible', newCallback);
28052 settings.onBottomVisible = newCallback;
28053 }
28054 if(calculations.bottomVisible) {
28055 module.execute(callback, callbackName);
28056 }
28057 else if(!settings.once) {
28058 module.remove.occurred(callbackName);
28059 }
28060 if(newCallback === undefined) {
28061 return calculations.bottomVisible;
28062 }
28063 },
28064
28065 topPassed: function(newCallback) {
28066 var
28067 calculations = module.get.elementCalculations(),
28068 callback = newCallback || settings.onTopPassed,
28069 callbackName = 'topPassed'
28070 ;
28071 if(newCallback) {
28072 module.debug('Adding callback for top passed', newCallback);
28073 settings.onTopPassed = newCallback;
28074 }
28075 if(calculations.topPassed) {
28076 module.execute(callback, callbackName);
28077 }
28078 else if(!settings.once) {
28079 module.remove.occurred(callbackName);
28080 }
28081 if(newCallback === undefined) {
28082 return calculations.topPassed;
28083 }
28084 },
28085
28086 bottomPassed: function(newCallback) {
28087 var
28088 calculations = module.get.elementCalculations(),
28089 callback = newCallback || settings.onBottomPassed,
28090 callbackName = 'bottomPassed'
28091 ;
28092 if(newCallback) {
28093 module.debug('Adding callback for bottom passed', newCallback);
28094 settings.onBottomPassed = newCallback;
28095 }
28096 if(calculations.bottomPassed) {
28097 module.execute(callback, callbackName);
28098 }
28099 else if(!settings.once) {
28100 module.remove.occurred(callbackName);
28101 }
28102 if(newCallback === undefined) {
28103 return calculations.bottomPassed;
28104 }
28105 },
28106
28107 passingReverse: function(newCallback) {
28108 var
28109 calculations = module.get.elementCalculations(),
28110 callback = newCallback || settings.onPassingReverse,
28111 callbackName = 'passingReverse'
28112 ;
28113 if(newCallback) {
28114 module.debug('Adding callback for passing reverse', newCallback);
28115 settings.onPassingReverse = newCallback;
28116 }
28117 if(!calculations.passing) {
28118 if(module.get.occurred('passing')) {
28119 module.execute(callback, callbackName);
28120 }
28121 }
28122 else if(!settings.once) {
28123 module.remove.occurred(callbackName);
28124 }
28125 if(newCallback !== undefined) {
28126 return !calculations.passing;
28127 }
28128 },
28129
28130
28131 topVisibleReverse: function(newCallback) {
28132 var
28133 calculations = module.get.elementCalculations(),
28134 callback = newCallback || settings.onTopVisibleReverse,
28135 callbackName = 'topVisibleReverse'
28136 ;
28137 if(newCallback) {
28138 module.debug('Adding callback for top visible reverse', newCallback);
28139 settings.onTopVisibleReverse = newCallback;
28140 }
28141 if(!calculations.topVisible) {
28142 if(module.get.occurred('topVisible')) {
28143 module.execute(callback, callbackName);
28144 }
28145 }
28146 else if(!settings.once) {
28147 module.remove.occurred(callbackName);
28148 }
28149 if(newCallback === undefined) {
28150 return !calculations.topVisible;
28151 }
28152 },
28153
28154 bottomVisibleReverse: function(newCallback) {
28155 var
28156 calculations = module.get.elementCalculations(),
28157 callback = newCallback || settings.onBottomVisibleReverse,
28158 callbackName = 'bottomVisibleReverse'
28159 ;
28160 if(newCallback) {
28161 module.debug('Adding callback for bottom visible reverse', newCallback);
28162 settings.onBottomVisibleReverse = newCallback;
28163 }
28164 if(!calculations.bottomVisible) {
28165 if(module.get.occurred('bottomVisible')) {
28166 module.execute(callback, callbackName);
28167 }
28168 }
28169 else if(!settings.once) {
28170 module.remove.occurred(callbackName);
28171 }
28172 if(newCallback === undefined) {
28173 return !calculations.bottomVisible;
28174 }
28175 },
28176
28177 topPassedReverse: function(newCallback) {
28178 var
28179 calculations = module.get.elementCalculations(),
28180 callback = newCallback || settings.onTopPassedReverse,
28181 callbackName = 'topPassedReverse'
28182 ;
28183 if(newCallback) {
28184 module.debug('Adding callback for top passed reverse', newCallback);
28185 settings.onTopPassedReverse = newCallback;
28186 }
28187 if(!calculations.topPassed) {
28188 if(module.get.occurred('topPassed')) {
28189 module.execute(callback, callbackName);
28190 }
28191 }
28192 else if(!settings.once) {
28193 module.remove.occurred(callbackName);
28194 }
28195 if(newCallback === undefined) {
28196 return !calculations.onTopPassed;
28197 }
28198 },
28199
28200 bottomPassedReverse: function(newCallback) {
28201 var
28202 calculations = module.get.elementCalculations(),
28203 callback = newCallback || settings.onBottomPassedReverse,
28204 callbackName = 'bottomPassedReverse'
28205 ;
28206 if(newCallback) {
28207 module.debug('Adding callback for bottom passed reverse', newCallback);
28208 settings.onBottomPassedReverse = newCallback;
28209 }
28210 if(!calculations.bottomPassed) {
28211 if(module.get.occurred('bottomPassed')) {
28212 module.execute(callback, callbackName);
28213 }
28214 }
28215 else if(!settings.once) {
28216 module.remove.occurred(callbackName);
28217 }
28218 if(newCallback === undefined) {
28219 return !calculations.bottomPassed;
28220 }
28221 },
28222
28223 execute: function(callback, callbackName) {
28224 var
28225 calculations = module.get.elementCalculations(),
28226 screen = module.get.screenCalculations()
28227 ;
28228 callback = callback || false;
28229 if(callback) {
28230 if(settings.continuous) {
28231 module.debug('Callback being called continuously', callbackName, calculations);
28232 callback.call(element, calculations, screen);
28233 }
28234 else if(!module.get.occurred(callbackName)) {
28235 module.debug('Conditions met', callbackName, calculations);
28236 callback.call(element, calculations, screen);
28237 }
28238 }
28239 module.save.occurred(callbackName);
28240 },
28241
28242 remove: {
28243 fixed: function() {
28244 module.debug('Removing fixed position');
28245 $module
28246 .removeClass(className.fixed)
28247 .css({
28248 position : '',
28249 top : '',
28250 left : '',
28251 zIndex : ''
28252 })
28253 ;
28254 settings.onUnfixed.call(element);
28255 },
28256 placeholder: function() {
28257 module.debug('Removing placeholder content');
28258 if($placeholder) {
28259 $placeholder.remove();
28260 }
28261 },
28262 occurred: function(callback) {
28263 if(callback) {
28264 var
28265 occurred = module.cache.occurred
28266 ;
28267 if(occurred[callback] !== undefined && occurred[callback] === true) {
28268 module.debug('Callback can now be called again', callback);
28269 module.cache.occurred[callback] = false;
28270 }
28271 }
28272 else {
28273 module.cache.occurred = {};
28274 }
28275 }
28276 },
28277
28278 save: {
28279 calculations: function() {
28280 module.verbose('Saving all calculations necessary to determine positioning');
28281 module.save.direction();
28282 module.save.screenCalculations();
28283 module.save.elementCalculations();
28284 },
28285 occurred: function(callback) {
28286 if(callback) {
28287 if(module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
28288 module.verbose('Saving callback occurred', callback);
28289 module.cache.occurred[callback] = true;
28290 }
28291 }
28292 },
28293 scroll: function(scrollPosition) {
28294 scrollPosition = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
28295 module.cache.scroll = scrollPosition;
28296 },
28297 direction: function() {
28298 var
28299 scroll = module.get.scroll(),
28300 lastScroll = module.get.lastScroll(),
28301 direction
28302 ;
28303 if(scroll > lastScroll && lastScroll) {
28304 direction = 'down';
28305 }
28306 else if(scroll < lastScroll && lastScroll) {
28307 direction = 'up';
28308 }
28309 else {
28310 direction = 'static';
28311 }
28312 module.cache.direction = direction;
28313 return module.cache.direction;
28314 },
28315 elementPosition: function() {
28316 var
28317 element = module.cache.element,
28318 screen = module.get.screenSize()
28319 ;
28320 module.verbose('Saving element position');
28321 // (quicker than $.extend)
28322 element.fits = (element.height < screen.height);
28323 element.offset = $module.offset();
28324 element.width = $module.outerWidth();
28325 element.height = $module.outerHeight();
28326 // compensate for scroll in context
28327 if(module.is.verticallyScrollableContext()) {
28328 element.offset.top += $context.scrollTop() - $context.offset().top;
28329 }
28330 if(module.is.horizontallyScrollableContext()) {
28331 element.offset.left += $context.scrollLeft() - $context.offset().left;
28332 }
28333 // store
28334 module.cache.element = element;
28335 return element;
28336 },
28337 elementCalculations: function() {
28338 var
28339 screen = module.get.screenCalculations(),
28340 element = module.get.elementPosition()
28341 ;
28342 // offset
28343 if(settings.includeMargin) {
28344 element.margin = {};
28345 element.margin.top = parseInt($module.css('margin-top'), 10);
28346 element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
28347 element.top = element.offset.top - element.margin.top;
28348 element.bottom = element.offset.top + element.height + element.margin.bottom;
28349 }
28350 else {
28351 element.top = element.offset.top;
28352 element.bottom = element.offset.top + element.height;
28353 }
28354
28355 // visibility
28356 element.topPassed = (screen.top >= element.top);
28357 element.bottomPassed = (screen.top >= element.bottom);
28358 element.topVisible = (screen.bottom >= element.top) && !element.topPassed;
28359 element.bottomVisible = (screen.bottom >= element.bottom) && !element.bottomPassed;
28360 element.pixelsPassed = 0;
28361 element.percentagePassed = 0;
28362
28363 // meta calculations
28364 element.onScreen = ((element.topVisible || element.passing) && !element.bottomPassed);
28365 element.passing = (element.topPassed && !element.bottomPassed);
28366 element.offScreen = (!element.onScreen);
28367
28368 // passing calculations
28369 if(element.passing) {
28370 element.pixelsPassed = (screen.top - element.top);
28371 element.percentagePassed = (screen.top - element.top) / element.height;
28372 }
28373 module.cache.element = element;
28374 module.verbose('Updated element calculations', element);
28375 return element;
28376 },
28377 screenCalculations: function() {
28378 var
28379 scroll = module.get.scroll()
28380 ;
28381 module.save.direction();
28382 module.cache.screen.top = scroll;
28383 module.cache.screen.bottom = scroll + module.cache.screen.height;
28384 return module.cache.screen;
28385 },
28386 screenSize: function() {
28387 module.verbose('Saving window position');
28388 module.cache.screen = {
28389 height: $context.height()
28390 };
28391 },
28392 position: function() {
28393 module.save.screenSize();
28394 module.save.elementPosition();
28395 }
28396 },
28397
28398 get: {
28399 pixelsPassed: function(amount) {
28400 var
28401 element = module.get.elementCalculations()
28402 ;
28403 if(amount.search('%') > -1) {
28404 return ( element.height * (parseInt(amount, 10) / 100) );
28405 }
28406 return parseInt(amount, 10);
28407 },
28408 occurred: function(callback) {
28409 return (module.cache.occurred !== undefined)
28410 ? module.cache.occurred[callback] || false
28411 : false
28412 ;
28413 },
28414 direction: function() {
28415 if(module.cache.direction === undefined) {
28416 module.save.direction();
28417 }
28418 return module.cache.direction;
28419 },
28420 elementPosition: function() {
28421 if(module.cache.element === undefined) {
28422 module.save.elementPosition();
28423 }
28424 return module.cache.element;
28425 },
28426 elementCalculations: function() {
28427 if(module.cache.element === undefined) {
28428 module.save.elementCalculations();
28429 }
28430 return module.cache.element;
28431 },
28432 screenCalculations: function() {
28433 if(module.cache.screen === undefined) {
28434 module.save.screenCalculations();
28435 }
28436 return module.cache.screen;
28437 },
28438 screenSize: function() {
28439 if(module.cache.screen === undefined) {
28440 module.save.screenSize();
28441 }
28442 return module.cache.screen;
28443 },
28444 scroll: function() {
28445 if(module.cache.scroll === undefined) {
28446 module.save.scroll();
28447 }
28448 return module.cache.scroll;
28449 },
28450 lastScroll: function() {
28451 if(module.cache.screen === undefined) {
28452 module.debug('First scroll event, no last scroll could be found');
28453 return false;
28454 }
28455 return module.cache.screen.top;
28456 }
28457 },
28458
28459 setting: function(name, value) {
28460 if( $.isPlainObject(name) ) {
28461 $.extend(true, settings, name);
28462 }
28463 else if(value !== undefined) {
28464 settings[name] = value;
28465 }
28466 else {
28467 return settings[name];
28468 }
28469 },
28470 internal: function(name, value) {
28471 if( $.isPlainObject(name) ) {
28472 $.extend(true, module, name);
28473 }
28474 else if(value !== undefined) {
28475 module[name] = value;
28476 }
28477 else {
28478 return module[name];
28479 }
28480 },
28481 debug: function() {
28482 if(!settings.silent && settings.debug) {
28483 if(settings.performance) {
28484 module.performance.log(arguments);
28485 }
28486 else {
28487 module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
28488 module.debug.apply(console, arguments);
28489 }
28490 }
28491 },
28492 verbose: function() {
28493 if(!settings.silent && settings.verbose && settings.debug) {
28494 if(settings.performance) {
28495 module.performance.log(arguments);
28496 }
28497 else {
28498 module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
28499 module.verbose.apply(console, arguments);
28500 }
28501 }
28502 },
28503 error: function() {
28504 if(!settings.silent) {
28505 module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
28506 module.error.apply(console, arguments);
28507 }
28508 },
28509 performance: {
28510 log: function(message) {
28511 var
28512 currentTime,
28513 executionTime,
28514 previousTime
28515 ;
28516 if(settings.performance) {
28517 currentTime = new Date().getTime();
28518 previousTime = time || currentTime;
28519 executionTime = currentTime - previousTime;
28520 time = currentTime;
28521 performance.push({
28522 'Name' : message[0],
28523 'Arguments' : [].slice.call(message, 1) || '',
28524 'Element' : element,
28525 'Execution Time' : executionTime
28526 });
28527 }
28528 clearTimeout(module.performance.timer);
28529 module.performance.timer = setTimeout(module.performance.display, 500);
28530 },
28531 display: function() {
28532 var
28533 title = settings.name + ':',
28534 totalTime = 0
28535 ;
28536 time = false;
28537 clearTimeout(module.performance.timer);
28538 $.each(performance, function(index, data) {
28539 totalTime += data['Execution Time'];
28540 });
28541 title += ' ' + totalTime + 'ms';
28542 if(moduleSelector) {
28543 title += ' \'' + moduleSelector + '\'';
28544 }
28545 if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
28546 console.groupCollapsed(title);
28547 if(console.table) {
28548 console.table(performance);
28549 }
28550 else {
28551 $.each(performance, function(index, data) {
28552 console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
28553 });
28554 }
28555 console.groupEnd();
28556 }
28557 performance = [];
28558 }
28559 },
28560 invoke: function(query, passedArguments, context) {
28561 var
28562 object = instance,
28563 maxDepth,
28564 found,
28565 response
28566 ;
28567 passedArguments = passedArguments || queryArguments;
28568 context = element || context;
28569 if(typeof query == 'string' && object !== undefined) {
28570 query = query.split(/[\. ]/);
28571 maxDepth = query.length - 1;
28572 $.each(query, function(depth, value) {
28573 var camelCaseValue = (depth != maxDepth)
28574 ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
28575 : query
28576 ;
28577 if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
28578 object = object[camelCaseValue];
28579 }
28580 else if( object[camelCaseValue] !== undefined ) {
28581 found = object[camelCaseValue];
28582 return false;
28583 }
28584 else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
28585 object = object[value];
28586 }
28587 else if( object[value] !== undefined ) {
28588 found = object[value];
28589 return false;
28590 }
28591 else {
28592 module.error(error.method, query);
28593 return false;
28594 }
28595 });
28596 }
28597 if ( $.isFunction( found ) ) {
28598 response = found.apply(context, passedArguments);
28599 }
28600 else if(found !== undefined) {
28601 response = found;
28602 }
28603 if(Array.isArray(returnedValue)) {
28604 returnedValue.push(response);
28605 }
28606 else if(returnedValue !== undefined) {
28607 returnedValue = [returnedValue, response];
28608 }
28609 else if(response !== undefined) {
28610 returnedValue = response;
28611 }
28612 return found;
28613 }
28614 };
28615
28616 if(methodInvoked) {
28617 if(instance === undefined) {
28618 module.initialize();
28619 }
28620 instance.save.scroll();
28621 instance.save.calculations();
28622 module.invoke(query);
28623 }
28624 else {
28625 if(instance !== undefined) {
28626 instance.invoke('destroy');
28627 }
28628 module.initialize();
28629 }
28630 })
28631 ;
28632
28633 return (returnedValue !== undefined)
28634 ? returnedValue
28635 : this
28636 ;
28637};
28638
28639$.fn.visibility.settings = {
28640
28641 name : 'Visibility',
28642 namespace : 'visibility',
28643
28644 debug : false,
28645 verbose : false,
28646 performance : true,
28647
28648 // whether to use mutation observers to follow changes
28649 observeChanges : true,
28650
28651 // check position immediately on init
28652 initialCheck : true,
28653
28654 // whether to refresh calculations after all page images load
28655 refreshOnLoad : true,
28656
28657 // whether to refresh calculations after page resize event
28658 refreshOnResize : true,
28659
28660 // should call callbacks on refresh event (resize, etc)
28661 checkOnRefresh : true,
28662
28663 // callback should only occur one time
28664 once : true,
28665
28666 // callback should fire continuously whe evaluates to true
28667 continuous : false,
28668
28669 // offset to use with scroll top
28670 offset : 0,
28671
28672 // whether to include margin in elements position
28673 includeMargin : false,
28674
28675 // scroll context for visibility checks
28676 context : window,
28677
28678 // visibility check delay in ms (defaults to animationFrame)
28679 throttle : false,
28680
28681 // special visibility type (image, fixed)
28682 type : false,
28683
28684 // z-index to use with visibility 'fixed'
28685 zIndex : '10',
28686
28687 // image only animation settings
28688 transition : 'fade in',
28689 duration : 1000,
28690
28691 // array of callbacks for percentage
28692 onPassed : {},
28693
28694 // standard callbacks
28695 onOnScreen : false,
28696 onOffScreen : false,
28697 onPassing : false,
28698 onTopVisible : false,
28699 onBottomVisible : false,
28700 onTopPassed : false,
28701 onBottomPassed : false,
28702
28703 // reverse callbacks
28704 onPassingReverse : false,
28705 onTopVisibleReverse : false,
28706 onBottomVisibleReverse : false,
28707 onTopPassedReverse : false,
28708 onBottomPassedReverse : false,
28709
28710 // special callbacks for image
28711 onLoad : function() {},
28712 onAllLoaded : function() {},
28713
28714 // special callbacks for fixed position
28715 onFixed : function() {},
28716 onUnfixed : function() {},
28717
28718 // utility callbacks
28719 onUpdate : false, // disabled by default for performance
28720 onRefresh : function(){},
28721
28722 metadata : {
28723 src: 'src'
28724 },
28725
28726 className: {
28727 fixed : 'fixed',
28728 placeholder : 'constraint',
28729 visible : 'visible'
28730 },
28731
28732 error : {
28733 method : 'The method you called is not defined.',
28734 visible : 'Element is hidden, you must call refresh after element becomes visible'
28735 }
28736
28737};
28738
28739})( jQuery, window, document );