/* * # Fomantic UI - 2.9.3 * https://github.com/fomantic/Fomantic-UI * https://fomantic-ui.com/ * * Copyright 2023 Contributors * Released under the MIT license * https://opensource.org/licenses/MIT * */ /*! * # Fomantic-UI 2.9.3 - Site * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.site = function (parameters) { var time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), settings = $.isPlainObject(parameters) ? $.extend(true, {}, $.site.settings, parameters) : $.extend({}, $.site.settings), namespace = settings.namespace, error = settings.error, moduleNamespace = 'module-' + namespace, $document = $(document), $module = $document, element = this, instance = $module.data(moduleNamespace), module, returnedValue ; module = { initialize: function () { module.instantiate(); }, instantiate: function () { module.verbose('Storing instance of site', module); instance = module; $module .data(moduleNamespace, module) ; }, normalize: function () { // keep the function for backward compatibility // eslint-disable-next-line no-useless-return return; }, fix: { consoleClear: function () { module.debug('Disabling programmatic console clearing'); window.console.clear = function () {}; }, }, moduleExists: function (name) { return $.fn[name] !== undefined && $.fn[name].settings !== undefined; }, enabled: { modules: function (modules) { var enabledModules = [] ; modules = modules || settings.modules; $.each(modules, function (index, name) { if (module.moduleExists(name)) { enabledModules.push(name); } }); return enabledModules; }, }, disabled: { modules: function (modules) { var disabledModules = [] ; modules = modules || settings.modules; $.each(modules, function (index, name) { if (!module.moduleExists(name)) { disabledModules.push(name); } }); return disabledModules; }, }, change: { setting: function (setting, value, modules, modifyExisting) { modules = typeof modules === 'string' ? (modules === 'all' ? settings.modules : [modules]) : modules || settings.modules; modifyExisting = modifyExisting !== undefined ? modifyExisting : true; $.each(modules, function (index, name) { var namespace = module.moduleExists(name) ? $.fn[name].settings.namespace || false : true, $existingModules ; if (module.moduleExists(name)) { module.verbose('Changing default setting', setting, value, name); $.fn[name].settings[setting] = value; if (modifyExisting && namespace) { $existingModules = $(':data(module-' + namespace + ')'); if ($existingModules.length > 0) { module.verbose('Modifying existing settings', $existingModules); $existingModules[name]('setting', setting, value); } } } }); }, settings: function (newSettings, modules, modifyExisting) { modules = typeof modules === 'string' ? [modules] : modules || settings.modules; modifyExisting = modifyExisting !== undefined ? modifyExisting : true; $.each(modules, function (index, name) { var $existingModules ; if (module.moduleExists(name)) { module.verbose('Changing default setting', newSettings, name); $.extend(true, $.fn[name].settings, newSettings); if (modifyExisting && namespace) { $existingModules = $(':data(module-' + namespace + ')'); if ($existingModules.length > 0) { module.verbose('Modifying existing settings', $existingModules); $existingModules[name]('setting', newSettings); } } } }); }, }, enable: { console: function () { module.console(true); }, debug: function (modules, modifyExisting) { modules = modules || settings.modules; module.debug('Enabling debug for modules', modules); module.change.setting('debug', true, modules, modifyExisting); }, verbose: function (modules, modifyExisting) { modules = modules || settings.modules; module.debug('Enabling verbose debug for modules', modules); module.change.setting('verbose', true, modules, modifyExisting); }, }, disable: { console: function () { module.console(false); }, debug: function (modules, modifyExisting) { modules = modules || settings.modules; module.debug('Disabling debug for modules', modules); module.change.setting('debug', false, modules, modifyExisting); }, verbose: function (modules, modifyExisting) { modules = modules || settings.modules; module.debug('Disabling verbose debug for modules', modules); module.change.setting('verbose', false, modules, modifyExisting); }, }, console: function (enable) { if (enable) { if (instance.cache.console === undefined) { module.error(error.console); return; } module.debug('Restoring console function'); window.console = instance.cache.console; } else { module.debug('Disabling console function'); instance.cache.console = window.console; window.console = { clear: function () {}, error: function () {}, group: function () {}, groupCollapsed: function () {}, groupEnd: function () {}, info: function () {}, log: function () {}, table: function () {}, warn: function () {}, }; } }, destroy: function () { module.verbose('Destroying previous site for', $module); $module .removeData(moduleNamespace) ; }, cache: {}, setting: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { settings[name] = value; } else { return settings[name]; } }, internal: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, module, name); } else if (value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function () { if (settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = Date.now(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ Element: element, Name: message[0], Arguments: [].slice.call(message, 1) || '', 'Execution Time': executionTime, }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(function () { module.performance.display(); }, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if (performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data.Name + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; }, }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query === 'string' && object !== undefined) { query = query.split(/[ .]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = depth !== maxDepth ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query ; if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if (isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; }, }; if (methodInvoked) { if (instance === undefined) { module.initialize(); } module.invoke(query); } else { if (instance !== undefined) { module.destroy(); } module.initialize(); } return returnedValue !== undefined ? returnedValue : this; }; $.site = $.fn.site; $.site.settings = { name: 'Site', namespace: 'site', error: { console: 'Console cannot be restored, most likely it was overwritten outside of module', method: 'The method you called is not defined.', }, debug: false, verbose: false, performance: true, modules: [ 'accordion', 'api', 'calendar', 'checkbox', 'dimmer', 'dropdown', 'embed', 'flyout', 'form', 'modal', 'nag', 'popup', 'progress', 'rating', 'search', 'shape', 'sidebar', 'slider', 'state', 'sticky', 'tab', 'toast', 'transition', 'visibility', ], siteNamespace: 'site', namespaceStub: { cache: {}, config: {}, sections: {}, section: {}, utilities: {}, }, }; // allows for selection of elements with data attributes $.extend($.expr.pseudos, { data: $.expr.createPseudo(function (dataName) { return function (elem) { return !!$.data(elem, dataName); }; }), }); })(jQuery, window, document); /*! * # Fomantic-UI 2.9.3 - Form Validation * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.form = function (parameters) { var $allModules = $(this), $window = $(window), time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), returnedValue ; $allModules.each(function () { var $module = $(this), element = this, formErrors = [], keyHeldDown = false, // set at run-time $field, $group, $message, $prompt, $submit, $clear, $reset, settings, validation, metadata, selector, className, regExp, error, namespace, moduleNamespace, eventNamespace, attachEventsSelector, attachEventsAction, submitting = false, dirty = false, history = ['clean', 'clean'], instance, module ; module = { initialize: function () { // settings grabbed at run time module.get.settings(); $module.addClass(className.initial); if (methodInvoked) { if (instance === undefined) { module.instantiate(); } module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); module.refresh(); } module.verbose('Initializing form validation', $module, settings); module.bindEvents(); module.set.defaults(); if (settings.autoCheckRequired) { module.set.autoCheck(); } module.instantiate(); } }, instantiate: function () { module.verbose('Storing instance of module', module); instance = module; $module .data(moduleNamespace, module) ; }, destroy: function () { module.verbose('Destroying previous module', instance); module.removeEvents(); $module .removeData(moduleNamespace) ; }, refresh: function () { module.verbose('Refreshing selector cache'); $field = $module.find(selector.field); $group = $module.find(selector.group); $message = $module.find(selector.message); $prompt = $module.find(selector.prompt); $submit = $module.find(selector.submit); $clear = $module.find(selector.clear); $reset = $module.find(selector.reset); }, refreshEvents: function () { module.removeEvents(); module.bindEvents(); }, submit: function (event) { module.verbose('Submitting form', $module); submitting = true; $module.trigger('submit'); if (event) { event.preventDefault(); } }, attachEvents: function (selector, action) { if (!action) { action = 'submit'; } $(selector).on('click' + eventNamespace, function (event) { module[action](); event.preventDefault(); }); attachEventsSelector = selector; attachEventsAction = action; }, bindEvents: function () { module.verbose('Attaching form events'); $module .on('submit' + eventNamespace, module.validate.form) .on('blur' + eventNamespace, selector.field, module.event.field.blur) .on('click' + eventNamespace, selector.submit, module.submit) .on('click' + eventNamespace, selector.reset, module.reset) .on('click' + eventNamespace, selector.clear, module.clear) ; $field.on('invalid' + eventNamespace, module.event.field.invalid); if (settings.keyboardShortcuts) { $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown); } $field.each(function (index, el) { var $input = $(el), type = $input.prop('type'), inputEvent = module.get.changeEvent(type, $input) ; $input.on(inputEvent + eventNamespace, module.event.field.change); }); // Dirty events if (settings.preventLeaving) { $window.on('beforeunload' + eventNamespace, module.event.beforeUnload); } $field.on('change' + eventNamespace + ' click' + eventNamespace + ' keyup' + eventNamespace + ' keydown' + eventNamespace + ' blur' + eventNamespace, function (e) { module.determine.isDirty(); }); $module.on('dirty' + eventNamespace, function (e) { settings.onDirty.call(); }); $module.on('clean' + eventNamespace, function (e) { settings.onClean.call(); }); if (attachEventsSelector) { module.attachEvents(attachEventsSelector, attachEventsAction); } }, clear: function () { $field.each(function (index, el) { var $field = $(el), $element = $field.parent(), $fieldGroup = $field.closest($group), $prompt = $fieldGroup.find(selector.prompt), $calendar = $field.closest(selector.uiCalendar), defaultValue = $field.data(metadata.defaultValue) || '', isCheckbox = $field.is(selector.checkbox), isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'), isCalendar = $calendar.length > 0 && module.can.useElement('calendar'), isErrored = $fieldGroup.hasClass(className.error) ; if (isErrored) { module.verbose('Resetting error on field', $fieldGroup); $fieldGroup.removeClass(className.error); $prompt.remove(); } if (isDropdown) { module.verbose('Resetting dropdown value', $element, defaultValue); $element.dropdown('clear', true); } else if (isCheckbox) { $field.prop('checked', false); } else if (isCalendar) { $calendar.calendar('clear'); } else { module.verbose('Resetting field value', $field, defaultValue); $field.val(''); } }); module.remove.states(); }, reset: function () { $field.each(function (index, el) { var $field = $(el), $element = $field.parent(), $fieldGroup = $field.closest($group), $calendar = $field.closest(selector.uiCalendar), $prompt = $fieldGroup.find(selector.prompt), defaultValue = $field.data(metadata.defaultValue), isCheckbox = $field.is(selector.checkbox), isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'), isCalendar = $calendar.length > 0 && module.can.useElement('calendar'), isFile = $field.is(selector.file), isErrored = $fieldGroup.hasClass(className.error) ; if (defaultValue === undefined) { return; } if (isErrored) { module.verbose('Resetting error on field', $fieldGroup); $fieldGroup.removeClass(className.error); $prompt.remove(); } if (isDropdown) { module.verbose('Resetting dropdown value', $element, defaultValue); $element.dropdown('restore defaults', true); } else if (isCheckbox) { module.verbose('Resetting checkbox value', $field, defaultValue); $field.prop('checked', defaultValue); } else if (isCalendar) { $calendar.calendar('set date', defaultValue); } else { module.verbose('Resetting field value', $field, defaultValue); $field.val(isFile ? '' : defaultValue); } }); module.remove.states(); }, determine: { isValid: function () { var allValid = true ; $field.each(function (index, el) { var $el = $(el), validation = module.get.validation($el) || {}, identifier = module.get.identifier(validation, $el) ; if (!module.validate.field(validation, identifier, true)) { allValid = false; } }); return allValid; }, isDirty: function (e) { var formIsDirty = false; $field.each(function (index, el) { var $el = $(el), isCheckbox = $el.filter(selector.checkbox).length > 0, isDirty ; isDirty = isCheckbox ? module.is.checkboxDirty($el) : module.is.fieldDirty($el); $el.data(settings.metadata.isDirty, isDirty); formIsDirty = formIsDirty || isDirty; }); if (formIsDirty) { module.set.dirty(); } else { module.set.clean(); } }, }, is: { bracketedRule: function (rule) { return rule.type && rule.type.match(settings.regExp.bracket); }, // duck type rule test shorthandRules: function (rules) { return typeof rules === 'string' || Array.isArray(rules); }, empty: function ($field) { if (!$field || $field.length === 0) { return true; } if ($field.is(selector.checkbox)) { return !$field.is(':checked'); } return module.is.blank($field); }, blank: function ($field) { return String($field.val()).trim() === ''; }, valid: function (field, showErrors) { var allValid = true ; if (field) { module.verbose('Checking if field is valid', field); return module.validate.field(validation[field], field, !!showErrors); } module.verbose('Checking if form is valid'); $.each(validation, function (fieldName, field) { if (!module.is.valid(fieldName, showErrors)) { allValid = false; } }); return allValid; }, dirty: function () { return dirty; }, clean: function () { return !dirty; }, fieldDirty: function ($el) { var initialValue = $el.data(metadata.defaultValue); // Explicitly check for undefined/null here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work if (initialValue === undefined || initialValue === null) { initialValue = ''; } else if (Array.isArray(initialValue)) { initialValue = initialValue.toString(); } var currentValue = $el.val(); if (currentValue === undefined || currentValue === null) { currentValue = ''; } else if (Array.isArray(currentValue)) { // multiple select values are returned as arrays which are never equal, so do string conversion first currentValue = currentValue.toString(); } // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison var boolRegex = /^(true|false)$/i; var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue); if (isBoolValue) { var regex = new RegExp('^' + initialValue + '$', 'i'); return !regex.test(currentValue); } return currentValue !== initialValue; }, checkboxDirty: function ($el) { var initialValue = $el.data(metadata.defaultValue); var currentValue = $el.is(':checked'); return initialValue !== currentValue; }, justDirty: function () { return history[0] === 'dirty'; }, justClean: function () { return history[0] === 'clean'; }, }, removeEvents: function () { $module.off(eventNamespace); $field.off(eventNamespace); $submit.off(eventNamespace); if (settings.preventLeaving) { $window.off(eventNamespace); } if (attachEventsSelector) { $(attachEventsSelector).off(eventNamespace); attachEventsSelector = undefined; } }, event: { field: { keydown: function (event) { var $field = $(this), key = event.which, isInput = $field.is(selector.input), isCheckbox = $field.is(selector.checkbox), isInDropdown = $field.closest(selector.uiDropdown).length > 0, keyCode = { enter: 13, escape: 27, } ; if (key === keyCode.escape) { module.verbose('Escape key pressed blurring field'); $field[0] .blur() ; } if (!event.ctrlKey && key === keyCode.enter && isInput && !isInDropdown && !isCheckbox) { if (!keyHeldDown) { $field.one('keyup' + eventNamespace, module.event.field.keyup); module.submit(event); module.debug('Enter pressed on input submitting form'); } keyHeldDown = true; } }, keyup: function () { keyHeldDown = false; }, invalid: function (event) { event.preventDefault(); }, blur: function (event) { var $field = $(this), validationRules = module.get.validation($field) || {}, identifier = module.get.identifier(validationRules, $field) ; if (settings.on === 'blur' || (!$module.hasClass(className.initial) && settings.revalidate)) { module.debug('Revalidating field', $field, validationRules); module.validate.field(validationRules, identifier); if (!settings.inline) { module.validate.form(false, true); } } }, change: function (event) { var $field = $(this), validationRules = module.get.validation($field) || {}, identifier = module.get.identifier(validationRules, $field) ; if (settings.on === 'change' || (!$module.hasClass(className.initial) && settings.revalidate)) { clearTimeout(module.timer); module.timer = setTimeout(function () { module.debug('Revalidating field', $field, validationRules); module.validate.field(validationRules, identifier); if (!settings.inline) { module.validate.form(false, true); } }, settings.delay); } }, }, beforeUnload: function (event) { if (module.is.dirty() && !submitting) { event = event || window.event; // For modern browsers if (event) { event.returnValue = settings.text.leavingMessage; } // For olders... return settings.text.leavingMessage; } }, }, get: { ancillaryValue: function (rule) { if (!rule.type || (!rule.value && !module.is.bracketedRule(rule))) { return false; } return rule.value !== undefined ? rule.value : rule.type.match(settings.regExp.bracket)[1] + ''; }, ruleName: function (rule) { if (module.is.bracketedRule(rule)) { return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], ''); } return rule.type; }, changeEvent: function (type, $input) { return ['file', 'checkbox', 'radio', 'hidden'].indexOf(type) >= 0 || $input.is('select') ? 'change' : 'input'; }, fieldsFromShorthand: function (fields) { var fullFields = {} ; $.each(fields, function (name, rules) { if (!Array.isArray(rules) && typeof rules === 'object') { fullFields[name] = rules; } else { if (typeof rules === 'string') { rules = [rules]; } fullFields[name] = { rules: [], }; $.each(rules, function (index, rule) { fullFields[name].rules.push({ type: rule }); }); } }); return fullFields; }, identifier: function (validation, $el) { return validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate); }, prompt: function (rule, field) { var ruleName = module.get.ruleName(rule), ancillary = module.get.ancillaryValue(rule), $field = module.get.field(field.identifier), value = $field.val(), prompt = isFunction(rule.prompt) ? rule.prompt(value) : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule, requiresValue = prompt.search('{value}') !== -1, requiresName = prompt.search('{name}') !== -1, parts, suffixPrompt ; if (ancillary && ['integer', 'decimal', 'number', 'size'].indexOf(ruleName) >= 0 && ancillary.indexOf('..') >= 0) { parts = ancillary.split('..', 2); if (!rule.prompt && ruleName !== 'size') { suffixPrompt = parts[0] === '' ? settings.prompt.maxValue.replace(/{ruleValue}/g, '{max}') : (parts[1] === '' ? settings.prompt.minValue.replace(/{ruleValue}/g, '{min}') : settings.prompt.range); prompt += suffixPrompt.replace(/{name}/g, ' ' + settings.text.and); } prompt = prompt.replace(/{min}/g, parts[0]); prompt = prompt.replace(/{max}/g, parts[1]); } if (ancillary && ['match', 'different'].indexOf(ruleName) >= 0) { prompt = prompt.replace(/{ruleValue}/g, module.get.fieldLabel(ancillary, true)); } if (requiresValue) { prompt = prompt.replace(/{value}/g, $field.val()); } if (requiresName) { prompt = prompt.replace(/{name}/g, module.get.fieldLabel($field)); } prompt = prompt.replace(/{identifier}/g, field.identifier); prompt = prompt.replace(/{ruleValue}/g, ancillary); if (!rule.prompt) { module.verbose('Using default validation prompt for type', prompt, ruleName); } return prompt; }, settings: function () { if ($.isPlainObject(parameters)) { if (parameters.fields) { parameters.fields = module.get.fieldsFromShorthand(parameters.fields); } settings = $.extend(true, {}, $.fn.form.settings, parameters); validation = $.extend(true, {}, $.fn.form.settings.defaults, settings.fields); module.verbose('Extending settings', validation, settings); } else { settings = $.extend(true, {}, $.fn.form.settings); validation = $.extend(true, {}, $.fn.form.settings.defaults); module.verbose('Using default form validation', validation, settings); } // shorthand namespace = settings.namespace; metadata = settings.metadata; selector = settings.selector; className = settings.className; regExp = settings.regExp; error = settings.error; moduleNamespace = 'module-' + namespace; eventNamespace = '.' + namespace; // grab instance instance = $module.data(moduleNamespace); // refresh selector cache (instance || module).refresh(); }, field: function (identifier, strict) { module.verbose('Finding field with identifier', identifier); identifier = module.escape.string(identifier); var t; t = $field.filter('#' + identifier); if (t.length > 0) { return t; } t = $field.filter('[name="' + identifier + '"]'); if (t.length > 0) { return t; } t = $field.filter('[name="' + identifier + '[]"]'); if (t.length > 0) { return t; } t = $field.filter('[data-' + metadata.validate + '="' + identifier + '"]'); if (t.length > 0) { return t; } module.error(error.noField.replace('{identifier}', identifier)); return strict ? $() : $(''); }, fields: function (fields, strict) { var $fields = $() ; $.each(fields, function (index, name) { $fields = $fields.add(module.get.field(name, strict)); }); return $fields; }, fieldLabel: function (identifier, useIdAsFallback) { var $field = typeof identifier === 'string' ? module.get.field(identifier) : identifier, $label = $field.closest(selector.group).find('label:not(:empty)').eq(0) ; return $label.length === 1 ? $label.text() : $field.prop('placeholder') || (useIdAsFallback ? identifier : settings.text.unspecifiedField); }, validation: function ($field) { var fieldValidation, identifier ; if (!validation) { return false; } $.each(validation, function (fieldName, field) { identifier = field.identifier || fieldName; $.each(module.get.field(identifier), function (index, groupField) { if (groupField == $field[0]) { field.identifier = identifier; fieldValidation = field; return false; } }); }); return fieldValidation || false; }, value: function (field, strict) { var fields = [], results, resultKeys ; fields.push(field); results = module.get.values.call(element, fields, strict); resultKeys = Object.keys(results); return resultKeys.length > 0 ? results[resultKeys[0]] : undefined; }, values: function (fields, strict) { var $fields = Array.isArray(fields) && fields.length > 0 ? module.get.fields(fields, strict) : $field, values = {} ; $fields.each(function (index, field) { var $field = $(field), $calendar = $field.closest(selector.uiCalendar), name = $field.prop('name'), value = $field.val(), isCheckbox = $field.is(selector.checkbox), isRadio = $field.is(selector.radio), isMultiple = name.indexOf('[]') !== -1, isCalendar = $calendar.length > 0 && module.can.useElement('calendar'), isChecked = isCheckbox ? $field.is(':checked') : false ; if (name) { if (isMultiple) { name = name.replace('[]', ''); if (!values[name]) { values[name] = []; } if (isCheckbox) { if (isChecked) { values[name].push(value || true); } else { values[name].push(false); } } else { values[name].push(value); } } else { if (isRadio) { if (values[name] === undefined || values[name] === false) { values[name] = isChecked ? value || true : false; } } else if (isCheckbox) { values[name] = isChecked ? value || true : false; } else if (isCalendar) { var date = $calendar.calendar('get date'); if (date !== null) { switch (settings.dateHandling) { case 'date': { values[name] = date; break; } case 'input': { values[name] = $calendar.calendar('get input date'); break; } case 'formatter': { var type = $calendar.calendar('setting', 'type'); switch (type) { case 'date': { values[name] = settings.formatter.date(date); break; } case 'datetime': { values[name] = settings.formatter.datetime(date); break; } case 'time': { values[name] = settings.formatter.time(date); break; } case 'month': { values[name] = settings.formatter.month(date); break; } case 'year': { values[name] = settings.formatter.year(date); break; } default: { module.debug('Wrong calendar mode', $calendar, type); values[name] = ''; } } break; } } } else { values[name] = ''; } } else { values[name] = value; } } } }); return values; }, dirtyFields: function () { return $field.filter(function (index, e) { return $(e).data(metadata.isDirty); }); }, }, has: { field: function (identifier) { module.verbose('Checking for existence of a field with identifier', identifier); return module.get.field(identifier, true).length > 0; }, }, can: { useElement: function (element) { if ($.fn[element] !== undefined) { return true; } module.error(error.noElement.replace('{element}', element)); return false; }, }, escape: { string: function (text) { text = String(text); return text.replace(regExp.escape, '\\$&'); }, }, checkErrors: function (errors, internal) { if (!errors || errors.length === 0) { if (!internal) { module.error(settings.error.noErrorMessage); } return false; } if (!internal) { errors = typeof errors === 'string' ? [errors] : errors; } return errors; }, add: { // alias rule: function (name, rules) { module.add.field(name, rules); }, field: function (name, rules) { // Validation should have at least a standard format if (validation[name] === undefined || validation[name].rules === undefined) { validation[name] = { rules: [], }; } var newValidation = { rules: [], } ; if (module.is.shorthandRules(rules)) { rules = Array.isArray(rules) ? rules : [rules]; $.each(rules, function (_index, rule) { newValidation.rules.push({ type: rule }); }); } else { newValidation.rules = rules.rules; } // For each new rule, check if there's not already one with the same type $.each(newValidation.rules, function (_index, rule) { if ($.grep(validation[name].rules, function (item) { return item.type === rule.type; }).length === 0) { validation[name].rules.push(rule); } }); module.debug('Adding rules', newValidation.rules, validation); module.refreshEvents(); }, fields: function (fields) { validation = $.extend(true, {}, validation, module.get.fieldsFromShorthand(fields)); module.refreshEvents(); }, prompt: function (identifier, errors, internal) { errors = module.checkErrors(errors); if (errors === false) { return; } var $field = module.get.field(identifier), $fieldGroup = $field.closest($group), $prompt = $fieldGroup.children(selector.prompt), promptExists = $prompt.length > 0, canTransition = settings.transition && module.can.useElement('transition') ; module.verbose('Adding field error state', identifier); if (!internal) { $fieldGroup .addClass(className.error) ; } if (settings.inline) { if (promptExists) { if (canTransition) { if ($prompt.transition('is animating')) { $prompt.transition('stop all'); } } else if ($prompt.is(':animated')) { $prompt.stop(true, true); } $prompt = $fieldGroup.children(selector.prompt); promptExists = $prompt.length > 0; } if (!promptExists) { $prompt = $('
').addClass(className.label); if (!canTransition) { $prompt.css('display', 'none'); } $prompt .appendTo($fieldGroup) ; } $prompt .html(settings.templates.prompt(errors)) ; if (!promptExists) { if (canTransition) { module.verbose('Displaying error with css transition', settings.transition); $prompt.transition(settings.transition + ' in', settings.duration); } else { module.verbose('Displaying error with fallback javascript animation'); $prompt .fadeIn(settings.duration) ; } } } else { module.verbose('Inline errors are disabled, no inline error added', identifier); } }, errors: function (errors) { errors = module.checkErrors(errors); if (errors === false) { return; } module.debug('Adding form error messages', errors); module.set.error(); var customErrors = [], tempErrors ; if ($.isPlainObject(errors)) { $.each(Object.keys(errors), function (i, id) { if (module.checkErrors(errors[id], true) !== false) { if (settings.inline) { module.add.prompt(id, errors[id]); } else { tempErrors = module.checkErrors(errors[id]); if (tempErrors !== false) { $.each(tempErrors, function (index, tempError) { customErrors.push(settings.prompt.addErrors .replace(/{name}/g, module.get.fieldLabel(id)) .replace(/{error}/g, tempError)); }); } } } }); } else { customErrors = errors; } if (customErrors.length > 0) { $message .html(settings.templates.error(customErrors)) ; } }, }, remove: { errors: function () { module.debug('Removing form error messages'); $message.empty(); }, states: function () { $module.removeClass(className.error).removeClass(className.success).addClass(className.initial); if (!settings.inline) { module.remove.errors(); } module.determine.isDirty(); }, rule: function (field, rule) { var rules = Array.isArray(rule) ? rule : [rule] ; if (validation[field] === undefined || !Array.isArray(validation[field].rules)) { return; } if (rule === undefined) { module.debug('Removed all rules'); if (module.has.field(field)) { validation[field].rules = []; } else { delete validation[field]; } return; } $.each(validation[field].rules, function (index, rule) { if (rule && rules.indexOf(rule.type) !== -1) { module.debug('Removed rule', rule.type); validation[field].rules.splice(index, 1); } }); }, field: function (field) { var fields = Array.isArray(field) ? field : [field] ; $.each(fields, function (index, field) { module.remove.rule(field); }); module.refreshEvents(); }, // alias rules: function (field, rules) { if (Array.isArray(field)) { $.each(field, function (index, field) { module.remove.rule(field, rules); }); } else { module.remove.rule(field, rules); } }, fields: function (fields) { module.remove.field(fields); }, prompt: function (identifier) { var $field = module.get.field(identifier), $fieldGroup = $field.closest($group), $prompt = $fieldGroup.children(selector.prompt) ; $fieldGroup .removeClass(className.error) ; if (settings.inline && $prompt.is(':visible')) { module.verbose('Removing prompt for field', identifier); if (settings.transition && module.can.useElement('transition')) { $prompt.transition(settings.transition + ' out', settings.duration, function () { $prompt.remove(); }); } else { $prompt .fadeOut(settings.duration, function () { $prompt.remove(); }) ; } } }, }, set: { success: function () { $module .removeClass(className.error) .addClass(className.success) ; }, defaults: function () { $field.each(function (index, el) { var $el = $(el), $parent = $el.parent(), isCheckbox = $el.filter(selector.checkbox).length > 0, isDropdown = ($parent.is(selector.uiDropdown) || $el.is(selector.uiDropdown)) && module.can.useElement('dropdown'), $calendar = $el.closest(selector.uiCalendar), isCalendar = $calendar.length > 0 && module.can.useElement('calendar'), value = isCheckbox ? $el.is(':checked') : $el.val() ; if (isDropdown) { if ($parent.is(selector.uiDropdown)) { $parent.dropdown('save defaults'); } else { $el.dropdown('save defaults'); } } else if (isCalendar) { $calendar.calendar('refresh'); } $el.data(metadata.defaultValue, value); $el.data(metadata.isDirty, false); }); }, error: function () { $module .removeClass(className.success) .addClass(className.error) ; }, value: function (field, value) { var fields = {} ; fields[field] = value; return module.set.values.call(element, fields); }, values: function (fields) { if ($.isEmptyObject(fields)) { return; } $.each(fields, function (key, value) { var $field = module.get.field(key), $element = $field.parent(), $calendar = $field.closest(selector.uiCalendar), isFile = $field.is(selector.file), isMultiple = Array.isArray(value), isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'), isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'), isRadio = $field.is(selector.radio) && isCheckbox, isCalendar = $calendar.length > 0 && module.can.useElement('calendar'), fieldExists = $field.length > 0, $multipleField ; if (fieldExists) { if (isMultiple && isCheckbox) { module.verbose('Selecting multiple', value, $field); $element.checkbox('uncheck'); $.each(value, function (index, value) { $multipleField = $field.filter('[value="' + value + '"]'); $element = $multipleField.parent(); if ($multipleField.length > 0) { $element.checkbox('check'); } }); } else if (isRadio) { module.verbose('Selecting radio value', value, $field); $field.filter('[value="' + value + '"]') .parent(selector.uiCheckbox) .checkbox('check') ; } else if (isCheckbox) { module.verbose('Setting checkbox value', value, $element); if (value === true || value === 1 || value === 'on') { $element.checkbox('check'); } else { $element.checkbox('uncheck'); } if (typeof value === 'string') { $field.val(value); } } else if (isDropdown) { module.verbose('Setting dropdown value', value, $element); $element.dropdown('set selected', value); } else if (isCalendar) { $calendar.calendar('set date', value); } else { module.verbose('Setting field value', value, $field); $field.val(isFile ? '' : value); } } }); }, dirty: function () { module.verbose('Setting state dirty'); dirty = true; history[0] = history[1]; history[1] = 'dirty'; if (module.is.justClean()) { $module.trigger('dirty'); } }, clean: function () { module.verbose('Setting state clean'); dirty = false; history[0] = history[1]; history[1] = 'clean'; if (module.is.justDirty()) { $module.trigger('clean'); } }, asClean: function () { module.set.defaults(); module.set.clean(); }, asDirty: function () { module.set.defaults(); module.set.dirty(); }, autoCheck: function () { module.debug('Enabling auto check on required fields'); if (validation) { $.each(validation, function (fieldName) { if (!module.has.field(fieldName)) { module.verbose('Field not found, removing from validation', fieldName); module.remove.field(fieldName); } }); } $field.each(function (_index, el) { var $el = $(el), $elGroup = $el.closest($group), isCheckbox = $el.filter(selector.checkbox).length > 0, isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required), isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled), validation = module.get.validation($el), hasEmptyRule = validation ? $.grep(validation.rules, function (rule) { return rule.type === 'empty'; }) !== 0 : false, identifier = module.get.identifier(validation, $el) ; if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) { if (isCheckbox) { module.verbose("Adding 'checked' rule on field", identifier); module.add.rule(identifier, 'checked'); } else { module.verbose("Adding 'empty' rule on field", identifier); module.add.rule(identifier, 'empty'); } } }); }, optional: function (identifier, bool) { bool = bool !== false; $.each(validation, function (fieldName, field) { if (identifier === fieldName || identifier === field.identifier) { field.optional = bool; } }); }, }, validate: { form: function (event, ignoreCallbacks) { var values = module.get.values(); // input keydown event will fire submit repeatedly by browser default if (keyHeldDown) { return false; } $module.removeClass(className.initial); // reset errors formErrors = []; if (module.determine.isValid()) { module.debug('Form has no validation errors, submitting'); module.set.success(); if (!settings.inline) { module.remove.errors(); } if (ignoreCallbacks !== true) { return settings.onSuccess.call(element, event, values); } } else { module.debug('Form has errors'); submitting = false; module.set.error(); if (!settings.inline) { module.add.errors(formErrors); } // prevent ajax submit if (event && $module.data('moduleApi') !== undefined) { event.stopImmediatePropagation(); } if (settings.errorFocus && ignoreCallbacks !== true) { var $focusElement, hasTabIndex = true ; if (typeof settings.errorFocus === 'string') { $focusElement = $(document).find(settings.errorFocus); hasTabIndex = $focusElement.is('[tabindex]'); // to be able to focus/scroll into non input elements we need a tabindex if (!hasTabIndex) { $focusElement.attr('tabindex', -1); } } else { $focusElement = $group.filter('.' + className.error).first().find(selector.field); } $focusElement.trigger('focus'); // only remove tabindex if it was dynamically created above if (!hasTabIndex) { $focusElement.removeAttr('tabindex'); } } if (ignoreCallbacks !== true) { return settings.onFailure.call(element, formErrors, values); } } }, // takes a validation object and returns whether field passes validation field: function (field, fieldName, showErrors) { showErrors = showErrors !== undefined ? showErrors : true; if (typeof field === 'string') { module.verbose('Validating field', field); fieldName = field; field = validation[field]; } if (!field) { module.debug('Unable to find field validation. Skipping', fieldName); return true; } var identifier = field.identifier || fieldName, $field = module.get.field(identifier), $dependsField = field.depends ? module.get.field(field.depends) : false, fieldValid = true, fieldErrors = [], isDisabled = $field.filter(':not(:disabled)').length === 0, validationMessage = $field[0].validationMessage, errorLimit ; if (!field.identifier) { module.debug('Using field name as identifier', identifier); field.identifier = identifier; } if (validationMessage) { module.debug('Field is natively invalid', identifier); fieldErrors.push(validationMessage); fieldValid = false; if (showErrors) { $field.closest($group).addClass(className.error); } } else if (showErrors) { $field.closest($group).removeClass(className.error); } if (isDisabled) { module.debug('Field is disabled. Skipping', identifier); } else if (field.optional && module.is.blank($field)) { module.debug('Field is optional and blank. Skipping', identifier); } else if (field.depends && module.is.empty($dependsField)) { module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField); } else if (field.rules !== undefined) { errorLimit = field.errorLimit || settings.errorLimit; $.each(field.rules, function (index, rule) { if (module.has.field(identifier) && (!errorLimit || fieldErrors.length < errorLimit)) { var invalidFields = module.validate.rule(field, rule, true) || []; if (invalidFields.length > 0) { module.debug('Field is invalid', identifier, rule.type); fieldErrors.push(module.get.prompt(rule, field)); fieldValid = false; if (showErrors) { $(invalidFields).closest($group).addClass(className.error); } } } }); } if (fieldValid) { if (showErrors) { module.remove.prompt(identifier); settings.onValid.call($field); } } else { if (showErrors) { formErrors = formErrors.concat(fieldErrors); module.add.prompt(identifier, fieldErrors, true); settings.onInvalid.call($field, fieldErrors); } return false; } return true; }, // takes validation rule and returns whether field passes rule rule: function (field, rule, internal) { var $field = module.get.field(field.identifier), ancillary = module.get.ancillaryValue(rule), ruleName = module.get.ruleName(rule), ruleFunction = settings.rules[ruleName], invalidFields = [], isCheckbox = $field.is(selector.checkbox), isValid = function (field) { var value = isCheckbox ? $(field).filter(':checked').val() : $(field).val(); // cast to string avoiding encoding special values value = value === undefined || value === '' || value === null ? '' : ((settings.shouldTrim && rule.shouldTrim !== false) || rule.shouldTrim ? String(value + '').trim() : String(value + '')); return ruleFunction.call(field, value, ancillary, module); } ; if (!isFunction(ruleFunction)) { module.error(error.noRule, ruleName); return; } if (isCheckbox) { if (!isValid($field)) { invalidFields = $field; } } else { $.each($field, function (index, field) { if (!isValid(field)) { invalidFields.push(field); } }); } return internal ? invalidFields : invalidFields.length === 0; }, }, setting: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { settings[name] = value; } else { return settings[name]; } }, internal: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, module, name); } else if (value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function () { if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = Date.now(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ Name: message[0], Arguments: [].slice.call(message, 1) || '', Element: element, 'Execution Time': executionTime, }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(function () { module.performance.display(); }, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if ($allModules.length > 1) { title += ' (' + $allModules.length + ')'; } if (performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data.Name + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; }, }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query === 'string' && object !== undefined) { query = query.split(/[ .]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = depth !== maxDepth ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query; if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if (isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; }, }; module.initialize(); }); return returnedValue !== undefined ? returnedValue : this; }; $.fn.form.settings = { name: 'Form', namespace: 'form', debug: false, verbose: false, performance: true, fields: false, keyboardShortcuts: true, on: 'submit', inline: false, delay: 200, revalidate: true, shouldTrim: true, transition: 'scale', duration: 200, autoCheckRequired: false, preventLeaving: false, errorFocus: true, dateHandling: 'date', // 'date', 'input', 'formatter' errorLimit: 0, onValid: function () {}, onInvalid: function () {}, onSuccess: function () { return true; }, onFailure: function () { return false; }, onDirty: function () {}, onClean: function () {}, metadata: { defaultValue: 'default', validate: 'validate', isDirty: 'isDirty', }, regExp: { htmlID: /^[A-Za-z][\w.:-]*$/g, bracket: /\[(.*)]/i, decimal: /^\d+\.?\d*$/, email: /^[\w!#$%&'*+./=?^`{|}~-]+@[\da-z]([\da-z-]*[\da-z])?(\.[\da-z]([\da-z-]*[\da-z])?)*$/i, escape: /[$()*+,./:=?@[\\\]^{|}-]/g, flags: /^\/(.*)\/(.*)?/, integer: /^-?\d+$/, number: /^-?\d*(\.\d+)?$/, url: /(https?:\/\/(?:www\.|(?!www))[^\s.]+\.\S{2,}|www\.\S+\.\S{2,})/i, }, text: { and: 'and', unspecifiedRule: 'Please enter a valid value', unspecifiedField: 'This field', leavingMessage: 'There are unsaved changes on this page which will be discarded if you continue.', }, prompt: { range: '{name} must be in a range from {min} to {max}', maxValue: '{name} must have a maximum value of {ruleValue}', minValue: '{name} must have a minimum value of {ruleValue}', empty: '{name} must have a value', checked: '{name} must be checked', email: '{name} must be a valid e-mail', url: '{name} must be a valid url', regExp: '{name} is not formatted correctly', integer: '{name} must be an integer', decimal: '{name} must be a decimal number', number: '{name} must be set to a number', is: '{name} must be "{ruleValue}"', isExactly: '{name} must be exactly "{ruleValue}"', not: '{name} cannot be set to "{ruleValue}"', notExactly: '{name} cannot be set to exactly "{ruleValue}"', contains: '{name} must contain "{ruleValue}"', containsExactly: '{name} must contain exactly "{ruleValue}"', doesntContain: '{name} cannot contain "{ruleValue}"', doesntContainExactly: '{name} cannot contain exactly "{ruleValue}"', minLength: '{name} must be at least {ruleValue} characters', exactLength: '{name} must be exactly {ruleValue} characters', maxLength: '{name} cannot be longer than {ruleValue} characters', size: '{name} must have a length between {min} and {max} characters', match: '{name} must match {ruleValue} field', different: '{name} must have a different value than {ruleValue} field', creditCard: '{name} must be a valid credit card number', minCount: '{name} must have at least {ruleValue} choices', exactCount: '{name} must have exactly {ruleValue} choices', maxCount: '{name} must have {ruleValue} or less choices', addErrors: '{name}: {error}', }, selector: { checkbox: 'input[type="checkbox"], input[type="radio"]', clear: '.clear', field: 'input:not(.search):not([type="reset"]):not([type="button"]):not([type="submit"]), textarea, select', file: 'input[type="file"]', group: '.field', input: 'input', message: '.error.message', prompt: '.prompt.label', radio: 'input[type="radio"]', reset: '.reset:not([type="reset"])', submit: '.submit:not([type="submit"])', uiCheckbox: '.ui.checkbox', uiDropdown: '.ui.dropdown', uiCalendar: '.ui.calendar', }, className: { initial: 'initial', error: 'error', label: 'ui basic red pointing prompt label', pressed: 'down', success: 'success', required: 'required', disabled: 'disabled', }, error: { method: 'The method you called is not defined.', noRule: 'There is no rule matching the one you specified', noField: 'Field identifier {identifier} not found', noElement: 'This module requires ui {element}', noErrorMessage: 'No error message provided', }, templates: { // template that produces error message error: function (errors) { var html = ''; return html; }, // template that produces label content prompt: function (errors) { if (errors.length === 1) { return errors[0]; } var html = ''; return html; }, }, formatter: { date: function (date) { return Intl.DateTimeFormat('en-GB').format(date); }, datetime: function (date) { return Intl.DateTimeFormat('en-GB', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }).format(date); }, time: function (date) { return Intl.DateTimeFormat('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit', }).format(date); }, month: function (date) { return Intl.DateTimeFormat('en-GB', { month: '2-digit', year: 'numeric', }).format(date); }, year: function (date) { return Intl.DateTimeFormat('en-GB', { year: 'numeric', }).format(date); }, }, rules: { // is not empty or blank string empty: function (value) { return !(value === undefined || value === '' || (Array.isArray(value) && value.length === 0)); }, // checkbox checked checked: function () { return $(this).filter(':checked').length > 0; }, // is most likely an email email: function (value) { return $.fn.form.settings.regExp.email.test(value); }, // value is most likely url url: function (value) { return $.fn.form.settings.regExp.url.test(value); }, // matches specified regExp regExp: function (value, regExp) { if (regExp instanceof RegExp) { return value.match(regExp); } var regExpParts = regExp.match($.fn.form.settings.regExp.flags), flags ; // regular expression specified as /baz/gi (flags) if (regExpParts) { regExp = regExpParts.length >= 2 ? regExpParts[1] : regExp; flags = regExpParts.length >= 3 ? regExpParts[2] : ''; } return value.match(new RegExp(regExp, flags)); }, minValue: function (value, range) { return $.fn.form.settings.rules.range(value, range + '..', 'number'); }, maxValue: function (value, range) { return $.fn.form.settings.rules.range(value, '..' + range, 'number'); }, // is valid integer or matches range integer: function (value, range) { return $.fn.form.settings.rules.range(value, range, 'integer'); }, range: function (value, range, regExp, testLength) { if (typeof regExp === 'string') { regExp = $.fn.form.settings.regExp[regExp]; } if (!(regExp instanceof RegExp)) { regExp = $.fn.form.settings.regExp.integer; } var min, max, parts ; if (!range || ['', '..'].indexOf(range) !== -1) { // do nothing } else if (range.indexOf('..') === -1) { if (regExp.test(range)) { min = range - 0; max = min; } } else { parts = range.split('..', 2); if (regExp.test(parts[0])) { min = parts[0] - 0; } if (regExp.test(parts[1])) { max = parts[1] - 0; } } if (testLength) { value = value.length; } return ( regExp.test(value) && (min === undefined || value >= min) && (max === undefined || value <= max) ); }, // is valid number (with decimal) decimal: function (value, range) { return $.fn.form.settings.rules.range(value, range, 'decimal'); }, // is valid number number: function (value, range) { return $.fn.form.settings.rules.range(value, range, 'number'); }, // is value (case insensitive) is: function (value, text) { text = typeof text === 'string' ? text.toLowerCase() : text; value = typeof value === 'string' ? value.toLowerCase() : value; return value == text; }, // is value isExactly: function (value, text) { return value == text; }, // value is not another value (case insensitive) not: function (value, notValue) { value = typeof value === 'string' ? value.toLowerCase() : value; notValue = typeof notValue === 'string' ? notValue.toLowerCase() : notValue; return value != notValue; }, // value is not another value (case sensitive) notExactly: function (value, notValue) { return value != notValue; }, // value contains text (insensitive) contains: function (value, text) { // escape regex characters text = text.replace($.fn.form.settings.regExp.escape, '\\$&'); return value.search(new RegExp(text, 'i')) !== -1; }, // value contains text (case sensitive) containsExactly: function (value, text) { // escape regex characters text = text.replace($.fn.form.settings.regExp.escape, '\\$&'); return value.search(new RegExp(text)) !== -1; }, // value contains text (insensitive) doesntContain: function (value, text) { // escape regex characters text = text.replace($.fn.form.settings.regExp.escape, '\\$&'); return value.search(new RegExp(text, 'i')) === -1; }, // value contains text (case sensitive) doesntContainExactly: function (value, text) { // escape regex characters text = text.replace($.fn.form.settings.regExp.escape, '\\$&'); return value.search(new RegExp(text)) === -1; }, // is at least string length minLength: function (value, minLength) { return $.fn.form.settings.rules.range(value, minLength + '..', 'integer', true); }, // is exactly length exactLength: function (value, requiredLength) { return $.fn.form.settings.rules.range(value, requiredLength + '..' + requiredLength, 'integer', true); }, // is less than length maxLength: function (value, maxLength) { return $.fn.form.settings.rules.range(value, '..' + maxLength, 'integer', true); }, size: function (value, range) { return $.fn.form.settings.rules.range(value, range, 'integer', true); }, // matches another field match: function (value, identifier, module) { var matchingValue = module.get.value(identifier, true); return matchingValue !== undefined ? value.toString() === matchingValue.toString() : false; }, // different than another field different: function (value, identifier, module) { var matchingValue = module.get.value(identifier, true); return matchingValue !== undefined ? value.toString() !== matchingValue.toString() : false; }, creditCard: function (cardNumber, cardTypes) { var cards = { visa: { pattern: /^4/, length: [16], }, amex: { pattern: /^3[47]/, length: [15], }, mastercard: { pattern: /^5[1-5]/, length: [16], }, discover: { pattern: /^(6011|622(12[6-9]|1[3-9]\d|[2-8]\d{2}|9[01]\d|92[0-5]|64[4-9])|65)/, length: [16], }, unionPay: { pattern: /^(62|88)/, length: [16, 17, 18, 19], }, jcb: { pattern: /^35(2[89]|[3-8]\d)/, length: [16], }, maestro: { pattern: /^(5018|5020|5038|6304|6759|676[1-3])/, length: [12, 13, 14, 15, 16, 17, 18, 19], }, dinersClub: { pattern: /^(30[0-5]|^36)/, length: [14], }, laser: { pattern: /^(6304|670[69]|6771)/, length: [16, 17, 18, 19], }, visaElectron: { pattern: /^(4026|417500|4508|4844|491(3|7))/, length: [16], }, }, valid = {}, validCard = false, requiredTypes = typeof cardTypes === 'string' ? cardTypes.split(',') : false, unionPay, validation ; if (typeof cardNumber !== 'string' || cardNumber.length === 0) { return; } // allow dashes and spaces in card cardNumber = cardNumber.replace(/[\s-]/g, ''); // verify card types if (requiredTypes) { $.each(requiredTypes, function (index, type) { // verify each card type validation = cards[type]; if (validation) { valid = { length: $.inArray(cardNumber.length, validation.length) !== -1, pattern: cardNumber.search(validation.pattern) !== -1, }; if (valid.length > 0 && valid.pattern) { validCard = true; } } }); if (!validCard) { return false; } } // skip luhn for UnionPay unionPay = { number: $.inArray(cardNumber.length, cards.unionPay.length) !== -1, pattern: cardNumber.search(cards.unionPay.pattern) !== -1, }; if (unionPay.number && unionPay.pattern) { return true; } // verify luhn, adapted from var length = cardNumber.length, multiple = 0, producedValue = [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9], ], sum = 0 ; while (length--) { sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)]; multiple ^= 1; // eslint-disable-line no-bitwise } return sum % 10 === 0 && sum > 0; }, minCount: function (value, minCount) { minCount = Number(minCount); if (minCount === 0) { return true; } if (minCount === 1) { return value !== ''; } return value.split(',').length >= minCount; }, exactCount: function (value, exactCount) { exactCount = Number(exactCount); if (exactCount === 0) { return value === ''; } if (exactCount === 1) { return value !== '' && value.search(',') === -1; } return value.split(',').length === exactCount; }, maxCount: function (value, maxCount) { maxCount = Number(maxCount); if (maxCount === 0) { return false; } if (maxCount === 1) { return value.search(',') === -1; } return value.split(',').length <= maxCount; }, }, }; })(jQuery, window, document); /*! * # Fomantic-UI 2.9.3 - Accordion * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.accordion = function (parameters) { var $allModules = $(this), time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), returnedValue ; $allModules.each(function () { var settings = $.isPlainObject(parameters) ? $.extend(true, {}, $.fn.accordion.settings, parameters) : $.extend({}, $.fn.accordion.settings), className = settings.className, namespace = settings.namespace, selector = settings.selector, error = settings.error, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $title = $module.find(selector.title), $content = $module.find(selector.content), element = this, instance = $module.data(moduleNamespace), observer, module ; module = { initialize: function () { module.debug('Initializing', $module); module.bind.events(); if (settings.observeChanges) { module.observeChanges(); } module.instantiate(); }, instantiate: function () { instance = module; $module .data(moduleNamespace, module) ; }, destroy: function () { module.debug('Destroying previous instance', $module); $module .off(eventNamespace) .removeData(moduleNamespace) ; }, refresh: function () { $title = $module.find(selector.title); $content = $module.find(selector.content); }, observeChanges: function () { if ('MutationObserver' in window) { observer = new MutationObserver(function (mutations) { module.debug('DOM tree modified, updating selector cache'); module.refresh(); }); observer.observe(element, { childList: true, subtree: true, }); module.debug('Setting up mutation observer', observer); } }, bind: { events: function () { module.debug('Binding delegated events'); $module .on(settings.on + eventNamespace, selector.trigger, module.event.click) ; }, }, event: { click: function (event) { if ($(event.target).closest(selector.ignore).length === 0) { module.toggle.call(this); } }, }, toggle: function (query) { var $activeTitle = query !== undefined ? (typeof query === 'number' ? $title.eq(query) : $(query).closest(selector.title)) : $(this).closest(selector.title), $activeContent = $activeTitle.next($content), isAnimating = $activeContent.hasClass(className.animating), isActive = $activeContent.hasClass(className.active), isOpen = isActive && !isAnimating, isOpening = !isActive && isAnimating ; module.debug('Toggling visibility of content', $activeTitle); if (isOpen || isOpening) { if (settings.collapsible) { module.close.call($activeTitle); } else { module.debug('Cannot close accordion content collapsing is disabled'); } } else { module.open.call($activeTitle); } }, open: function (query) { var $activeTitle = query !== undefined ? (typeof query === 'number' ? $title.eq(query) : $(query).closest(selector.title)) : $(this).closest(selector.title), $activeContent = $activeTitle.next($content), isAnimating = $activeContent.hasClass(className.animating), isActive = $activeContent.hasClass(className.active), isOpen = isActive || isAnimating ; if (isOpen) { module.debug('Accordion already open, skipping', $activeContent); return; } module.debug('Opening accordion content', $activeTitle); settings.onOpening.call($activeContent); settings.onChanging.call($activeContent); if (settings.exclusive) { module.closeOthers.call($activeTitle); } $activeTitle .addClass(className.active) ; $activeContent .stop(true, true) .addClass(className.animating) ; if (settings.animateChildren) { if ($.fn.transition !== undefined) { $activeContent .children() .transition({ animation: 'fade in', queue: false, useFailSafe: true, debug: settings.debug, verbose: settings.verbose, silent: settings.silent, duration: settings.duration, skipInlineHidden: true, onComplete: function () { $activeContent.children().removeClass(className.transition); }, }) ; } else { $activeContent .children() .stop(true, true) .animate({ opacity: 1, }, settings.duration, module.resetOpacity); } } $activeContent .slideDown(settings.duration, settings.easing, function () { $activeContent .removeClass(className.animating) .addClass(className.active) ; module.reset.display.call(this); settings.onOpen.call(this); settings.onChange.call(this); }) ; }, close: function (query) { var $activeTitle = query !== undefined ? (typeof query === 'number' ? $title.eq(query) : $(query).closest(selector.title)) : $(this).closest(selector.title), $activeContent = $activeTitle.next($content), isAnimating = $activeContent.hasClass(className.animating), isActive = $activeContent.hasClass(className.active), isOpening = !isActive && isAnimating, isClosing = isActive && isAnimating ; if ((isActive || isOpening) && !isClosing) { module.debug('Closing accordion content', $activeContent); settings.onClosing.call($activeContent); settings.onChanging.call($activeContent); $activeTitle .removeClass(className.active) ; $activeContent .stop(true, true) .addClass(className.animating) ; if (settings.animateChildren) { if ($.fn.transition !== undefined) { $activeContent .children() .transition({ animation: 'fade out', queue: false, useFailSafe: true, debug: settings.debug, verbose: settings.verbose, silent: settings.silent, duration: settings.duration, skipInlineHidden: true, }) ; } else { $activeContent .children() .stop(true, true) .animate({ opacity: 0, }, settings.duration, module.resetOpacity); } } $activeContent .slideUp(settings.duration, settings.easing, function () { $activeContent .removeClass(className.animating) .removeClass(className.active) ; module.reset.display.call(this); settings.onClose.call(this); settings.onChange.call(this); }) ; } }, closeOthers: function (index) { var $activeTitle = index !== undefined ? $title.eq(index) : $(this).closest(selector.title), $parentTitles = $activeTitle.parents(selector.content).prev(selector.title), $activeAccordion = $activeTitle.closest(selector.accordion), activeSelector = selector.title + '.' + className.active + ':visible', activeContent = selector.content + '.' + className.active + ':visible', $openTitles, $nestedTitles, $openContents ; if (settings.closeNested) { $openTitles = $activeAccordion.find(activeSelector).not($parentTitles); $openContents = $openTitles.next($content); } else { $openTitles = $activeAccordion.find(activeSelector).not($parentTitles); $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles); $openTitles = $openTitles.not($nestedTitles); $openContents = $openTitles.next($content); } if ($openTitles.length > 0) { module.debug('Exclusive enabled, closing other content', $openTitles); $openTitles .removeClass(className.active) ; $openContents .removeClass(className.animating) .stop(true, true) ; if (settings.animateChildren) { if ($.fn.transition !== undefined) { $openContents .children() .transition({ animation: 'fade out', useFailSafe: true, debug: settings.debug, verbose: settings.verbose, silent: settings.silent, duration: settings.duration, skipInlineHidden: true, }) ; } else { $openContents .children() .stop(true, true) .animate({ opacity: 0, }, settings.duration, module.resetOpacity); } } $openContents .slideUp(settings.duration, settings.easing, function () { $(this).removeClass(className.active); module.reset.display.call(this); }) ; } }, reset: { display: function () { module.verbose('Removing inline display from element', this); var $element = $(this); $element.css('display', ''); if ($element.attr('style') === '') { $element .attr('style', '') .removeAttr('style') ; } }, opacity: function () { module.verbose('Removing inline opacity from element', this); var $element = $(this); $element.css('opacity', ''); if ($element.attr('style') === '') { $element .attr('style', '') .removeAttr('style') ; } }, }, setting: function (name, value) { module.debug('Changing setting', name, value); if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { if ($.isPlainObject(settings[name])) { $.extend(true, settings[name], value); } else { settings[name] = value; } } else { return settings[name]; } }, internal: function (name, value) { module.debug('Changing internal', name, value); if (value !== undefined) { if ($.isPlainObject(name)) { $.extend(true, module, name); } else { module[name] = value; } } else { return module[name]; } }, debug: function () { if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = Date.now(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ Name: message[0], Arguments: [].slice.call(message, 1) || '', Element: element, 'Execution Time': executionTime, }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(function () { module.performance.display(); }, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if (performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data.Name + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; }, }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query === 'string' && object !== undefined) { query = query.split(/[ .]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = depth !== maxDepth ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query; if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if (isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; }, }; if (methodInvoked) { if (instance === undefined) { module.initialize(); } module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } }); return returnedValue !== undefined ? returnedValue : this; }; $.fn.accordion.settings = { name: 'Accordion', namespace: 'accordion', silent: false, debug: false, verbose: false, performance: true, on: 'click', // event on title that opens accordion observeChanges: true, // whether accordion should automatically refresh on DOM insertion exclusive: true, // whether a single accordion content panel should be open at once collapsible: true, // whether accordion content can be closed closeNested: false, // whether nested content should be closed when a panel is closed animateChildren: true, // whether children opacity should be animated duration: 350, // duration of animation easing: 'easeOutQuad', // easing equation for animation onOpening: function () {}, // callback before open animation onClosing: function () {}, // callback before closing animation onChanging: function () {}, // callback before closing or opening animation onOpen: function () {}, // callback after open animation onClose: function () {}, // callback after closing animation onChange: function () {}, // callback after closing or opening animation error: { method: 'The method you called is not defined', }, className: { active: 'active', animating: 'animating', transition: 'transition', }, selector: { accordion: '.accordion', title: '.title', trigger: '.title', ignore: '.ui.dropdown', content: '.content', }, }; // Adds easing $.extend($.easing, { easeOutQuad: function (x) { return 1 - (1 - x) * (1 - x); }, }); })(jQuery, window, document); /*! * # Fomantic-UI 2.9.3 - Calendar * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.calendar = function (parameters) { var $allModules = $(this), $document = $(document), time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), returnedValue, timeGapTable = { 5: { row: 4, column: 3 }, 10: { row: 3, column: 2 }, 15: { row: 2, column: 2 }, 20: { row: 3, column: 1 }, 30: { row: 2, column: 1 }, }, numberText = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'] ; $allModules.each(function () { var settings = $.isPlainObject(parameters) ? $.extend(true, {}, $.fn.calendar.settings, parameters) : $.extend({}, $.fn.calendar.settings), className = settings.className, namespace = settings.namespace, selector = settings.selector, formatter = settings.formatter, parser = settings.parser, metadata = settings.metadata, timeGap = timeGapTable[settings.minTimeGap], error = settings.error, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $input = $module.find(selector.input), $activator = $module.find(selector.activator), element = this, instance = $module.data(moduleNamespace), $container = instance && instance.popupId ? $document.find('#' + instance.popupId) : $module.find(selector.popup), isTouch, isTouchDown = false, isInverted = $module.hasClass(className.inverted), focusDateUsedForRange = false, selectionComplete = false, classObserver, module ; module = { initialize: function () { module.debug('Initializing calendar for', element, $module); isTouch = module.get.isTouch(); module.setup.config(); module.setup.popup(); module.setup.inline(); module.setup.input(); module.setup.date(); module.create.calendar(); module.bind.events(); module.observeChanges(); module.instantiate(); }, instantiate: function () { module.verbose('Storing instance of calendar'); instance = module; $module.data(moduleNamespace, instance); }, destroy: function () { module.verbose('Destroying previous calendar for', element); $module.removeData(moduleNamespace); module.unbind.events(); module.disconnect.classObserver(); }, setup: { config: function () { if (module.get.minDate() !== null) { module.set.minDate($module.data(metadata.minDate)); } if (module.get.maxDate() !== null) { module.set.maxDate($module.data(metadata.maxDate)); } module.setting('type', module.get.type()); module.setting('on', settings.on || 'click'); }, popup: function () { if (settings.inline) { return; } if ($activator.length === 0) { $activator = $module.children().first(); if ($activator.length === 0) { return; } } if ($.fn.popup === undefined) { module.error(error.popup); return; } if ($container.length === 0) { if (settings.context) { module.popupId = namespace + '_popup_' + (Math.random().toString(16) + '000000000').slice(2, 10); $container = $('
', { id: module.popupId }).addClass(className.popup).appendTo($document.find(settings.context)); } else { // prepend the popup element to the activator's parent so that it has less chance of messing with // the styling (eg input action button needs to be the last child to have correct border radius) var $activatorParent = $activator.parent(), domPositionFunction = $activatorParent.closest(selector.append).length > 0 ? 'appendTo' : 'prependTo' ; $container = $('
').addClass(className.popup)[domPositionFunction]($activatorParent); } } $container.addClass(className.calendar); if (isInverted) { $container.addClass(className.inverted); } var onVisible = function () { module.refreshTooltips(); return settings.onVisible.apply($container, arguments); }; var onHidden = function () { module.blur(); return settings.onHidden.apply($container, arguments); }; if ($input.length === 0) { // no input, $container has to handle focus/blur $container.attr('tabindex', '0'); onVisible = function () { module.refreshTooltips(); module.focus(); return settings.onVisible.apply($container, arguments); }; } var onShow = function () { // reset the focus date onShow module.set.focusDate(module.get.date()); module.set.mode(module.get.validatedMode(settings.startMode)); return settings.onShow.apply($container, arguments); }; var on = module.setting('on'); var options = $.extend({}, settings.popupOptions, { popup: $container, movePopup: !settings.context, on: on, hoverable: on === 'hover', closable: on === 'click', onShow: onShow, onVisible: onVisible, onHide: settings.onHide, onHidden: onHidden, }); module.popup(options); }, inline: function () { if ($activator.length > 0 && !settings.inline) { return; } settings.inline = true; $container = $('
').addClass(className.calendar).appendTo($module); if ($input.length === 0) { $container.attr('tabindex', '0'); } }, input: function () { if (settings.touchReadonly && $input.length > 0 && isTouch) { $input.prop('readonly', true); } module.check.disabled(); }, date: function () { var date; if (settings.initialDate) { date = parser.date(settings.initialDate, settings); } else if ($module.data(metadata.date) !== undefined) { date = parser.date($module.data(metadata.date), settings); } else if ($input.length > 0) { date = parser.date($input.val(), settings); } module.set.date(date, settings.formatInput, false); module.set.mode(module.get.mode(), false); }, }, trigger: { change: function () { var inputElement = $input[0] ; if (inputElement) { var events = document.createEvent('HTMLEvents'); module.verbose('Triggering native change event'); events.initEvent('change', true, false); inputElement.dispatchEvent(events); } }, }, create: { calendar: function () { var i, r, c, p, row, cell, pageGrid ; var mode = module.get.mode(), today = new Date(), date = module.get.date(), focusDate = module.get.focusDate(), display = module.helper.dateInRange(focusDate || date || parser.date(settings.initialDate, settings) || today) ; if (!focusDate) { focusDate = display; module.set.focusDate(focusDate, false, false); } var isYear = mode === 'year', isMonth = mode === 'month', isDay = mode === 'day', isHour = mode === 'hour', isMinute = mode === 'minute', isTimeOnly = settings.type === 'time' ; var multiMonth = Math.max(settings.multiMonth, 1); var monthOffset = !isDay ? 0 : module.get.monthOffset(); var minute = display.getMinutes(), hour = display.getHours(), day = display.getDate(), startMonth = display.getMonth() + monthOffset, year = display.getFullYear() ; var columns = isDay ? (settings.showWeekNumbers ? 8 : 7) : (isHour ? 4 : timeGap.column); var rows = isDay || isHour ? 6 : timeGap.row; var pages = isDay ? multiMonth : 1; var container = $container; var tooltipPosition = container.hasClass('left') ? 'right center' : 'left center'; container.empty(); if (pages > 1) { pageGrid = $('
').addClass(className.grid).appendTo(container); } for (p = 0; p < pages; p++) { if (pages > 1) { var pageColumn = $('
').addClass(className.column).appendTo(pageGrid); container = pageColumn; } var month = startMonth + p; var firstMonthDayColumn = (new Date(year, month, 1).getDay() - (settings.firstDayOfWeek % 7) + 7) % 7; if (!settings.constantHeight && isDay) { var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn; rows = Math.ceil(requiredCells / 7); } var yearChange = isYear ? 10 : (isMonth ? 1 : 0), monthChange = isDay ? 1 : 0, dayChange = isHour || isMinute ? 1 : 0, prevNextDay = isHour || isMinute ? day : 1, prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour), nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour), prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) : (isMonth ? new Date(year, 0, 0) : (isDay // eslint-disable-line unicorn/no-nested-ternary ? new Date(year, month, 0) : new Date(year, month, day, -1))), nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) : (isMonth ? new Date(year + 1, 0, 1) : (isDay // eslint-disable-line unicorn/no-nested-ternary ? new Date(year, month + 1, 1) : new Date(year, month, day + 1))) ; var tempMode = mode; if (isDay && settings.showWeekNumbers) { tempMode += ' andweek'; } var table = $('').addClass(className.table).addClass(tempMode).addClass(numberText[columns] + ' column') .appendTo(container); if (isInverted) { table.addClass(className.inverted); } var textColumns = columns; // no header for time-only mode if (!isTimeOnly) { var thead = $('').appendTo(table); row = $('').appendTo(thead); cell = $('').appendTo(thead); if (settings.showWeekNumbers) { cell = $('').appendTo(table); i = isYear ? Math.ceil(year / 10) * 10 - 9 : (isDay ? 1 - firstMonthDayColumn : 0); for (r = 0; r < rows; r++) { row = $('').appendTo(tbody); if (isDay && settings.showWeekNumbers) { cell = $('').appendTo(tbody); var todayButton = $('
').attr('colspan', '' + columns).appendTo(row); var headerDate = isYear || isMonth ? new Date(year, 0, 1) : (isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute)); var headerText = $('').addClass(className.link).appendTo(cell); headerText.text(module.helper.dateFormat(formatter[mode + 'Header'], headerDate)); var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') : (isDay ? (settings.disableMonth ? 'year' : 'month') // eslint-disable-line unicorn/no-nested-ternary : 'day'); headerText.data(metadata.mode, newMode); if (p === 0) { var prev = $('').addClass(className.prev).appendTo(cell); prev.data(metadata.focusDate, prevDate); prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode)); $('').addClass(className.prevIcon).appendTo(prev); } if (p === pages - 1) { var next = $('').addClass(className.next).appendTo(cell); next.data(metadata.focusDate, nextDate); next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode)); $('').addClass(className.nextIcon).appendTo(next); } if (isDay) { row = $('
').appendTo(row); cell.text(settings.text.weekNo); cell.addClass(className.weekCell); textColumns--; } for (i = 0; i < textColumns; i++) { cell = $('').appendTo(row); cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings)); } } } var tbody = $('
').appendTo(row); cell.text(module.get.weekOfYear(year, month, i + 1 - settings.firstDayOfWeek)); cell.addClass(className.weekCell); } for (c = 0; c < textColumns; c++, i++) { var cellDate = isYear ? new Date(i, month, 1, hour, minute) : (isMonth ? new Date(year, i, 1, hour, minute) : (isDay // eslint-disable-line unicorn/no-nested-ternary ? new Date(year, month, i, hour, minute) : (isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap)))); var cellText = isYear ? i : (isMonth ? settings.text.monthsShort[i] : (isDay // eslint-disable-line unicorn/no-nested-ternary ? cellDate.getDate() : module.helper.dateFormat(formatter.cellTime, cellDate))); cell = $('').addClass(className.cell).appendTo(row); cell.text(cellText); cell.data(metadata.date, cellDate); var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12); var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode); var eventDate; if (disabled) { var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates); if (disabledDate !== null && disabledDate[metadata.message]) { cell.attr('data-tooltip', disabledDate[metadata.message]); cell.attr('data-position', disabledDate[metadata.position] || tooltipPosition); if (disabledDate[metadata.inverted] || (isInverted && disabledDate[metadata.inverted] === undefined)) { cell.attr('data-inverted', ''); } if (disabledDate[metadata.variation]) { cell.attr('data-variation', disabledDate[metadata.variation]); } } if (mode === 'hour') { var disabledHour = module.helper.findHourAsObject(cellDate, mode, settings.disabledHours); if (disabledHour !== null && disabledHour[metadata.message]) { cell.attr('data-tooltip', disabledHour[metadata.message]); cell.attr('data-position', disabledHour[metadata.position] || tooltipPosition); if (disabledHour[metadata.inverted] || (isInverted && disabledHour[metadata.inverted] === undefined)) { cell.attr('data-inverted', ''); } if (disabledHour[metadata.variation]) { cell.attr('data-variation', disabledHour[metadata.variation]); } } } } else { eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates); if (eventDate !== null) { cell.addClass(eventDate[metadata.class] || settings.eventClass); if (eventDate[metadata.message]) { cell.attr('data-tooltip', eventDate[metadata.message]); cell.attr('data-position', eventDate[metadata.position] || tooltipPosition); if (eventDate[metadata.inverted] || (isInverted && eventDate[metadata.inverted] === undefined)) { cell.attr('data-inverted', ''); } if (eventDate[metadata.variation]) { cell.attr('data-variation', eventDate[metadata.variation]); } } } } var active = module.helper.dateEqual(cellDate, date, mode); var isToday = module.helper.dateEqual(cellDate, today, mode); cell.toggleClass(className.adjacentCell, adjacent && !eventDate); cell.toggleClass(className.disabledCell, disabled); cell.toggleClass(className.activeCell, active && !(adjacent && disabled)); if (!isHour && !isMinute) { cell.toggleClass(className.todayCell, !adjacent && isToday); } // Allow for external modifications of each cell var cellOptions = { mode: mode, adjacent: adjacent, disabled: disabled, active: active, today: isToday, }; formatter.cell(cell, cellDate, cellOptions); if (module.helper.dateEqual(cellDate, focusDate, mode)) { // ensure that the focus date is exactly equal to the cell date // so that, if selected, the correct value is set module.set.focusDate(cellDate, false, false); } } } if (settings.today) { var todayRow = $('
').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow); todayButton.text(formatter.today(settings)); todayButton.data(metadata.date, today); } module.update.focus(false, table); if (settings.inline) { module.refreshTooltips(); } } }, }, update: { focus: function (updateRange, container) { container = container || $container; var mode = module.get.mode(); var date = module.get.date(); var focusDate = module.get.focusDate(); var startDate = module.get.startDate(); var endDate = module.get.endDate(); var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null); container.find('td').each(function () { var $cell = $(this); var cellDate = $cell.data(metadata.date); if (!cellDate) { return; } var disabled = $cell.hasClass(className.disabledCell); var active = $cell.hasClass(className.activeCell); var adjacent = $cell.hasClass(className.adjacentCell); var focused = module.helper.dateEqual(cellDate, focusDate, mode); var inRange = !rangeDate ? false : (!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) || (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate)); $cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled); if (module.helper.isTodayButton($cell)) { return; } $cell.toggleClass(className.rangeCell, inRange && !active && !disabled); }); }, }, refresh: function () { module.create.calendar(); }, refreshTooltips: function () { var winWidth = $(window).width(); $container.find('td[data-position]').each(function () { var $cell = $(this); var tooltipWidth = window.getComputedStyle($cell[0], '::after').width.replace(/[^\d.]/g, ''); var tooltipPosition = $cell.attr('data-position'); // use a fallback width of 250 (calendar width) for IE/Edge (which return "auto") var calcPosition = (winWidth - $cell.width() - (parseInt(tooltipWidth, 10) || 250)) > $cell.offset().left ? 'right' : 'left'; if (tooltipPosition.indexOf(calcPosition) === -1) { $cell.attr('data-position', tooltipPosition.replace(/(left|right)/, calcPosition)); } }); }, bind: { events: function () { module.debug('Binding events'); $container.on('mousedown' + eventNamespace, module.event.mousedown); $container.on('touchstart' + eventNamespace, module.event.mousedown); $container.on('mouseup' + eventNamespace, module.event.mouseup); $container.on('touchend' + eventNamespace, module.event.mouseup); $container.on('mouseover' + eventNamespace, module.event.mouseover); if ($input.length > 0) { $input.on('input' + eventNamespace, module.event.inputChange); $input.on('focus' + eventNamespace, module.event.inputFocus); $input.on('blur' + eventNamespace, module.event.inputBlur); $input.on('keydown' + eventNamespace, module.event.keydown); } else { $container.on('keydown' + eventNamespace, module.event.keydown); } }, }, unbind: { events: function () { module.debug('Unbinding events'); $container.off(eventNamespace); if ($input.length > 0) { $input.off(eventNamespace); } }, }, event: { mouseover: function (event) { var target = $(event.target); var date = target.data(metadata.date); var mousedown = event.buttons === 1; if (date) { module.set.focusDate(date, false, true, mousedown); } }, mousedown: function (event) { if ($input.length > 0) { // prevent the mousedown on the calendar causing the input to lose focus event.preventDefault(); } isTouchDown = event.type.indexOf('touch') >= 0; var target = $(event.target); var date = target.data(metadata.date); if (date) { module.set.focusDate(date, false, true, true); } }, mouseup: function (event) { // ensure input has focus so that it receives keydown events for calendar navigation module.focus(); event.preventDefault(); event.stopPropagation(); isTouchDown = false; var target = $(event.target); if (target.hasClass('disabled')) { return; } var parent = target.parent(); if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) { // clicked on a child element, switch to parent (used when clicking directly on prev/next icon element) target = parent; } var date = target.data(metadata.date); var focusDate = target.data(metadata.focusDate); var mode = target.data(metadata.mode); if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) { var forceSet = target.hasClass(className.today); module.selectDate(date, forceSet); } else if (focusDate) { module.set.focusDate(focusDate); } else if (mode) { module.set.mode(mode); } }, keydown: function (event) { var keyCode = event.which; if (keyCode === 9) { // tab module.popup('hide'); } if (module.popup('is visible')) { var mode = module.get.mode(); switch (keyCode) { // arrow keys case 37: case 38: case 39: case 40: { var bigIncrement = mode === 'day' ? 7 : (mode === 'hour' ? 4 : (mode === 'minute' ? timeGap.column : 3)); // eslint-disable-line unicorn/no-nested-ternary var increment = keyCode === 37 ? -1 : (keyCode === 38 ? -bigIncrement : (keyCode === 39 ? 1 : bigIncrement)); // eslint-disable-line unicorn/no-nested-ternary increment *= mode === 'minute' ? settings.minTimeGap : 1; var focusDate = module.get.focusDate() || module.get.date() || new Date(); var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0); var month = focusDate.getMonth() + (mode === 'month' ? increment : 0); var day = focusDate.getDate() + (mode === 'day' ? increment : 0); var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0); var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0); var newFocusDate = new Date(year, month, day, hour, minute); if (settings.type === 'time') { newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate); } if (module.helper.isDateInRange(newFocusDate, mode)) { module.set.focusDate(newFocusDate); } break; } // enter key case 13: { var date = module.get.focusDate(); if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) { if (settings.onSelect.call(element, date, module.get.mode()) !== false) { module.selectDate(date); } } // disable form submission: event.preventDefault(); event.stopPropagation(); break; } // escape key case 27: { module.popup('hide'); event.stopPropagation(); break; } } } if (keyCode === 38 || keyCode === 40) { // arrow-up || arrow-down event.preventDefault(); // don't scroll module.popup('show'); } }, inputChange: function () { var val = $input.val(); var date = parser.date(val, settings); module.set.date(date, false); }, inputFocus: function () { $container.addClass(className.active); }, inputBlur: function () { $container.removeClass(className.active); if (settings.formatInput) { var date = module.get.date(); var text = module.helper.dateFormat(formatter[settings.type], date); $input.val(text); } if (selectionComplete) { module.trigger.change(); selectionComplete = false; } }, class: { mutation: function (mutations) { mutations.forEach(function (mutation) { if (mutation.attributeName === 'class') { module.check.disabled(); } }); }, }, }, observeChanges: function () { if ('MutationObserver' in window) { classObserver = new MutationObserver(module.event.class.mutation); module.debug('Setting up mutation observer', classObserver); module.observe.class(); } }, disconnect: { classObserver: function () { if ($input.length > 0 && classObserver) { classObserver.disconnect(); } }, }, observe: { class: function () { if ($input.length > 0 && classObserver) { classObserver.observe($module[0], { attributes: true, }); } }, }, is: { disabled: function () { return $module.hasClass(className.disabled); }, }, check: { disabled: function () { $input.attr('tabindex', module.is.disabled() ? -1 : 0); }, }, get: { weekOfYear: function (weekYear, weekMonth, weekDay) { // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm var ms1d = 24 * 3600 * 1000, ms7d = 7 * ms1d, DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an absolute day number AWN = Math.floor(DC3 / 7), // an absolute week number Wyr = new Date(AWN * ms7d).getUTCFullYear() ; return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1; }, formattedDate: function (format, date) { return module.helper.dateFormat(format || formatter[settings.type], date || module.get.date()); }, date: function () { return module.helper.sanitiseDate($module.data(metadata.date)) || null; }, inputDate: function () { return $input.val(); }, focusDate: function () { return $module.data(metadata.focusDate) || null; }, startDate: function () { var startModule = module.get.calendarModule(settings.startCalendar); return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null; }, endDate: function () { var endModule = module.get.calendarModule(settings.endCalendar); return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null; }, minDate: function () { return $module.data(metadata.minDate) || null; }, maxDate: function () { return $module.data(metadata.maxDate) || null; }, monthOffset: function () { return $module.data(metadata.monthOffset) || settings.monthOffset || 0; }, mode: function () { // only returns valid modes for the current settings var mode = $module.data(metadata.mode) || settings.startMode; return module.get.validatedMode(mode); }, validatedMode: function (mode) { var validModes = module.get.validModes(); if ($.inArray(mode, validModes) >= 0) { return mode; } return settings.type === 'time' ? 'hour' : (settings.type === 'month' ? 'month' : (settings.type === 'year' ? 'year' : 'day')); // eslint-disable-line unicorn/no-nested-ternary }, type: function () { return $module.data(metadata.type) || settings.type; }, validModes: function () { var validModes = []; if (settings.type !== 'time') { if (!settings.disableYear || settings.type === 'year') { validModes.push('year'); } if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') { validModes.push('month'); } if (settings.type.indexOf('date') >= 0) { validModes.push('day'); } } if (settings.type.indexOf('time') >= 0) { validModes.push('hour'); if (!settings.disableMinute) { validModes.push('minute'); } } return validModes; }, isTouch: function () { try { document.createEvent('TouchEvent'); return true; } catch (e) { return false; } }, calendarModule: function (selector) { if (!selector) { return null; } if (!(selector instanceof $)) { selector = $document.find(selector).first(); } // assume range related calendars are using the same namespace return selector.data(moduleNamespace); }, }, set: { date: function (date, updateInput, fireChange) { updateInput = updateInput !== false; fireChange = fireChange !== false; date = module.helper.sanitiseDate(date); date = module.helper.dateInRange(date); var mode = module.get.mode(); var text = module.helper.dateFormat(formatter[settings.type], date); if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) { return false; } module.set.focusDate(date); if (settings.isDisabled(date, mode)) { return false; } var endDate = module.get.endDate(); if (!!endDate && !!date && date > endDate) { // selected date is greater than end date in range, so clear end date module.set.endDate(); } module.set.dataKeyValue(metadata.date, date); if (updateInput && $input.length > 0) { $input.val(text); } if (fireChange) { settings.onChange.call(element, date, text, mode); } }, startDate: function (date, refreshCalendar) { date = module.helper.sanitiseDate(date); var startModule = module.get.calendarModule(settings.startCalendar); if (startModule) { startModule.set.date(date); } module.set.dataKeyValue(metadata.startDate, date, refreshCalendar); }, endDate: function (date, refreshCalendar) { date = module.helper.sanitiseDate(date); var endModule = module.get.calendarModule(settings.endCalendar); if (endModule) { endModule.set.date(date); } module.set.dataKeyValue(metadata.endDate, date, refreshCalendar); }, focusDate: function (date, refreshCalendar, updateFocus, updateRange) { date = module.helper.sanitiseDate(date); date = module.helper.dateInRange(date); var isDay = module.get.mode() === 'day'; var oldFocusDate = module.get.focusDate(); if (isDay && date && oldFocusDate) { var yearDelta = date.getFullYear() - oldFocusDate.getFullYear(); var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth(); if (monthDelta) { var monthOffset = module.get.monthOffset() - monthDelta; module.set.monthOffset(monthOffset, false); } } var changed = module.set.dataKeyValue(metadata.focusDate, date, !!date && refreshCalendar); updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange; focusDateUsedForRange = updateRange; if (updateFocus) { module.update.focus(updateRange); } }, minDate: function (date) { date = module.helper.sanitiseDate(date); if (settings.maxDate !== null && settings.maxDate <= date) { module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate); } else { module.setting('minDate', date); module.set.dataKeyValue(metadata.minDate, date); } }, maxDate: function (date) { date = module.helper.sanitiseDate(date); if (settings.minDate !== null && settings.minDate >= date) { module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate); } else { module.setting('maxDate', date); module.set.dataKeyValue(metadata.maxDate, date); } }, monthOffset: function (monthOffset, refreshCalendar) { var multiMonth = Math.max(settings.multiMonth, 1); monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset)); module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar); }, mode: function (mode, refreshCalendar) { module.set.dataKeyValue(metadata.mode, mode, refreshCalendar); }, dataKeyValue: function (key, value, refreshCalendar) { var oldValue = $module.data(key); var equal = oldValue === value || (oldValue <= value && oldValue >= value); // equality test for dates and string objects if (value) { $module.data(key, value); } else { $module.removeData(key); } refreshCalendar = refreshCalendar !== false && !equal; if (refreshCalendar) { module.refresh(); } return !equal; }, }, selectDate: function (date, forceSet) { module.verbose('New date selection', date); var mode = module.get.mode(); var complete = forceSet || mode === 'minute' || (settings.disableMinute && mode === 'hour') || (settings.type === 'date' && mode === 'day') || (settings.type === 'month' && mode === 'month') || (settings.type === 'year' && mode === 'year'); if (complete) { var canceled = module.set.date(date) === false; if (!canceled) { selectionComplete = true; if (settings.closable) { module.popup('hide'); // if this is a range calendar, focus the container or input. This will open the popup from its event listeners. var endModule = module.get.calendarModule(settings.endCalendar); if (endModule) { endModule.refresh(); if (endModule.setting('on') !== 'focus') { endModule.popup('show'); } endModule.focus(); } } } } else { var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') : (mode === 'month' ? 'day' : (mode === 'day' ? 'hour' : 'minute')); // eslint-disable-line unicorn/no-nested-ternary module.set.mode(newMode); if (mode === 'hour' || (mode === 'day' && module.get.date())) { // the user has chosen enough to consider a valid date/time has been chosen module.set.date(date, true, false); } else { module.set.focusDate(date); } } }, changeDate: function (date) { module.set.date(date); }, clear: function () { module.set.date(); }, popup: function () { return $activator.popup.apply($activator, arguments); }, focus: function () { if ($input.length > 0) { $input.trigger('focus'); } else { $container.trigger('focus'); } }, blur: function () { if ($input.length > 0) { $input.trigger('blur'); } else { $container.trigger('blur'); } }, helper: { dateFormat: function (format, date) { if (!(date instanceof Date)) { return ''; } if (typeof format === 'function') { return format.call(module, date, settings); } var D = date.getDate(), M = date.getMonth(), Y = date.getFullYear(), d = date.getDay(), H = date.getHours(), m = date.getMinutes(), s = date.getSeconds(), w = module.get.weekOfYear(Y, M, D + 1 - settings.firstDayOfWeek), h = H % 12 || 12, a = H < 12 ? settings.text.am.toLowerCase() : settings.text.pm.toLowerCase(), tokens = { D: D, DD: ('0' + D).slice(-2), M: M + 1, MM: ('0' + (M + 1)).slice(-2), MMM: settings.text.monthsShort[M], MMMM: settings.text.months[M], Y: Y, YY: String(Y).slice(2), YYYY: Y, d: d, dd: settings.text.dayNamesShort[d].slice(0, 2), ddd: settings.text.dayNamesShort[d], dddd: settings.text.dayNames[d], h: h, hh: ('0' + h).slice(-2), H: H, HH: ('0' + H).slice(-2), m: m, mm: ('0' + m).slice(-2), s: s, ss: ('0' + s).slice(-2), a: a, A: a.toUpperCase(), S: ['th', 'st', 'nd', 'rd'][(D % 10) > 3 ? 0 : ((D % 100) - (D % 10) === 10 ? 0 : D % 10)], w: w, ww: ('0' + w).slice(-2), } ; return format.replace(settings.regExp.token, function (match) { if (match in tokens) { return tokens[match]; } return match.slice(1, -1); }); }, isDisabled: function (date, mode) { return (mode === 'day' || mode === 'month' || mode === 'year' || mode === 'hour') && (((mode === 'day' && settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function (d) { var blocked = false; if (typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { blocked = module.helper.dateEqual(date, d, mode); } else if (d !== null && typeof d === 'object') { if (d[metadata.year]) { if (typeof d[metadata.year] === 'number') { blocked = date.getFullYear() === d[metadata.year]; } else if (Array.isArray(d[metadata.year])) { blocked = d[metadata.year].indexOf(date.getFullYear()) > -1; } } else if (d[metadata.month]) { if (typeof d[metadata.month] === 'number') { blocked = date.getMonth() === d[metadata.month]; } else if (Array.isArray(d[metadata.month])) { blocked = d[metadata.month].indexOf(date.getMonth()) > -1; } else if (d[metadata.month] instanceof Date) { var sdate = module.helper.sanitiseDate(d[metadata.month]); blocked = (date.getMonth() === sdate.getMonth()) && (date.getFullYear() === sdate.getFullYear()); } } else if (d[metadata.date] && mode === 'day') { if (d[metadata.date] instanceof Date) { blocked = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode); } else if (Array.isArray(d[metadata.date])) { blocked = d[metadata.date].some(function (idate) { return module.helper.dateEqual(date, idate, mode); }); } } } return blocked; })) || (mode === 'hour' && settings.disabledHours.some(function (d) { var blocked = false; if (typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { blocked = module.helper.dateEqual(date, d, mode); } else if (typeof d === 'number') { blocked = date.getHours() === d; } else if (d !== null && typeof d === 'object') { if (d[metadata.date]) { if (d[metadata.date] instanceof Date) { blocked = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date])); } else if (Array.isArray(d[metadata.date])) { blocked = d[metadata.date].some(function (idate) { return module.helper.dateEqual(date, idate, mode); }); } } if (d[metadata.days]) { if (typeof d[metadata.days] === 'number') { blocked = date.getDay() === d[metadata.days]; } else if (Array.isArray(d[metadata.days])) { blocked = d[metadata.days].indexOf(date.getDay()) > -1; } } if (d[metadata.hours]) { if (typeof d[metadata.hours] === 'number') { blocked = blocked && date.getHours() === d[metadata.hours]; } else if (Array.isArray(d[metadata.hours])) { blocked = blocked && d[metadata.hours].indexOf(date.getHours()) > -1; } } } return blocked; }))); }, isEnabled: function (date, mode) { if (mode === 'day') { return settings.enabledDates.length === 0 || settings.enabledDates.some(function (d) { var enabled = false; if (typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { enabled = module.helper.dateEqual(date, d, mode); } else if (d !== null && typeof d === 'object' && d[metadata.date]) { enabled = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode); } return enabled; }); } return true; }, findDayAsObject: function (date, mode, dates) { if (mode === 'day' || mode === 'month' || mode === 'year') { var d; for (var i = 0; i < dates.length; i++) { d = dates[i]; if (typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date && module.helper.dateEqual(date, d, mode)) { var dateObject = {}; dateObject[metadata.date] = d; return dateObject; } if (d !== null && typeof d === 'object') { if (d[metadata.year]) { if (typeof d[metadata.year] === 'number' && date.getFullYear() === d[metadata.year]) { return d; } if (Array.isArray(d[metadata.year])) { if (d[metadata.year].indexOf(date.getFullYear()) > -1) { return d; } } } else if (d[metadata.month]) { if (typeof d[metadata.month] === 'number' && date.getMonth() === d[metadata.month]) { return d; } if (Array.isArray(d[metadata.month])) { if (d[metadata.month].indexOf(date.getMonth()) > -1) { return d; } } else if (d[metadata.month] instanceof Date) { var sdate = module.helper.sanitiseDate(d[metadata.month]); if ((date.getMonth() === sdate.getMonth()) && (date.getFullYear() === sdate.getFullYear())) { return d; } } } else if (d[metadata.date] && mode === 'day') { if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode)) { return d; } if (Array.isArray(d[metadata.date])) { if (d[metadata.date].some(function (idate) { return module.helper.dateEqual(date, idate, mode); })) { return d; } } } } } } return null; }, findHourAsObject: function (date, mode, hours) { if (mode === 'hour') { var d; var hourCheck = function (date, d) { if (d[metadata.hours]) { if (typeof d[metadata.hours] === 'number' && date.getHours() === d[metadata.hours]) { return d; } if (Array.isArray(d[metadata.hours])) { if (d[metadata.hours].indexOf(date.getHours()) > -1) { return d; } } } }; for (var i = 0; i < hours.length; i++) { d = hours[i]; if (typeof d === 'number' && date.getHours() === d) { return null; } if (d !== null && typeof d === 'object') { if (d[metadata.days] && hourCheck(date, d)) { if (typeof d[metadata.days] === 'number' && date.getDay() === d[metadata.days]) { return d; } if (Array.isArray(d[metadata.days])) { if (d[metadata.days].indexOf(date.getDay()) > -1) { return d; } } } else if (d[metadata.date] && hourCheck(date, d)) { if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]))) { return d; } if (Array.isArray(d[metadata.date])) { if (d[metadata.date].some(function (idate) { return module.helper.dateEqual(date, idate, mode); })) { return d; } } } else if (hourCheck(date, d)) { return d; } } } } return null; }, sanitiseDate: function (date) { if (!(date instanceof Date)) { date = parser.date('' + date, settings); } if (!date || isNaN(date.getTime())) { return null; } return date; }, dateDiff: function (date1, date2, mode) { if (!mode) { mode = 'day'; } var isTimeOnly = settings.type === 'time'; var isYear = mode === 'year'; var isYearOrMonth = isYear || mode === 'month'; var isMinute = mode === 'minute'; var isHourOrMinute = isMinute || mode === 'hour'; // only care about a minute accuracy of settings.minTimeGap date1 = new Date( isTimeOnly ? 2000 : date1.getFullYear(), isTimeOnly ? 0 : (isYear ? 0 : date1.getMonth()), isTimeOnly ? 1 : (isYearOrMonth ? 1 : date1.getDate()), !isHourOrMinute ? 0 : date1.getHours(), !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap) ); date2 = new Date( isTimeOnly ? 2000 : date2.getFullYear(), isTimeOnly ? 0 : (isYear ? 0 : date2.getMonth()), isTimeOnly ? 1 : (isYearOrMonth ? 1 : date2.getDate()), !isHourOrMinute ? 0 : date2.getHours(), !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap) ); return date2.getTime() - date1.getTime(); }, dateEqual: function (date1, date2, mode) { return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0; }, isDateInRange: function (date, mode, minDate, maxDate) { if (!minDate && !maxDate) { var startDate = module.get.startDate(); minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; maxDate = settings.maxDate; } minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap)); return !(!date || (minDate && module.helper.dateDiff(date, minDate, mode) > 0) || (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0)); }, dateInRange: function (date, minDate, maxDate) { if (!minDate && !maxDate) { var startDate = module.get.startDate(); minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; maxDate = settings.maxDate; } minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap)); var isTimeOnly = settings.type === 'time'; return !date ? date : (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0 ? (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) // eslint-disable-line unicorn/no-nested-ternary : (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0 // eslint-disable-line unicorn/no-nested-ternary ? (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) : date)); }, mergeDateTime: function (date, time) { return !date || !time ? time : new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes()); }, isTodayButton: function (element) { return element.text() === settings.text.today; }, }, setting: function (name, value) { module.debug('Changing setting', name, value); if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { if ($.isPlainObject(settings[name])) { $.extend(true, settings[name], value); } else { settings[name] = value; } } else { return settings[name]; } }, internal: function (name, value) { if ($.isPlainObject(name)) { $.extend(true, module, name); } else if (value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function () { if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = Date.now(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ Name: message[0], Arguments: [].slice.call(message, 1) || '', Element: element, 'Execution Time': executionTime, }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(function () { module.performance.display(); }, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if (performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data.Name + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; }, }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query === 'string' && object !== undefined) { query = query.split(/[ .]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = depth !== maxDepth ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query ; if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if (isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; }, }; if (methodInvoked) { if (instance === undefined) { module.initialize(); } module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } }); return returnedValue !== undefined ? returnedValue : this; }; $.fn.calendar.settings = { name: 'Calendar', namespace: 'calendar', silent: false, debug: false, verbose: false, performance: true, context: false, type: 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year' firstDayOfWeek: 0, // day for first day column (0 = Sunday) constantHeight: true, // add rows to shorter months to keep day calendar height consistent (6 rows) today: false, // show a 'today/now' button at the bottom of the calendar closable: true, // close the popup after selecting a date/time monthFirst: true, // month before day when parsing date from text touchReadonly: true, // set input to readonly on touch devices inline: false, // create the calendar inline instead of inside a popup on: null, // when to show the popup (defaults to 'focus' for input, 'click' for others) initialDate: null, // date to display initially when no date is selected (null = now) startMode: false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day') minDate: null, // minimum date/time that can be selected, dates/times before are disabled maxDate: null, // maximum date/time that can be selected, dates/times after are disabled disableYear: false, // disable year selection mode disableMonth: false, // disable month selection mode disableMinute: false, // disable minute selection mode formatInput: true, // format the input text upon input blur and module creation startCalendar: null, // jquery object or selector for another calendar that represents the start date of a date range endCalendar: null, // jquery object or selector for another calendar that represents the end date of a date range multiMonth: 1, // show multiple months when in 'day' mode monthOffset: 0, // position current month by offset when multimonth > 1 minTimeGap: 5, showWeekNumbers: false, // show Number of Week at the very first column of a dayView disabledHours: [], // specific hour(s) which won't be selectable and contain additional information. disabledDates: [], // specific day(s) which won't be selectable and contain additional information. disabledDaysOfWeek: [], // day(s) which won't be selectable(s) (0 = Sunday) enabledDates: [], // specific day(s) which will be selectable, all other days will be disabled eventDates: [], // specific day(s) which will be shown in a different color and using tooltips centuryBreak: 60, // starting short year until 99 where it will be assumed to belong to the last century currentCentury: 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1) selectAdjacentDays: false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable. // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden) popupOptions: { position: 'bottom left', lastResort: 'bottom left', prefer: 'opposite', observeChanges: false, hideOnScroll: false, }, text: { days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], today: 'Today', now: 'Now', am: 'AM', pm: 'PM', weekNo: 'Week', }, formatter: { yearHeader: function (date, settings) { var decadeYear = Math.ceil(date.getFullYear() / 10) * 10; return (decadeYear - 9) + ' - ' + (decadeYear + 2); }, monthHeader: 'YYYY', dayHeader: 'MMMM YYYY', hourHeader: 'MMMM D, YYYY', minuteHeader: 'MMMM D, YYYY', dayColumnHeader: function (day, settings) { return settings.text.days[day]; }, datetime: 'MMMM D, YYYY h:mm A', date: 'MMMM D, YYYY', time: 'h:mm A', cellTime: 'h:mm A', month: 'MMMM YYYY', year: 'YYYY', today: function (settings) { return settings.type === 'date' ? settings.text.today : settings.text.now; }, cell: function (cell, date, cellOptions) {}, }, parser: { date: function (text, settings) { if (text instanceof Date) { return text; } if (!text) { return null; } text = String(text).trim().replace(/([./:-])\s+/g, '$1').replace(/\s+([./:-])/g, '$1') .replace(/\s+/g, ' '); if (text.length === 0) { return null; } if (/^\d{4}(?:[./-]\d{1,2}){2}$/.test(text)) { text = text.replace(/[./-]/g, '/') + ' 00:00:00'; } // Reverse date and month in some cases text = settings.monthFirst || !/^\d{1,2}[./-]/.test(text) ? text : text.replace(/[./-]/g, '/').replace(/(\d+)\/(\d+)/, '$2/$1'); var textDate = new Date(text); var numberOnly = text.match(/^\d+$/) !== null; if (!numberOnly && !isNaN(textDate.getDate())) { return textDate; } text = text.toLowerCase(); var i, j, k ; var minute = -1, hour = -1, day = -1, month = -1, year = -1 ; var isAm; var isTimeOnly = settings.type === 'time'; var isDateOnly = settings.type.indexOf('time') < 0; var words = text.split(settings.regExp.dateWords), word ; var numbers = text.split(settings.regExp.dateNumbers), number ; var parts; var monthString; if (!isDateOnly) { // am/pm isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true : ($.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined); // time with ':' for (i = 0; i < numbers.length; i++) { number = numbers[i]; if (number.indexOf(':') >= 0) { if (hour < 0 || minute < 0) { parts = number.split(':'); for (k = 0; k < Math.min(2, parts.length); k++) { j = parseInt(parts[k], 10); if (isNaN(j)) { j = 0; } if (k === 0) { hour = j % 24; } else { minute = j % 60; } } } numbers.splice(i, 1); } } } if (!isTimeOnly) { // textual month for (i = 0; i < words.length; i++) { word = words[i]; if (word.length <= 0) { continue; } for (j = 0; j < settings.text.months.length; j++) { monthString = settings.text.months[j]; monthString = monthString.slice(0, word.length).toLowerCase(); if (monthString === word) { month = j + 1; break; } } if (month >= 0) { break; } } // year > settings.centuryBreak for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i], 10); if (isNaN(j)) { continue; } if (j >= settings.centuryBreak && i === numbers.length - 1) { if (j <= 99) { j += settings.currentCentury - 100; } year = j; numbers.splice(i, 1); break; } } // numeric month if (month < 0) { for (i = 0; i < numbers.length; i++) { k = i > 1 || settings.monthFirst ? i : (i === 1 ? 0 : 1); j = parseInt(numbers[k], 10); if (isNaN(j)) { continue; } if (j >= 1 && j <= 12) { month = j; numbers.splice(k, 1); break; } } } // day for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i], 10); if (isNaN(j)) { continue; } if (j >= 1 && j <= 31) { day = j; numbers.splice(i, 1); break; } } // year <= settings.centuryBreak if (year < 0) { for (i = numbers.length - 1; i >= 0; i--) { j = parseInt(numbers[i], 10); if (isNaN(j)) { continue; } if (j <= 99) { j += settings.currentCentury; } year = j; numbers.splice(i, 1); break; } } } if (!isDateOnly) { // hour if (hour < 0) { for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i], 10); if (isNaN(j)) { continue; } if (j >= 0 && j <= 23) { hour = j; numbers.splice(i, 1); break; } } } // minute if (minute < 0) { for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i], 10); if (isNaN(j)) { continue; } if (j >= 0 && j <= 59) { minute = j; numbers.splice(i, 1); break; } } } } if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) { return null; } if (minute < 0) { minute = 0; } if (hour < 0) { hour = 0; } if (day < 0) { day = 1; } if (month < 0) { month = 1; } if (year < 0) { year = new Date().getFullYear(); } if (isAm !== undefined) { if (isAm) { if (hour === 12) { hour = 0; } } else if (hour < 12) { hour += 12; } } var date = new Date(year, month - 1, day, hour, minute); if (date.getMonth() !== month - 1 || date.getFullYear() !== year) { // month or year don't match up, switch to last day of the month date = new Date(year, month, 0, hour, minute); } return isNaN(date.getTime()) ? null : date; }, }, // callback before date is changed, return false to cancel the change onBeforeChange: function (date, text, mode) { return true; }, // callback when date changes onChange: function (date, text, mode) {}, // callback before show animation, return false to prevent show onShow: function () {}, // callback after show animation onVisible: function () {}, // callback before hide animation, return false to prevent hide onHide: function () {}, // callback after hide animation onHidden: function () {}, // callback before item is selected, return false to prevent selection onSelect: function (date, mode) {}, // is the given date disabled? isDisabled: function (date, mode) { return false; }, selector: { popup: '.ui.popup', input: 'input', activator: 'input', append: '.inline.field,.inline.fields', }, regExp: { dateWords: /[^A-Za-z\u00C0-\u024F]+/g, dateNumbers: /[^\d:]+/g, token: /d{1,4}|D{1,2}|M{1,4}|YY(?:YY)?|([Hhmsw])\1?|[ASYa]|"[^"]*"|'[^']*'/g, }, error: { popup: 'UI Popup, a required component is not included in this page', method: 'The method you called is not defined.', }, className: { calendar: 'calendar', active: 'active', popup: 'ui popup', grid: 'ui equal width grid', column: 'column', table: 'ui celled center aligned unstackable table', inverted: 'inverted', prev: 'prev link', next: 'next link', prevIcon: 'chevron left icon', nextIcon: 'chevron right icon', link: 'link', cell: 'link', disabledCell: 'disabled', weekCell: 'disabled', adjacentCell: 'adjacent', activeCell: 'active', rangeCell: 'range', focusCell: 'focus', todayCell: 'today', today: 'today link', disabled: 'disabled', }, metadata: { date: 'date', focusDate: 'focusDate', startDate: 'startDate', endDate: 'endDate', minDate: 'minDate', maxDate: 'maxDate', mode: 'mode', type: 'type', monthOffset: 'monthOffset', message: 'message', class: 'class', inverted: 'inverted', variation: 'variation', position: 'position', month: 'month', year: 'year', hours: 'hours', days: 'days', }, eventClass: 'blue', }; })(jQuery, window, document); /*! * # Fomantic-UI 2.9.3 - Checkbox * https://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * https://opensource.org/licenses/MIT * */ (function ($, window, document) { 'use strict'; function isFunction(obj) { return typeof obj === 'function' && typeof obj.nodeType !== 'number'; } window = window !== undefined && window.Math === Math ? window : globalThis; $.fn.checkbox = function (parameters) { var $allModules = $(this), time = Date.now(), performance = [], query = arguments[0], methodInvoked = typeof query === 'string', queryArguments = [].slice.call(arguments, 1), returnedValue ; $allModules.each(function () { var settings = $.extend(true, {}, $.fn.checkbox.settings, parameters), className = settings.className, namespace = settings.namespace, selector = settings.selector, error = settings.error, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $label = $(this).children(selector.label), $input = $(this).children(selector.input), input = $input[0], initialLoad = false, shortcutPressed = false, instance = $module.data(moduleNamespace), observer, element = this, module ; module = { initialize: function () { module.verbose('Initializing checkbox', settings); module.create.label(); module.bind.events(); module.set.tabbable(); module.hide.input(); module.observeChanges(); module.instantiate(); module.setup(); }, instantiate: function () { module.verbose('Storing instance of module', module); instance = module; $module .data(moduleNamespace, module) ; }, destroy: function () { module.verbose('Destroying module'); module.unbind.events(); module.show.input(); $module.removeData(moduleNamespace); }, fix: { reference: function () { if ($module.is(selector.input)) { module.debug('Behavior called on adjusting invoked element'); $module = $module.closest(selector.checkbox); module.refresh(); } }, }, setup: function () { module.set.initialLoad(); if (module.is.indeterminate()) { module.debug('Initial value is indeterminate'); module.indeterminate(); } else if (module.is.checked()) { module.debug('Initial value is checked'); module.check(); } else { module.debug('Initial value is unchecked'); module.uncheck(); } module.remove.initialLoad(); }, refresh: function () { $label = $module.children(selector.label); $input = $module.children(selector.input); input = $input[0]; }, hide: { input: function () { module.verbose('Modifying z-index to be unselectable'); $input.addClass(className.hidden); }, }, show: { input: function () { module.verbose('Modifying z-index to be selectable'); $input.removeClass(className.hidden); }, }, observeChanges: function () { if ('MutationObserver' in window) { observer = new MutationObserver(function (mutations) { module.debug('DOM tree modified, updating selector cache'); module.refresh(); }); observer.observe(element, { childList: true, subtree: true, }); module.debug('Setting up mutation observer', observer); } }, attachEvents: function (selector, event) { var $element = $(selector) ; event = isFunction(module[event]) ? module[event] : module.toggle; if ($element.length > 0) { module.debug('Attaching checkbox events to element', selector, event); $element .on('click' + eventNamespace, event) ; } else { module.error(error.notFound); } }, preventDefaultOnInputTarget: function () { if (event !== undefined && event !== null && $(event.target).is(selector.input)) { module.verbose('Preventing default check action after manual check action'); event.preventDefault(); } }, event: { change: function (event) { if (!module.should.ignoreCallbacks()) { settings.onChange.call(input); } }, click: function (event) { var $target = $(event.target) ; if ($target.is(selector.input)) { module.verbose('Using default check action on initialized checkbox'); return; } if ($target.is(selector.link)) { module.debug('Clicking link inside checkbox, skipping toggle'); return; } module.toggle(); $input.trigger('focus'); event.preventDefault(); }, keydown: function (event) { var key = event.which, keyCode = { enter: 13, space: 32, escape: 27, left: 37, up: 38, right: 39, down: 40, } ; var r = module.get.radios(), rIndex = r.index($module), rLen = r.length, checkIndex = false ; if (key === keyCode.left || key === keyCode.up) { checkIndex = (rIndex === 0 ? rLen : rIndex) - 1; } else if (key === keyCode.right || key === keyCode.down) { checkIndex = rIndex === rLen - 1 ? 0 : rIndex + 1; } if (!module.should.ignoreCallbacks() && checkIndex !== false) { if (settings.beforeUnchecked.apply(input) === false) { module.verbose('Option not allowed to be unchecked, cancelling key navigation'); return false; } if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0]) === false) { module.verbose('Next option should not allow check, cancelling key navigation'); return false; } } shortcutPressed = false; if (key === keyCode.escape) { module.verbose('Escape key pressed blurring field'); $input.trigger('blur'); shortcutPressed = true; event.stopPropagation(); } else if (!module.can.change()) { shortcutPressed = true; } else if (!event.ctrlKey) { if (key === keyCode.space || (key === keyCode.enter && settings.enableEnterKey)) { module.verbose('Enter/space key pressed, toggling checkbox'); module.toggle(); shortcutPressed = true; } else if ($module.is('.toggle, .slider') && !module.is.radio()) { if (key === keyCode.left && module.is.checked()) { module.uncheck(); shortcutPressed = true; } else if (key === keyCode.right && module.is.unchecked()) { module.check(); shortcutPressed = true; } } } }, keyup: function (event) { if (shortcutPressed) { event.preventDefault(); } }, }, check: function () { if (!module.should.allowCheck()) { return; } module.debug('Checking checkbox', $input); module.set.checked(); if (!module.should.ignoreCallbacks()) { settings.onChecked.call(input); module.trigger.change(); } module.preventDefaultOnInputTarget(); }, uncheck: function () { if (!module.should.allowUncheck()) { return; } module.debug('Unchecking checkbox'); module.set.unchecked(); if (!module.should.ignoreCallbacks()) { settings.onUnchecked.call(input); module.trigger.change(); } module.preventDefaultOnInputTarget(); }, indeterminate: function () { if (module.should.allowIndeterminate()) { module.debug('Checkbox is already indeterminate'); return; } module.debug('Making checkbox indeterminate'); module.set.indeterminate(); if (!module.should.ignoreCallbacks()) { settings.onIndeterminate.call(input); module.trigger.change(); } }, determinate: function () { if (module.should.allowDeterminate()) { module.debug('Checkbox is already determinate'); return; } module.debug('Making checkbox determinate'); module.set.determinate(); if (!module.should.ignoreCallbacks()) { settings.onDeterminate.call(input); module.trigger.change(); } }, enable: function () { if (module.is.enabled()) { module.debug('Checkbox is already enabled'); return; } module.debug('Enabling checkbox'); module.set.enabled(); if (!module.should.ignoreCallbacks()) { settings.onEnable.call(input); // preserve legacy callbacks settings.onEnabled.call(input); } }, disable: function () { if (module.is.disabled()) { module.debug('Checkbox is already disabled'); return; } module.debug('Disabling checkbox'); module.set.disabled(); if (!module.should.ignoreCallbacks()) { settings.onDisable.call(input); // preserve legacy callbacks settings.onDisabled.call(input); } }, get: { radios: function () { var name = module.get.name() ; return $('input[name="' + name + '"]').closest(selector.checkbox); }, otherRadios: function () { return module.get.radios().not($module); }, name: function () { return $input.attr('name'); }, }, is: { initialLoad: function () { return initialLoad; }, radio: function () { return $input.hasClass(className.radio) || $input.attr('type') === 'radio'; }, indeterminate: function () { return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate'); }, checked: function () { return $input.prop('checked') !== undefined && $input.prop('checked'); }, disabled: function () { return $input.prop('disabled') !== undefined && $input.prop('disabled'); }, enabled: function () { return !module.is.disabled(); }, determinate: function () { return !module.is.indeterminate(); }, unchecked: function () { return !module.is.checked(); }, }, should: { allowCheck: function () { if (module.is.determinate() && module.is.checked() && !module.is.initialLoad()) { module.debug('Should not allow check, checkbox is already checked'); return false; } if (!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) { module.debug('Should not allow check, beforeChecked cancelled'); return false; } return true; }, allowUncheck: function () { if (module.is.determinate() && module.is.unchecked() && !module.is.initialLoad()) { module.debug('Should not allow uncheck, checkbox is already unchecked'); return false; } if (!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) { module.debug('Should not allow uncheck, beforeUnchecked cancelled'); return false; } return true; }, allowIndeterminate: function () { if (module.is.indeterminate() && !module.is.initialLoad()) { module.debug('Should not allow indeterminate, checkbox is already indeterminate'); return false; } if (!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) { module.debug('Should not allow indeterminate, beforeIndeterminate cancelled'); return false; } return true; }, allowDeterminate: function () { if (module.is.determinate() && !module.is.initialLoad()) { module.debug('Should not allow determinate, checkbox is already determinate'); return false; } if (!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) { module.debug('Should not allow determinate, beforeDeterminate cancelled'); return false; } return true; }, ignoreCallbacks: function () { return initialLoad && !settings.fireOnInit; }, }, can: { change: function () { return !($module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly')); }, uncheck: function () { return typeof settings.uncheckable === 'boolean' ? settings.uncheckable : !module.is.radio(); }, }, set: { initialLoad: function () { initialLoad = true; }, checked: function () { module.verbose('Setting class to checked'); $module .removeClass(className.indeterminate) .addClass(className.checked) ; if (module.is.radio()) { module.uncheckOthers(); } if (!module.is.indeterminate() && module.is.checked()) { module.debug('Input is already checked, skipping input property change'); return; } module.verbose('Setting state to checked', input); $input .prop('indeterminate', false) .prop('checked', true) ; }, unchecked: function () { module.verbose('Removing checked class'); $module .removeClass(className.indeterminate) .removeClass(className.checked) ; if (!module.is.indeterminate() && module.is.unchecked()) { module.debug('Input is already unchecked'); return; } module.debug('Setting state to unchecked'); $input .prop('indeterminate', false) .prop('checked', false) ; }, indeterminate: function () { module.verbose('Setting class to indeterminate'); $module .addClass(className.indeterminate) ; if (module.is.indeterminate()) { module.debug('Input is already indeterminate, skipping input property change'); return; } module.debug('Setting state to indeterminate'); $input .prop('indeterminate', true) ; }, determinate: function () { module.verbose('Removing indeterminate class'); $module .removeClass(className.indeterminate) ; if (module.is.determinate()) { module.debug('Input is already determinate, skipping input property change'); return; } module.debug('Setting state to determinate'); $input .prop('indeterminate', false) ; }, disabled: function () { module.verbose('Setting class to disabled'); $module .addClass(className.disabled) ; if (module.is.disabled()) { module.debug('Input is already disabled, skipping input property change'); return; } module.debug('Setting state to disabled'); $input .prop('disabled', 'disabled') ; }, enabled: function () { module.verbose('Removing disabled class'); $module.removeClass(className.disabled); if (module.is.enabled()) { module.debug('Input is already enabled, skipping input property change'); return; } module.debug('Setting state to enabled'); $input .prop('disabled', false) ; }, tabbable: function () { module.verbose('Adding tabindex to checkbox'); if ($input.attr('tabindex') === undefined) { $input.attr('tabindex', 0); } }, }, remove: { initialLoad: function () { initialLoad = false; }, }, trigger: { change: function () { var inputElement = $input[0] ; if (inputElement) { var events = document.createEvent('HTMLEvents'); module.verbose('Triggering native change event'); events.initEvent('change', true, false); inputElement.dispatchEvent(events); } }, }, create: { label: function () { if ($input.prevAll(selector.label).length > 0) { $input.prev(selector.label).detach().insertAfter($input); module.debug('Moving existing label', $label); } else if (!module.has.label()) { $label = $('