/*
 * # 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 ? $() : $('<input/>');
                    },
                    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 = $('<div/>').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 = '<ul class="list">'
                ;
                $.each(errors, function (index, value) {
                    html += '<li>' + value + '</li>';
                });
                html += '</ul>';

                return html;
            },

            // template that produces label content
            prompt: function (errors) {
                if (errors.length === 1) {
                    return errors[0];
                }
                var
                    html = '<ul class="ui list">'
                ;
                $.each(errors, function (index, value) {
                    html += '<li>' + value + '</li>';
                });
                html += '</ul>';

                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  <https://gist.github.com/2134376>
                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 = $('<div/>', { 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 = $('<div/>').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 = $('<div/>').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 = $('<div/>').addClass(className.grid).appendTo(container);
                        }

                        for (p = 0; p < pages; p++) {
                            if (pages > 1) {
                                var pageColumn = $('<div/>').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 = $('<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 = $('<thead/>').appendTo(table);

                                row = $('<tr/>').appendTo(thead);
                                cell = $('<th/>').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 = $('<span/>').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 = $('<span/>').addClass(className.prev).appendTo(cell);
                                    prev.data(metadata.focusDate, prevDate);
                                    prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
                                    $('<i/>').addClass(className.prevIcon).appendTo(prev);
                                }

                                if (p === pages - 1) {
                                    var next = $('<span/>').addClass(className.next).appendTo(cell);
                                    next.data(metadata.focusDate, nextDate);
                                    next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
                                    $('<i/>').addClass(className.nextIcon).appendTo(next);
                                }
                                if (isDay) {
                                    row = $('<tr/>').appendTo(thead);
                                    if (settings.showWeekNumbers) {
                                        cell = $('<th/>').appendTo(row);
                                        cell.text(settings.text.weekNo);
                                        cell.addClass(className.weekCell);
                                        textColumns--;
                                    }
                                    for (i = 0; i < textColumns; i++) {
                                        cell = $('<th/>').appendTo(row);
                                        cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
                                    }
                                }
                            }

                            var tbody = $('<tbody/>').appendTo(table);
                            i = isYear
                                ? Math.ceil(year / 10) * 10 - 9
                                : (isDay ? 1 - firstMonthDayColumn : 0);
                            for (r = 0; r < rows; r++) {
                                row = $('<tr/>').appendTo(tbody);
                                if (isDay && settings.showWeekNumbers) {
                                    cell = $('<th/>').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 = $('<td/>').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 = $('<tr/>').appendTo(tbody);
                                var todayButton = $('<td/>').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 <i> 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 <input> 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 <input> z-index to be unselectable');
                        $input.addClass(className.hidden);
                    },
                },
                show: {
                    input: function () {
                        module.verbose('Modifying <input> 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 = $('<label>').insertAfter($input);
                            module.debug('Creating label', $label);
                        }
                    },
                },

                has: {
                    label: function () {
                        return $label.length > 0;
                    },
                },

                bind: {
                    events: function () {
                        module.verbose('Attaching checkbox events');
                        $module
                            .on('click' + eventNamespace, module.event.click)
                            .on('change' + eventNamespace, module.event.change)
                            .on('keydown' + eventNamespace, selector.input, module.event.keydown)
                            .on('keyup' + eventNamespace, selector.input, module.event.keyup)
                        ;
                    },
                },

                unbind: {
                    events: function () {
                        module.debug('Removing events');
                        $module
                            .off(eventNamespace)
                        ;
                    },
                },

                uncheckOthers: function () {
                    var
                        $radios = module.get.otherRadios()
                    ;
                    module.debug('Unchecking other radios', $radios);
                    $radios.removeClass(className.checked);
                },

                toggle: function () {
                    if (!module.can.change()) {
                        if (!module.is.radio()) {
                            module.debug('Checkbox is read-only or disabled, ignoring toggle');
                        }

                        return;
                    }
                    if (module.is.indeterminate() || module.is.unchecked()) {
                        module.debug('Currently unchecked');
                        module.check();
                    } else if (module.is.checked() && module.can.uncheck()) {
                        module.debug('Currently checked');
                        module.uncheck();
                    }
                },
                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.checkbox.settings = {

        name: 'Checkbox',
        namespace: 'checkbox',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        // delegated event context
        uncheckable: 'auto',
        fireOnInit: false,
        enableEnterKey: true,

        onChange: function () {},

        beforeChecked: function () {},
        beforeUnchecked: function () {},
        beforeDeterminate: function () {},
        beforeIndeterminate: function () {},

        onChecked: function () {},
        onUnchecked: function () {},

        onDeterminate: function () {},
        onIndeterminate: function () {},

        onEnable: function () {},
        onDisable: function () {},

        // preserve misspelled callbacks (will be removed in 3.0)
        onEnabled: function () {},
        onDisabled: function () {},

        className: {
            checked: 'checked',
            indeterminate: 'indeterminate',
            disabled: 'disabled',
            hidden: 'hidden',
            radio: 'radio',
            readOnly: 'read-only',
        },

        error: {
            method: 'The method you called is not defined',
        },

        selector: {
            checkbox: '.ui.checkbox',
            label: 'label',
            input: 'input[type="checkbox"], input[type="radio"]',
            link: 'a[href]',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Dimmer
 * 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.dimmer = 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.dimmer.settings, parameters)
                    : $.extend({}, $.fn.dimmer.settings),

                selector        = settings.selector,
                namespace       = settings.namespace,
                className       = settings.className,
                error           = settings.error,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                clickEvent      = 'ontouchstart' in document.documentElement
                    ? 'touchstart'
                    : 'click',

                $module = $(this),
                $dimmer,
                $dimmable,

                element   = this,
                instance  = $module.data(moduleNamespace),
                module
            ;

            module = {

                preinitialize: function () {
                    if (module.is.dimmer()) {
                        $dimmable = $module.parent();
                        $dimmer = $module;
                    } else {
                        $dimmable = $module;
                        if (module.has.dimmer()) {
                            $dimmer = settings.dimmerName
                                ? $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName)
                                : $dimmable.find(selector.dimmer);
                        } else {
                            $dimmer = module.create();
                        }
                    }
                },

                initialize: function () {
                    module.debug('Initializing dimmer', settings);

                    module.bind.events();
                    module.set.dimmable();
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous module', $dimmer);
                    module.unbind.events();
                    module.remove.variation();
                    $dimmable
                        .off(eventNamespace)
                    ;
                },

                bind: {
                    events: function () {
                        if (settings.on === 'hover') {
                            $dimmable
                                .on('mouseenter' + eventNamespace, module.show)
                                .on('mouseleave' + eventNamespace, module.hide)
                            ;
                        } else if (settings.on === 'click') {
                            $dimmable
                                .on(clickEvent + eventNamespace, module.toggle)
                            ;
                        }
                        if (module.is.page()) {
                            module.debug('Setting as a page dimmer', $dimmable);
                            module.set.pageDimmer();
                        }

                        if (module.is.closable()) {
                            module.verbose('Adding dimmer close event', $dimmer);
                            $dimmable
                                .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
                            ;
                        }
                    },
                },

                unbind: {
                    events: function () {
                        $module
                            .removeData(moduleNamespace)
                        ;
                        $dimmable
                            .off(eventNamespace)
                        ;
                    },
                },

                event: {
                    click: function (event) {
                        module.verbose('Determining if event occurred on dimmer', event);
                        if ($dimmer.find(event.target).length === 0 || $(event.target).is(selector.content)) {
                            module.hide();
                            event.stopImmediatePropagation();
                        }
                    },
                },

                addContent: function (element) {
                    var
                        $content = $(element)
                    ;
                    module.debug('Add content to dimmer', $content);
                    if ($content.parent()[0] !== $dimmer[0]) {
                        $content.detach().appendTo($dimmer);
                    }
                },

                create: function () {
                    var
                        $element = $(settings.template.dimmer(settings))
                    ;
                    if (settings.dimmerName) {
                        module.debug('Creating named dimmer', settings.dimmerName);
                        $element.addClass(settings.dimmerName);
                    }
                    $element
                        .appendTo($dimmable)
                    ;

                    return $element;
                },

                show: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if ((!module.is.dimmed() || module.is.animating()) && module.is.enabled()) {
                        if (settings.onShow.call(element) === false) {
                            module.verbose('Show callback returned false cancelling dimmer show');

                            return;
                        }
                        module.debug('Showing dimmer', $dimmer, settings);
                        module.set.variation();
                        module.animate.show(callback);
                        settings.onChange.call(element);
                    } else {
                        module.debug('Dimmer is already shown or disabled');
                    }
                },

                hide: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.dimmed() || module.is.animating()) {
                        if (settings.onHide.call(element) === false) {
                            module.verbose('Hide callback returned false cancelling dimmer hide');

                            return;
                        }
                        module.debug('Hiding dimmer', $dimmer);
                        module.animate.hide(callback);
                        settings.onChange.call(element);
                    } else {
                        module.debug('Dimmer is not visible');
                    }
                },

                toggle: function () {
                    module.verbose('Toggling dimmer visibility', $dimmer);
                    if (!module.is.dimmed()) {
                        module.show();
                    } else {
                        if (module.is.closable()) {
                            module.hide();
                        }
                    }
                },

                animate: {
                    show: function (callback) {
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if (settings.useCSS && $.fn.transition !== undefined) {
                            if (settings.useFlex) {
                                module.debug('Using flex dimmer');
                                module.remove.legacy();
                            } else {
                                module.debug('Using legacy non-flex dimmer');
                                module.set.legacy();
                            }
                            if (settings.opacity !== 'auto') {
                                module.set.opacity();
                            }
                            $dimmer
                                .transition({
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    displayType: settings.useFlex
                                        ? 'flex'
                                        : 'block',
                                    animation: (settings.transition.showMethod || settings.transition) + ' in',
                                    queue: false,
                                    duration: module.get.duration(),
                                    useFailSafe: true,
                                    onStart: function () {
                                        module.set.dimmed();
                                    },
                                    onComplete: function () {
                                        module.set.active();
                                        settings.onVisible.call($dimmer);
                                        callback();
                                    },
                                })
                            ;
                        } else {
                            module.verbose('Showing dimmer animation with javascript');
                            module.set.dimmed();
                            if (settings.opacity === 'auto') {
                                settings.opacity = 0.8;
                            }
                            $dimmer
                                .stop()
                                .css({
                                    opacity: 0,
                                    width: '100%',
                                    height: '100%',
                                })
                                .fadeTo(module.get.duration(), settings.opacity, function () {
                                    $dimmer.removeAttr('style');
                                    module.set.active();
                                    settings.onVisible.call($dimmer);
                                    callback();
                                })
                            ;
                        }
                    },
                    hide: function (callback) {
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if (settings.useCSS && $.fn.transition !== undefined) {
                            module.verbose('Hiding dimmer with css');
                            $dimmer
                                .transition({
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    displayType: settings.useFlex
                                        ? 'flex'
                                        : 'block',
                                    animation: (settings.transition.hideMethod || settings.transition) + ' out',
                                    queue: false,
                                    duration: module.get.duration(),
                                    useFailSafe: true,
                                    onComplete: function () {
                                        module.remove.dimmed();
                                        module.remove.variation();
                                        module.remove.active();
                                        settings.onHidden.call($dimmer);
                                        callback();
                                    },
                                })
                            ;
                        } else {
                            module.verbose('Hiding dimmer with javascript');
                            $dimmer
                                .stop()
                                .fadeOut(module.get.duration(), function () {
                                    module.remove.dimmed();
                                    module.remove.active();
                                    $dimmer.removeAttr('style');
                                    settings.onHidden.call($dimmer);
                                    callback();
                                })
                            ;
                        }
                    },
                },

                get: {
                    dimmer: function () {
                        return $dimmer;
                    },
                    duration: function () {
                        if (module.is.active()) {
                            return settings.transition.hideDuration || settings.duration.hide || settings.duration;
                        }

                        return settings.transition.showDuration || settings.duration.show || settings.duration;
                    },
                },

                has: {
                    dimmer: function () {
                        if (settings.dimmerName) {
                            return $module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0;
                        }

                        return $module.find(selector.dimmer).length > 0;
                    },
                },

                is: {
                    active: function () {
                        return $dimmer.hasClass(className.active);
                    },
                    animating: function () {
                        return $dimmer.is(':animated') || $dimmer.hasClass(className.animating);
                    },
                    closable: function () {
                        if (settings.closable === 'auto') {
                            return settings.on !== 'hover';
                        }

                        return settings.closable;
                    },
                    dimmer: function () {
                        return $module.hasClass(className.dimmer);
                    },
                    dimmable: function () {
                        return $module.hasClass(className.dimmable);
                    },
                    dimmed: function () {
                        return $dimmable.hasClass(className.dimmed);
                    },
                    disabled: function () {
                        return $dimmable.hasClass(className.disabled);
                    },
                    enabled: function () {
                        return !module.is.disabled();
                    },
                    page: function () {
                        return $dimmable.is('body');
                    },
                    pageDimmer: function () {
                        return $dimmer.hasClass(className.pageDimmer);
                    },
                },

                can: {
                    show: function () {
                        return !$dimmer.hasClass(className.disabled);
                    },
                },

                set: {
                    opacity: function (opacity) {
                        var
                            color      = $dimmer.css('background-color'),
                            colorArray = color.split(','),
                            isRGB      = colorArray && colorArray.length >= 3
                        ;
                        opacity = settings.opacity === 0 ? 0 : settings.opacity || opacity;
                        if (isRGB) {
                            colorArray[2] = colorArray[2].replace(')', '');
                            colorArray[3] = opacity + ')';
                            color = colorArray.join(',');
                        } else {
                            color = 'rgba(0, 0, 0, ' + opacity + ')';
                        }
                        module.debug('Setting opacity to', opacity);
                        $dimmer.css('background-color', color);
                    },
                    legacy: function () {
                        $dimmer.addClass(className.legacy);
                    },
                    active: function () {
                        $dimmer.addClass(className.active);
                    },
                    dimmable: function () {
                        $dimmable.addClass(className.dimmable);
                    },
                    dimmed: function () {
                        $dimmable.addClass(className.dimmed);
                    },
                    pageDimmer: function () {
                        $dimmer.addClass(className.pageDimmer);
                    },
                    disabled: function () {
                        $dimmer.addClass(className.disabled);
                    },
                    variation: function (variation) {
                        variation = variation || settings.variation;
                        if (variation) {
                            $dimmer.addClass(variation);
                        }
                    },
                },

                remove: {
                    active: function () {
                        $dimmer
                            .removeClass(className.active)
                        ;
                    },
                    legacy: function () {
                        $dimmer.removeClass(className.legacy);
                    },
                    dimmed: function () {
                        $dimmable.removeClass(className.dimmed);
                    },
                    disabled: function () {
                        $dimmer.removeClass(className.disabled);
                    },
                    variation: function (variation) {
                        variation = variation || settings.variation;
                        if (variation) {
                            $dimmer.removeClass(variation);
                        }
                    },
                },

                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 ($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.preinitialize();

            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.dimmer.settings = {

        name: 'Dimmer',
        namespace: 'dimmer',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        // whether should use flex layout
        useFlex: true,

        // name to distinguish between multiple dimmers in context
        dimmerName: false,

        // whether to add a variation type
        variation: false,

        // whether to bind close events
        closable: 'auto',

        // whether to use css animations
        useCSS: true,

        // css animation to use
        transition: 'fade',

        // event to bind to
        on: false,

        // overriding opacity value
        opacity: 'auto',

        // transition durations
        duration: {
            show: 500,
            hide: 500,
        },
        // whether the dynamically created dimmer should have a loader
        displayLoader: false,
        loaderText: false,
        loaderVariation: '',

        onChange: function () {},
        onShow: function () {},
        onHide: function () {},
        onVisible: function () {},
        onHidden: function () {},

        error: {
            method: 'The method you called is not defined.',
        },

        className: {
            active: 'active',
            animating: 'animating',
            dimmable: 'dimmable',
            dimmed: 'dimmed',
            dimmer: 'dimmer',
            disabled: 'disabled',
            hide: 'hide',
            legacy: 'legacy',
            pageDimmer: 'page',
            show: 'show',
            loader: 'ui loader',
        },

        selector: {
            dimmer: '> .ui.dimmer',
            content: '.ui.dimmer > .content, .ui.dimmer > .content > .center',
        },

        template: {
            dimmer: function (settings) {
                var
                    d = $('<div/>').addClass('ui dimmer'),
                    l
                ;
                if (settings.displayLoader) {
                    l = $('<div/>')
                        .addClass(settings.className.loader)
                        .addClass(settings.loaderVariation)
                    ;
                    if (settings.loaderText) {
                        l.text(settings.loaderText);
                        l.addClass('text');
                    }
                    d.append(l);
                }

                return d;
            },
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Dropdown
 * 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.dropdown = function (parameters) {
        var
            $allModules    = $(this),
            $document      = $(document),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            returnedValue
        ;

        $allModules.each(function (elementIndex) {
            var
                settings          = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
                    : $.extend({}, $.fn.dropdown.settings),

                className       = settings.className,
                message         = settings.message,
                fields          = settings.fields,
                keys            = settings.keys,
                metadata        = settings.metadata,
                namespace       = settings.namespace,
                regExp          = settings.regExp,
                selector        = settings.selector,
                error           = settings.error,
                templates       = settings.templates,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $context        = contextCheck(settings.context, window),
                $text           = $module.find(selector.text),
                $search         = $module.find(selector.search),
                $sizer          = $module.find(selector.sizer),
                $input          = $module.find(selector.input),
                $icon           = $module.find(selector.icon),
                $clear          = $module.find(selector.clearIcon),

                $combo = $module.prev().find(selector.text).length > 0
                    ? $module.prev().find(selector.text)
                    : $module.prev(),

                $menu           = $module.children(selector.menu),
                $item           = $menu.find(selector.item),
                $divider        = settings.hideDividers
                    ? $item.parent().children(selector.divider)
                    : $(),

                activated       = false,
                itemActivated   = false,
                internalChange  = false,
                iconClicked     = false,
                element         = this,
                focused         = false,
                instance        = $module.data(moduleNamespace),

                selectActionActive,
                initialLoad,
                pageLostFocus,
                willRefocus,
                elementNamespace,
                id,
                selectObserver,
                menuObserver,
                classObserver,
                module,
                tempDisableApiCache = false
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing dropdown', settings);

                    if (module.is.alreadySetup()) {
                        module.setup.reference();
                    } else {
                        if (settings.ignoreDiacritics && !String.prototype.normalize) {
                            settings.ignoreDiacritics = false;
                            module.error(error.noNormalize, element);
                        }

                        module.create.id();
                        module.setup.layout();

                        if (settings.values) {
                            module.set.initialLoad();
                            module.change.values(settings.values);
                            module.remove.initialLoad();
                        }

                        module.refreshData();

                        module.save.defaults();
                        module.restore.selected();

                        module.bind.events();

                        module.observeChanges();
                        module.instantiate();
                    }
                },

                instantiate: function () {
                    module.verbose('Storing instance of dropdown', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous dropdown', $module);
                    module.remove.tabbable();
                    module.remove.active();
                    $menu.transition('stop all');
                    $menu.removeClass(className.visible).addClass(className.hidden);
                    $module
                        .off(eventNamespace)
                        .removeData(moduleNamespace)
                    ;
                    $menu
                        .off(eventNamespace)
                    ;
                    $document
                        .off(elementNamespace)
                    ;
                    module.disconnect.menuObserver();
                    module.disconnect.selectObserver();
                    module.disconnect.classObserver();
                },

                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        selectObserver = new MutationObserver(module.event.select.mutation);
                        menuObserver = new MutationObserver(module.event.menu.mutation);
                        classObserver = new MutationObserver(module.event.class.mutation);
                        module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
                        module.observe.select();
                        module.observe.menu();
                        module.observe.class();
                    }
                },

                disconnect: {
                    menuObserver: function () {
                        if (menuObserver) {
                            menuObserver.disconnect();
                        }
                    },
                    selectObserver: function () {
                        if (selectObserver) {
                            selectObserver.disconnect();
                        }
                    },
                    classObserver: function () {
                        if (classObserver) {
                            classObserver.disconnect();
                        }
                    },
                },
                observe: {
                    select: function () {
                        if (module.has.input() && selectObserver) {
                            selectObserver.observe($module[0], {
                                attributes: true,
                                childList: true,
                                subtree: true,
                            });
                        }
                    },
                    menu: function () {
                        if (module.has.menu() && menuObserver) {
                            menuObserver.observe($menu[0], {
                                childList: true,
                                subtree: true,
                            });
                        }
                    },
                    class: function () {
                        if (module.has.search() && classObserver) {
                            classObserver.observe($module[0], {
                                attributes: true,
                            });
                        }
                    },
                },

                create: {
                    id: function () {
                        id = (Math.random().toString(16) + '000000000').slice(2, 10);
                        elementNamespace = '.' + id;
                        module.verbose('Creating unique id for element', id);
                    },
                    userChoice: function (values) {
                        var
                            $userChoices,
                            $userChoice,
                            html
                        ;
                        values = values || module.get.userValues();
                        if (!values) {
                            return false;
                        }
                        values = Array.isArray(values)
                            ? values
                            : [values];
                        $.each(values, function (index, value) {
                            if (module.get.item(value) === false) {
                                html = settings.templates.addition(module.add.variables(message.addResult, value));
                                $userChoice = $('<div />')
                                    .html(html)
                                    .attr('data-' + metadata.value, value)
                                    .attr('data-' + metadata.text, value)
                                    .addClass(className.addition)
                                    .addClass(className.item)
                                ;
                                if (settings.hideAdditions) {
                                    $userChoice.addClass(className.hidden);
                                }
                                $userChoices = $userChoices === undefined
                                    ? $userChoice
                                    : $userChoices.add($userChoice);
                                module.verbose('Creating user choices for value', value, $userChoice);
                            }
                        });

                        return $userChoices;
                    },
                    userLabels: function (value) {
                        var
                            userValues = module.get.userValues()
                        ;
                        if (userValues) {
                            module.debug('Adding user labels', userValues);
                            $.each(userValues, function (index, value) {
                                module.verbose('Adding custom user value');
                                module.add.label(value, value);
                            });
                        }
                    },
                    menu: function () {
                        $menu = $('<div />')
                            .addClass(className.menu)
                            .appendTo($module)
                        ;
                    },
                    sizer: function () {
                        $sizer = $('<span />')
                            .addClass(className.sizer)
                            .insertAfter($search)
                        ;
                    },
                },

                search: function (query) {
                    query = query !== undefined
                        ? query
                        : module.get.query();
                    module.verbose('Searching for query', query);
                    if (settings.fireOnInit === false && module.is.initialLoad()) {
                        module.verbose('Skipping callback on initial load', settings.onSearch);
                    } else if (module.has.minCharacters(query) && settings.onSearch.call(element, query) !== false) {
                        module.filter(query);
                    } else {
                        module.hide(null, true);
                    }
                },

                select: {
                    firstUnfiltered: function () {
                        module.verbose('Selecting first non-filtered element');
                        module.remove.selectedItem();
                        $item
                            .not(selector.unselectable)
                            .not(selector.addition + selector.hidden)
                            .eq(0)
                            .addClass(className.selected)
                        ;
                    },
                    nextAvailable: function ($selected) {
                        $selected = $selected.eq(0);
                        var
                            $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
                            $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
                            hasNext        = $nextAvailable.length > 0
                        ;
                        if (hasNext) {
                            module.verbose('Moving selection to', $nextAvailable);
                            $nextAvailable.addClass(className.selected);
                        } else {
                            module.verbose('Moving selection to', $prevAvailable);
                            $prevAvailable.addClass(className.selected);
                        }
                    },
                },

                setup: {
                    api: function () {
                        var
                            apiSettings = {
                                debug: settings.debug,
                                urlData: {
                                    value: module.get.value(),
                                    query: module.get.query(),
                                },
                                on: false,
                            }
                        ;
                        module.verbose('First request, initializing API');
                        $module
                            .api(apiSettings)
                        ;
                    },
                    layout: function () {
                        if ($module.is('select')) {
                            module.setup.select();
                            module.setup.returnedObject();
                        }
                        if (!module.has.menu()) {
                            module.create.menu();
                        }
                        if (module.is.clearable() && !module.has.clearItem()) {
                            module.verbose('Adding clear icon');
                            $clear = $('<i />')
                                .addClass('remove icon')
                                .insertAfter($icon)
                            ;
                        }
                        if (module.is.search() && !module.has.search()) {
                            module.verbose('Adding search input');
                            var
                                labelNode = $module.prev('label')
                            ;
                            $search = $('<input />')
                                .addClass(className.search)
                                .prop('autocomplete', module.is.chrome() ? 'fomantic-search' : 'off')
                            ;
                            if (labelNode.length > 0) {
                                if (!labelNode.attr('id')) {
                                    labelNode.attr('id', '_' + module.get.id() + '_formLabel');
                                }
                                $search.attr('aria-labelledby', labelNode.attr('id'));
                            }
                            $search.insertBefore($text);
                        }
                        if (module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
                            module.create.sizer();
                        }
                        if (settings.allowTab) {
                            module.set.tabbable();
                        }
                    },
                    select: function () {
                        var
                            selectValues  = module.get.selectValues()
                        ;
                        module.debug('Dropdown initialized on a select', selectValues);
                        if ($module.is('select')) {
                            $input = $module;
                        }
                        // see if select is placed correctly already
                        if ($input.parent(selector.dropdown).length > 0) {
                            module.debug('UI dropdown already exists. Creating dropdown menu only');
                            $module = $input.closest(selector.dropdown);
                            if (!module.has.menu()) {
                                module.create.menu();
                            }
                            $menu = $module.children(selector.menu);
                            module.setup.menu(selectValues);
                        } else {
                            module.debug('Creating entire dropdown from select');
                            $module = $('<div />')
                                .attr('class', $input.attr('class'))
                                .addClass(className.selection)
                                .addClass(className.dropdown)
                                .html(templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className))
                                .insertBefore($input)
                            ;
                            if ($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
                                module.error(error.missingMultiple);
                                $input.prop('multiple', true);
                            }
                            if ($input.is('[multiple]')) {
                                module.set.multiple();
                            }
                            if ($input.prop('disabled')) {
                                module.debug('Disabling dropdown');
                                $module.addClass(className.disabled);
                            }
                            if ($input.is('[required]')) {
                                settings.forceSelection = true;
                            }
                            if (!settings.allowTab) {
                                $input.removeAttr('tabindex');
                            }
                            $input
                                .prop('required', false)
                                .removeAttr('class')
                                .detach()
                                .prependTo($module)
                            ;
                        }
                        module.refresh();
                    },
                    menu: function (values) {
                        $menu.html(templates.menu(values, fields, settings.preserveHTML, settings.className));
                        $item = $menu.find(selector.item);
                        $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
                    },
                    reference: function () {
                        module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
                        // replace module reference
                        $module = $module.parent(selector.dropdown);
                        instance = $module.data(moduleNamespace);
                        element = $module[0];
                        module.refresh();
                        module.setup.returnedObject();
                    },
                    returnedObject: function () {
                        var
                            $firstModules = $allModules.slice(0, elementIndex),
                            $lastModules  = $allModules.slice(elementIndex + 1)
                        ;
                        // adjust all modules to use correct reference
                        $allModules = $firstModules.add($module).add($lastModules);
                    },
                },

                refresh: function () {
                    module.refreshSelectors();
                    module.refreshData();
                },

                refreshItems: function () {
                    $item = $menu.find(selector.item);
                    $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
                },

                refreshSelectors: function () {
                    module.verbose('Refreshing selector cache');
                    $text = $module.find(selector.text);
                    $search = $module.find(selector.search);
                    $input = $module.find(selector.input);
                    $icon = $module.find(selector.icon);
                    $combo = $module.prev().find(selector.text).length > 0
                        ? $module.prev().find(selector.text)
                        : $module.prev();
                    $menu = $module.children(selector.menu);
                    $item = $menu.find(selector.item);
                    $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
                },

                refreshData: function () {
                    module.verbose('Refreshing cached metadata');
                    $item
                        .removeData(metadata.text)
                        .removeData(metadata.value)
                    ;
                },

                clearData: function () {
                    module.verbose('Clearing metadata');
                    $item
                        .removeData(metadata.text)
                        .removeData(metadata.value)
                    ;
                    $module
                        .removeData(metadata.defaultText)
                        .removeData(metadata.defaultValue)
                        .removeData(metadata.placeholderText)
                    ;
                },

                clearItems: function () {
                    $menu.empty();
                    module.refreshItems();
                },

                toggle: function () {
                    module.verbose('Toggling menu visibility');
                    if (!module.is.active()) {
                        module.show();
                    } else {
                        module.hide();
                    }
                },

                show: function (callback, preventFocus) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if ((focused || iconClicked) && module.is.remote() && module.is.noApiCache() && !module.has.maxSelections()) {
                        module.clearItems();
                    }
                    if (!module.can.show() && module.is.remote()) {
                        module.debug('No API results retrieved, searching before show');
                        module.queryRemote(module.get.query(), module.show, [callback, preventFocus]);
                    }
                    if (module.can.show() && !module.is.active()) {
                        module.debug('Showing dropdown');
                        if (module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered())) {
                            module.remove.message();
                        }
                        if (module.is.allFiltered()) {
                            return true;
                        }
                        if (settings.onShow.call(element) !== false) {
                            module.remove.empty();
                            module.animate.show(function () {
                                module.bind.intent();
                                if (module.has.search() && !preventFocus) {
                                    module.focusSearch();
                                }
                                module.set.visible();
                                callback.call(element);
                            });
                        }
                    }
                },

                hide: function (callback, preventBlur) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.active() && !module.is.animatingOutward()) {
                        module.debug('Hiding dropdown');
                        if (settings.onHide.call(element) !== false) {
                            module.animate.hide(function () {
                                module.remove.visible();
                                // hiding search focus
                                if (module.is.focusedOnSearch() && preventBlur !== true) {
                                    $search.trigger('blur');
                                }
                                callback.call(element);
                            });
                            // Hide submenus explicitly. On some browsers (esp. mobile), they will not automatically receive a
                            // mouseleave event
                            var $subMenu = $module.find(selector.menu);
                            if ($subMenu.length > 0) {
                                module.verbose('Hiding sub-menu', $subMenu);
                                $subMenu.each(function () {
                                    var $sub = $(this);
                                    if (!module.is.animating($sub)) {
                                        module.animate.hide(false, $sub);
                                    }
                                });
                            }
                        }
                    } else {
                        module.unbind.intent();
                    }
                    iconClicked = false;
                    focused = false;
                },

                hideOthers: function () {
                    module.verbose('Finding other dropdowns to hide');
                    $allModules
                        .not($module)
                        .has(selector.menu + '.' + className.visible)
                        .dropdown('hide')
                    ;
                },

                hideMenu: function () {
                    module.verbose('Hiding menu  instantaneously');
                    module.remove.active();
                    module.remove.visible();
                    $menu.transition('destroy').transition('hide');
                },

                hideSubMenus: function () {
                    var
                        $subMenus = $menu.children(selector.item).find(selector.menu)
                    ;
                    module.verbose('Hiding sub menus', $subMenus);
                    $subMenus.transition('hide');
                },

                bind: {
                    events: function () {
                        module.bind.keyboardEvents();
                        module.bind.inputEvents();
                        module.bind.mouseEvents();
                    },
                    keyboardEvents: function () {
                        module.verbose('Binding keyboard events');
                        $module
                            .on('keydown' + eventNamespace, module.event.keydown)
                        ;
                        if (module.has.search()) {
                            $module
                                .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
                            ;
                        }
                        if (module.is.multiple()) {
                            $document
                                .on('keydown' + elementNamespace, module.event.document.keydown)
                            ;
                        }
                    },
                    inputEvents: function () {
                        module.verbose('Binding input change events');
                        $module
                            .on('change' + eventNamespace, selector.input, module.event.change)
                        ;
                        if (module.is.multiple() && module.is.searchSelection()) {
                            $module
                                .on('paste' + eventNamespace, selector.search, module.event.paste)
                            ;
                        }
                    },
                    mouseEvents: function () {
                        module.verbose('Binding mouse events');
                        if (module.is.multiple()) {
                            $module
                                .on('click' + eventNamespace, selector.label, module.event.label.click)
                                .on('click' + eventNamespace, selector.remove, module.event.remove.click)
                            ;
                        }
                        if (module.is.searchSelection()) {
                            $module
                                .on('mousedown' + eventNamespace, module.event.mousedown)
                                .on('mouseup' + eventNamespace, module.event.mouseup)
                                .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
                                .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
                                .on('click' + eventNamespace, selector.icon, module.event.icon.click)
                                .on('click' + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
                                .on('focus' + eventNamespace, selector.search, module.event.search.focus)
                                .on('click' + eventNamespace, selector.search, module.event.search.focus)
                                .on('blur' + eventNamespace, selector.search, module.event.search.blur)
                                .on('click' + eventNamespace, selector.text, module.event.text.focus)
                            ;
                            if (module.is.multiple()) {
                                $module
                                    .on('click' + eventNamespace, module.event.click)
                                    .on('click' + eventNamespace, module.event.search.focus)
                                ;
                            }
                        } else {
                            if (settings.on === 'click') {
                                $module
                                    .on('click' + eventNamespace, selector.icon, module.event.icon.click)
                                    .on('click' + eventNamespace, module.event.test.toggle)
                                ;
                            } else if (settings.on === 'hover') {
                                $module
                                    .on('mouseenter' + eventNamespace, module.delay.show)
                                    .on('mouseleave' + eventNamespace, module.delay.hide)
                                    .on('touchstart' + eventNamespace, module.event.test.toggle)
                                    .on('touchstart' + eventNamespace, selector.icon, module.event.icon.click)
                                ;
                            } else {
                                $module
                                    .on(settings.on + eventNamespace, module.toggle)
                                ;
                            }
                            $module
                                .on('mousedown' + eventNamespace, module.event.mousedown)
                                .on('mouseup' + eventNamespace, module.event.mouseup)
                                .on('focus' + eventNamespace, module.event.focus)
                                .on('click' + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
                            ;
                            if (module.has.menuSearch()) {
                                $module
                                    .on('blur' + eventNamespace, selector.search, module.event.search.blur)
                                ;
                            } else {
                                $module
                                    .on('blur' + eventNamespace, module.event.blur)
                                ;
                            }
                        }
                        $menu
                            .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
                            .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
                            .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
                            .on('click' + eventNamespace, selector.item, module.event.item.click)
                        ;
                    },
                    intent: function () {
                        module.verbose('Binding hide intent event to document');
                        $document
                            .on('click' + elementNamespace, module.event.test.hide)
                        ;
                    },
                },

                unbind: {
                    intent: function () {
                        module.verbose('Removing hide intent event from document');
                        $document
                            .off('click' + elementNamespace)
                        ;
                    },
                },

                filter: function (query) {
                    var
                        searchTerm = query !== undefined
                            ? query
                            : module.get.query(),
                        afterFiltered = function () {
                            if (module.is.multiple()) {
                                module.filterActive();
                            }
                            if (query || (!query && module.get.activeItem().length === 0)) {
                                module.select.firstUnfiltered();
                            }
                            if (module.has.allResultsFiltered()) {
                                if (settings.onNoResults.call(element, searchTerm)) {
                                    if (settings.allowAdditions) {
                                        if (settings.hideAdditions) {
                                            module.verbose('User addition with no menu, setting empty style');
                                            module.set.empty();
                                            module.hideMenu();
                                        }
                                    } else {
                                        module.verbose('All items filtered, showing message', searchTerm);
                                        module.add.message(message.noResults);
                                    }
                                } else {
                                    module.verbose('All items filtered, hiding dropdown', searchTerm);
                                    module.set.empty();
                                    module.hideMenu();
                                }
                            } else {
                                module.remove.empty();
                                module.remove.message();
                            }
                            if (settings.allowAdditions) {
                                module.add.userSuggestion(module.escape.htmlEntities(query));
                            }
                            if (module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() && !module.is.empty()) {
                                module.show();
                            }
                        }
                    ;
                    if (settings.useLabels && module.has.maxSelections()) {
                        module.show();

                        return;
                    }
                    if (settings.apiSettings) {
                        if (module.can.useAPI()) {
                            module.queryRemote(searchTerm, function () {
                                if (settings.filterRemoteData) {
                                    module.filterItems(searchTerm);
                                }
                                var preSelected = $input.val();
                                if (!Array.isArray(preSelected)) {
                                    preSelected = preSelected && preSelected !== '' ? preSelected.split(settings.delimiter) : [];
                                }
                                if (module.is.multiple()) {
                                    $.each(preSelected, function (index, value) {
                                        $item.filter('[data-value="' + value + '"]')
                                            .addClass(className.filtered)
                                        ;
                                    });
                                }
                                module.focusSearch(true);
                                afterFiltered();
                            });
                        } else {
                            module.error(error.noAPI);
                        }
                    } else {
                        module.filterItems(searchTerm);
                        afterFiltered();
                    }
                },

                queryRemote: function (query, callback, callbackParameters) {
                    if (!Array.isArray(callbackParameters)) {
                        callbackParameters = [callbackParameters];
                    }
                    var
                        apiSettings = {
                            errorDuration: false,
                            cache: 'local',
                            throttle: settings.throttle,
                            urlData: {
                                query: query,
                            },
                        },
                        apiCallbacks = {
                            onError: function (errorMessage, $module, xhr) {
                                module.add.message(message.serverError);
                                iconClicked = false;
                                focused = false;
                                callback.apply(null, callbackParameters);
                                if (typeof settings.apiSettings.onError === 'function') {
                                    settings.apiSettings.onError.call(this, errorMessage, $module, xhr);
                                }
                            },
                            onFailure: function (response, $module, xhr) {
                                module.add.message(message.serverError);
                                iconClicked = false;
                                focused = false;
                                callback.apply(null, callbackParameters);
                                if (typeof settings.apiSettings.onFailure === 'function') {
                                    settings.apiSettings.onFailure.call(this, response, $module, xhr);
                                }
                            },
                            onSuccess: function (response, $module, xhr) {
                                var
                                    values          = response[fields.remoteValues]
                                ;
                                if (!Array.isArray(values)) {
                                    values = [];
                                }
                                module.remove.message();
                                var menuConfig = {};
                                menuConfig[fields.values] = values;
                                module.setup.menu(menuConfig);

                                if (values.length === 0 && !settings.allowAdditions) {
                                    module.add.message(message.noResults);
                                } else {
                                    var value = module.is.multiple() ? module.get.values() : module.get.value();
                                    if (value !== '') {
                                        module.verbose('Value(s) present after click icon, select value(s) in items');
                                        module.set.selected(value, null, true, true);
                                    }
                                }
                                iconClicked = false;
                                focused = false;
                                callback.apply(null, callbackParameters);
                                if (typeof settings.apiSettings.onSuccess === 'function') {
                                    settings.apiSettings.onSuccess.call(this, response, $module, xhr);
                                }
                            },
                        }
                    ;
                    if (!$module.api('get request')) {
                        module.setup.api();
                    }
                    apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings, apiCallbacks, tempDisableApiCache ? { cache: false } : {});
                    $module
                        .api('setting', apiSettings)
                        .api('query')
                    ;
                    tempDisableApiCache = false;
                },

                filterItems: function (query) {
                    var
                        searchTerm = module.remove.diacritics(
                            query !== undefined
                                ? query
                                : module.get.query()
                        ),
                        results          =  null,
                        escapedTerm      = module.escape.string(searchTerm),
                        regExpFlags      = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
                        beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
                    ;
                    // avoid loop if we're matching nothing
                    if (module.has.query()) {
                        results = [];

                        module.verbose('Searching for matching values', searchTerm);
                        $item
                            .each(function () {
                                var
                                    $choice = $(this),
                                    text,
                                    value
                                ;
                                if ($choice.hasClass(className.unfilterable)) {
                                    results.push(this);

                                    return true;
                                }
                                if (settings.match === 'both' || settings.match === 'text') {
                                    text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
                                    if (text.search(beginsWithRegExp) !== -1
                                        || (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text))
                                        || (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text))
                                    ) {
                                        results.push(this);

                                        return true;
                                    }
                                }
                                if (settings.match === 'both' || settings.match === 'value') {
                                    value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
                                    if (value.search(beginsWithRegExp) !== -1
                                        || (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value))
                                        || (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value))
                                    ) {
                                        results.push(this);

                                        return true;
                                    }
                                }
                            })
                        ;
                    }
                    module.debug('Showing only matched items', searchTerm);
                    module.remove.filteredItem();
                    if (results) {
                        $item
                            .not(results)
                            .addClass(className.filtered)
                        ;
                    }

                    if (!module.has.query()) {
                        $divider
                            .removeClass(className.hidden)
                        ;
                    } else if (settings.hideDividers === true) {
                        $divider
                            .addClass(className.hidden)
                        ;
                    } else if (settings.hideDividers === 'empty') {
                        $divider
                            .removeClass(className.hidden)
                            .filter(function () {
                                // First find the last divider in this divider group
                                // Dividers which are direct siblings are considered a group
                                var $lastDivider = $(this).nextUntil(selector.item);

                                return ($lastDivider.length > 0 ? $lastDivider : $(this))
                                    // Count all non-filtered items until the next divider (or end of the dropdown)
                                    .nextUntil(selector.divider)
                                    .filter(selector.item + ':not(.' + className.filtered + ')')
                                    // Hide divider if no items are found
                                    .length === 0;
                            })
                            .addClass(className.hidden)
                        ;
                    }
                },

                fuzzySearch: function (query, term) {
                    var
                        termLength  = term.length,
                        queryLength = query.length
                    ;
                    query = settings.ignoreSearchCase ? query.toLowerCase() : query;
                    term = settings.ignoreSearchCase ? term.toLowerCase() : term;
                    if (queryLength > termLength) {
                        return false;
                    }
                    if (queryLength === termLength) {
                        return query === term;
                    }
                    for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
                        var
                            continueSearch = false,
                            queryCharacter = query.charCodeAt(characterIndex)
                        ;
                        while (nextCharacterIndex < termLength) {
                            if (term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
                                continueSearch = true;

                                break;
                            }
                        }

                        if (!continueSearch) {
                            return false;
                        }
                    }

                    return true;
                },
                exactSearch: function (query, term) {
                    query = settings.ignoreSearchCase ? query.toLowerCase() : query;
                    term = settings.ignoreSearchCase ? term.toLowerCase() : term;

                    return term.indexOf(query) > -1;
                },
                filterActive: function () {
                    if (settings.useLabels) {
                        $item.filter('.' + className.active)
                            .addClass(className.filtered)
                        ;
                    }
                },

                focusSearch: function (skipHandler) {
                    if (module.has.search() && !module.is.focusedOnSearch()) {
                        if (skipHandler) {
                            $module.off('focus' + eventNamespace, selector.search);
                            $search.trigger('focus');
                            $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
                        } else {
                            $search.trigger('focus');
                        }
                    }
                },

                blurSearch: function () {
                    if (module.has.search()) {
                        $search.trigger('blur');
                    }
                },

                forceSelection: function () {
                    var
                        $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
                        $activeItem        = $item.not(className.filtered).filter('.' + className.active).eq(0),
                        $selectedItem      = $currentlySelected.length > 0
                            ? $currentlySelected
                            : $activeItem,
                        hasSelected = $selectedItem.length > 0
                    ;
                    if (settings.allowAdditions || (hasSelected && !module.is.multiple())) {
                        module.debug('Forcing partial selection to selected item', $selectedItem);
                        module.event.item.click.call($selectedItem, {}, true);
                    } else {
                        module.remove.searchTerm();
                    }
                },

                change: {
                    values: function (values) {
                        if (!settings.allowAdditions) {
                            module.clear();
                        }
                        module.debug('Creating dropdown with specified values', values);
                        var menuConfig = {};
                        menuConfig[fields.values] = values;
                        module.setup.menu(menuConfig);
                        $.each(values, function (index, item) {
                            if (item.selected === true) {
                                module.debug('Setting initial selection to', item[fields.value]);
                                module.set.selected(item[fields.value]);
                                if (!module.is.multiple()) {
                                    return false;
                                }
                            }
                        });

                        if (module.has.selectInput()) {
                            module.disconnect.selectObserver();
                            $input.html('');
                            $input.append('<option disabled selected value></option>');
                            $.each(values, function (index, item) {
                                var
                                    value = settings.templates.deQuote(item[fields.value]),
                                    name = settings.templates.escape(
                                        item[fields.name] || '',
                                        settings.preserveHTML
                                    )
                                ;
                                $input.append('<option value="' + value + '"' + (item.selected === true ? ' selected' : '') + '>' + name + '</option>');
                            });
                            module.observe.select();
                        }
                    },
                },

                event: {
                    paste: function (event) {
                        var
                            pasteValue = (event.originalEvent.clipboardData || window.clipboardData).getData('text'),
                            tokens = pasteValue.split(settings.delimiter),
                            notFoundTokens = []
                        ;
                        tokens.forEach(function (value) {
                            if (module.set.selected(module.escape.htmlEntities(value.trim()), null, false, true) === false) {
                                notFoundTokens.push(value.trim());
                            }
                        });
                        event.preventDefault();
                        if (notFoundTokens.length > 0) {
                            var searchEl = $search[0],
                                startPos = searchEl.selectionStart,
                                endPos = searchEl.selectionEnd,
                                orgText = searchEl.value,
                                pasteText = notFoundTokens.join(settings.delimiter),
                                newEndPos = startPos + pasteText.length
                            ;
                            $search.val(orgText.slice(0, startPos) + pasteText + orgText.slice(endPos));
                            searchEl.selectionStart = newEndPos;
                            searchEl.selectionEnd = newEndPos;
                            module.event.input(event);
                        }
                    },
                    change: function () {
                        if (!internalChange) {
                            module.debug('Input changed, updating selection');
                            module.set.selected();
                        }
                    },
                    focus: function () {
                        if (settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
                            focused = true;
                            module.show();
                        }
                    },
                    blur: function (event) {
                        pageLostFocus = document.activeElement === this;
                        if (!activated && !pageLostFocus) {
                            module.remove.activeLabel();
                            module.hide();
                        }
                    },
                    mousedown: function () {
                        if (module.is.searchSelection(true)) {
                            // prevent menu hiding on immediate re-focus
                            willRefocus = true;
                        } else {
                            // prevents focus callback from occurring on mousedown
                            activated = true;
                        }
                    },
                    mouseup: function () {
                        if (module.is.searchSelection(true)) {
                            // prevent menu hiding on immediate re-focus
                            willRefocus = false;
                        } else {
                            activated = false;
                        }
                    },
                    click: function (event) {
                        var
                            $target = $(event.target)
                        ;
                        // focus search
                        if ($target.is($module)) {
                            if (!module.is.focusedOnSearch()) {
                                module.focusSearch();
                            } else {
                                module.show();
                            }
                        }
                    },
                    search: {
                        focus: function (event) {
                            activated = true;
                            if (module.is.multiple()) {
                                module.remove.activeLabel();
                            }
                            if (!focused && !module.is.active() && (settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) && event.type !== 'touchstart') {
                                focused = true;
                                module.search();
                            }
                        },
                        blur: function (event) {
                            pageLostFocus = document.activeElement === this;
                            if (module.is.searchSelection(true) && !willRefocus) {
                                if (!itemActivated && !pageLostFocus) {
                                    if (settings.forceSelection) {
                                        module.forceSelection();
                                    } else if (!settings.allowAdditions) {
                                        module.remove.searchTerm();
                                    }
                                    module.hide();
                                }
                            }
                            willRefocus = false;
                        },
                    },
                    clearIcon: {
                        click: function (event) {
                            module.clear();
                            if (module.is.searchSelection()) {
                                module.remove.searchTerm();
                            }
                            module.hide();
                            event.stopPropagation();
                        },
                    },
                    icon: {
                        click: function (event) {
                            iconClicked = true;
                            if (module.has.search()) {
                                if (!module.is.active()) {
                                    if (settings.showOnFocus) {
                                        module.focusSearch();
                                    } else {
                                        module.toggle();
                                    }
                                } else {
                                    module.blurSearch();
                                }
                            } else {
                                module.toggle();
                            }
                            event.stopPropagation();
                        },
                    },
                    text: {
                        focus: function (event) {
                            activated = true;
                            module.focusSearch();
                        },
                    },
                    input: function (event) {
                        if (module.is.multiple() || module.is.searchSelection()) {
                            module.set.filtered();
                        }
                        clearTimeout(module.timer);
                        module.timer = setTimeout(function () { module.search(); }, settings.delay.search);
                    },
                    label: {
                        click: function (event) {
                            var
                                $label        = $(this),
                                $labels       = $module.find(selector.label),
                                $activeLabels = $labels.filter('.' + className.active),
                                $nextActive   = $label.nextAll('.' + className.active),
                                $prevActive   = $label.prevAll('.' + className.active),
                                $range = $nextActive.length > 0
                                    ? $label.nextUntil($nextActive).add($activeLabels).add($label)
                                    : $label.prevUntil($prevActive).add($activeLabels).add($label)
                            ;
                            if (event.shiftKey) {
                                $activeLabels.removeClass(className.active);
                                $range.addClass(className.active);
                            } else if (event.ctrlKey) {
                                $label.toggleClass(className.active);
                            } else {
                                $activeLabels.removeClass(className.active);
                                $label.addClass(className.active);
                            }
                            settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
                            event.stopPropagation();
                        },
                    },
                    remove: {
                        click: function (event) {
                            var
                                $label = $(this).parent()
                            ;
                            if ($label.hasClass(className.active)) {
                                // remove all selected labels
                                module.remove.activeLabels();
                            } else {
                                // remove this label only
                                module.remove.activeLabels($label);
                            }
                            event.stopPropagation();
                        },
                    },
                    test: {
                        toggle: function (event) {
                            var
                                toggleBehavior = module.is.multiple()
                                    ? module.show
                                    : module.toggle
                            ;
                            if (module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
                                return;
                            }
                            if (!module.is.multiple() || (module.is.multiple() && !module.is.active())) {
                                focused = true;
                            }
                            if (module.determine.eventOnElement(event, toggleBehavior) && event.type !== 'touchstart') {
                                // do not preventDefault of touchstart; so emulated mouseenter is triggered on first touch and not later
                                // (when selecting an item). The double-showing of the dropdown through both events does not hurt.
                                event.preventDefault();
                            }
                        },
                        hide: function (event) {
                            if (module.determine.eventInModule(event, module.hide)) {
                                if (element.id && $(event.target).attr('for') === element.id) {
                                    event.preventDefault();
                                }
                            }
                        },
                    },
                    class: {
                        mutation: function (mutations) {
                            mutations.forEach(function (mutation) {
                                if (mutation.attributeName === 'class') {
                                    module.check.disabled();
                                }
                            });
                        },
                    },
                    select: {
                        mutation: function (mutations) {
                            if (module.is.selectMutation(mutations)) {
                                module.debug('<select> modified, recreating menu');
                                module.disconnect.selectObserver();
                                module.refresh();
                                module.setup.select();
                                module.set.selected();
                                module.observe.select();
                            }
                        },
                    },
                    menu: {
                        mutation: function (mutations) {
                            var
                                mutation   = mutations[0],
                                $addedNode = mutation.addedNodes
                                    ? $(mutation.addedNodes[0])
                                    : $(false),
                                $removedNode = mutation.removedNodes
                                    ? $(mutation.removedNodes[0])
                                    : $(false),
                                $changedNodes  = $addedNode.add($removedNode),
                                isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
                                isMessage      = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
                            ;
                            if (isUserAddition || isMessage) {
                                module.debug('Updating item selector cache');
                                module.refreshItems();
                            } else {
                                module.debug('Menu modified, updating selector cache');
                                module.refresh();
                            }
                        },
                        mousedown: function () {
                            itemActivated = true;
                        },
                        mouseup: function () {
                            itemActivated = false;
                        },
                    },
                    item: {
                        mouseenter: function (event) {
                            var
                                $target        = $(event.target),
                                $item          = $(this),
                                $subMenu       = $item.children(selector.menu),
                                $otherMenus    = $item.siblings(selector.item).children(selector.menu),
                                hasSubMenu     = $subMenu.length > 0,
                                isBubbledEvent = $subMenu.find($target).length > 0
                            ;
                            if (!isBubbledEvent && hasSubMenu) {
                                clearTimeout(module.itemTimer);
                                module.itemTimer = setTimeout(function () {
                                    module.verbose('Showing sub-menu', $subMenu);
                                    $.each($otherMenus, function () {
                                        module.animate.hide(false, $(this));
                                    });
                                    module.animate.show(false, $subMenu);
                                }, settings.delay.show);
                                event.preventDefault();
                            }
                        },
                        mouseleave: function (event) {
                            var
                                $subMenu = $(this).find(selector.menu)
                            ;
                            if ($subMenu.length > 0) {
                                clearTimeout(module.itemTimer);
                                module.itemTimer = setTimeout(function () {
                                    module.verbose('Hiding sub-menu', $subMenu);
                                    $subMenu.each(function () {
                                        module.animate.hide(false, $(this));
                                    });
                                }, settings.delay.hide);
                            }
                        },
                        click: function (event, skipRefocus) {
                            var
                                $choice        = $(this),
                                $target        = event
                                    ? $(event.target || '')
                                    : $(''),
                                $subMenu       = $choice.find(selector.menu),
                                text           = module.get.choiceText($choice),
                                value          = module.get.choiceValue($choice, text),
                                hasSubMenu     = $subMenu.length > 0,
                                isBubbledEvent = $subMenu.find($target).length > 0
                            ;
                            // prevents IE11 bug where menu receives focus even though `tabindex=-1`
                            if (document.activeElement.tagName.toLowerCase() !== 'input') {
                                $(document.activeElement).trigger('blur');
                            }
                            if (!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
                                if (module.is.searchSelection()) {
                                    if (settings.allowAdditions) {
                                        module.remove.userAddition();
                                    }
                                    if (!settings.keepSearchTerm) {
                                        module.remove.filteredItem();
                                        module.remove.searchTerm();
                                    }
                                    if (!module.is.visible() && $target.length > 0) {
                                        module.show();
                                    }
                                    if (!module.is.focusedOnSearch() && skipRefocus !== true) {
                                        module.focusSearch(true);
                                    }
                                }
                                if (!settings.useLabels) {
                                    module.remove.filteredItem();
                                    module.set.scrollPosition($choice);
                                }
                                module.determine.selectAction.call(this, text, value);
                            }
                        },
                    },

                    document: {
                        // label selection should occur even when element has no focus
                        keydown: function (event) {
                            var
                                pressedKey    = event.which,
                                isShortcutKey = module.is.inObject(pressedKey, keys)
                            ;
                            if (isShortcutKey) {
                                var
                                    $label            = $module.find(selector.label),
                                    $activeLabel      = $label.filter('.' + className.active),
                                    activeValue       = $activeLabel.data(metadata.value),
                                    labelIndex        = $label.index($activeLabel),
                                    labelCount        = $label.length,
                                    hasActiveLabel    = $activeLabel.length > 0,
                                    hasMultipleActive = $activeLabel.length > 1,
                                    isFirstLabel      = labelIndex === 0,
                                    isLastLabel       = labelIndex + 1 === labelCount,
                                    isSearch          = module.is.searchSelection(),
                                    isFocusedOnSearch = module.is.focusedOnSearch(),
                                    isFocused         = module.is.focused(),
                                    caretAtStart      = isFocusedOnSearch && module.get.caretPosition(false) === 0,
                                    isSelectedSearch  = caretAtStart && module.get.caretPosition(true) !== 0
                                ;
                                if (isSearch && !hasActiveLabel && !isFocusedOnSearch) {
                                    return;
                                }

                                switch (pressedKey) {
                                    case keys.leftArrow: {
                                        // activate previous label
                                        if ((isFocused || caretAtStart) && !hasActiveLabel) {
                                            module.verbose('Selecting previous label');
                                            $label.last().addClass(className.active);
                                        } else if (hasActiveLabel) {
                                            if (!event.shiftKey) {
                                                module.verbose('Selecting previous label');
                                                $label.removeClass(className.active);
                                            } else {
                                                module.verbose('Adding previous label to selection');
                                            }
                                            if (isFirstLabel && !hasMultipleActive) {
                                                $activeLabel.addClass(className.active);
                                            } else {
                                                $activeLabel.prev(selector.siblingLabel)
                                                    .addClass(className.active)
                                                    .end()
                                                ;
                                            }
                                            event.preventDefault();
                                        }

                                        break;
                                    }
                                    case keys.rightArrow: {
                                        // activate first label
                                        if (isFocused && !hasActiveLabel) {
                                            $label.first().addClass(className.active);
                                        }
                                        // activate next label
                                        if (hasActiveLabel) {
                                            if (!event.shiftKey) {
                                                module.verbose('Selecting next label');
                                                $label.removeClass(className.active);
                                            } else {
                                                module.verbose('Adding next label to selection');
                                            }
                                            if (isLastLabel) {
                                                if (isSearch) {
                                                    if (!isFocusedOnSearch) {
                                                        module.focusSearch();
                                                    } else {
                                                        $label.removeClass(className.active);
                                                    }
                                                } else if (hasMultipleActive) {
                                                    $activeLabel.next(selector.siblingLabel).addClass(className.active);
                                                } else {
                                                    $activeLabel.addClass(className.active);
                                                }
                                            } else {
                                                $activeLabel.next(selector.siblingLabel).addClass(className.active);
                                            }
                                            event.preventDefault();
                                        }

                                        break;
                                    }
                                    case keys.deleteKey:
                                    case keys.backspace: {
                                        if (hasActiveLabel) {
                                            module.verbose('Removing active labels');
                                            if (isLastLabel) {
                                                if (isSearch && !isFocusedOnSearch) {
                                                    module.focusSearch();
                                                }
                                            }
                                            $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
                                            module.remove.activeLabels($activeLabel);
                                            if (!module.is.visible()) {
                                                module.show();
                                            }
                                            event.preventDefault();
                                        } else if (caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey === keys.backspace) {
                                            module.verbose('Removing last label on input backspace');
                                            $activeLabel = $label.last().addClass(className.active);
                                            module.remove.activeLabels($activeLabel);
                                            if (!module.is.visible()) {
                                                module.show();
                                            }
                                        }

                                        break;
                                    }
                                    default: {
                                        $activeLabel.removeClass(className.active);
                                    }
                                }
                            }
                        },
                    },

                    keydown: function (event) {
                        var
                            pressedKey    = event.which,
                            isShortcutKey = module.is.inObject(pressedKey, keys) || event.key === settings.delimiter
                        ;
                        if (isShortcutKey) {
                            var
                                $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
                                $activeItem        = $menu.children('.' + className.active).eq(0),
                                $selectedItem      = $currentlySelected.length > 0
                                    ? $currentlySelected
                                    : $activeItem,
                                $visibleItems = $selectedItem.length > 0
                                    ? $selectedItem.siblings(':not(.' + className.filtered + ')').addBack()
                                    : $menu.children(':not(.' + className.filtered + ')'),
                                $subMenu              = $selectedItem.children(selector.menu),
                                $parentMenu           = $selectedItem.closest(selector.menu),
                                inVisibleMenu         = $parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0,
                                hasSubMenu            = $subMenu.length > 0,
                                hasSelectedItem       = $selectedItem.length > 0,
                                selectedIsSelectable  = $selectedItem.not(selector.unselectable).length > 0,
                                delimiterPressed      = event.key === settings.delimiter && module.is.multiple(),
                                isAdditionWithoutMenu = settings.allowAdditions && (pressedKey === keys.enter || delimiterPressed),
                                $nextItem,
                                isSubMenuItem
                            ;
                            // allow selection with menu closed
                            if (isAdditionWithoutMenu) {
                                if (selectedIsSelectable && settings.hideAdditions) {
                                    module.verbose('Selecting item from keyboard shortcut', $selectedItem);
                                    module.event.item.click.call($selectedItem, event);
                                }
                                if (module.is.searchSelection()) {
                                    module.remove.searchTerm();
                                }
                                if (module.is.multiple()) {
                                    event.preventDefault();
                                }
                            }

                            // visible menu keyboard shortcuts
                            if (module.is.visible()) {
                                // enter (select or open sub-menu)
                                if (pressedKey === keys.enter || delimiterPressed) {
                                    if (pressedKey === keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
                                        module.verbose('Pressed enter on unselectable category, opening sub menu');
                                        pressedKey = keys.rightArrow;
                                    } else if (selectedIsSelectable) {
                                        module.verbose('Selecting item from keyboard shortcut', $selectedItem);
                                        module.event.item.click.call($selectedItem, event);
                                        if (module.is.searchSelection()) {
                                            if (!settings.keepSearchTerm) {
                                                module.remove.searchTerm();
                                            }
                                            if (module.is.multiple()) {
                                                $search.trigger('focus');
                                            }
                                        }
                                    }
                                    event.preventDefault();
                                }

                                // sub-menu actions
                                if (hasSelectedItem) {
                                    if (pressedKey === keys.leftArrow) {
                                        isSubMenuItem = $parentMenu[0] !== $menu[0];

                                        if (isSubMenuItem) {
                                            module.verbose('Left key pressed, closing sub-menu');
                                            module.animate.hide(false, $parentMenu);
                                            $selectedItem
                                                .removeClass(className.selected)
                                            ;
                                            $parentMenu
                                                .closest(selector.item)
                                                .addClass(className.selected)
                                            ;
                                            event.preventDefault();
                                        }
                                    }

                                    // right arrow (show sub-menu)
                                    if (pressedKey === keys.rightArrow) {
                                        if (hasSubMenu) {
                                            module.verbose('Right key pressed, opening sub-menu');
                                            module.animate.show(false, $subMenu);
                                            $selectedItem
                                                .removeClass(className.selected)
                                            ;
                                            $subMenu
                                                .find(selector.item).eq(0)
                                                .addClass(className.selected)
                                            ;
                                            event.preventDefault();
                                        }
                                    }
                                }

                                // up arrow (traverse menu up)
                                if (pressedKey === keys.upArrow) {
                                    $nextItem = hasSelectedItem && inVisibleMenu
                                        ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
                                        : $item.eq(0);
                                    if ($visibleItems.index($nextItem) < 0) {
                                        module.verbose('Up key pressed but reached top of current menu');
                                        event.preventDefault();

                                        return;
                                    }

                                    module.verbose('Up key pressed, changing active item');
                                    $selectedItem
                                        .removeClass(className.selected)
                                    ;
                                    $nextItem
                                        .addClass(className.selected)
                                    ;
                                    module.set.scrollPosition($nextItem);
                                    if (settings.selectOnKeydown && module.is.single() && !$nextItem.hasClass(className.actionable)) {
                                        module.set.selectedItem($nextItem);
                                    }

                                    event.preventDefault();
                                }

                                // down arrow (traverse menu down)
                                if (pressedKey === keys.downArrow) {
                                    $nextItem = hasSelectedItem && inVisibleMenu
                                        ? $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
                                        : $item.eq(0);
                                    if ($nextItem.length === 0) {
                                        module.verbose('Down key pressed but reached bottom of current menu');
                                        event.preventDefault();

                                        return;
                                    }

                                    module.verbose('Down key pressed, changing active item');
                                    $item
                                        .removeClass(className.selected)
                                    ;
                                    $nextItem
                                        .addClass(className.selected)
                                    ;
                                    module.set.scrollPosition($nextItem);
                                    if (settings.selectOnKeydown && module.is.single() && !$nextItem.hasClass(className.actionable)) {
                                        module.set.selectedItem($nextItem);
                                    }

                                    event.preventDefault();
                                }

                                // page down (show next page)
                                if (pressedKey === keys.pageUp) {
                                    module.scrollPage('up');
                                    event.preventDefault();
                                }
                                if (pressedKey === keys.pageDown) {
                                    module.scrollPage('down');
                                    event.preventDefault();
                                }

                                // escape (close menu)
                                if (pressedKey === keys.escape) {
                                    module.verbose('Escape key pressed, closing dropdown');
                                    module.hide();
                                    event.stopPropagation();
                                }
                            } else {
                                // delimiter key
                                if (pressedKey === keys.enter || delimiterPressed) {
                                    event.preventDefault();
                                }
                                // down arrow (open menu)
                                if (pressedKey === keys.downArrow && !module.is.visible()) {
                                    module.verbose('Down key pressed, showing dropdown');
                                    module.show();
                                    event.preventDefault();
                                }
                            }
                        } else {
                            if (!module.has.search()) {
                                module.set.selectedLetter(String.fromCharCode(pressedKey));
                            }
                        }
                    },
                },

                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);
                        }
                    },
                },

                determine: {
                    selectAction: function (text, value) {
                        selectActionActive = true;
                        module.verbose('Determining action', settings.action);
                        if (isFunction(module.action[settings.action])) {
                            module.verbose('Triggering preset action', settings.action, text, value);
                            module.action[settings.action].call(element, text, value, this);
                        } else if (isFunction(settings.action)) {
                            module.verbose('Triggering user action', settings.action, text, value);
                            settings.action.call(element, text, value, this);
                        } else {
                            module.error(error.action, settings.action);
                        }
                        selectActionActive = false;
                    },
                    eventInModule: function (event, callback) {
                        var
                            $target    = $(event.target),
                            inDocument = $target.closest(document.documentElement).length > 0,
                            inModule   = $target.closest($module).length > 0
                        ;
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if (inDocument && !inModule) {
                            module.verbose('Triggering event', callback);
                            callback();

                            return true;
                        }

                        module.verbose('Event occurred in dropdown, canceling callback');

                        return false;
                    },
                    eventOnElement: function (event, callback) {
                        var
                            $target      = $(event.target),
                            $label       = $target.closest(selector.siblingLabel),
                            inVisibleDOM = document.body.contains(event.target),
                            notOnLabel   = $module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels),
                            notInMenu    = $target.closest($menu).length === 0
                        ;
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if (inVisibleDOM && notOnLabel && notInMenu) {
                            module.verbose('Triggering event', callback);
                            callback();

                            return true;
                        }

                        module.verbose('Event occurred in dropdown menu, canceling callback');

                        return false;
                    },
                },

                action: {

                    nothing: function () {},

                    activate: function (text, value, element) {
                        value = value !== undefined
                            ? value
                            : text;
                        if (module.can.activate($(element))) {
                            module.set.selected(value, $(element), false, settings.keepSearchTerm);
                            if (!module.is.multiple() && !(!settings.collapseOnActionable && $(element).hasClass(className.actionable))) {
                                module.hideAndClear();
                            }
                        }
                    },

                    select: function (text, value, element) {
                        value = value !== undefined
                            ? value
                            : text;
                        if (module.can.activate($(element))) {
                            module.set.value(value, text, $(element));
                            if (!module.is.multiple() && !(!settings.collapseOnActionable && $(element).hasClass(className.actionable))) {
                                module.hideAndClear();
                            }
                        }
                    },

                    combo: function (text, value, element) {
                        value = value !== undefined
                            ? value
                            : text;
                        module.set.selected(value, $(element));
                        module.hideAndClear();
                    },

                    hide: function (text, value, element) {
                        module.set.value(value, text, $(element));
                        module.hideAndClear();
                    },

                },

                get: {
                    id: function () {
                        return id;
                    },
                    defaultText: function () {
                        return $module.data(metadata.defaultText);
                    },
                    defaultValue: function () {
                        return $module.data(metadata.defaultValue);
                    },
                    placeholderText: function () {
                        if (settings.placeholder !== 'auto' && typeof settings.placeholder === 'string') {
                            return settings.placeholder;
                        }

                        return $module.data(metadata.placeholderText) || '';
                    },
                    text: function () {
                        return settings.preserveHTML ? $text.html() : $text.text();
                    },
                    query: function () {
                        return String($search.val()).trim();
                    },
                    searchWidth: function (value) {
                        value = value !== undefined
                            ? value
                            : $search.val();
                        $sizer.text(value);

                        // prevent rounding issues
                        return Math.ceil($sizer.width() + (module.is.edge() ? 3 : 1));
                    },
                    selectionCount: function () {
                        var
                            values = module.get.values(),
                            count
                        ;
                        count = module.is.multiple()
                            ? (Array.isArray(values) ? values.length : 0)
                            : (module.get.value() !== '' ? 1 : 0);

                        return count;
                    },
                    transition: function ($subMenu) {
                        return settings.transition === 'auto'
                            ? (module.is.upward($subMenu) ? 'slide up' : 'slide down')
                            : settings.transition;
                    },
                    userValues: function () {
                        var
                            values = module.get.values(true)
                        ;
                        if (!values) {
                            return false;
                        }
                        values = Array.isArray(values)
                            ? values
                            : [values];

                        return $.grep(values, function (value) {
                            return module.get.item(value) === false;
                        });
                    },
                    uniqueArray: function (array) {
                        return $.grep(array, function (value, index) {
                            return $.inArray(value, array) === index;
                        });
                    },
                    caretPosition: function (returnEndPos) {
                        var
                            input = $search[0],
                            range,
                            rangeLength
                        ;
                        if (returnEndPos && 'selectionEnd' in input) {
                            return input.selectionEnd;
                        }
                        if (!returnEndPos && 'selectionStart' in input) {
                            return input.selectionStart;
                        }
                        if (document.selection) {
                            input.focus();
                            range = document.selection.createRange();
                            rangeLength = range.text.length;
                            if (returnEndPos) {
                                return rangeLength;
                            }
                            range.moveStart('character', -input.value.length);

                            return range.text.length - rangeLength;
                        }
                    },
                    value: function () {
                        var
                            value = $input.length > 0
                                ? $input.val()
                                : $module.data(metadata.value),
                            isEmptyMultiselect = Array.isArray(value) && value.length === 1 && value[0] === ''
                        ;

                        // prevents placeholder element from being selected when multiple
                        return value === undefined || isEmptyMultiselect
                            ? ''
                            : value;
                    },
                    values: function (raw) {
                        var
                            value = module.get.value()
                        ;
                        if (value === '') {
                            return '';
                        }

                        return !module.has.selectInput() && module.is.multiple()
                            ? (typeof value === 'string' // delimited string
                                ? (raw
                                    ? value
                                    : module.escape.htmlEntities(value)).split(settings.delimiter)
                                : '')
                            : value;
                    },
                    remoteValues: function () {
                        var
                            values = module.get.values(),
                            remoteValues = false
                        ;
                        if (values) {
                            if (typeof values === 'string') {
                                values = [values];
                            }
                            $.each(values, function (index, value) {
                                var
                                    name = module.read.remoteData(value)
                                ;
                                module.verbose('Restoring value from session data', name, value);
                                if (name) {
                                    if (!remoteValues) {
                                        remoteValues = {};
                                    }
                                    remoteValues[value] = name;
                                }
                            });
                        }

                        return remoteValues;
                    },
                    choiceText: function ($choice, preserveHTML) {
                        preserveHTML = preserveHTML !== undefined
                            ? preserveHTML
                            : settings.preserveHTML;
                        if ($choice) {
                            if ($choice.find(selector.menu).length > 0) {
                                module.verbose('Retrieving text of element with sub-menu');
                                $choice = $choice.clone();
                                $choice.find(selector.menu).remove();
                                $choice.find(selector.menuIcon).remove();
                            }

                            return $choice.data(metadata.text) !== undefined
                                ? $choice.data(metadata.text)
                                : (preserveHTML
                                    ? $choice.html() && $choice.html().trim()
                                    : $choice.text() && $choice.text().trim());
                        }
                    },
                    choiceValue: function ($choice, choiceText) {
                        choiceText = choiceText || module.get.choiceText($choice);
                        if (!$choice) {
                            return false;
                        }

                        return $choice.data(metadata.value) !== undefined
                            ? String($choice.data(metadata.value))
                            : (typeof choiceText === 'string'
                                ? String(
                                    settings.ignoreSearchCase
                                        ? choiceText.toLowerCase()
                                        : choiceText
                                ).trim()
                                : String(choiceText));
                    },
                    inputEvent: function () {
                        var
                            input = $search[0]
                        ;
                        if (input) {
                            return input.oninput !== undefined
                                ? 'input'
                                : (input.onpropertychange !== undefined
                                    ? 'propertychange'
                                    : 'keyup');
                        }

                        return false;
                    },
                    selectValues: function () {
                        var
                            select = {},
                            oldGroup = [],
                            values = []
                        ;
                        $module
                            .find('option')
                            .each(function () {
                                var
                                    $option  = $(this),
                                    name     = $option.html(),
                                    disabled = $option.attr('disabled'),
                                    value    = $option.attr('value') !== undefined
                                        ? $option.attr('value')
                                        : name,
                                    text     = $option.data(metadata.text) !== undefined
                                        ? $option.data(metadata.text)
                                        : name,
                                    group = $option.parent('optgroup')
                                ;
                                if (settings.placeholder === 'auto' && value === '') {
                                    select.placeholder = name;
                                } else {
                                    if (group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
                                        values.push({
                                            type: 'header',
                                            divider: settings.headerDivider,
                                            name: group.attr('label') || '',
                                        });
                                        oldGroup = group;
                                    }
                                    values.push({
                                        name: name,
                                        value: value,
                                        text: module.escape.htmlEntities(text, true),
                                        disabled: disabled,
                                    });
                                }
                            })
                        ;
                        if (settings.placeholder && settings.placeholder !== 'auto') {
                            module.debug('Setting placeholder value to', settings.placeholder);
                            select.placeholder = settings.placeholder;
                        }
                        if (settings.sortSelect) {
                            if (settings.sortSelect === true) {
                                values.sort(function (a, b) {
                                    return a.name.localeCompare(b.name);
                                });
                            } else if (settings.sortSelect === 'natural') {
                                values.sort(function (a, b) {
                                    return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
                                });
                            } else if (isFunction(settings.sortSelect)) {
                                values.sort(settings.sortSelect);
                            }
                            select[fields.values] = values;
                            module.debug('Retrieved and sorted values from select', select);
                        } else {
                            select[fields.values] = values;
                            module.debug('Retrieved values from select', select);
                        }

                        return select;
                    },
                    activeItem: function () {
                        return $item.filter('.' + className.active);
                    },
                    selectedItem: function () {
                        var
                            $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
                        ;

                        return $selectedItem.length > 0
                            ? $selectedItem
                            : $item.eq(0);
                    },
                    itemWithAdditions: function (value) {
                        var
                            $items       = module.get.item(value),
                            $userItems   = module.create.userChoice(value),
                            hasUserItems = $userItems && $userItems.length > 0
                        ;
                        if (hasUserItems) {
                            $items = $items.length > 0
                                ? $items.add($userItems)
                                : $userItems;
                        }

                        return $items;
                    },
                    item: function (value, strict) {
                        var
                            $selectedItem = false,
                            shouldSearch,
                            isMultiple
                        ;
                        value = value !== undefined
                            ? value
                            : (module.get.values() !== undefined
                                ? module.get.values()
                                : module.get.text());
                        isMultiple = module.is.multiple() && Array.isArray(value);
                        shouldSearch = isMultiple
                            ? value.length > 0
                            : value !== undefined && value !== null;
                        strict = value === '' || value === false || value === true
                            ? true
                            : strict || false;
                        if (shouldSearch) {
                            $item
                                .each(function () {
                                    var
                                        $choice       = $(this),
                                        optionText    = module.get.choiceText($choice),
                                        optionValue   = module.get.choiceValue($choice, optionText)
                                    ;
                                    // safe early exit
                                    if (optionValue === null || optionValue === undefined) {
                                        return;
                                    }
                                    if (isMultiple) {
                                        if ($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(String).map(module.escape.htmlEntities)) !== -1) {
                                            $selectedItem = $selectedItem
                                                ? $selectedItem.add($choice)
                                                : $choice;
                                        }
                                    } else if (strict) {
                                        module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
                                        if (optionValue === value) {
                                            $selectedItem = $choice;

                                            return true;
                                        }
                                    } else {
                                        if (settings.ignoreCase) {
                                            optionValue = optionValue.toLowerCase();
                                            value = value.toLowerCase();
                                        }
                                        if (module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
                                            module.verbose('Found select item by value', optionValue, value);
                                            $selectedItem = $choice;

                                            return true;
                                        }
                                    }
                                })
                            ;
                        }

                        return $selectedItem;
                    },
                    displayType: function () {
                        return $module.hasClass('column') ? 'flex' : settings.displayType;
                    },
                },

                check: {
                    maxSelections: function (selectionCount) {
                        if (settings.maxSelections) {
                            selectionCount = selectionCount !== undefined
                                ? selectionCount
                                : module.get.selectionCount();
                            if (selectionCount >= settings.maxSelections) {
                                module.debug('Maximum selection count reached');
                                if (settings.useLabels) {
                                    $item.addClass(className.filtered);
                                    module.add.message(message.maxSelections);
                                }

                                return true;
                            }

                            module.verbose('No longer at maximum selection count');
                            module.remove.message();
                            module.remove.filteredItem();
                            if (module.is.searchSelection()) {
                                module.filterItems();
                            }

                            return false;
                        }

                        return false;
                    },
                    disabled: function () {
                        $search.attr('tabindex', module.is.disabled() ? -1 : 0);
                    },
                },

                restore: {
                    defaults: function (preventChangeTrigger) {
                        module.clear(preventChangeTrigger);
                        module.restore.defaultText();
                        module.restore.defaultValue();
                    },
                    defaultText: function () {
                        var
                            defaultText     = module.get.defaultText(),
                            placeholderText = module.get.placeholderText
                        ;
                        if (defaultText === placeholderText) {
                            module.debug('Restoring default placeholder text', defaultText);
                            module.set.placeholderText(defaultText);
                        } else {
                            module.debug('Restoring default text', defaultText);
                            module.set.text(defaultText);
                        }
                    },
                    placeholderText: function () {
                        module.set.placeholderText();
                    },
                    defaultValue: function () {
                        var
                            defaultValue = module.get.defaultValue()
                        ;
                        if (defaultValue !== undefined) {
                            module.debug('Restoring default value', defaultValue);
                            if (defaultValue !== '') {
                                module.set.value(defaultValue);
                                module.set.selected();
                            } else {
                                module.remove.activeItem();
                                module.remove.selectedItem();
                            }
                        }
                    },
                    labels: function () {
                        if (settings.allowAdditions) {
                            if (!settings.useLabels) {
                                module.error(error.labels);
                                settings.useLabels = true;
                            }
                            module.debug('Restoring selected values');
                            module.create.userLabels();
                        }
                        module.check.maxSelections();
                    },
                    selected: function () {
                        module.restore.values();
                        if (module.is.multiple()) {
                            module.debug('Restoring previously selected values and labels');
                            module.restore.labels();
                        } else {
                            module.debug('Restoring previously selected values');
                        }
                    },
                    values: function () {
                        // prevents callbacks from occurring on initial load
                        module.set.initialLoad();
                        if (settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
                            module.restore.remoteValues();
                        } else {
                            module.set.selected();
                        }
                        var value = module.get.value();
                        if (value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
                            $input.removeClass(className.noselection);
                        } else {
                            $input.addClass(className.noselection);
                        }
                        module.remove.initialLoad();
                    },
                    remoteValues: function () {
                        var
                            values = module.get.remoteValues()
                        ;
                        module.debug('Recreating selected from session data', values);
                        if (values) {
                            if (module.is.single()) {
                                $.each(values, function (value, name) {
                                    module.set.text(name);
                                });
                            } else if (settings.useLabels) {
                                $.each(values, function (value, name) {
                                    module.add.label(value, name);
                                });
                            }
                        }
                    },
                },

                read: {
                    remoteData: function (value) {
                        var
                            name
                        ;
                        if (window.Storage === undefined) {
                            module.error(error.noStorage);

                            return;
                        }
                        name = sessionStorage.getItem(value + elementNamespace);

                        return name !== undefined
                            ? name
                            : false;
                    },
                },

                save: {
                    defaults: function () {
                        module.save.defaultText();
                        module.save.placeholderText();
                        module.save.defaultValue();
                    },
                    defaultValue: function () {
                        var
                            value = module.get.value()
                        ;
                        module.verbose('Saving default value as', value);
                        $module.data(metadata.defaultValue, value);
                    },
                    defaultText: function () {
                        var
                            text = module.get.text()
                        ;
                        module.verbose('Saving default text as', text);
                        $module.data(metadata.defaultText, text);
                    },
                    placeholderText: function () {
                        var
                            text
                        ;
                        if (settings.placeholder !== false && $text.hasClass(className.placeholder)) {
                            text = module.get.text();
                            module.verbose('Saving placeholder text as', text);
                            $module.data(metadata.placeholderText, text);
                        }
                    },
                    remoteData: function (name, value) {
                        if (window.Storage === undefined) {
                            module.error(error.noStorage);

                            return;
                        }
                        module.verbose('Saving remote data to session storage', value, name);
                        sessionStorage.setItem(value + elementNamespace, name);
                    },
                },

                clear: function (preventChangeTrigger) {
                    if (module.is.multiple() && settings.useLabels) {
                        module.remove.labels($module.find(selector.label), preventChangeTrigger);
                    } else {
                        module.remove.activeItem();
                        module.remove.selectedItem();
                        module.remove.filteredItem();
                    }
                    module.set.placeholderText();
                    module.clearValue(preventChangeTrigger);
                },

                clearValue: function (preventChangeTrigger) {
                    module.set.value('', null, null, preventChangeTrigger);
                },

                clearCache: function () {
                    module.debug('Clearing API cache once');
                    tempDisableApiCache = true;
                },

                scrollPage: function (direction, $selectedItem) {
                    var
                        $currentItem  = $selectedItem || module.get.selectedItem(),
                        $menu         = $currentItem.closest(selector.menu),
                        menuHeight    = $menu.outerHeight(),
                        currentScroll = $menu.scrollTop(),
                        itemHeight    = $item.eq(0).outerHeight(),
                        itemsPerPage  = Math.floor(menuHeight / itemHeight),
                        newScroll     = direction === 'up'
                            ? currentScroll - (itemHeight * itemsPerPage)
                            : currentScroll + (itemHeight * itemsPerPage),
                        $selectableItem = $item.not(selector.unselectable),
                        isWithinRange,
                        $nextSelectedItem,
                        elementIndex
                    ;
                    elementIndex = direction === 'up'
                        ? $selectableItem.index($currentItem) - itemsPerPage
                        : $selectableItem.index($currentItem) + itemsPerPage;
                    isWithinRange = direction === 'up'
                        ? elementIndex >= 0
                        : elementIndex < $selectableItem.length;
                    $nextSelectedItem = isWithinRange
                        ? $selectableItem.eq(elementIndex)
                        : (direction === 'up'
                            ? $selectableItem.first()
                            : $selectableItem.last());
                    if ($nextSelectedItem.length > 0) {
                        module.debug('Scrolling page', direction, $nextSelectedItem);
                        $currentItem
                            .removeClass(className.selected)
                        ;
                        $nextSelectedItem
                            .addClass(className.selected)
                        ;
                        if (settings.selectOnKeydown && module.is.single() && !$nextSelectedItem.hasClass(className.actionable)) {
                            module.set.selectedItem($nextSelectedItem);
                        }
                        $menu
                            .scrollTop(newScroll)
                        ;
                    }
                },

                set: {
                    filtered: function () {
                        var
                            isMultiple       = module.is.multiple(),
                            isSearch         = module.is.searchSelection(),
                            isSearchMultiple = isMultiple && isSearch,
                            searchValue      = isSearch
                                ? module.get.query()
                                : '',
                            hasSearchValue   = typeof searchValue === 'string' && searchValue.length > 0,
                            searchWidth      = module.get.searchWidth(),
                            valueIsSet       = searchValue !== ''
                        ;
                        if (isMultiple && hasSearchValue) {
                            module.verbose('Adjusting input width', searchWidth);
                            $search.css('width', searchWidth + 'px');
                        }
                        if (hasSearchValue || (isSearchMultiple && valueIsSet)) {
                            module.verbose('Hiding placeholder text');
                            $text.addClass(className.filtered);
                        } else if (!isMultiple || (isSearchMultiple && !valueIsSet)) {
                            module.verbose('Showing placeholder text');
                            $text.removeClass(className.filtered);
                        }
                    },
                    empty: function () {
                        $module.addClass(className.empty);
                    },
                    loading: function () {
                        $module.addClass(className.loading);
                    },
                    placeholderText: function (text) {
                        text = text || module.get.placeholderText();
                        module.debug('Setting placeholder text', text);
                        module.set.text(text);
                        $text.addClass(className.placeholder);
                    },
                    tabbable: function () {
                        if (module.is.searchSelection()) {
                            module.debug('Added tabindex to searchable dropdown');
                            $search
                                .val('')
                            ;
                            module.check.disabled();
                            $menu
                                .attr('tabindex', -1)
                            ;
                        } else {
                            module.debug('Added tabindex to dropdown');
                            if ($module.attr('tabindex') === undefined) {
                                $module
                                    .attr('tabindex', $input.attr('tabindex') || 0)
                                ;
                                $menu
                                    .attr('tabindex', -1)
                                ;
                            }
                        }
                        $input.removeAttr('tabindex');
                    },
                    initialLoad: function () {
                        module.verbose('Setting initial load');
                        initialLoad = true;
                    },
                    activeItem: function ($item) {
                        if (settings.allowAdditions && $item.filter(selector.addition).length > 0) {
                            $item.addClass(className.filtered);
                        } else {
                            $item.addClass(className.active);
                        }
                    },
                    partialSearch: function (text) {
                        var
                            length = module.get.query().length
                        ;
                        $search.val(text.slice(0, length));
                    },
                    scrollPosition: function ($item, forceScroll) {
                        var
                            edgeTolerance = 5,
                            $menu,
                            hasActive,
                            offset,
                            itemOffset,
                            menuOffset,
                            menuScroll,
                            menuHeight,
                            abovePage,
                            belowPage
                        ;

                        $item = $item || module.get.selectedItem();
                        $menu = $item.closest(selector.menu);
                        hasActive = $item && $item.length > 0;
                        forceScroll = forceScroll !== undefined
                            ? forceScroll
                            : false;
                        if (module.get.activeItem().length === 0) {
                            forceScroll = false;
                        }
                        if ($item && $menu.length > 0 && hasActive) {
                            itemOffset = $item.position().top;

                            $menu.addClass(className.loading);
                            menuScroll = $menu.scrollTop();
                            menuOffset = $menu.offset().top;
                            itemOffset = $item.offset().top;
                            offset = menuScroll - menuOffset + itemOffset;
                            if (!forceScroll) {
                                menuHeight = $menu.height();
                                belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
                                abovePage = (offset - edgeTolerance) < menuScroll;
                            }
                            module.debug('Scrolling to active item', offset);
                            if (forceScroll || abovePage || belowPage) {
                                $menu.scrollTop(offset);
                            }
                            $menu.removeClass(className.loading);
                        }
                    },
                    text: function (text, isNotPlaceholder) {
                        if (settings.action === 'combo') {
                            module.debug('Changing combo button text', text, $combo);
                            if (settings.preserveHTML) {
                                $combo.html(text);
                            } else {
                                $combo.text(text);
                            }
                        } else if (settings.action === 'activate') {
                            if (text !== module.get.placeholderText() || isNotPlaceholder) {
                                $text.removeClass(className.placeholder);
                            }
                            module.debug('Changing text', text, $text);
                            $text
                                .removeClass(className.filtered)
                            ;
                            if (settings.preserveHTML) {
                                $text.html(text);
                            } else {
                                $text.text(text);
                            }
                        }
                    },
                    selectedItem: function ($item) {
                        var
                            value      = module.get.choiceValue($item),
                            searchText = module.get.choiceText($item, false),
                            text       = module.get.choiceText($item)
                        ;
                        module.debug('Setting user selection to item', $item);
                        module.remove.activeItem();
                        module.set.partialSearch(searchText);
                        module.set.activeItem($item);
                        module.set.selected(value, $item);
                        module.set.text(text);
                    },
                    selectedLetter: function (letter) {
                        var
                            $selectedItem         = $item.filter('.' + className.selected),
                            alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
                            $nextValue            = false,
                            $nextItem
                        ;
                        // check next of same letter
                        if (alreadySelectedLetter) {
                            $nextItem = $selectedItem.nextAll($item).eq(0);
                            if (module.has.firstLetter($nextItem, letter)) {
                                $nextValue = $nextItem;
                            }
                        }
                        // check all values
                        if (!$nextValue) {
                            $item
                                .each(function () {
                                    if (module.has.firstLetter($(this), letter)) {
                                        $nextValue = $(this);

                                        return false;
                                    }
                                })
                            ;
                        }
                        // set next value
                        if ($nextValue) {
                            module.verbose('Scrolling to next value with letter', letter);
                            module.set.scrollPosition($nextValue);
                            $selectedItem.removeClass(className.selected);
                            $nextValue.addClass(className.selected);
                            if (settings.selectOnKeydown && module.is.single() && !$nextItem.hasClass(className.actionable)) {
                                module.set.selectedItem($nextValue);
                            }
                        }
                    },
                    direction: function ($menu) {
                        if (settings.direction === 'auto') {
                            // reset position, remove upward if it's base menu
                            if (!$menu) {
                                module.remove.upward();
                            } else if (module.is.upward($menu)) {
                                // we need make sure when make assertion openDownward for $menu, $menu does not have upward class
                                module.remove.upward($menu);
                            }

                            if (module.can.openDownward($menu)) {
                                module.remove.upward($menu);
                            } else {
                                module.set.upward($menu);
                            }
                            if (!module.is.leftward($menu) && !module.can.openRightward($menu)) {
                                module.set.leftward($menu);
                            }
                        } else if (settings.direction === 'upward') {
                            module.set.upward($menu);
                        }
                    },
                    upward: function ($currentMenu) {
                        var $element = $currentMenu || $module;
                        $element.addClass(className.upward);
                    },
                    leftward: function ($currentMenu) {
                        var $element = $currentMenu || $menu;
                        $element.addClass(className.leftward);
                    },
                    value: function (value, text, $selected, preventChangeTrigger) {
                        if (typeof text === 'boolean') {
                            preventChangeTrigger = text;
                            $selected = undefined;
                            text = undefined;
                        }
                        if (value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
                            $input.removeClass(className.noselection);
                        } else {
                            $input.addClass(className.noselection);
                        }
                        var
                            escapedValue = module.escape.value(value),
                            hasInput     = $input.length > 0,
                            currentValue = module.get.values(),
                            stringValue  = value !== undefined
                                ? String(value)
                                : value
                        ;
                        if (hasInput) {
                            if (!settings.allowReselection && stringValue == currentValue) {
                                module.verbose('Skipping value update already same value', value, currentValue);
                                if (!module.is.initialLoad()) {
                                    return;
                                }
                            }

                            if (module.is.single() && module.has.selectInput() && module.can.extendSelect()) {
                                module.debug('Adding user option', value);
                                module.add.optionValue(value);
                            }
                            module.debug('Updating input value', escapedValue, currentValue);
                            internalChange = true;
                            $input
                                .val(escapedValue)
                            ;
                            if (settings.fireOnInit === false && module.is.initialLoad()) {
                                module.debug('Input native change event ignored on initial load');
                            } else if (preventChangeTrigger !== true) {
                                module.trigger.change();
                            }
                            internalChange = false;
                        } else {
                            module.verbose('Storing value in metadata', escapedValue, $input);
                            if (escapedValue !== currentValue) {
                                $module.data(metadata.value, stringValue);
                            }
                        }
                        if (settings.fireOnInit === false && module.is.initialLoad()) {
                            module.verbose('No callback on initial load', settings.onChange);
                        } else if (preventChangeTrigger !== true) {
                            settings.onChange.call(element, value, text, $selected);
                        }
                    },
                    active: function () {
                        $module
                            .addClass(className.active)
                        ;
                    },
                    multiple: function () {
                        $module.addClass(className.multiple);
                    },
                    visible: function () {
                        $module.addClass(className.visible);
                    },
                    exactly: function (value, $selectedItem, preventChangeTrigger) {
                        if (typeof $selectedItem === 'boolean') {
                            preventChangeTrigger = $selectedItem;
                            $selectedItem = undefined;
                        }
                        module.debug('Setting selected to exact values');
                        module.clear();
                        module.set.selected(value, $selectedItem, preventChangeTrigger);
                    },
                    selected: function (value, $selectedItem, preventChangeTrigger, keepSearchTerm) {
                        if (typeof $selectedItem === 'boolean') {
                            keepSearchTerm = preventChangeTrigger;
                            preventChangeTrigger = $selectedItem;
                            $selectedItem = undefined;
                        }
                        var
                            isMultiple = module.is.multiple()
                        ;
                        $selectedItem = settings.allowAdditions
                            ? $selectedItem || module.get.itemWithAdditions(value)
                            : $selectedItem || module.get.item(value);
                        if (!$selectedItem) {
                            return false;
                        }
                        module.debug('Setting selected menu item to', $selectedItem);
                        if (module.is.multiple() && !keepSearchTerm) {
                            module.remove.searchWidth();
                        }
                        if (module.is.single()) {
                            module.remove.activeItem();
                            module.remove.selectedItem();
                        } else if (settings.useLabels) {
                            module.remove.selectedItem();
                        }
                        // select each item
                        $selectedItem
                            .each(function () {
                                var
                                    $selected      = $(this),
                                    selectedText   = module.get.choiceText($selected),
                                    selectedValue  = module.get.choiceValue($selected, selectedText),

                                    isFiltered     = $selected.hasClass(className.filtered),
                                    isActive       = $selected.hasClass(className.active),
                                    isActionable   = $selected.hasClass(className.actionable),
                                    isUserValue    = $selected.hasClass(className.addition),
                                    shouldAnimate  = isMultiple && $selectedItem && $selectedItem.length === 1
                                ;
                                if (isActionable) {
                                    if ((!isMultiple || (!isActive || isUserValue)) && settings.apiSettings && settings.saveRemoteData) {
                                        module.save.remoteData(selectedText, selectedValue);
                                    }
                                    settings.onActionable.call(element, selectedValue, selectedText, $selected);
                                } else if (isMultiple) {
                                    if (!isActive || isUserValue) {
                                        if (settings.apiSettings && settings.saveRemoteData) {
                                            module.save.remoteData(selectedText, selectedValue);
                                        }
                                        if (settings.useLabels) {
                                            module.add.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                            module.add.label(selectedValue, selectedText, shouldAnimate);
                                            module.set.activeItem($selected);
                                            module.filterActive();
                                            module.select.nextAvailable($selectedItem);
                                        } else {
                                            module.add.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                            module.set.text(module.add.variables(message.count));
                                            module.set.activeItem($selected);
                                        }
                                    } else if (!isFiltered && (settings.useLabels || selectActionActive)) {
                                        module.debug('Selected active value, removing label');
                                        module.remove.selected(selectedValue);
                                    }
                                } else {
                                    if (settings.apiSettings && settings.saveRemoteData) {
                                        module.save.remoteData(selectedText, selectedValue);
                                    }
                                    if (!keepSearchTerm && !$selected.hasClass(className.actionable)) {
                                        module.set.text(selectedText, true);
                                    }
                                    module.set.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                    $selected
                                        .addClass(className.active)
                                        .addClass(className.selected)
                                    ;
                                }
                            })
                        ;
                        if (!keepSearchTerm) {
                            module.remove.searchTerm();
                        }
                        if (module.is.allFiltered()) {
                            module.set.empty();
                            module.hideMenu();
                        }
                    },
                },

                add: {
                    label: function (value, text, shouldAnimate) {
                        var
                            $next  = module.is.searchSelection()
                                ? $search
                                : $text,
                            escapedValue = module.escape.value(value),
                            $label
                        ;
                        if (settings.ignoreCase) {
                            escapedValue = escapedValue.toLowerCase();
                        }
                        $label = $('<a />')
                            .addClass(className.label)
                            .attr('data-' + metadata.value, escapedValue)
                            .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
                        ;
                        $label = settings.onLabelCreate.call($label, escapedValue, text);

                        if (module.has.label(value)) {
                            module.debug('User selection already exists, skipping', escapedValue);

                            return;
                        }
                        if (settings.label.variation) {
                            $label.addClass(settings.label.variation);
                        }
                        if (shouldAnimate === true && settings.label.transition) {
                            module.debug('Animating in label', $label);
                            $label
                                .addClass(className.hidden)
                                .insertBefore($next)
                                .transition({
                                    animation: settings.label.transition,
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    duration: settings.label.duration,
                                })
                            ;
                        } else {
                            module.debug('Adding selection label', $label);
                            $label
                                .insertBefore($next)
                            ;
                        }
                    },
                    message: function (message) {
                        var
                            $message = $menu.children(selector.message),
                            html     = settings.templates.message(module.add.variables(message))
                        ;
                        if ($message.length > 0) {
                            $message
                                .html(html)
                            ;
                        } else {
                            $('<div/>')
                                .html(html)
                                .addClass(className.message)
                                .appendTo($menu)
                            ;
                        }
                    },
                    optionValue: function (value) {
                        var
                            escapedValue = module.escape.value(value),
                            $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
                            hasOption    = $option.length > 0
                        ;
                        if (hasOption) {
                            return;
                        }
                        // temporarily disconnect observer
                        module.disconnect.selectObserver();
                        if (module.is.single()) {
                            module.verbose('Removing previous user addition');
                            $input.find('option.' + className.addition).remove();
                        }
                        $('<option/>')
                            .prop('value', escapedValue)
                            .addClass(className.addition)
                            .text(value)
                            .appendTo($input)
                        ;
                        module.verbose('Adding user addition as an <option>', value);
                        module.observe.select();
                    },
                    userSuggestion: function (value) {
                        var
                            $addition         = $menu.children(selector.addition),
                            $existingItem     = module.get.item(value),
                            alreadyHasValue   = $existingItem && $existingItem.not(selector.addition).length > 0,
                            hasUserSuggestion = $addition.length > 0,
                            html
                        ;
                        if (settings.useLabels && module.has.maxSelections()) {
                            return;
                        }
                        if (value === '' || alreadyHasValue) {
                            $addition.remove();

                            return;
                        }
                        if (hasUserSuggestion) {
                            $addition
                                .data(metadata.value, value)
                                .data(metadata.text, value)
                                .attr('data-' + metadata.value, value)
                                .attr('data-' + metadata.text, value)
                                .removeClass(className.filtered)
                            ;
                            if (!settings.hideAdditions) {
                                html = settings.templates.addition(module.add.variables(message.addResult, value));
                                $addition
                                    .html(html)
                                ;
                            }
                            module.verbose('Replacing user suggestion with new value', $addition);
                        } else {
                            $addition = module.create.userChoice(value);
                            $addition
                                .prependTo($menu)
                            ;
                            module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
                        }
                        if (!settings.hideAdditions || module.is.allFiltered()) {
                            $addition
                                .addClass(className.selected)
                                .siblings()
                                .removeClass(className.selected)
                            ;
                        }
                        module.refreshItems();
                    },
                    variables: function (message, term) {
                        var
                            hasCount    = message.search('{count}') !== -1,
                            hasMaxCount = message.search('{maxCount}') !== -1,
                            hasTerm     = message.search('{term}') !== -1,
                            query
                        ;
                        module.verbose('Adding templated variables to message', message);
                        if (hasCount) {
                            message = message.replace('{count}', module.get.selectionCount());
                        }
                        if (hasMaxCount) {
                            message = message.replace('{maxCount}', settings.maxSelections);
                        }
                        if (hasTerm) {
                            query = term || module.get.query();
                            message = message.replace('{term}', query);
                        }

                        return message;
                    },
                    value: function (addedValue, addedText, $selectedItem, preventChangeTrigger) {
                        if (typeof addedText === 'boolean') {
                            preventChangeTrigger = addedText;
                            $selectedItem = undefined;
                            addedText = undefined;
                        }
                        var
                            currentValue = module.get.values(true),
                            newValue
                        ;
                        if (module.has.value(addedValue)) {
                            module.debug('Value already selected');

                            return;
                        }
                        if (addedValue === '') {
                            module.debug('Cannot select blank values from multiselect');

                            return;
                        }
                        // extend current array
                        if (Array.isArray(currentValue)) {
                            newValue = $selectedItem && $selectedItem.hasClass(className.actionable) ? currentValue : currentValue.concat([addedValue]);
                            newValue = module.get.uniqueArray(newValue);
                        } else {
                            newValue = [addedValue];
                        }
                        // add values
                        if (module.has.selectInput()) {
                            if (module.can.extendSelect()) {
                                module.debug('Adding value to select', addedValue, newValue, $input);
                                module.add.optionValue(addedValue);
                            }
                        } else {
                            newValue = newValue.join(settings.delimiter);
                            module.debug('Setting hidden input to delimited value', newValue, $input);
                        }

                        if (settings.fireOnInit === false && module.is.initialLoad()) {
                            module.verbose('Skipping onadd callback on initial load', settings.onAdd);
                        } else {
                            settings.onAdd.call(element, addedValue, addedText, $selectedItem);
                        }
                        module.set.value(newValue, addedText, $selectedItem, preventChangeTrigger);
                        module.check.maxSelections();
                    },
                },

                remove: {
                    active: function () {
                        $module.removeClass(className.active);
                    },
                    activeLabel: function () {
                        $module.find(selector.label).removeClass(className.active);
                    },
                    empty: function () {
                        $module.removeClass(className.empty);
                    },
                    loading: function () {
                        $module.removeClass(className.loading);
                    },
                    initialLoad: function () {
                        initialLoad = false;
                    },
                    upward: function ($currentMenu) {
                        var $element = $currentMenu || $module;
                        $element.removeClass(className.upward);
                    },
                    leftward: function ($currentMenu) {
                        var $element = $currentMenu || $menu;
                        $element.removeClass(className.leftward);
                    },
                    visible: function () {
                        $module.removeClass(className.visible);
                    },
                    activeItem: function () {
                        $item.removeClass(className.active);
                    },
                    filteredItem: function () {
                        if (settings.useLabels && module.has.maxSelections()) {
                            return;
                        }
                        if (settings.useLabels && module.is.multiple()) {
                            $item.not('.' + className.active).removeClass(className.filtered);
                        } else {
                            $item.removeClass(className.filtered);
                        }
                        if (settings.hideDividers) {
                            $divider.removeClass(className.hidden);
                        }
                        module.remove.empty();
                    },
                    optionValue: function (value) {
                        var
                            escapedValue = module.escape.value(value),
                            $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
                            hasOption    = $option.length > 0
                        ;
                        if (!hasOption || !$option.hasClass(className.addition)) {
                            return;
                        }
                        // temporarily disconnect observer
                        module.disconnect.selectObserver();
                        $option.remove();
                        module.verbose('Removing user addition as an <option>', escapedValue);
                        module.observe.select();
                    },
                    message: function () {
                        $menu.children(selector.message).remove();
                    },
                    searchWidth: function () {
                        $search.css('width', '');
                    },
                    searchTerm: function () {
                        module.verbose('Cleared search term');
                        $search.val('');
                        module.set.filtered();
                    },
                    userAddition: function () {
                        $item.filter(selector.addition).remove();
                    },
                    selected: function (value, $selectedItem, preventChangeTrigger) {
                        $selectedItem = settings.allowAdditions
                            ? $selectedItem || module.get.itemWithAdditions(value)
                            : $selectedItem || module.get.item(value);

                        if (!$selectedItem) {
                            return false;
                        }

                        $selectedItem
                            .each(function () {
                                var
                                    $selected     = $(this),
                                    selectedText  = module.get.choiceText($selected),
                                    selectedValue = module.get.choiceValue($selected, selectedText)
                                ;
                                if (module.is.multiple()) {
                                    if (settings.useLabels) {
                                        module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                        module.remove.label(selectedValue);
                                    } else {
                                        module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                        if (module.get.selectionCount() === 0) {
                                            module.set.placeholderText();
                                        } else {
                                            module.set.text(module.add.variables(message.count));
                                        }
                                    }
                                } else {
                                    module.remove.value(selectedValue, selectedText, $selected, preventChangeTrigger);
                                }
                                $selected
                                    .removeClass(className.filtered)
                                    .removeClass(className.active)
                                ;
                                if (settings.useLabels) {
                                    $selected.removeClass(className.selected);
                                }
                            })
                        ;
                    },
                    selectedItem: function () {
                        $item.removeClass(className.selected);
                    },
                    value: function (removedValue, removedText, $removedItem, preventChangeTrigger) {
                        var
                            values = module.get.values(true),
                            newValue
                        ;
                        if (module.has.selectInput()) {
                            module.verbose('Input is <select> removing selected option', removedValue);
                            newValue = module.remove.arrayValue(removedValue, values);
                            module.remove.optionValue(removedValue);
                        } else {
                            module.verbose('Removing from delimited values', removedValue);
                            newValue = module.remove.arrayValue(removedValue, values);
                            newValue = newValue.join(settings.delimiter);
                        }
                        if (settings.fireOnInit === false && module.is.initialLoad()) {
                            module.verbose('No callback on initial load', settings.onRemove);
                        } else {
                            settings.onRemove.call(element, removedValue, removedText, $removedItem);
                        }
                        module.set.value(newValue, removedText, $removedItem, preventChangeTrigger);
                        module.check.maxSelections();
                    },
                    arrayValue: function (removedValue, values) {
                        if (!Array.isArray(values)) {
                            values = [values];
                        }
                        values = $.grep(values, function (value) {
                            return removedValue != value;
                        });
                        module.verbose('Removed value from delimited string', removedValue, values);

                        return values;
                    },
                    label: function (value, shouldAnimate) {
                        var
                            escapedValue  = module.escape.value(value),
                            $labels       = $module.find(selector.label),
                            $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? escapedValue.toLowerCase() : escapedValue) + '"]')
                        ;
                        module.verbose('Removing label', $removedLabel);
                        $removedLabel.remove();
                    },
                    activeLabels: function ($activeLabels) {
                        $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
                        module.verbose('Removing active label selections', $activeLabels);
                        module.remove.labels($activeLabels);
                    },
                    labels: function ($labels, preventChangeTrigger) {
                        $labels = $labels || $module.find(selector.label);
                        module.verbose('Removing labels', $labels);
                        $labels
                            .each(function () {
                                var
                                    $label      = $(this),
                                    value       = $label.data(metadata.value),
                                    stringValue = value !== undefined
                                        ? String(value)
                                        : value,
                                    isUserValue = module.is.userValue(stringValue)
                                ;
                                if (settings.onLabelRemove.call($label, value) === false) {
                                    module.debug('Label remove callback cancelled removal');

                                    return;
                                }
                                module.remove.message();
                                if (isUserValue) {
                                    module.remove.value(stringValue, stringValue, module.get.item(stringValue), preventChangeTrigger);
                                    module.remove.label(stringValue);
                                } else {
                                    // selected will also remove label
                                    module.remove.selected(stringValue, false, preventChangeTrigger);
                                }
                            })
                        ;
                    },
                    tabbable: function () {
                        if (module.is.searchSelection()) {
                            module.debug('Searchable dropdown initialized');
                            $search
                                .removeAttr('tabindex')
                            ;
                            $menu
                                .removeAttr('tabindex')
                            ;
                        } else {
                            module.debug('Simple selection dropdown initialized');
                            $module
                                .removeAttr('tabindex')
                            ;
                            $menu
                                .removeAttr('tabindex')
                            ;
                        }
                    },
                    diacritics: function (text) {
                        return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036F]/g, '') : text;
                    },
                },

                has: {
                    menuSearch: function () {
                        return module.has.search() && $search.closest($menu).length > 0;
                    },
                    clearItem: function () {
                        return $clear.length > 0;
                    },
                    search: function () {
                        return $search.length > 0;
                    },
                    sizer: function () {
                        return $sizer.length > 0;
                    },
                    selectInput: function () {
                        return $input.is('select');
                    },
                    minCharacters: function (searchTerm) {
                        if (settings.minCharacters && !iconClicked) {
                            searchTerm = searchTerm !== undefined
                                ? String(searchTerm)
                                : String(module.get.query());

                            return searchTerm.length >= settings.minCharacters;
                        }
                        iconClicked = false;

                        return true;
                    },
                    firstLetter: function ($item, letter) {
                        var
                            text,
                            firstLetter
                        ;
                        if (!$item || $item.length === 0 || typeof letter !== 'string') {
                            return false;
                        }
                        text = module.get.choiceText($item, false);
                        letter = letter.toLowerCase();
                        firstLetter = String(text).charAt(0).toLowerCase();

                        return letter == firstLetter;
                    },
                    input: function () {
                        return $input.length > 0;
                    },
                    items: function () {
                        return $item.length > 0;
                    },
                    menu: function () {
                        return $menu.length > 0;
                    },
                    subMenu: function ($currentMenu) {
                        return ($currentMenu || $menu).find(selector.menu).length > 0;
                    },
                    message: function () {
                        return $menu.children(selector.message).length > 0;
                    },
                    label: function (value) {
                        var
                            escapedValue = module.escape.value(value),
                            $labels      = $module.find(selector.label)
                        ;
                        if (settings.ignoreCase) {
                            escapedValue = escapedValue.toLowerCase();
                        }

                        return $labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) + '"]').length > 0;
                    },
                    maxSelections: function () {
                        return settings.maxSelections && module.get.selectionCount() >= settings.maxSelections;
                    },
                    allResultsFiltered: function () {
                        var
                            $normalResults = $item.not(selector.addition)
                        ;

                        return $normalResults.filter(selector.unselectable).length === $normalResults.length;
                    },
                    userSuggestion: function () {
                        return $menu.children(selector.addition).length > 0;
                    },
                    query: function () {
                        return module.get.query() !== '';
                    },
                    value: function (value) {
                        return settings.ignoreCase
                            ? module.has.valueIgnoringCase(value)
                            : module.has.valueMatchingCase(value);
                    },
                    valueMatchingCase: function (value) {
                        var
                            values   = module.get.values(true),
                            hasValue = Array.isArray(values)
                                ? values && ($.inArray(value, values) !== -1)
                                : values == value
                        ;

                        return !!hasValue;
                    },
                    valueIgnoringCase: function (value) {
                        var
                            values   = module.get.values(true),
                            hasValue = false
                        ;
                        if (!Array.isArray(values)) {
                            values = [values];
                        }
                        $.each(values, function (index, existingValue) {
                            if (String(value).toLowerCase() === String(existingValue).toLowerCase()) {
                                hasValue = true;

                                return false;
                            }
                        });

                        return hasValue;
                    },
                },

                is: {
                    active: function () {
                        return $module.hasClass(className.active);
                    },
                    animatingInward: function () {
                        return $menu.transition('is inward');
                    },
                    animatingOutward: function () {
                        return $menu.transition('is outward');
                    },
                    bubbledLabelClick: function (event) {
                        return $(event.target).is('select, input') && $module.closest('label').length > 0;
                    },
                    bubbledIconClick: function (event) {
                        return $(event.target).closest($icon).length > 0;
                    },
                    edge: function () {
                        return !!window.chrome && !!window.StyleMedia;
                    },
                    empty: function () {
                        return $module.hasClass(className.empty);
                    },
                    chrome: function () {
                        return !!window.chrome && !window.StyleMedia;
                    },
                    alreadySetup: function () {
                        return $module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0;
                    },
                    animating: function ($subMenu) {
                        return $subMenu
                            ? $subMenu.transition && $subMenu.transition('is animating')
                            : $menu.transition && $menu.transition('is animating');
                    },
                    leftward: function ($subMenu) {
                        var $selectedMenu = $subMenu || $menu;

                        return $selectedMenu.hasClass(className.leftward);
                    },
                    clearable: function () {
                        return $module.hasClass(className.clearable) || settings.clearable;
                    },
                    disabled: function () {
                        return $module.hasClass(className.disabled);
                    },
                    focused: function () {
                        return document.activeElement === $module[0];
                    },
                    focusedOnSearch: function () {
                        return document.activeElement === $search[0];
                    },
                    allFiltered: function () {
                        return (module.is.multiple() || module.has.search()) && !(!settings.hideAdditions && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered();
                    },
                    hidden: function ($subMenu) {
                        return !module.is.visible($subMenu);
                    },
                    initialLoad: function () {
                        return initialLoad;
                    },
                    inObject: function (needle, object) {
                        var
                            found = false
                        ;
                        $.each(object, function (index, property) {
                            if (property == needle) {
                                found = true;

                                return true;
                            }
                        });

                        return found;
                    },
                    multiple: function () {
                        return $module.hasClass(className.multiple);
                    },
                    remote: function () {
                        return settings.apiSettings && module.can.useAPI();
                    },
                    noApiCache: function () {
                        return tempDisableApiCache || (settings.apiSettings && !settings.apiSettings.cache);
                    },
                    single: function () {
                        return !module.is.multiple();
                    },
                    selectMutation: function (mutations) {
                        var
                            selectChanged = false
                        ;
                        $.each(mutations, function (index, mutation) {
                            if ($(mutation.target).is('option, optgroup') || $(mutation.addedNodes).is('select') || ($(mutation.target).is('select') && mutation.type !== 'attributes')) {
                                selectChanged = true;

                                return false;
                            }
                        });

                        return selectChanged;
                    },
                    search: function () {
                        return $module.hasClass(className.search);
                    },
                    searchSelection: function (deep) {
                        return module.has.search() && (deep ? $search.parents(selector.dropdown) : $search.parent(selector.dropdown)).length === 1;
                    },
                    selection: function () {
                        return $module.hasClass(className.selection);
                    },
                    userValue: function (value) {
                        return $.inArray(value, module.get.userValues()) !== -1;
                    },
                    upward: function ($menu) {
                        var $element = $menu || $module;

                        return $element.hasClass(className.upward);
                    },
                    visible: function ($subMenu) {
                        return $subMenu
                            ? $subMenu.hasClass(className.visible)
                            : $menu.hasClass(className.visible);
                    },
                    verticallyScrollableContext: function () {
                        var
                            overflowY = $context[0] !== window
                                ? $context.css('overflow-y')
                                : false
                        ;

                        return overflowY === 'auto' || overflowY === 'scroll';
                    },
                    horizontallyScrollableContext: function () {
                        var
                            overflowX = $context[0] !== window
                                ? $context.css('overflow-X')
                                : false
                        ;

                        return overflowX === 'auto' || overflowX === 'scroll';
                    },
                },

                can: {
                    activate: function ($item) {
                        return (
                            settings.useLabels
                                || !module.has.maxSelections()
                                || (module.has.maxSelections() && $item.hasClass(className.active))
                        );
                    },
                    openDownward: function ($subMenu) {
                        var
                            $currentMenu    = $subMenu || $menu,
                            canOpenDownward,
                            onScreen,
                            calculations
                        ;
                        $currentMenu
                            .addClass(className.loading)
                        ;
                        calculations = {
                            context: {
                                offset: $context[0] === window
                                    ? { top: 0, left: 0 }
                                    : $context.offset(),
                                scrollTop: $context.scrollTop(),
                                height: $context.outerHeight(),
                            },
                            menu: {
                                offset: $currentMenu.offset(),
                                height: $currentMenu.outerHeight(),
                            },
                        };
                        if (module.is.verticallyScrollableContext()) {
                            calculations.menu.offset.top += calculations.context.scrollTop;
                        }
                        if (module.has.subMenu($currentMenu)) {
                            calculations.menu.height += $currentMenu.find(selector.menu).first().outerHeight();
                        }
                        onScreen = {
                            above: calculations.context.scrollTop <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
                            below: (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height,
                        };
                        if (onScreen.below) {
                            module.verbose('Dropdown can fit in context downward', onScreen);
                            canOpenDownward = true;
                        } else if (!onScreen.below && !onScreen.above) {
                            module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
                            canOpenDownward = true;
                        } else {
                            module.verbose('Dropdown cannot fit below, opening upward', onScreen);
                            canOpenDownward = false;
                        }
                        $currentMenu.removeClass(className.loading);

                        return canOpenDownward;
                    },
                    openRightward: function ($subMenu) {
                        var
                            $currentMenu     = $subMenu || $menu,
                            canOpenRightward = true,
                            isOffscreenRight = false,
                            calculations
                        ;
                        $currentMenu
                            .addClass(className.loading)
                        ;
                        calculations = {
                            context: {
                                offset: $context[0] === window
                                    ? { top: 0, left: 0 }
                                    : $context.offset(),
                                scrollLeft: $context.scrollLeft(),
                                width: $context.outerWidth(),
                            },
                            menu: {
                                offset: $currentMenu.offset(),
                                width: $currentMenu.outerWidth(),
                            },
                        };
                        if (module.is.horizontallyScrollableContext()) {
                            calculations.menu.offset.left += calculations.context.scrollLeft;
                        }
                        isOffscreenRight = calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width;
                        if (isOffscreenRight) {
                            module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
                            canOpenRightward = false;
                        }
                        $currentMenu.removeClass(className.loading);

                        return canOpenRightward;
                    },
                    extendSelect: function () {
                        return settings.allowAdditions || settings.apiSettings;
                    },
                    show: function () {
                        return !module.is.disabled() && (module.has.items() || module.has.message());
                    },
                    useAPI: function () {
                        return $.fn.api !== undefined;
                    },
                    useElement: function (element) {
                        if ($.fn[element] !== undefined) {
                            return true;
                        }
                        module.error(error.noElement.replace('{element}', element));

                        return false;
                    },
                },

                animate: {
                    show: function (callback, $subMenu) {
                        var
                            $currentMenu = $subMenu || $menu,
                            start = $subMenu
                                ? function () {}
                                : function () {
                                    module.hideSubMenus();
                                    module.hideOthers();
                                    module.set.active();
                                },
                            transition
                        ;
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        module.verbose('Doing menu show animation', $currentMenu);
                        module.set.direction($subMenu);
                        transition = settings.transition.showMethod || module.get.transition($subMenu);
                        if (module.is.selection()) {
                            module.set.scrollPosition(module.get.selectedItem(), true);
                        }
                        if (module.is.hidden($currentMenu) || module.is.animating($currentMenu)) {
                            if (transition === 'none') {
                                start();
                                $currentMenu.transition({
                                    displayType: module.get.displayType(),
                                }).transition('show');
                                callback.call(element);
                            } else if (module.can.useElement('transition')) {
                                $currentMenu
                                    .transition({
                                        animation: transition + ' in',
                                        debug: settings.debug,
                                        verbose: settings.verbose,
                                        silent: settings.silent,
                                        duration: settings.transition.showDuration || settings.duration,
                                        queue: true,
                                        onStart: start,
                                        displayType: module.get.displayType(),
                                        onComplete: function () {
                                            callback.call(element);
                                        },
                                    })
                                ;
                            }
                        }
                    },
                    hide: function (callback, $subMenu) {
                        var
                            $currentMenu = $subMenu || $menu,
                            start = $subMenu
                                ? function () {}
                                : function () {
                                    module.unbind.intent();
                                    module.remove.active();
                                },
                            transition = settings.transition.hideMethod || module.get.transition($subMenu)
                        ;
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if (module.is.visible($currentMenu) || module.is.animating($currentMenu)) {
                            module.verbose('Doing menu hide animation', $currentMenu);

                            if (transition === 'none') {
                                start();
                                $currentMenu.transition({
                                    displayType: module.get.displayType(),
                                }).transition('hide');
                                callback.call(element);
                            } else if ($.fn.transition !== undefined) {
                                $currentMenu
                                    .transition({
                                        animation: transition + ' out',
                                        duration: settings.transition.hideDuration || settings.duration,
                                        debug: settings.debug,
                                        verbose: settings.verbose,
                                        silent: settings.silent,
                                        queue: false,
                                        onStart: start,
                                        displayType: module.get.displayType(),
                                        onComplete: function () {
                                            callback.call(element);
                                        },
                                    })
                                ;
                            } else {
                                module.error(error.transition);
                            }
                        }
                    },
                },

                hideAndClear: function () {
                    module.remove.searchTerm();
                    if (module.has.maxSelections()) {
                        return;
                    }
                    if (module.has.search()) {
                        module.hide(function () {
                            module.remove.filteredItem();
                        });
                    } else {
                        module.hide();
                    }
                },

                delay: {
                    show: function () {
                        module.verbose('Delaying show event to ensure user intent');
                        clearTimeout(module.timer);
                        module.timer = setTimeout(function () { module.show(); }, settings.delay.show);
                    },
                    hide: function () {
                        module.verbose('Delaying hide event to ensure user intent');
                        clearTimeout(module.timer);
                        module.timer = setTimeout(function () { module.hide(); }, settings.delay.hide);
                    },
                },

                escape: {
                    value: function (value) {
                        var
                            multipleValues = Array.isArray(value),
                            stringValue    = typeof value === 'string',
                            isUnparsable   = !stringValue && !multipleValues,
                            hasQuotes      = stringValue && value.search(regExp.quote) !== -1,
                            values         = []
                        ;
                        if (isUnparsable || !hasQuotes) {
                            return value;
                        }
                        module.debug('Encoding quote values for use in select', value);
                        if (multipleValues) {
                            $.each(value, function (index, value) {
                                values.push(value.replace(regExp.quote, '&quot;'));
                            });

                            return values;
                        }

                        return value.replace(regExp.quote, '&quot;');
                    },
                    string: function (text) {
                        text = String(text);

                        return text.replace(regExp.escape, '\\$&');
                    },
                    htmlEntities: function (string, forceAmpersand) {
                        var
                            badChars     = /["'<>`]/g,
                            shouldEscape = /["&'<>`]/,
                            escape       = {
                                '<': '&lt;',
                                '>': '&gt;',
                                '"': '&quot;',
                                "'": '&#x27;',
                                '`': '&#x60;',
                            },
                            escapedChar  = function (chr) {
                                return escape[chr];
                            }
                        ;
                        if (shouldEscape.test(string)) {
                            string = string.replace(forceAmpersand ? /&/g : /&(?![\d#a-z]{1,12};)/gi, '&amp;');

                            return string.replace(badChars, escapedChar);
                        }

                        return string;
                    },
                },

                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
            : $allModules;
    };

    $.fn.dropdown.settings = {

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        on: 'click', // what event should show menu action on item selection
        action: 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})

        values: false, // specify values to use for dropdown

        clearable: false, // whether the value of the dropdown can be cleared

        apiSettings: false,
        selectOnKeydown: true, // Whether selection should occur automatically when keyboard shortcuts used
        minCharacters: 0, // Minimum characters required to trigger API call

        filterRemoteData: false, // Whether API results should be filtered after being returned for query term
        saveRemoteData: true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh

        throttle: 200, // How long to wait after last user input to search remotely

        context: window, // Context to use when determining if on screen
        direction: 'auto', // Whether dropdown should always open in one direction
        keepOnScreen: true, // Whether dropdown should check whether it is on screen before showing

        match: 'both', // what to match against with search selection (both, text, or label)
        fullTextSearch: 'exact', // search anywhere in value (set to 'exact' to require exact matches)
        ignoreDiacritics: false, // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
        hideDividers: false, // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)

        placeholder: 'auto', // whether to convert blank <select> values to placeholder text
        preserveHTML: true, // preserve html when selecting value
        sortSelect: false, // sort selection on init

        forceSelection: false, // force a choice on blur with search selection

        allowAdditions: false, // whether multiple select should allow user added values
        keepSearchTerm: false, // whether the search value should be kept and menu stays filtered on item selection
        ignoreCase: false, // whether to consider case sensitivity when creating labels
        ignoreSearchCase: true, // whether to consider case sensitivity when filtering items
        hideAdditions: true, // whether or not to hide special message prompting a user they can enter a value

        maxSelections: false, // When set to a number limits the number of selections to this count
        useLabels: true, // whether multiple select should filter currently active selections from choices
        delimiter: ',', // when multiselect uses normal <input> the values will be delimited with this character

        showOnFocus: false, // show menu on focus
        allowReselection: false, // whether current value should trigger callbacks when reselected
        allowTab: true, // add tabindex to element
        allowCategorySelection: false, // allow elements with sub-menus to be selected

        fireOnInit: false, // Whether callbacks should fire when initializing dropdown values

        transition: 'auto', // auto transition will slide down or up based on direction
        duration: 200, // duration of transition
        displayType: false, // displayType of transition

        headerDivider: true, // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>

        collapseOnActionable: true, // whether the dropdown should collapse upon selection of an actionable item

        // label settings on multi-select
        label: {
            transition: 'scale',
            duration: 200,
            variation: false,
        },

        // delay before event
        delay: {
            hide: 300,
            show: 200,
            search: 20,
        },

        /* Callbacks */
        onChange: function (value, text, $selected) {},
        onAdd: function (value, text, $selected) {},
        onRemove: function (value, text, $selected) {},
        onActionable: function (value, text, $selected) {},
        onSearch: function (searchTerm) {},

        onLabelSelect: function ($selectedLabels) {},
        onLabelCreate: function (value, text) {
            return $(this);
        },
        onLabelRemove: function (value) {
            return true;
        },
        onNoResults: function (searchTerm) {
            return true;
        },
        onShow: function () {},
        onHide: function () {},

        /* Component */
        name: 'Dropdown',
        namespace: 'dropdown',

        message: {
            addResult: 'Add <b>{term}</b>',
            count: '{count} selected',
            maxSelections: 'Max {maxCount} selections',
            noResults: 'No results found.',
            serverError: 'There was an error contacting the server',
        },

        error: {
            action: 'You called a dropdown action that was not defined',
            alreadySetup: 'Once a select has been initialized behaviors must be called on the created ui dropdown',
            labels: 'Allowing user additions currently requires the use of labels.',
            missingMultiple: '<select> requires multiple property to be set to correctly preserve multiple values',
            method: 'The method you called is not defined.',
            noAPI: 'The API module is required to load resources remotely',
            noStorage: 'Saving remote data requires session storage',
            noElement: 'This module requires ui {element}',
            noNormalize: '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.',
        },

        regExp: {
            escape: /[\s#$()*+,.:=?@[\\\]^{|}-]/g,
            quote: /"/g,
        },

        metadata: {
            defaultText: 'defaultText',
            defaultValue: 'defaultValue',
            placeholderText: 'placeholder',
            text: 'text',
            value: 'value',
        },

        // property names for remote query
        fields: {
            remoteValues: 'results', // grouping for api results
            values: 'values', // grouping for all dropdown values
            disabled: 'disabled', // whether value should be disabled
            name: 'name', // displayed dropdown text
            description: 'description', // displayed dropdown description
            descriptionVertical: 'descriptionVertical', // whether description should be vertical
            value: 'value', // actual dropdown value
            text: 'text', // displayed text when selected
            type: 'type', // type of dropdown element
            image: 'image', // optional image path
            imageClass: 'imageClass', // optional individual class for image
            icon: 'icon', // optional icon name
            iconClass: 'iconClass', // optional individual class for icon (for example to use flag instead)
            class: 'class', // optional individual class for item/header
            divider: 'divider', // optional divider append for group headers
            actionable: 'actionable', // optional actionable item
        },

        keys: {
            backspace: 8,
            deleteKey: 46,
            enter: 13,
            escape: 27,
            pageUp: 33,
            pageDown: 34,
            leftArrow: 37,
            upArrow: 38,
            rightArrow: 39,
            downArrow: 40,
        },

        selector: {
            addition: '.addition',
            divider: '.divider, .header',
            dropdown: '.ui.dropdown',
            hidden: '.hidden',
            icon: '> .dropdown.icon',
            input: '> input[type="hidden"], > select',
            item: '.item',
            label: '> .label',
            remove: '> .label > .delete.icon',
            siblingLabel: '.label',
            menu: '.menu',
            message: '.message',
            menuIcon: '.dropdown.icon',
            search: 'input.search, .menu > .search > input, .menu input.search',
            sizer: '> span.sizer',
            text: '> .text:not(.icon)',
            unselectable: '.disabled, .filtered',
            clearIcon: '> .remove.icon',
        },

        className: {
            active: 'active',
            addition: 'addition',
            animating: 'animating',
            description: 'description',
            descriptionVertical: 'vertical',
            disabled: 'disabled',
            empty: 'empty',
            dropdown: 'ui dropdown',
            filtered: 'filtered',
            hidden: 'hidden transition',
            icon: 'icon',
            image: 'image',
            item: 'item',
            label: 'ui label',
            loading: 'loading',
            menu: 'menu',
            message: 'message',
            multiple: 'multiple',
            placeholder: 'default',
            sizer: 'sizer',
            search: 'search',
            selected: 'selected',
            selection: 'selection',
            text: 'text',
            upward: 'upward',
            leftward: 'left',
            visible: 'visible',
            clearable: 'clearable',
            noselection: 'noselection',
            delete: 'delete',
            header: 'header',
            divider: 'divider',
            groupIcon: '',
            unfilterable: 'unfilterable',
            actionable: 'actionable',
        },

    };

    /* Templates */
    $.fn.dropdown.settings.templates = {
        deQuote: function (string, encode) {
            return String(string).replace(/"/g, encode ? '&quot;' : '');
        },
        escape: function (string, preserveHTML) {
            if (preserveHTML) {
                return string;
            }
            var
                badChars     = /["'<>`]/g,
                shouldEscape = /["&'<>`]/,
                escape       = {
                    '<': '&lt;',
                    '>': '&gt;',
                    '"': '&quot;',
                    "'": '&#x27;',
                    '`': '&#x60;',
                },
                escapedChar  = function (chr) {
                    return escape[chr];
                }
            ;
            if (shouldEscape.test(string)) {
                string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                return string.replace(badChars, escapedChar);
            }

            return string;
        },
        // generates dropdown from select values
        dropdown: function (select, fields, preserveHTML, className) {
            var
                placeholder = select.placeholder || false,
                html        = '',
                escape = $.fn.dropdown.settings.templates.escape,
                deQuote = $.fn.dropdown.settings.templates.deQuote
            ;
            html += '<i class="dropdown icon"></i>';
            html += placeholder
                ? '<div class="default text">' + escape(placeholder, preserveHTML) + '</div>'
                : '<div class="text"></div>';
            html += '<div class="' + deQuote(className.menu) + '">';
            html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML, className);
            html += '</div>';

            return html;
        },

        // generates just menu from select
        menu: function (response, fields, preserveHTML, className) {
            var
                values = response[fields.values] || [],
                html   = '',
                escape = $.fn.dropdown.settings.templates.escape,
                deQuote = $.fn.dropdown.settings.templates.deQuote
            ;
            $.each(values, function (index, option) {
                var
                    itemType = option[fields.type] || 'item',
                    isMenu = itemType.indexOf('menu') !== -1
                ;

                if (itemType === 'item' || isMenu) {
                    var
                        maybeText = option[fields.text]
                            ? ' data-text="' + deQuote(option[fields.text], true) + '"'
                            : '',
                        maybeActionable = option[fields.actionable]
                            ? className.actionable + ' '
                            : '',
                        maybeDisabled = option[fields.disabled]
                            ? className.disabled + ' '
                            : '',
                        maybeDescriptionVertical = option[fields.descriptionVertical]
                            ? className.descriptionVertical + ' '
                            : '',
                        hasDescription = escape(option[fields.description] || '', preserveHTML) !== ''
                    ;
                    html += '<div class="' + deQuote(maybeActionable + maybeDisabled + maybeDescriptionVertical + (option[fields.class] || className.item)) + '" data-value="' + deQuote(option[fields.value], true) + '"' + maybeText + '>';
                    if (isMenu) {
                        html += '<i class="' + (itemType.indexOf('left') !== -1 ? 'left' : '') + ' dropdown icon"></i>';
                    }
                    if (option[fields.image]) {
                        html += '<img class="' + deQuote(option[fields.imageClass] || className.image) + '" src="' + deQuote(option[fields.image]) + '">';
                    }
                    if (option[fields.icon]) {
                        html += '<i class="' + deQuote(option[fields.icon] + ' ' + (option[fields.iconClass] || className.icon)) + '"></i>';
                    }
                    if (hasDescription) {
                        html += '<span class="' + deQuote(className.description) + '">' + escape(option[fields.description] || '', preserveHTML) + '</span>';
                        html += !isMenu ? '<span class="' + deQuote(className.text) + '">' : '';
                    }
                    if (isMenu) {
                        html += '<span class="' + deQuote(className.text) + '">';
                    }
                    html += escape(option[fields.name] || '', preserveHTML);
                    if (isMenu) {
                        html += '</span>';
                        html += '<div class="' + deQuote(itemType) + '">';
                        html += $.fn.dropdown.settings.templates.menu(option, fields, preserveHTML, className);
                        html += '</div>';
                    } else if (hasDescription) {
                        html += '</span>';
                    }
                    html += '</div>';
                } else if (itemType === 'header') {
                    var
                        groupName = escape(option[fields.name] || '', preserveHTML),
                        groupIcon = deQuote(option[fields.icon] || className.groupIcon)
                    ;
                    if (groupName !== '' || groupIcon !== '') {
                        html += '<div class="' + deQuote(option[fields.class] || className.header) + '">';
                        if (groupIcon !== '') {
                            html += '<i class="' + deQuote(groupIcon + ' ' + (option[fields.iconClass] || className.icon)) + '"></i>';
                        }
                        html += groupName;
                        html += '</div>';
                    }
                    if (option[fields.divider]) {
                        html += '<div class="' + deQuote(className.divider) + '"></div>';
                    }
                }
            });

            return html;
        },

        // generates label for multiselect
        label: function (value, text, preserveHTML, className) {
            var
                escape = $.fn.dropdown.settings.templates.escape,
                deQuote = $.fn.dropdown.settings.templates.deQuote
            ;

            return escape(text, preserveHTML) + '<i class="' + deQuote(className.delete) + ' icon"></i>';
        },

        // generates messages like "No results"
        message: function (message) {
            return message;
        },

        // generates user addition to selection menu
        addition: function (choice) {
            return choice;
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Embed
 * 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.embed = 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.embed.settings, parameters)
                    : $.extend({}, $.fn.embed.settings),

                selector        = settings.selector,
                className       = settings.className,
                sources         = settings.sources,
                error           = settings.error,
                metadata        = settings.metadata,
                namespace       = settings.namespace,
                templates       = settings.templates,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $placeholder    = $module.find(selector.placeholder),
                $icon           = $module.find(selector.icon),
                $embed          = $module.find(selector.embed),

                element         = this,
                instance        = $module.data(moduleNamespace),
                module
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing embed');
                    module.determine.autoplay();
                    module.create();
                    module.bind.events();
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous instance of embed');
                    module.reset();
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                },

                refresh: function () {
                    module.verbose('Refreshing selector cache');
                    $placeholder = $module.find(selector.placeholder);
                    $icon = $module.find(selector.icon);
                    $embed = $module.find(selector.embed);
                },

                bind: {
                    events: function () {
                        if (module.has.placeholder()) {
                            module.debug('Adding placeholder events');
                            $module
                                .on('click' + eventNamespace, selector.placeholder, module.createAndShow)
                                .on('click' + eventNamespace, selector.icon, module.createAndShow)
                            ;
                        }
                    },
                },

                create: function () {
                    var
                        placeholder = module.get.placeholder()
                    ;
                    if (placeholder) {
                        module.createPlaceholder();
                    } else {
                        module.createAndShow();
                    }
                },

                createPlaceholder: function (placeholder) {
                    var
                        icon  = module.get.icon()
                    ;
                    placeholder = placeholder || module.get.placeholder();
                    $module.html(templates.placeholder(placeholder, icon));
                    module.debug('Creating placeholder for embed', placeholder, icon);
                },

                createEmbed: function (url) {
                    module.refresh();
                    url = url || module.get.url();
                    $embed = $('<div/>')
                        .addClass(className.embed)
                        .html(module.generate.embed(url))
                        .appendTo($module)
                    ;
                    settings.onCreate.call(element, url);
                    module.debug('Creating embed object', $embed);
                },

                changeEmbed: function (url) {
                    $embed
                        .html(module.generate.embed(url))
                    ;
                },

                createAndShow: function () {
                    module.createEmbed();
                    module.show();
                },

                // sets new embed
                change: function (source, id, url) {
                    module.debug('Changing video to ', source, id, url);
                    $module
                        .data(metadata.source, source)
                        .data(metadata.id, id)
                    ;
                    if (url) {
                        $module.data(metadata.url, url);
                    } else {
                        $module.removeData(metadata.url);
                    }
                    if (module.has.embed()) {
                        module.changeEmbed();
                    } else {
                        module.create();
                    }
                },

                // clears embed
                reset: function () {
                    module.debug('Clearing embed and showing placeholder');
                    module.remove.data();
                    module.remove.active();
                    module.remove.embed();
                    module.showPlaceholder();
                    settings.onReset.call(element);
                },

                // shows current embed
                show: function () {
                    module.debug('Showing embed');
                    module.set.active();
                    settings.onDisplay.call(element);
                },

                hide: function () {
                    module.debug('Hiding embed');
                    module.showPlaceholder();
                },

                showPlaceholder: function () {
                    module.debug('Showing placeholder image');
                    module.remove.active();
                    settings.onPlaceholderDisplay.call(element);
                },

                get: {
                    id: function () {
                        return settings.id || $module.data(metadata.id);
                    },
                    placeholder: function () {
                        return settings.placeholder || $module.data(metadata.placeholder);
                    },
                    icon: function () {
                        return settings.icon || ($module.data(metadata.icon) !== undefined
                            ? $module.data(metadata.icon)
                            : module.determine.icon());
                    },
                    source: function (url) {
                        return settings.source || ($module.data(metadata.source) !== undefined
                            ? $module.data(metadata.source)
                            : module.determine.source());
                    },
                    type: function () {
                        var source = module.get.source();

                        return sources[source] !== undefined
                            ? sources[source].type
                            : false;
                    },
                    url: function () {
                        return settings.url || ($module.data(metadata.url) !== undefined
                            ? $module.data(metadata.url)
                            : module.determine.url());
                    },
                },

                determine: {
                    autoplay: function () {
                        if (module.should.autoplay()) {
                            settings.autoplay = true;
                        }
                    },
                    source: function (url) {
                        var
                            matchedSource = false
                        ;
                        url = url || module.get.url();
                        if (url) {
                            $.each(sources, function (name, source) {
                                if (url.search(source.domain) !== -1) {
                                    matchedSource = name;

                                    return false;
                                }
                            });
                        }

                        return matchedSource;
                    },
                    icon: function () {
                        var
                            source = module.get.source()
                        ;

                        return sources[source] !== undefined
                            ? sources[source].icon
                            : false;
                    },
                    url: function () {
                        var
                            id     = settings.id || $module.data(metadata.id),
                            source = settings.source || $module.data(metadata.source),
                            url
                        ;
                        url = sources[source] !== undefined
                            ? sources[source].url.replace('{id}', id)
                            : false;
                        if (url) {
                            $module.data(metadata.url, url);
                        }

                        return url;
                    },
                },

                set: {
                    active: function () {
                        $module.addClass(className.active);
                    },
                },

                remove: {
                    data: function () {
                        $module
                            .removeData(metadata.id)
                            .removeData(metadata.icon)
                            .removeData(metadata.placeholder)
                            .removeData(metadata.source)
                            .removeData(metadata.url)
                        ;
                    },
                    active: function () {
                        $module.removeClass(className.active);
                    },
                    embed: function () {
                        $embed.empty();
                    },
                },

                encode: {
                    parameters: function (parameters) {
                        var
                            urlString = [],
                            index
                        ;
                        for (index in parameters) {
                            urlString.push(encodeURIComponent(index) + '=' + encodeURIComponent(parameters[index]));
                        }

                        return urlString.join('&amp;');
                    },
                },

                generate: {
                    embed: function (url) {
                        module.debug('Generating embed html');
                        var
                            source = module.get.source(),
                            html,
                            parameters
                        ;
                        url = module.get.url(url);
                        if (url) {
                            parameters = module.generate.parameters(source);
                            html = templates.iframe(url, parameters);
                        } else {
                            module.error(error.noURL, $module);
                        }

                        return html;
                    },
                    parameters: function (source, extraParameters) {
                        var
                            parameters = sources[source] && sources[source].parameters !== undefined
                                ? sources[source].parameters(settings)
                                : {}
                        ;
                        extraParameters = extraParameters || settings.parameters;
                        if (extraParameters) {
                            parameters = $.extend({}, parameters, extraParameters);
                        }
                        parameters = settings.onEmbed(parameters);

                        return module.encode.parameters(parameters);
                    },
                },

                has: {
                    embed: function () {
                        return $embed.length > 0;
                    },
                    placeholder: function () {
                        return settings.placeholder || $module.data(metadata.placeholder);
                    },
                },

                should: {
                    autoplay: function () {
                        return settings.autoplay === 'auto'
                            ? settings.placeholder || $module.data(metadata.placeholder) !== undefined
                            : settings.autoplay;
                    },
                },

                is: {
                    video: function () {
                        return module.get.type() === 'video';
                    },
                },

                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 ($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;
                },
            };

            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.embed.settings = {

        name: 'Embed',
        namespace: 'embed',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        icon: false,
        source: false,
        url: false,
        id: false,

        // standard video settings
        autoplay: 'auto',
        color: '#444',
        hd: true,
        brandedUI: false,

        // additional parameters to include with the embed
        parameters: false,

        onDisplay: function () {},
        onPlaceholderDisplay: function () {},
        onReset: function () {},
        onCreate: function (url) {},
        onEmbed: function (parameters) {
            return parameters;
        },

        metadata: {
            id: 'id',
            icon: 'icon',
            placeholder: 'placeholder',
            source: 'source',
            url: 'url',
        },

        error: {
            noURL: 'No URL specified',
            method: 'The method you called is not defined',
        },

        className: {
            active: 'active',
            embed: 'embed',
        },

        selector: {
            embed: '.embed',
            placeholder: '.placeholder',
            icon: '.icon',
        },

        sources: {
            youtube: {
                name: 'youtube',
                type: 'video',
                icon: 'video play',
                domain: 'youtube.com',
                url: '//www.youtube.com/embed/{id}',
                parameters: function (settings) {
                    return {
                        autohide: !settings.brandedUI,
                        autoplay: settings.autoplay,
                        color: settings.color || undefined,
                        hq: settings.hd,
                        jsapi: settings.api,
                        modestbranding: !settings.brandedUI,
                    };
                },
            },
            vimeo: {
                name: 'vimeo',
                type: 'video',
                icon: 'video play',
                domain: 'vimeo.com',
                url: '//player.vimeo.com/video/{id}',
                parameters: function (settings) {
                    return {
                        api: settings.api,
                        autoplay: settings.autoplay,
                        byline: settings.brandedUI,
                        color: settings.color || undefined,
                        portrait: settings.brandedUI,
                        title: settings.brandedUI,
                    };
                },
            },
        },

        templates: {
            deQuote: function (string, encode) {
                return String(string).replace(/"/g, encode ? '&quot;' : '');
            },
            iframe: function (url, parameters) {
                var
                    src = url,
                    deQuote = $.fn.embed.settings.templates.deQuote
                ;
                if (parameters) {
                    src += '?' + parameters;
                }

                return ''
                    + '<iframe src="' + deQuote(src) + '"'
                    + ' width="100%" height="100%"'
                    + ' msallowFullScreen allowFullScreen></iframe>';
            },
            placeholder: function (image, icon) {
                var
                    html = '',
                    deQuote = $.fn.embed.settings.templates.deQuote
                ;
                if (icon) {
                    html += '<i class="' + deQuote(icon) + ' icon"></i>';
                }
                if (image) {
                    html += '<img class="placeholder" src="' + deQuote(image) + '">';
                }

                return html;
            },
        },

        // NOT YET IMPLEMENTED
        api: false,
        onPause: function () {},
        onPlay: function () {},
        onStop: function () {},

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Flyout
 * 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.flyout = function (parameters) {
        var
            $allModules     = $(this),
            $window         = $(window),
            $document       = $(document),
            $html           = $('html'),
            $head           = $('head'),
            $body           = $('body'),

            time            = Date.now(),
            performance     = [],

            query           = arguments[0],
            methodInvoked   = typeof query === 'string',
            queryArguments  = [].slice.call(arguments, 1),
            contextCheck    = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $body;
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },
            returnedValue
        ;

        $allModules.each(function () {
            var
                settings             = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.flyout.settings, parameters)
                    : $.extend({}, $.fn.flyout.settings),

                selector             = settings.selector,
                className            = settings.className,
                namespace            = settings.namespace,
                fields               = settings.fields,
                regExp               = settings.regExp,
                error                = settings.error,

                eventNamespace       = '.' + namespace,
                moduleNamespace      = 'module-' + namespace,

                $module              = $(this),
                $context             = contextCheck(settings.context, window),
                $closeIcon           = $module.find(selector.close),
                $inputs,
                $focusedElement,

                $flyouts             = $module.children(selector.flyout),
                $pusher              = $context.children(selector.pusher),
                $style,

                isFlyoutComponent    = $module.hasClass('flyout'),

                element              = this,
                instance             = isFlyoutComponent ? $module.data(moduleNamespace) : undefined,

                ignoreRepeatedEvents = false,
                isBody               = $context[0] === $body[0],
                initialBodyMargin    = '',
                tempBodyMargin       = '',
                hadScrollbar         = false,
                windowRefocused      = false,

                elementNamespace,
                id,
                observer,
                observeAttributes = false,
                currentScroll,

                module
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing flyout', parameters);

                    module.create.id();
                    if (!isFlyoutComponent) {
                        module.create.flyout();
                        if (!isFunction(settings.onHidden)) {
                            settings.onHidden = function () {
                                module.destroy();
                                $module.remove();
                            };
                        }
                        if (!settings.autoShow) {
                            settings.autoShow = true;
                        }
                    }
                    $module.addClass(settings.class);
                    if (settings.title !== '') {
                        $module.find(selector.header).html(module.helpers.escape(settings.title, settings.preserveHTML)).addClass(settings.classTitle);
                    }
                    if (settings.content !== '') {
                        $module.find(selector.content).html(module.helpers.escape(settings.content, settings.preserveHTML)).addClass(settings.classContent);
                    }
                    if (module.has.configActions()) {
                        var $actions = $module.find(selector.actions).addClass(settings.classActions);
                        if ($actions.length === 0) {
                            $actions = $('<div/>', { class: className.actions + ' ' + (settings.classActions || '') }).appendTo($module);
                        } else {
                            $actions.empty();
                        }
                        settings.actions.forEach(function (el) {
                            var
                                icon = el[fields.icon]
                                    ? '<i ' + (el[fields.text] ? 'aria-hidden="true"' : '') + ' class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>'
                                    : '',
                                text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
                                cls = module.helpers.deQuote(el[fields.class] || ''),
                                click = el[fields.click] && isFunction(el[fields.click])
                                    ? el[fields.click]
                                    : function () {}
                            ;
                            $actions.append($('<button/>', {
                                html: icon + text,
                                'aria-label': (el[fields.text] || el[fields.icon] || '').replace(/<[^>]+(>|$)/g, ''),
                                class: className.button + ' ' + cls,
                                on: {
                                    click: function () {
                                        if (click.call(element, $module) === false) {
                                            return;
                                        }
                                        module.hide();
                                    },
                                },
                            }));
                        });
                    }

                    // avoids locking rendering if initialized in onReady
                    if (settings.delaySetup) {
                        requestAnimationFrame(module.setup.layout);
                    } else {
                        module.setup.layout();
                    }

                    requestAnimationFrame(function () {
                        module.setup.cache();
                    });

                    if (module.get.direction() === 'left' || module.get.direction() === 'right') {
                        module.setup.heights();
                        module.bind.resize();
                    }
                    module.bind.events();
                    module.observeChanges();
                    module.instantiate();

                    if (settings.autoShow) {
                        module.show();
                    }
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                create: {
                    flyout: function () {
                        module.verbose('Programmaticaly create flyout', $context);
                        $module = $('<div/>', { class: className.flyout, role: 'dialog', 'aria-modal': settings.dimPage });
                        if (settings.closeIcon) {
                            $closeIcon = $('<i/>', {
                                class: className.close,
                                role: 'button',
                                tabindex: 0,
                                'aria-label': settings.text.close,
                            });
                            $module.append($closeIcon);
                        }
                        if (settings.title !== '') {
                            var titleId = '_' + module.get.id() + 'title';
                            $module.attr('aria-labelledby', titleId);
                            $('<div/>', { class: className.header, id: titleId }).appendTo($module);
                        }
                        if (settings.content !== '') {
                            var descId = '_' + module.get.id() + 'desc';
                            $module.attr('aria-describedby', descId);
                            $('<div/>', { class: className.content, id: descId }).appendTo($module);
                        }
                        if (module.has.configActions()) {
                            $('<div/>', { class: className.actions }).appendTo($module);
                        }
                        $module.prependTo($context);
                        element = $module[0];
                    },
                    id: function () {
                        id = (Math.random().toString(16) + '000000000').slice(2, 10);
                        elementNamespace = '.' + id;
                        module.verbose('Creating unique id for element', id);
                    },
                },

                destroy: function () {
                    if (observer) {
                        observer.disconnect();
                    }
                    module.verbose('Destroying previous module for', $module);
                    $module
                        .off(eventNamespace)
                        .removeData(moduleNamespace)
                    ;
                    $closeIcon.off(elementNamespace);
                    if ($inputs) {
                        $inputs.off(elementNamespace);
                    }
                    // bound by uuid
                    $context.off(elementNamespace);
                    $window.off(elementNamespace);
                    $document.off(elementNamespace);
                },

                event: {
                    keyboard: function (event) {
                        var
                            keyCode   = event.which
                        ;
                        if (keyCode === settings.keys.escape) {
                            if (settings.closable) {
                                module.debug('Escape key pressed hiding flyout');
                                module.hide();
                            } else {
                                module.debug('Escape key pressed, but closable is set to false');
                            }
                            event.preventDefault();
                        }
                    },
                    resize: function () {
                        module.setup.heights();
                    },
                    focus: function () {
                        windowRefocused = true;
                    },
                    click: function (event) {
                        if (windowRefocused && document.activeElement !== event.target && module.is.visible() && settings.autofocus && settings.dimPage && $(document.activeElement).closest(selector.flyout).length === 0) {
                            requestAnimationFrame(module.set.autofocus);
                        }
                        windowRefocused = false;
                    },
                    clickaway: function (event) {
                        if (settings.closable) {
                            var
                                clickedInPusher = $pusher.find(event.target).length > 0 || $pusher.is(event.target),
                                clickedContext  = $context.is(event.target)
                            ;
                            if (clickedInPusher) {
                                module.verbose('User clicked on dimmed page');
                                module.hide();
                            }
                            if (clickedContext) {
                                module.verbose('User clicked on dimmable context (scaled out page)');
                                module.hide();
                            }
                        }
                    },
                    close: function (event) {
                        module.hide();
                    },
                    closeKeyUp: function (event) {
                        var
                            keyCode   = event.which
                        ;
                        if (keyCode === settings.keys.enter || keyCode === settings.keys.space) {
                            module.hide();
                        }
                    },
                    inputKeyDown: {
                        first: function (event) {
                            var
                                keyCode = event.which
                            ;
                            if (keyCode === settings.keys.tab && event.shiftKey) {
                                $inputs.last().trigger('focus');
                                event.preventDefault();
                            }
                        },
                        last: function (event) {
                            var
                                keyCode = event.which
                            ;
                            if (keyCode === settings.keys.tab && !event.shiftKey) {
                                $inputs.first().trigger('focus');
                                event.preventDefault();
                            }
                        },
                    },
                    approve: function (event) {
                        if (ignoreRepeatedEvents || settings.onApprove.call(module.element, $(this)) === false) {
                            module.verbose('Approve callback returned false cancelling close');

                            return;
                        }
                        ignoreRepeatedEvents = true;
                        module.hide(function () {
                            ignoreRepeatedEvents = false;
                        });
                    },
                    deny: function (event) {
                        if (ignoreRepeatedEvents || settings.onDeny.call(module.element, $(this)) === false) {
                            module.verbose('Deny callback returned false cancelling close');

                            return;
                        }
                        ignoreRepeatedEvents = true;
                        module.hide(function () {
                            ignoreRepeatedEvents = false;
                        });
                    },
                    touch: function (event) {
                        // event.stopPropagation();
                    },
                    containScroll: function (event) {
                        if (element.scrollTop <= 0) {
                            element.scrollTop = 1;
                        }
                        if ((element.scrollTop + element.offsetHeight) >= element.scrollHeight) {
                            element.scrollTop = element.scrollHeight - element.offsetHeight - 1;
                        }
                    },
                    scroll: function (event) {
                        if ($(event.target).closest(selector.flyout).length === 0) {
                            event.preventDefault();
                        }
                    },
                },

                bind: {
                    resize: function () {
                        module.verbose('Adding resize event to window', $window);
                        $window.on('resize' + elementNamespace, module.event.resize);
                    },
                    events: function () {
                        module.verbose('Attaching events');
                        $module
                            .on('click' + eventNamespace, selector.close, module.event.close)
                            .on('click' + eventNamespace, selector.approve, module.event.approve)
                            .on('click' + eventNamespace, selector.deny, module.event.deny)
                        ;
                        $closeIcon
                            .on('keyup' + elementNamespace, module.event.closeKeyUp)
                        ;
                        $window
                            .on('focus' + elementNamespace, module.event.focus)
                        ;
                        $context
                            .on('click' + elementNamespace, module.event.click)
                        ;
                    },
                    clickaway: function () {
                        module.verbose('Adding clickaway events to context', $context);
                        $context
                            .on('click' + elementNamespace, module.event.clickaway)
                            .on('touchend' + elementNamespace, module.event.clickaway)
                        ;
                    },
                    scrollLock: function () {
                        if (settings.scrollLock) {
                            module.debug('Disabling page scroll');
                            hadScrollbar = module.has.scrollbar();
                            if (hadScrollbar) {
                                module.save.bodyMargin();
                                module.set.bodyMargin();
                            }
                            $context.addClass(className.locked);
                        }
                        module.verbose('Adding events to contain flyout scroll');
                        $document
                            .on('touchmove' + elementNamespace, module.event.touch)
                        ;
                        $module
                            .on('scroll' + eventNamespace, module.event.containScroll)
                        ;
                    },
                },
                unbind: {
                    clickaway: function () {
                        module.verbose('Removing clickaway events from context', $context);
                        $context.off(elementNamespace);
                    },
                    scrollLock: function () {
                        module.verbose('Removing scroll lock from page');
                        if (hadScrollbar) {
                            module.restore.bodyMargin();
                        }
                        $context.removeClass(className.locked);
                        $document.off(elementNamespace);
                        $module.off('scroll' + eventNamespace);
                    },
                },

                add: {
                    inlineCSS: function () {
                        var
                            width     = module.cache.width || $module.outerWidth(),
                            height    = module.cache.height || $module.outerHeight(),
                            isRTL     = module.is.rtl(),
                            direction = module.get.direction(),
                            distance  = {
                                left: width,
                                right: -width,
                                top: height,
                                bottom: -height,
                            },
                            style
                        ;

                        if (isRTL) {
                            module.verbose('RTL detected, flipping widths');
                            distance.left = -width;
                            distance.right = width;
                        }

                        style = '<style>';

                        if (direction === 'left' || direction === 'right') {
                            module.debug('Adding CSS rules for animation distance', width);
                            style += ''
                                + ' .ui.visible.' + direction + '.flyout ~ .fixed,'
                                + ' .ui.visible.' + direction + '.flyout ~ .pusher {'
                                + '           transform: translate3d(' + distance[direction] + 'px, 0, 0);'
                                + ' }';
                        } else if (direction === 'top' || direction === 'bottom') {
                            style += ''
                                + ' .ui.visible.' + direction + '.flyout ~ .fixed,'
                                + ' .ui.visible.' + direction + '.flyout ~ .pusher {'
                                + '           transform: translate3d(0, ' + distance[direction] + 'px, 0);'
                                + ' }';
                        }

                        /* IE is only browser not to create context with transforms */
                        /* https://www.w3.org/Bugs/Public/show_bug.cgi?id=16328 */
                        if (module.is.ie()) {
                            if (direction === 'left' || direction === 'right') {
                                module.debug('Adding CSS rules for animation distance', width);
                                style += ''
                                    + ' body.pushable > .ui.visible.' + direction + '.flyout ~ .pusher::after {'
                                    + '           transform: translate3d(' + distance[direction] + 'px, 0, 0);'
                                    + ' }';
                            } else if (direction === 'top' || direction === 'bottom') {
                                style += ''
                                    + ' body.pushable > .ui.visible.' + direction + '.flyout ~ .pusher::after {'
                                    + '           transform: translate3d(0, ' + distance[direction] + 'px, 0);'
                                    + ' }';
                            }
                            /* opposite sides visible forces content overlay */
                            style += ''
                                + ' body.pushable > .ui.visible.left.flyout ~ .ui.visible.right.flyout ~ .pusher::after,'
                                + ' body.pushable > .ui.visible.right.flyout ~ .ui.visible.left.flyout ~ .pusher::after {'
                                + '           transform: translate3d(0, 0, 0);'
                                + ' }';
                        }
                        style += '</style>';
                        $style = $(style)
                            .appendTo($head)
                        ;
                        module.debug('Adding sizing css to head', $style);
                    },
                    keyboardShortcuts: function () {
                        module.verbose('Adding keyboard shortcuts');
                        $document
                            .on('keydown' + eventNamespace, module.event.keyboard)
                        ;
                    },
                },
                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        observer = new MutationObserver(function (mutations) {
                            var collectNodes = function (parent) {
                                    var nodes = [];
                                    for (var c = 0, cl = parent.length; c < cl; c++) {
                                        Array.prototype.push.apply(nodes, collectNodes(parent[c].childNodes));
                                        nodes.push(parent[c]);
                                    }

                                    return nodes;
                                },
                                shouldRefreshInputs = false,
                                ignoreAutofocus = true
                            ;
                            mutations.every(function (mutation) {
                                if (mutation.type === 'attributes') {
                                    if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
                                        shouldRefreshInputs = true;
                                    }
                                } else {
                                    // mutationobserver only provides the parent nodes
                                    // so let's collect all childs as well to find nested inputs
                                    var $addedInputs = $(collectNodes(mutation.addedNodes)).filter('a[href], [tabindex], :input:enabled').filter(':visible'),
                                        $removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
                                    if ($addedInputs.length > 0 || $removedInputs.length > 0) {
                                        shouldRefreshInputs = true;
                                        if ($addedInputs.filter(':input').length > 0 || $removedInputs.filter(':input').length > 0) {
                                            ignoreAutofocus = false;
                                        }
                                    }
                                }

                                return !shouldRefreshInputs;
                            });

                            if (shouldRefreshInputs) {
                                module.refreshInputs(ignoreAutofocus);
                            }
                        });
                        observer.observe(element, {
                            attributeFilter: ['class', 'disabled'],
                            attributes: true,
                            childList: true,
                            subtree: true,
                        });
                        module.debug('Setting up mutation observer', observer);
                    }
                },
                refresh: function () {
                    module.verbose('Refreshing selector cache');
                    $context = contextCheck(settings.context, window);
                    module.refreshFlyouts();
                    $pusher = $context.children(selector.pusher);
                    module.clear.cache();
                },

                refreshFlyouts: function () {
                    module.verbose('Refreshing other flyouts');
                    $flyouts = $context.children(selector.flyout);
                },

                refreshInputs: function (ignoreAutofocus) {
                    if ($inputs) {
                        $inputs
                            .off('keydown' + elementNamespace)
                        ;
                    }
                    if (!settings.dimPage) {
                        return;
                    }
                    $inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
                        return $(this).closest('.disabled').length === 0;
                    });
                    if ($inputs.filter(':input').length === 0) {
                        $inputs = $module.add($inputs);
                        $module.attr('tabindex', -1);
                    } else {
                        $module.removeAttr('tabindex');
                    }
                    $inputs.first()
                        .on('keydown' + elementNamespace, module.event.inputKeyDown.first)
                    ;
                    $inputs.last()
                        .on('keydown' + elementNamespace, module.event.inputKeyDown.last)
                    ;
                    if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
                        module.set.autofocus();
                    }
                },

                setup: {
                    cache: function () {
                        module.cache = {
                            width: $module.outerWidth(),
                            height: $module.outerHeight(),
                        };
                    },
                    layout: function () {
                        if ($context.children(selector.pusher).length === 0) {
                            module.debug('Adding wrapper element for flyout');
                            module.error(error.pusher);
                            $pusher = $('<div class="pusher" />');
                            $context
                                .children()
                                .not(selector.omitted)
                                .not($flyouts)
                                .wrapAll($pusher)
                            ;
                            module.refresh();
                        }
                        if ($module.nextAll(selector.pusher).length === 0 || $module.nextAll(selector.pusher)[0] !== $pusher[0]) {
                            module.debug('Moved flyout to correct parent element');
                            module.error(error.movedFlyout, element);
                            $module.detach().prependTo($context);
                            module.refresh();
                        }
                        if (module.is.mobile()) {
                            $module.addClass(className.fullscreen);
                        }
                        module.clear.cache();
                        module.set.pushable();
                        module.set.direction();
                    },
                    heights: function () {
                        module.debug('Setting up heights', $module);
                        var
                            $header = $module.children(selector.header),
                            $content = $module.children(selector.content),
                            $actions = $module.children(selector.actions),
                            newContentHeight = ($context.height() || 0) - ($header.outerHeight() || 0) - ($actions.outerHeight() || 0)
                        ;
                        if (newContentHeight > 0) {
                            $content.css('min-height', String(newContentHeight) + 'px');
                        }
                    },
                },

                attachEvents: function (selector, event) {
                    var
                        $toggle = $(selector)
                    ;
                    event = isFunction(module[event])
                        ? module[event]
                        : module.toggle;
                    if ($toggle.length > 0) {
                        module.debug('Attaching flyout events to element', selector, event);
                        $toggle
                            .on('click' + eventNamespace, event)
                        ;
                    } else {
                        module.error(error.notFound, selector);
                    }
                },

                show: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.hidden()) {
                        if (settings.onShow.call(element) === false) {
                            module.verbose('Show callback returned false cancelling show');

                            return;
                        }
                        module.refresh();
                        if (module.othersActive()) {
                            module.debug('Other flyouts currently visible');
                            if (settings.exclusive) {
                                module.hideOthers();
                            } else {
                                ignoreRepeatedEvents = false;
                            }
                        }
                        module.set.dimmerStyles();
                        module.set.observeAttributes(false);
                        module.pushPage(function () {
                            callback.call(element);
                            settings.onVisible.call(element);
                            if (settings.keyboardShortcuts) {
                                module.add.keyboardShortcuts();
                            }
                            module.save.focus();
                            module.refreshInputs();
                            requestAnimationFrame(module.set.observeAttributes);
                        });
                        settings.onChange.call(element);
                    } else {
                        module.debug('Flyout is already visible');
                    }
                },

                hide: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (settings.onHide.call(element, $(this)) === false) {
                        module.verbose('Hide callback returned false cancelling hide');
                        ignoreRepeatedEvents = false;

                        return false;
                    }
                    if (module.is.visible() || module.is.animating()) {
                        module.debug('Hiding flyout', callback);
                        module.refreshFlyouts();
                        module.set.observeAttributes(false);
                        module.pullPage(function () {
                            callback.call(element);
                            if (isFunction(settings.onHidden)) {
                                settings.onHidden.call(element);
                            }
                            module.restore.focus();
                        });
                        settings.onChange.call(element);
                    }
                },

                othersAnimating: function () {
                    return $flyouts.not($module).filter('.' + className.animating).length > 0;
                },
                othersVisible: function () {
                    return $flyouts.not($module).filter('.' + className.visible).length > 0;
                },
                othersActive: function () {
                    return module.othersVisible() || module.othersAnimating();
                },

                hideOthers: function (callback) {
                    var
                        $otherFlyouts = $flyouts.not($module).filter('.' + className.visible),
                        flyoutCount   = $otherFlyouts.length,
                        callbackCount  = 0
                    ;
                    callback = callback || function () {};
                    $otherFlyouts
                        .flyout('hide', function () {
                            callbackCount++;
                            if (callbackCount === flyoutCount) {
                                callback();
                            }
                        })
                    ;
                },

                toggle: function () {
                    module.verbose('Determining toggled direction');
                    if (module.is.hidden()) {
                        module.show();
                    } else {
                        module.hide();
                    }
                },

                pushPage: function (callback) {
                    var
                        animate,
                        dim,
                        transitionEnd
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    module.set.overlay();
                    if (settings.returnScroll) {
                        currentScroll = (isBody ? $window : $context).scrollTop();
                    }
                    module.bind.scrollLock();
                    animate = function () {
                        module.bind.clickaway();
                        module.add.inlineCSS();
                        module.set.animating();
                        module.set.visible();
                    };
                    dim = function () {
                        module.set.dimmed();
                    };
                    transitionEnd = function (event) {
                        if (event.target === $module[0]) {
                            $module.off('transitionend' + elementNamespace, transitionEnd);
                            module.remove.animating();
                            callback.call(element);
                        }
                    };
                    $module.off('transitionend' + elementNamespace);
                    $module.on('transitionend' + elementNamespace, transitionEnd);
                    requestAnimationFrame(animate);
                    if (settings.dimPage && !module.othersVisible()) {
                        requestAnimationFrame(dim);
                    }
                },

                pullPage: function (callback) {
                    var
                        animate,
                        transitionEnd
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    module.verbose('Removing context push state', module.get.direction());

                    module.unbind.clickaway();
                    if (!module.othersActive()) {
                        module.unbind.scrollLock();
                        if (settings.keyboardShortcuts) {
                            module.remove.keyboardShortcuts();
                        }
                    }

                    animate = function () {
                        module.set.overlay();
                        module.set.animating();
                        if (settings.dimPage && !module.othersVisible()) {
                            module.set.closing();
                        }
                        module.remove.visible();
                    };
                    transitionEnd = function (event) {
                        if (event.target === $module[0]) {
                            $module.off('transitionend' + elementNamespace, transitionEnd);
                            module.remove.animating();
                            module.remove.closing();
                            module.remove.overlay();
                            module.remove.inlineCSS();
                            if (settings.returnScroll) {
                                module.scrollBack();
                            }
                            if (settings.dimPage && !module.othersVisible()) {
                                $pusher.removeClass(className.dimmed);
                            }
                            callback.call(element);
                        }
                    };
                    $module.off('transitionend' + elementNamespace);
                    $module.on('transitionend' + elementNamespace, transitionEnd);
                    requestAnimationFrame(animate);
                },

                scrollToTop: function () {
                    module.verbose('Scrolling to top of page to avoid animation issues');
                    $module.scrollTop(0);
                    (isBody ? $window : $context)[0].scrollTo(0, 0);
                },

                scrollBack: function () {
                    module.verbose('Scrolling back to original page position');
                    (isBody ? $window : $context)[0].scrollTo(0, currentScroll);
                },

                clear: {
                    cache: function () {
                        module.verbose('Clearing cached dimensions');
                        module.cache = {};
                    },
                },

                set: {
                    observeAttributes: function (state) {
                        observeAttributes = state !== false;
                    },
                    autofocus: function () {
                        var
                            $autofocus = $inputs.filter('[autofocus]'),
                            $rawInputs = $inputs.filter(':input'),
                            $input     = ($autofocus.length > 0
                                ? $autofocus
                                : ($rawInputs.length > 0
                                    ? $rawInputs
                                    : $module)
                            ).first()
                        ;
                        $input.trigger('focus');
                    },
                    dimmerStyles: function () {
                        if (settings.blurring) {
                            $pusher.addClass(className.blurring);
                        } else {
                            $pusher.removeClass(className.blurring);
                        }
                    },
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        $context.css((isBody ? 'margin-' : 'padding-') + position, tempBodyMargin + 'px');
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, 'calc(' + el.css(attribute) + ' + ' + tempBodyMargin + 'px)');
                        });
                    },

                    // container
                    pushed: function () {
                        $context.addClass(className.pushed);
                    },
                    pushable: function () {
                        $context.addClass(className.pushable);
                    },

                    // pusher
                    dimmed: function () {
                        $pusher.addClass(className.dimmed);
                    },

                    // flyout
                    active: function () {
                        $module.addClass(className.active);
                    },
                    animating: function () {
                        $module.addClass(className.animating);
                    },
                    closing: function () {
                        $pusher.addClass(className.closing);
                    },
                    direction: function (direction) {
                        direction = direction || module.get.direction();
                        $module.addClass(className[direction]);
                    },
                    visible: function () {
                        $module.addClass(className.visible);
                    },
                    overlay: function () {
                        $module.addClass(className.overlay);
                    },
                },
                remove: {

                    inlineCSS: function () {
                        module.debug('Removing inline css styles', $style);
                        if ($style && $style.length > 0) {
                            $style.remove();
                        }
                    },
                    keyboardShortcuts: function () {
                        module.verbose('Removing keyboard shortcuts');
                        $document
                            .off('keydown' + eventNamespace)
                        ;
                    },

                    // context
                    pushed: function () {
                        $context.removeClass(className.pushed);
                    },
                    pushable: function () {
                        $context.removeClass(className.pushable);
                    },

                    // flyout
                    active: function () {
                        $module.removeClass(className.active);
                    },
                    animating: function () {
                        $module.removeClass(className.animating);
                    },
                    closing: function () {
                        $pusher.removeClass(className.closing);
                    },
                    direction: function (direction) {
                        direction = direction || module.get.direction();
                        $module.removeClass(className[direction]);
                    },
                    visible: function () {
                        $module.removeClass(className.visible);
                    },
                    overlay: function () {
                        $module.removeClass(className.overlay);
                    },
                },

                get: {
                    direction: function () {
                        if ($module.hasClass(className.top)) {
                            return className.top;
                        }
                        if ($module.hasClass(className.right)) {
                            return className.right;
                        }
                        if ($module.hasClass(className.bottom)) {
                            return className.bottom;
                        }

                        return className.left;
                    },
                    id: function () {
                        return id;
                    },
                    element: function () {
                        return $module;
                    },
                    settings: function () {
                        return settings;
                    },
                },

                can: {
                    leftBodyScrollbar: function () {
                        if (module.cache.leftBodyScrollbar === undefined) {
                            module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
                        }

                        return module.cache.leftBodyScrollbar;
                    },
                },

                save: {
                    focus: function () {
                        var
                            $activeElement = $(document.activeElement),
                            inCurrentFlyout = $activeElement.closest($module).length > 0
                        ;
                        if (!inCurrentFlyout) {
                            $focusedElement = $(document.activeElement).trigger('blur');
                        }
                    },
                    bodyMargin: function () {
                        initialBodyMargin = $context.css((isBody ? 'margin-' : 'padding-') + (module.can.leftBodyScrollbar() ? 'left' : 'right'));
                        var
                            bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, ''), 10),
                            bodyScrollbarWidth = isBody ? window.innerWidth - document.documentElement.clientWidth : $context[0].offsetWidth - $context[0].clientWidth
                        ;
                        tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
                    },
                },

                is: {
                    safari: function () {
                        if (module.cache.isSafari === undefined) {
                            module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
                        }

                        return module.cache.isSafari;
                    },
                    edge: function () {
                        if (module.cache.isEdge === undefined) {
                            module.cache.isEdge = !!window.setImmediate && !module.is.ie();
                        }

                        return module.cache.isEdge;
                    },
                    firefox: function () {
                        if (module.cache.isFirefox === undefined) {
                            module.cache.isFirefox = !!window.InstallTrigger;
                        }

                        return module.cache.isFirefox;
                    },
                    iframe: function () {
                        return !(self === top);
                    },
                    ie: function () {
                        if (module.cache.isIE === undefined) {
                            var
                                isIE11 = !window.ActiveXObject && 'ActiveXObject' in window,
                                isIE = 'ActiveXObject' in window
                            ;
                            module.cache.isIE = isIE11 || isIE;
                        }

                        return module.cache.isIE;
                    },
                    mobile: function () {
                        var
                            userAgent    = navigator.userAgent,
                            isMobile     = userAgent.match(regExp.mobile)
                        ;
                        if (isMobile) {
                            module.verbose('Browser was found to be mobile', userAgent);

                            return true;
                        }

                        module.verbose('Browser is not mobile, using regular transition', userAgent);

                        return false;
                    },
                    hidden: function () {
                        return !module.is.visible();
                    },
                    visible: function () {
                        return $module.hasClass(className.visible);
                    },
                    animating: function () {
                        return $context.hasClass(className.animating);
                    },
                    rtl: function () {
                        if (module.cache.isRTL === undefined) {
                            module.cache.isRTL = $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl' || $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl' || $context.attr('dir') === 'rtl' || $context.css('direction') === 'rtl';
                        }

                        return module.cache.isRTL;
                    },
                },

                has: {
                    configActions: function () {
                        return Array.isArray(settings.actions) && settings.actions.length > 0;
                    },
                    scrollbar: function () {
                        return isBody || $context.css('overflow-y') !== 'hidden';
                    },
                },

                restore: {
                    focus: function () {
                        if ($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
                            $focusedElement.trigger('focus');
                        }
                    },
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        $context.css((isBody ? 'margin-' : 'padding-') + position, initialBodyMargin);
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, '');
                        });
                    },
                },

                helpers: {
                    deQuote: function (string) {
                        return String(string).replace(/"/g, '');
                    },
                    escape: function (string, preserveHTML) {
                        if (preserveHTML) {
                            return string;
                        }
                        var
                            badChars     = /["'<>`]/g,
                            shouldEscape = /["&'<>`]/,
                            escape       = {
                                '<': '&lt;',
                                '>': '&gt;',
                                '"': '&quot;',
                                "'": '&#x27;',
                                '`': '&#x60;',
                            },
                            escapedChar  = function (chr) {
                                return escape[chr];
                            }
                        ;
                        if (shouldEscape.test(string)) {
                            string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                            return string.replace(badChars, escapedChar);
                        }

                        return string;
                    },
                },

                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 = element || context;
                    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) {
                    if (isFunction(settings.templates[query])) {
                        settings.autoShow = true;
                        settings.className.flyout = settings.className.template;
                        settings = $.extend(true, {}, settings, settings.templates[query].apply(module, queryArguments));

                        // reassign shortcuts
                        className = settings.className;
                        namespace = settings.namespace;
                        fields = settings.fields;
                        error = settings.error;
                    }
                    module.initialize();
                }
                if (!isFunction(settings.templates[query])) {
                    module.invoke(query);
                }
            } else {
                if (instance !== undefined) {
                    instance.invoke('destroy');
                }
                module.initialize();
                returnedValue = $module;
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };
    $.flyout = $.fn.flyout;

    $.fn.flyout.settings = {

        name: 'Flyout',
        namespace: 'flyout',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        context: 'body',
        exclusive: false,
        closable: true,
        autofocus: true,
        restoreFocus: true,
        dimPage: true,
        scrollLock: false,
        returnScroll: false,
        delaySetup: false,
        autoShow: false,

        keyboardShortcuts: true,

        // dynamic content
        title: '',
        content: '',
        class: '',
        classTitle: '',
        classContent: '',
        classActions: '',
        closeIcon: false,
        actions: false,
        preserveHTML: true,

        fields: {
            class: 'class',
            text: 'text',
            icon: 'icon',
            click: 'click',
        },

        onChange: function () {},
        onShow: function () {},
        onHide: function () {
            return true;
        },

        onHidden: false,
        onVisible: function () {},

        onApprove: function () {},
        onDeny: function () {},

        keys: {
            space: 32,
            enter: 13,
            escape: 27,
            tab: 9,
        },

        className: {
            flyout: 'ui flyout',
            close: 'close icon',
            header: 'ui header',
            content: 'content',
            actions: 'actions',
            active: 'active',
            animating: 'animating',
            blurring: 'blurring',
            closing: 'closing',
            dimmed: 'dimmed',
            locked: 'locked',
            pushable: 'pushable',
            pushed: 'pushed',
            right: 'right',
            top: 'top',
            left: 'left',
            bottom: 'bottom',
            visible: 'visible',
            overlay: 'overlay',
            fullscreen: 'fullscreen',
            template: 'ui flyout',
            button: 'ui button',
            ok: 'positive',
            cancel: 'negative',
            prompt: 'ui fluid input',
        },

        selector: {
            bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar, > .ui.right.flyout, > .ui.fixed.nag, > .ui.fixed.nag > .close',
            fixed: '.fixed',
            omitted: 'script, link, style, .ui.modal, .ui.dimmer, .ui.nag, .ui.fixed',
            pusher: '.pusher',
            flyout: '.ui.flyout',
            header: '.ui.header',
            content: '.content',
            actions: '.actions',
            close: '.close',
            approve: '.actions .positive, .actions .approve, .actions .ok',
            deny: '.actions .negative, .actions .deny, .actions .cancel',
        },

        regExp: {
            mobile: /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/g,
        },

        error: {
            method: 'The method you called is not defined.',
            pusher: 'Had to add pusher element. For optimal performance make sure body content is inside a pusher element',
            movedFlyout: 'Had to move flyout. For optimal performance make sure flyout and pusher are direct children of your body tag',
            notFound: 'There were no elements that matched the specified selector',
        },

        text: {
            ok: 'Ok',
            cancel: 'Cancel',
            close: 'Close',
        },
    };

    $.fn.flyout.settings.templates = {
        getArguments: function (args) {
            var queryArguments = [].slice.call(args);
            if ($.isPlainObject(queryArguments[0])) {
                return $.extend({
                    handler: function () {},
                    content: '',
                    title: '',
                }, queryArguments[0]);
            }
            if (!isFunction(queryArguments[queryArguments.length - 1])) {
                queryArguments.push(function () {});
            }

            return {
                handler: queryArguments.pop(),
                content: queryArguments.pop() || '',
                title: queryArguments.pop() || '',
            };
        },
        alert: function () {
            var
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments)
            ;

            return {
                title: args.title,
                content: args.content,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: args.handler,
                }],
            };
        },
        confirm: function () {
            var
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments)
            ;

            return {
                title: args.title,
                content: args.content,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: function () {
                        args.handler(true);
                    },
                }, {
                    text: settings.text.cancel,
                    class: settings.className.cancel,
                    click: function () {
                        args.handler(false);
                    },
                }],
            };
        },
        prompt: function () {
            var
                $this    = this,
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments),
                input    = $($.parseHTML(args.content)).filter('.ui.input')
            ;
            if (input.length === 0) {
                args.content += '<p><div class="' + settings.className.prompt + '"><input placeholder="' + this.helpers.deQuote(args.placeholder || '') + '" type="text" value="' + this.helpers.deQuote(args.defaultValue || '') + '"></div></p>';
            }

            return {
                title: args.title,
                content: args.content,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: function () {
                        var
                            settings = $this.get.settings(),
                            inputField = $this.get.element().find(settings.selector.prompt)[0]
                        ;
                        args.handler($(inputField).val());
                    },
                }, {
                    text: settings.text.cancel,
                    class: settings.className.cancel,
                    click: function () {
                        args.handler(null);
                    },
                }],
            };
        },
    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Modal
 * 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.modal = function (parameters) {
        var
            $allModules    = $(this),
            $window        = $(window),
            $document      = $(document),
            $body          = $('body'),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $body;
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },
            returnedValue
        ;

        $allModules.each(function () {
            var
                settings    = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.modal.settings, parameters)
                    : $.extend({}, $.fn.modal.settings),

                selector        = settings.selector,
                className       = settings.className,
                namespace       = settings.namespace,
                fields          = settings.fields,
                error           = settings.error,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $context        = contextCheck(settings.context, window),
                isBody          = $context[0] === $body[0],
                $closeIcon      = $module.find(selector.closeIcon),
                $inputs,

                $allModals,
                $otherModals,
                $focusedElement,
                $dimmable,
                $dimmer,

                isModalComponent = $module.hasClass('modal'),

                element         = this,
                instance        = isModalComponent ? $module.data(moduleNamespace) : undefined,

                ignoreRepeatedEvents = false,

                initialMouseDownInModal,
                initialMouseDownInScrollbar,
                initialBodyMargin = '',
                tempBodyMargin = '',
                keepScrollingClass = false,
                hadScrollbar = false,
                windowRefocused = false,

                elementEventNamespace,
                id,
                observer,
                observeAttributes = false,
                module
            ;
            module = {

                initialize: function () {
                    module.create.id();
                    if (!isModalComponent) {
                        module.create.modal();
                        if (!isFunction(settings.onHidden)) {
                            settings.onHidden = function () {
                                module.destroy();
                                $module.remove();
                            };
                        }
                    }
                    $module.addClass(settings.class);
                    if (settings.title !== '') {
                        $module.find(selector.title).html(module.helpers.escape(settings.title, settings.preserveHTML)).addClass(settings.classTitle);
                    }
                    if (settings.content !== '') {
                        $module.find(selector.content).html(module.helpers.escape(settings.content, settings.preserveHTML)).addClass(settings.classContent);
                    }
                    if (module.has.configActions()) {
                        var $actions = $module.find(selector.actions).addClass(settings.classActions);
                        if ($actions.length === 0) {
                            $actions = $('<div/>', { class: className.actions + ' ' + (settings.classActions || '') }).appendTo($module);
                        } else {
                            $actions.empty();
                        }
                        settings.actions.forEach(function (el) {
                            var
                                icon = el[fields.icon]
                                    ? '<i ' + (el[fields.text] ? 'aria-hidden="true"' : '') + ' class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>'
                                    : '',
                                text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
                                cls = module.helpers.deQuote(el[fields.class] || ''),
                                click = el[fields.click] && isFunction(el[fields.click])
                                    ? el[fields.click]
                                    : function () {}
                            ;
                            $actions.append($('<button/>', {
                                html: icon + text,
                                'aria-label': (el[fields.text] || el[fields.icon] || '').replace(/<[^>]+(>|$)/g, ''),
                                class: className.button + ' ' + cls,
                                on: {
                                    click: function () {
                                        var button = $(this);
                                        if (button.is(selector.approve) || button.is(selector.deny) || click.call(element, $module) === false) {
                                            return;
                                        }
                                        module.hide();
                                    },
                                },
                            }));
                        });
                    }
                    module.cache = {};
                    module.verbose('Initializing dimmer', $context);

                    module.create.dimmer();

                    if (settings.allowMultiple) {
                        module.create.innerDimmer();
                    }
                    if (!settings.centered) {
                        $module.addClass('top aligned');
                    }
                    module.refreshModals();
                    module.bind.events();
                    module.observeChanges();
                    module.instantiate();
                    if (settings.autoShow) {
                        module.show();
                    }
                },

                instantiate: function () {
                    module.verbose('Storing instance of modal');
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                create: {
                    modal: function () {
                        $module = $('<div/>', { class: className.modal, role: 'dialog', 'aria-modal': true });
                        if (settings.closeIcon) {
                            $closeIcon = $('<i/>', {
                                class: className.close,
                                role: 'button',
                                tabindex: 0,
                                'aria-label': settings.text.close,
                            });
                            $module.append($closeIcon);
                        }
                        if (settings.title !== '') {
                            var titleId = '_' + module.get.id() + 'title';
                            $module.attr('aria-labelledby', titleId);
                            $('<div/>', { class: className.title, id: titleId }).appendTo($module);
                        }
                        if (settings.content !== '') {
                            var descId = '_' + module.get.id() + 'desc';
                            $module.attr('aria-describedby', descId);
                            $('<div/>', { class: className.content, id: descId }).appendTo($module);
                        }
                        if (module.has.configActions()) {
                            $('<div/>', { class: className.actions }).appendTo($module);
                        }
                        $context.append($module);
                        element = $module[0];
                    },
                    dimmer: function () {
                        var
                            defaultSettings = {
                                debug: settings.debug,
                                dimmerName: 'modals',
                            },
                            dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
                        ;
                        if ($.fn.dimmer === undefined) {
                            module.error(error.dimmer);

                            return;
                        }
                        module.debug('Creating dimmer');
                        $dimmable = $context.dimmer(dimmerSettings);
                        keepScrollingClass = module.is.scrolling();
                        if (settings.detachable) {
                            module.verbose('Modal is detachable, moving content into dimmer');
                            $dimmable.dimmer('add content', $module);
                        } else {
                            module.set.undetached();
                        }
                        $dimmer = $dimmable.dimmer('get dimmer');
                    },
                    id: function () {
                        id = (Math.random().toString(16) + '000000000').slice(2, 10);
                        elementEventNamespace = '.' + id;
                        module.verbose('Creating unique id for element', id);
                    },
                    innerDimmer: function () {
                        if ($module.find(selector.dimmer).length === 0) {
                            $('<div/>', { class: className.innerDimmer }).prependTo($module);
                        }
                    },
                },

                destroy: function () {
                    if (observer) {
                        observer.disconnect();
                    }
                    module.verbose('Destroying previous modal');
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                    $window.off(elementEventNamespace);
                    $context.off(elementEventNamespace);
                    $dimmer.off(elementEventNamespace);
                    $closeIcon.off(elementEventNamespace);
                    if ($inputs) {
                        $inputs.off(elementEventNamespace);
                    }
                    $context.dimmer('destroy');
                },

                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        observer = new MutationObserver(function (mutations) {
                            var collectNodes = function (parent) {
                                    var nodes = [];
                                    for (var c = 0, cl = parent.length; c < cl; c++) {
                                        Array.prototype.push.apply(nodes, collectNodes(parent[c].childNodes));
                                        nodes.push(parent[c]);
                                    }

                                    return nodes;
                                },
                                shouldRefresh = false,
                                shouldRefreshInputs = false,
                                ignoreAutofocus = true
                            ;
                            mutations.every(function (mutation) {
                                if (mutation.type === 'attributes') {
                                    if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
                                        shouldRefreshInputs = true;
                                    }
                                } else {
                                    shouldRefresh = true;
                                    // mutationobserver only provides the parent nodes
                                    // so let's collect all childs as well to find nested inputs
                                    var $addedInputs = $(collectNodes(mutation.addedNodes)).filter('a[href], [tabindex], :input:enabled').filter(':visible'),
                                        $removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
                                    if ($addedInputs.length > 0 || $removedInputs.length > 0) {
                                        shouldRefreshInputs = true;
                                        if ($addedInputs.filter(':input').length > 0 || $removedInputs.filter(':input').length > 0) {
                                            ignoreAutofocus = false;
                                        }
                                    }
                                }

                                return !shouldRefreshInputs;
                            });

                            if (shouldRefresh && settings.observeChanges) {
                                module.debug('DOM tree modified, refreshing');
                                module.refresh();
                            }
                            if (shouldRefreshInputs) {
                                module.refreshInputs(ignoreAutofocus);
                            }
                        });
                        observer.observe(element, {
                            attributeFilter: ['class', 'disabled'],
                            attributes: true,
                            childList: true,
                            subtree: true,
                        });
                        module.debug('Setting up mutation observer', observer);
                    }
                },

                refresh: function () {
                    module.remove.scrolling();
                    module.cacheSizes();
                    if (!module.can.useFlex()) {
                        module.set.modalOffset();
                    }
                    module.set.screenHeight();
                    module.set.type();
                },

                refreshModals: function () {
                    $otherModals = $module.siblings(selector.modal);
                    $allModals = $otherModals.add($module);
                },

                refreshInputs: function (ignoreAutofocus) {
                    if ($inputs) {
                        $inputs
                            .off('keydown' + elementEventNamespace)
                        ;
                    }
                    $inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
                        return $(this).closest('.disabled').length === 0;
                    });
                    if ($inputs.filter(':input').length === 0) {
                        $inputs = $module.add($inputs);
                        $module.attr('tabindex', -1);
                    } else {
                        $module.removeAttr('tabindex');
                    }
                    $inputs.first()
                        .on('keydown' + elementEventNamespace, module.event.inputKeyDown.first)
                    ;
                    $inputs.last()
                        .on('keydown' + elementEventNamespace, module.event.inputKeyDown.last)
                    ;
                    if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
                        module.set.autofocus();
                    }
                },

                attachEvents: function (selector, event) {
                    var
                        $toggle = $(selector)
                    ;
                    event = isFunction(module[event])
                        ? module[event]
                        : module.toggle;
                    if ($toggle.length > 0) {
                        module.debug('Attaching modal events to element', selector, event);
                        $toggle
                            .off(eventNamespace)
                            .on('click' + eventNamespace, event)
                        ;
                    } else {
                        module.error(error.notFound, selector);
                    }
                },

                bind: {
                    events: function () {
                        module.verbose('Attaching events');
                        $module
                            .on('click' + eventNamespace, selector.close, module.event.close)
                            .on('click' + eventNamespace, selector.approve, module.event.approve)
                            .on('click' + eventNamespace, selector.deny, module.event.deny)
                        ;
                        $closeIcon
                            .on('keyup' + elementEventNamespace, module.event.closeKeyUp)
                        ;
                        $window
                            .on('resize' + elementEventNamespace, module.event.resize)
                            .on('focus' + elementEventNamespace, module.event.focus)
                        ;
                        $context
                            .on('click' + elementEventNamespace, module.event.click)
                        ;
                    },
                    scrollLock: function () {
                        // touch events default to passive, due to changes in chrome to optimize mobile perf
                        $dimmable[0].addEventListener('touchmove', module.event.preventScroll, { passive: false });
                    },
                },

                unbind: {
                    scrollLock: function () {
                        $dimmable[0].removeEventListener('touchmove', module.event.preventScroll, { passive: false });
                    },
                },

                get: {
                    id: function () {
                        return id;
                    },
                    element: function () {
                        return $module;
                    },
                    settings: function () {
                        return settings;
                    },
                },

                event: {
                    approve: function () {
                        if (ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
                            module.verbose('Approve callback returned false cancelling hide');

                            return;
                        }
                        ignoreRepeatedEvents = true;
                        module.hide(function () {
                            ignoreRepeatedEvents = false;
                        });
                    },
                    preventScroll: function (event) {
                        if (event.target.className.indexOf('dimmer') !== -1) {
                            event.preventDefault();
                        }
                    },
                    deny: function () {
                        if (ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
                            module.verbose('Deny callback returned false cancelling hide');

                            return;
                        }
                        ignoreRepeatedEvents = true;
                        module.hide(function () {
                            ignoreRepeatedEvents = false;
                        });
                    },
                    close: function () {
                        module.hide();
                    },
                    closeKeyUp: function (event) {
                        var
                            keyCode   = event.which
                        ;
                        if ((keyCode === settings.keys.enter || keyCode === settings.keys.space) && $module.hasClass(className.front)) {
                            module.hide();
                        }
                    },
                    inputKeyDown: {
                        first: function (event) {
                            var
                                keyCode = event.which
                            ;
                            if (keyCode === settings.keys.tab && event.shiftKey) {
                                $inputs.last().trigger('focus');
                                event.preventDefault();
                            }
                        },
                        last: function (event) {
                            var
                                keyCode = event.which
                            ;
                            if (keyCode === settings.keys.tab && !event.shiftKey) {
                                $inputs.first().trigger('focus');
                                event.preventDefault();
                            }
                        },
                    },
                    mousedown: function (event) {
                        var
                            $target   = $(event.target),
                            isRtl = module.is.rtl()
                        ;
                        initialMouseDownInModal = $target.closest(selector.modal).length > 0;
                        if (initialMouseDownInModal) {
                            module.verbose('Mouse down event registered inside the modal');
                        }
                        initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $window.outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
                        if (initialMouseDownInScrollbar) {
                            module.verbose('Mouse down event registered inside the scrollbar');
                        }
                    },
                    mouseup: function (event) {
                        if (!settings.closable) {
                            module.verbose('Dimmer clicked but closable setting is disabled');

                            return;
                        }
                        if (initialMouseDownInModal) {
                            module.debug('Dimmer clicked but mouse down was initially registered inside the modal');

                            return;
                        }
                        if (initialMouseDownInScrollbar) {
                            module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');

                            return;
                        }
                        var
                            $target   = $(event.target),
                            isInModal = $target.closest(selector.modal).length > 0,
                            isInDOM   = $.contains(document.documentElement, event.target)
                        ;
                        if (!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front)) {
                            module.debug('Dimmer clicked, hiding all modals');
                            if (settings.allowMultiple) {
                                if (!module.hideAll()) {
                                    return;
                                }
                            } else if (!module.hide()) {
                                return;
                            }
                            module.remove.clickaway();
                        }
                    },
                    debounce: function (method, delay) {
                        clearTimeout(module.timer);
                        module.timer = setTimeout(function () { method(); }, delay);
                    },
                    keyboard: function (event) {
                        var
                            keyCode   = event.which
                        ;
                        if (keyCode === settings.keys.escape) {
                            if (settings.closable) {
                                module.debug('Escape key pressed hiding modal');
                                if ($module.hasClass(className.front)) {
                                    module.hide();
                                }
                            } else {
                                module.debug('Escape key pressed, but closable is set to false');
                            }
                            event.preventDefault();
                        }
                    },
                    resize: function () {
                        if ($dimmable.dimmer('is active') && (module.is.animating() || module.is.active())) {
                            requestAnimationFrame(module.refresh);
                        }
                    },
                    focus: function () {
                        windowRefocused = true;
                    },
                    click: function (event) {
                        if (windowRefocused && document.activeElement !== event.target && $dimmable.dimmer('is active') && module.is.active() && settings.autofocus && $(document.activeElement).closest(selector.modal).length === 0) {
                            requestAnimationFrame(module.set.autofocus);
                        }
                        windowRefocused = false;
                    },
                },

                toggle: function () {
                    if (module.is.active() || module.is.animating()) {
                        module.hide();
                    } else {
                        module.show();
                    }
                },

                show: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    module.refreshModals();
                    module.set.dimmerSettings();
                    module.set.dimmerStyles();

                    module.showModal(callback);
                },

                hide: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    module.refreshModals();

                    return module.hideModal(callback);
                },

                showModal: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.animating() || !module.is.active()) {
                        if (settings.onShow.call(element) === false) {
                            module.verbose('Show callback returned false cancelling show');

                            return;
                        }
                        hadScrollbar = module.has.scrollbar();
                        module.showDimmer();
                        module.cacheSizes();
                        if (hadScrollbar) {
                            module.set.bodyMargin();
                        }
                        if (module.can.useFlex()) {
                            module.remove.legacy();
                        } else {
                            module.set.legacy();
                            module.set.modalOffset();
                            module.debug('Using non-flex legacy modal positioning.');
                        }
                        module.set.screenHeight();
                        module.set.type();
                        module.set.clickaway();

                        if (!settings.allowMultiple && module.others.active()) {
                            module.hideOthers(module.showModal);
                        } else {
                            ignoreRepeatedEvents = false;
                            if (settings.allowMultiple) {
                                if (module.others.active()) {
                                    $otherModals.filter('.' + className.active).find(selector.dimmer).removeClass('out').addClass('transition fade in active');
                                }

                                if (settings.detachable) {
                                    $module.detach().appendTo($dimmer);
                                }
                            }
                            if (settings.transition && $.fn.transition !== undefined) {
                                module.debug('Showing modal with css animations');
                                module.set.observeAttributes(false);
                                $module
                                    .transition({
                                        debug: settings.debug,
                                        verbose: settings.verbose,
                                        silent: settings.silent,
                                        animation: (settings.transition.showMethod || settings.transition) + ' in',
                                        queue: settings.queue,
                                        duration: settings.transition.showDuration || settings.duration,
                                        useFailSafe: true,
                                        onComplete: function () {
                                            settings.onVisible.apply(element);
                                            if (settings.keyboardShortcuts) {
                                                module.add.keyboardShortcuts();
                                            }
                                            module.save.focus();
                                            module.set.active();
                                            module.refreshInputs();
                                            requestAnimationFrame(module.set.observeAttributes);
                                            callback();
                                        },
                                    })
                                ;
                            } else {
                                module.error(error.noTransition);
                            }
                        }
                    } else {
                        module.debug('Modal is already visible');
                    }
                },

                hideModal: function (callback, keepDimmed, hideOthersToo) {
                    var
                        $previousModal = $otherModals.filter('.' + className.active).last()
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (settings.onHide.call(element, $(this)) === false) {
                        module.verbose('Hide callback returned false cancelling hide');
                        ignoreRepeatedEvents = false;

                        return false;
                    }

                    if (module.is.animating() || module.is.active()) {
                        module.debug('Hiding modal');
                        if (settings.transition && $.fn.transition !== undefined) {
                            module.remove.active();
                            module.set.observeAttributes(false);
                            $module
                                .transition({
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    animation: (settings.transition.hideMethod || settings.transition) + ' out',
                                    queue: settings.queue,
                                    duration: settings.transition.hideDuration || settings.duration,
                                    useFailSafe: true,
                                    onStart: function () {
                                        if (!module.others.active() && !module.others.animating() && !keepDimmed) {
                                            module.hideDimmer();
                                        } else if (settings.allowMultiple) {
                                            (hideOthersToo ? $allModals : $previousModal).find(selector.dimmer).removeClass('in').addClass('out');
                                        }
                                        if (settings.keyboardShortcuts && !module.others.active()) {
                                            module.remove.keyboardShortcuts();
                                        }
                                    },
                                    onComplete: function () {
                                        module.unbind.scrollLock();
                                        module.remove.active();
                                        if (settings.allowMultiple) {
                                            $previousModal.addClass(className.front);
                                            $module.removeClass(className.front);

                                            (hideOthersToo ? $allModals : $previousModal).find(selector.dimmer).removeClass('active');
                                        }
                                        if (isFunction(settings.onHidden)) {
                                            settings.onHidden.call(element);
                                        }
                                        module.remove.dimmerStyles();
                                        module.restore.focus();
                                        callback();
                                    },
                                })
                            ;
                        } else {
                            module.error(error.noTransition);
                        }
                    }
                },

                showDimmer: function () {
                    if ($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active')) {
                        if (hadScrollbar) {
                            if (!isBody) {
                                $dimmer.css('top', $dimmable.scrollTop());
                            }
                            module.save.bodyMargin();
                        }
                        module.debug('Showing dimmer');
                        $dimmable.dimmer('show');
                    } else {
                        module.debug('Dimmer already visible');
                    }
                },

                hideDimmer: function () {
                    if ($dimmable.dimmer('is animating') || $dimmable.dimmer('is active')) {
                        module.unbind.scrollLock();
                        $dimmable.dimmer('hide', function () {
                            if (hadScrollbar) {
                                module.restore.bodyMargin();
                            }
                            module.remove.clickaway();
                            module.remove.screenHeight();
                        });
                    } else {
                        module.debug('Dimmer is not visible cannot hide');
                    }
                },

                hideAll: function (callback) {
                    var
                        $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if ($visibleModals.length > 0) {
                        module.debug('Hiding all visible modals');
                        var hideOk = true;
                        // check in reverse order trying to hide most top displayed modal first
                        $($visibleModals.get().reverse()).each(function (index, element) {
                            if (hideOk) {
                                hideOk = $(element).modal('hide modal', callback, false, true);
                            }
                        });
                        if (hideOk) {
                            module.hideDimmer();
                        }

                        return hideOk;
                    }
                },

                hideOthers: function (callback) {
                    var
                        $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if ($visibleModals.length > 0) {
                        module.debug('Hiding other modals', $otherModals);
                        $visibleModals
                            .modal('hide modal', callback, true)
                        ;
                    }
                },

                others: {
                    active: function () {
                        return $otherModals.filter('.' + className.active).length > 0;
                    },
                    animating: function () {
                        return $otherModals.filter('.' + className.animating).length > 0;
                    },
                },

                add: {
                    keyboardShortcuts: function () {
                        module.verbose('Adding keyboard shortcuts');
                        $document
                            .on('keydown' + eventNamespace, module.event.keyboard)
                        ;
                    },
                },

                save: {
                    focus: function () {
                        var
                            $activeElement = $(document.activeElement),
                            inCurrentModal = $activeElement.closest($module).length > 0
                        ;
                        if (!inCurrentModal) {
                            $focusedElement = $(document.activeElement).trigger('blur');
                        }
                    },
                    bodyMargin: function () {
                        initialBodyMargin = $context.css((isBody ? 'margin-' : 'padding-') + (module.can.leftBodyScrollbar() ? 'left' : 'right'));
                        var
                            bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, ''), 10),
                            bodyScrollbarWidth = isBody ? window.innerWidth - document.documentElement.clientWidth : $context[0].offsetWidth - $context[0].clientWidth
                        ;
                        tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
                    },
                },

                restore: {
                    focus: function () {
                        if ($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
                            $focusedElement.trigger('focus');
                        }
                    },
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        $context.css((isBody ? 'margin-' : 'padding-') + position, initialBodyMargin);
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, '');
                        });
                    },
                },

                remove: {
                    active: function () {
                        $module.removeClass(className.active);
                    },
                    legacy: function () {
                        $module.removeClass(className.legacy);
                    },
                    clickaway: function () {
                        if (!settings.detachable) {
                            $module
                                .off('mousedown' + elementEventNamespace)
                            ;
                        }
                        $dimmer
                            .off('mousedown' + elementEventNamespace)
                        ;
                        $dimmer
                            .off('mouseup' + elementEventNamespace)
                        ;
                    },
                    dimmerStyles: function () {
                        $dimmer.removeClass(className.inverted);
                        $dimmable.removeClass(className.blurring);
                    },
                    bodyStyle: function () {
                        if ($context.attr('style') === '') {
                            module.verbose('Removing style attribute');
                            $context.removeAttr('style');
                        }
                    },
                    screenHeight: function () {
                        module.debug('Removing page height');
                        $context
                            .css('height', '')
                        ;
                        module.remove.bodyStyle();
                    },
                    keyboardShortcuts: function () {
                        module.verbose('Removing keyboard shortcuts');
                        $document
                            .off('keydown' + eventNamespace)
                        ;
                    },
                    scrolling: function () {
                        if (!keepScrollingClass) {
                            $dimmable.removeClass(className.scrolling);
                        }
                        $module.removeClass(className.scrolling);
                    },
                },

                cacheSizes: function () {
                    $module.addClass(className.loading);
                    var
                        scrollHeight = $module.prop('scrollHeight'),
                        modalWidth   = $module.outerWidth(),
                        modalHeight  = $module.outerHeight()
                    ;
                    if (module.cache.pageHeight === undefined || modalHeight !== 0) {
                        $.extend(module.cache, {
                            pageHeight: $document.outerHeight(),
                            width: modalWidth,
                            height: modalHeight + settings.offset,
                            scrollHeight: scrollHeight + settings.offset,
                            contextHeight: isBody
                                ? $window.height()
                                : $dimmable.height(),
                        });
                        module.cache.topOffset = -(module.cache.height / 2);
                    }
                    $module.removeClass(className.loading);
                    module.debug('Caching modal and container sizes', module.cache);
                },
                helpers: {
                    deQuote: function (string) {
                        return String(string).replace(/"/g, '');
                    },
                    escape: function (string, preserveHTML) {
                        if (preserveHTML) {
                            return string;
                        }
                        var
                            badChars     = /["'<>`]/g,
                            shouldEscape = /["&'<>`]/,
                            escape       = {
                                '<': '&lt;',
                                '>': '&gt;',
                                '"': '&quot;',
                                "'": '&#x27;',
                                '`': '&#x60;',
                            },
                            escapedChar  = function (chr) {
                                return escape[chr];
                            }
                        ;
                        if (shouldEscape.test(string)) {
                            string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                            return string.replace(badChars, escapedChar);
                        }

                        return string;
                    },
                },
                can: {
                    leftBodyScrollbar: function () {
                        if (module.cache.leftBodyScrollbar === undefined) {
                            module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
                        }

                        return module.cache.leftBodyScrollbar;
                    },
                    useFlex: function () {
                        if (settings.useFlex === 'auto') {
                            return settings.detachable && !module.is.ie();
                        }
                        if (settings.useFlex && module.is.ie()) {
                            module.debug('useFlex true is not supported in IE');
                        } else if (settings.useFlex && !settings.detachable) {
                            module.debug('useFlex true in combination with detachable false is not supported');
                        }

                        return settings.useFlex;
                    },
                    fit: function () {
                        var
                            contextHeight  = module.cache.contextHeight,
                            verticalCenter = module.cache.contextHeight / 2,
                            topOffset      = module.cache.topOffset,
                            scrollHeight   = module.cache.scrollHeight,
                            height         = module.cache.height,
                            paddingHeight  = settings.padding,
                            startPosition  = verticalCenter + topOffset
                        ;

                        return scrollHeight > height
                            ? startPosition + scrollHeight + paddingHeight < contextHeight
                            : height + (paddingHeight * 2) < contextHeight;
                    },
                },
                has: {
                    configActions: function () {
                        return Array.isArray(settings.actions) && settings.actions.length > 0;
                    },
                    scrollbar: function () {
                        return isBody || $context.css('overflow-y') !== 'hidden';
                    },
                },
                is: {
                    active: function () {
                        return $module.hasClass(className.active);
                    },
                    ie: function () {
                        if (module.cache.isIE === undefined) {
                            var
                                isIE11 = !window.ActiveXObject && 'ActiveXObject' in window,
                                isIE = 'ActiveXObject' in window
                            ;
                            module.cache.isIE = isIE11 || isIE;
                        }

                        return module.cache.isIE;
                    },
                    animating: function () {
                        return $module.transition('is animating');
                    },
                    scrolling: function () {
                        return $dimmable.hasClass(className.scrolling);
                    },
                    modernBrowser: function () {
                        // appName for IE11 reports 'Netscape' can no longer use
                        return !(window.ActiveXObject || 'ActiveXObject' in window);
                    },
                    rtl: function () {
                        if (module.cache.isRTL === undefined) {
                            module.cache.isRTL = $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl' || $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl' || $context.attr('dir') === 'rtl' || $context.css('direction') === 'rtl';
                        }

                        return module.cache.isRTL;
                    },
                    safari: function () {
                        if (module.cache.isSafari === undefined) {
                            module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
                        }

                        return module.cache.isSafari;
                    },
                    edge: function () {
                        if (module.cache.isEdge === undefined) {
                            module.cache.isEdge = !!window.setImmediate && !module.is.ie();
                        }

                        return module.cache.isEdge;
                    },
                    firefox: function () {
                        if (module.cache.isFirefox === undefined) {
                            module.cache.isFirefox = !!window.InstallTrigger;
                        }

                        return module.cache.isFirefox;
                    },
                    iframe: function () {
                        return !(self === top);
                    },
                },

                set: {
                    observeAttributes: function (state) {
                        observeAttributes = state !== false;
                    },
                    autofocus: function () {
                        var
                            $autofocus = $inputs.filter('[autofocus]'),
                            $rawInputs = $inputs.filter(':input'),
                            $input     = ($autofocus.length > 0
                                ? $autofocus
                                : ($rawInputs.length > 0
                                    ? $rawInputs
                                    : $module)
                            ).first()
                        ;
                        $input.trigger('focus');
                    },
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        if (settings.detachable || module.can.fit()) {
                            $context.css((isBody ? 'margin-' : 'padding-') + position, tempBodyMargin + 'px');
                        }
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, 'calc(' + el.css(attribute) + ' + ' + tempBodyMargin + 'px)');
                        });
                    },
                    clickaway: function () {
                        if (!settings.detachable) {
                            $module
                                .on('mousedown' + elementEventNamespace, module.event.mousedown)
                            ;
                        }
                        $dimmer
                            .on('mousedown' + elementEventNamespace, module.event.mousedown)
                        ;
                        $dimmer
                            .on('mouseup' + elementEventNamespace, module.event.mouseup)
                        ;
                    },
                    dimmerSettings: function () {
                        if ($.fn.dimmer === undefined) {
                            module.error(error.dimmer);

                            return;
                        }
                        var
                            defaultSettings = {
                                debug: settings.debug,
                                dimmerName: 'modals',
                                closable: 'auto',
                                useFlex: module.can.useFlex(),
                                duration: {
                                    show: settings.transition.showDuration || settings.duration,
                                    hide: settings.transition.hideDuration || settings.duration,
                                },
                            },
                            dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
                        ;
                        if (settings.inverted) {
                            dimmerSettings.variation = dimmerSettings.variation !== undefined
                                ? dimmerSettings.variation + ' inverted'
                                : 'inverted';
                        }
                        $context.dimmer('setting', dimmerSettings);
                    },
                    dimmerStyles: function () {
                        if (settings.inverted) {
                            $dimmer.addClass(className.inverted);
                        } else {
                            $dimmer.removeClass(className.inverted);
                        }
                        if (settings.blurring) {
                            $dimmable.addClass(className.blurring);
                        } else {
                            $dimmable.removeClass(className.blurring);
                        }
                    },
                    modalOffset: function () {
                        if (!settings.detachable) {
                            var canFit = module.can.fit();
                            $module
                                .css({
                                    top: !$module.hasClass('aligned') && canFit
                                        ? $document.scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
                                        : (!canFit || $module.hasClass('top')
                                            ? $document.scrollTop() + settings.padding
                                            : $document.scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding)),
                                    marginLeft: -(module.cache.width / 2),
                                })
                            ;
                        } else {
                            $module
                                .css({
                                    marginTop: !$module.hasClass('aligned') && module.can.fit()
                                        ? -(module.cache.height / 2)
                                        : settings.padding / 2,
                                    marginLeft: -(module.cache.width / 2),
                                })
                            ;
                        }
                        module.verbose('Setting modal offset for legacy mode');
                    },
                    screenHeight: function () {
                        if (module.can.fit()) {
                            $context.css('height', '');
                        } else if (!$module.hasClass('bottom')) {
                            module.debug('Modal is taller than page content, resizing page height');
                            $context
                                .css('height', module.cache.height + (settings.padding * 2) + 'px')
                            ;
                        }
                    },
                    active: function () {
                        $module.addClass(className.active + ' ' + className.front);
                        $otherModals.filter('.' + className.active).removeClass(className.front);
                    },
                    scrolling: function () {
                        $dimmable.addClass(className.scrolling);
                        $module.addClass(className.scrolling);
                        module.unbind.scrollLock();
                    },
                    legacy: function () {
                        $module.addClass(className.legacy);
                    },
                    type: function () {
                        if (module.can.fit()) {
                            module.verbose('Modal fits on screen');
                            if (!module.others.active() && !module.others.animating()) {
                                module.remove.scrolling();
                                module.bind.scrollLock();
                            }
                        } else if (!$module.hasClass('bottom')) {
                            module.verbose('Modal cannot fit on screen setting to scrolling');
                            module.set.scrolling();
                        } else {
                            module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
                        }
                    },
                    undetached: function () {
                        $dimmable.addClass(className.undetached);
                    },
                },

                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) {
                    if (isFunction(settings.templates[query])) {
                        settings.autoShow = true;
                        settings.className.modal = settings.className.template;
                        settings = $.extend(true, {}, settings, settings.templates[query].apply(module, queryArguments));

                        // reassign shortcuts
                        className = settings.className;
                        namespace = settings.namespace;
                        fields = settings.fields;
                        error = settings.error;
                    }
                    module.initialize();
                }
                if (!isFunction(settings.templates[query])) {
                    module.invoke(query);
                }
            } else {
                if (instance !== undefined) {
                    instance.invoke('destroy');
                }
                module.initialize();
                returnedValue = $module;
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };
    $.modal = $.fn.modal;

    $.fn.modal.settings = {

        name: 'Modal',
        namespace: 'modal',

        useFlex: 'auto',
        offset: 0,

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        observeChanges: false,

        allowMultiple: false,
        detachable: true,
        closable: true,
        autofocus: true,
        restoreFocus: true,
        autoShow: false,

        inverted: false,
        blurring: false,

        centered: true,

        dimmerSettings: {
            closable: false,
            useCSS: true,
        },

        // whether to use keyboard shortcuts
        keyboardShortcuts: true,

        context: 'body',

        queue: false,
        duration: 500,
        transition: 'scale',

        // padding with edge of page
        padding: 50,
        scrollbarWidth: 10,

        // dynamic content
        title: '',
        content: '',
        class: '',
        classTitle: '',
        classContent: '',
        classActions: '',
        closeIcon: false,
        actions: false,
        preserveHTML: true,

        fields: {
            class: 'class',
            text: 'text',
            icon: 'icon',
            click: 'click',
        },

        // called before show animation
        onShow: function () {},

        // called after show animation
        onVisible: function () {},

        // called before hide animation
        onHide: function () {
            return true;
        },

        // called after hide animation
        onHidden: false,

        // called after approve selector match
        onApprove: function () {
            return true;
        },

        // called after deny selector match
        onDeny: function () {
            return true;
        },

        keys: {
            space: 32,
            enter: 13,
            escape: 27,
            tab: 9,
        },

        selector: {
            title: '> .header',
            content: '> .content',
            actions: '> .actions',
            close: '> .close',
            closeIcon: '> .close',
            approve: '.actions .positive, .actions .approve, .actions .ok',
            deny: '.actions .negative, .actions .deny, .actions .cancel',
            modal: '.ui.modal',
            dimmer: '> .ui.dimmer',
            bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar, > .ui.fixed.nag, > .ui.fixed.nag > .close',
            prompt: '.ui.input > input',
        },
        error: {
            dimmer: 'UI Dimmer, a required component is not included in this page',
            method: 'The method you called is not defined.',
            notFound: 'The element you specified could not be found',
        },
        className: {
            active: 'active',
            animating: 'animating',
            blurring: 'blurring',
            inverted: 'inverted',
            legacy: 'legacy',
            loading: 'loading',
            scrolling: 'scrolling',
            undetached: 'undetached',
            front: 'front',
            close: 'close icon',
            button: 'ui button',
            modal: 'ui modal',
            title: 'header',
            content: 'content',
            actions: 'actions',
            template: 'ui tiny modal',
            ok: 'positive',
            cancel: 'negative',
            prompt: 'ui fluid input',
            innerDimmer: 'ui inverted dimmer',
        },
        text: {
            ok: 'Ok',
            cancel: 'Cancel',
            close: 'Close',
        },
    };

    $.fn.modal.settings.templates = {
        getArguments: function (args) {
            var queryArguments = [].slice.call(args);
            if ($.isPlainObject(queryArguments[0])) {
                return $.extend({
                    handler: function () {},
                    content: '',
                    title: '',
                }, queryArguments[0]);
            }
            if (!isFunction(queryArguments[queryArguments.length - 1])) {
                queryArguments.push(function () {});
            }

            return {
                handler: queryArguments.pop(),
                content: queryArguments.pop() || '',
                title: queryArguments.pop() || '',
            };
        },
        alert: function () {
            var
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments),
                approveFn = args.handler
            ;

            return {
                title: args.title,
                content: args.content,
                onApprove: approveFn,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: approveFn,
                }],
            };
        },
        confirm: function () {
            var
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments),
                approveFn = function () {
                    args.handler(true);
                },
                denyFn = function () {
                    args.handler(false);
                }
            ;

            return {
                title: args.title,
                content: args.content,
                onApprove: approveFn,
                onDeny: denyFn,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: approveFn,
                }, {
                    text: settings.text.cancel,
                    class: settings.className.cancel,
                    click: denyFn,
                }],
            };
        },
        prompt: function () {
            var
                $this    = this,
                settings = this.get.settings(),
                args     = settings.templates.getArguments(arguments),
                input    = $($.parseHTML(args.content)).filter('.ui.input'),
                approveFn = function () {
                    var
                        settings = $this.get.settings(),
                        inputField = $this.get.element().find(settings.selector.prompt)[0]
                    ;
                    args.handler($(inputField).val());
                },
                denyFn = function () {
                    args.handler(null);
                }
            ;
            if (input.length === 0) {
                args.content += '<p><div class="' + this.helpers.deQuote(settings.className.prompt) + '"><input placeholder="' + this.helpers.deQuote(args.placeholder || '') + '" type="text" value="' + this.helpers.deQuote(args.defaultValue || '') + '"></div></p>';
            }

            return {
                title: args.title,
                content: args.content,
                onApprove: approveFn,
                onDeny: denyFn,
                actions: [{
                    text: settings.text.ok,
                    class: settings.className.ok,
                    click: approveFn,
                }, {
                    text: settings.text.cancel,
                    class: settings.className.cancel,
                    click: denyFn,
                }],
            };
        },
    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Nag
 * 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.nag = function (parameters) {
        var
            $allModules    = $(this),
            $body          = $('body'),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },
            returnedValue
        ;
        $allModules.each(function () {
            var
                settings          = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.nag.settings, parameters)
                    : $.extend({}, $.fn.nag.settings),

                selector        = settings.selector,
                error           = settings.error,
                namespace       = settings.namespace,

                eventNamespace  = '.' + namespace,
                moduleNamespace = namespace + '-module',

                $module         = $(this),

                $context        = settings.context ? contextCheck(settings.context, window) : $body,

                element         = this,
                instance        = $module.data(moduleNamespace),
                storage,
                module
            ;
            module = {

                initialize: function () {
                    module.verbose('Initializing element');
                    if (typeof settings.value !== 'string') {
                        settings.value = JSON.stringify(settings.value);
                    }
                    storage = module.get.storage();
                    $module
                        .on('click' + eventNamespace, selector.close, module.dismiss)
                        .data(moduleNamespace, module)
                    ;

                    if (settings.detachable && $module.parent()[0] !== $context[0]) {
                        $module
                            .detach()
                            .prependTo($context)
                        ;
                    }

                    if (settings.displayTime > 0) {
                        setTimeout(function () { module.hide(); }, settings.displayTime);
                    }
                    module.show();
                },

                destroy: function () {
                    module.verbose('Destroying instance');
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                },

                show: function () {
                    if (module.should.show() && !$module.is(':visible')) {
                        if (settings.onShow.call(element) === false) {
                            module.debug('onShow callback returned false, cancelling nag animation');

                            return false;
                        }
                        module.debug('Showing nag', settings.animation.show);
                        if (settings.animation.show === 'fade') {
                            $module
                                .fadeIn(settings.duration, settings.easing, settings.onVisible)
                            ;
                        } else {
                            $module
                                .slideDown(settings.duration, settings.easing, settings.onVisible)
                            ;
                        }
                    }
                },

                hide: function () {
                    if (settings.onHide.call(element) === false) {
                        module.debug('onHide callback returned false, cancelling nag animation');

                        return false;
                    }
                    module.debug('Hiding nag', settings.animation.hide);
                    if (settings.animation.hide === 'fade') {
                        $module
                            .fadeOut(settings.duration, settings.easing, settings.onHidden)
                        ;
                    } else {
                        $module
                            .slideUp(settings.duration, settings.easing, settings.onHidden)
                        ;
                    }
                },

                dismiss: function (event) {
                    if (module.hide() !== false && settings.storageMethod) {
                        module.debug('Dismissing nag', settings.storageMethod, settings.key, settings.value, settings.expires);
                        module.storage.set(settings.key, settings.value);
                    }
                    event.stopImmediatePropagation();
                    event.preventDefault();
                },

                should: {
                    show: function () {
                        if (settings.persist) {
                            module.debug('Persistent nag is set, can show nag');

                            return true;
                        }
                        if (module.storage.get(settings.key) != settings.value.toString()) {
                            module.debug('Stored value is not set, can show nag', module.storage.get(settings.key));

                            return true;
                        }
                        module.debug('Stored value is set, cannot show nag', module.storage.get(settings.key));

                        return false;
                    },
                },

                get: {
                    expirationDate: function (expires) {
                        if (typeof expires === 'number') {
                            expires = new Date(Date.now() + expires * 864e5);
                        }
                        if (expires instanceof Date && expires.getTime()) {
                            return expires.toUTCString();
                        }

                        module.error(error.expiresFormat);
                    },
                    storage: function () {
                        if (settings.storageMethod === 'localstorage' && window.localStorage !== undefined) {
                            module.debug('Using local storage');

                            return window.localStorage;
                        }
                        if (settings.storageMethod === 'sessionstorage' && window.sessionStorage !== undefined) {
                            module.debug('Using session storage');

                            return window.sessionStorage;
                        }
                        if ('cookie' in document) {
                            module.debug('Using cookie');

                            return {
                                setItem: function (key, value, options) {
                                    // RFC6265 compliant encoding
                                    key = encodeURIComponent(key)
                                        .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
                                        .replace(/[()]/g, escape)
                                    ;
                                    value = encodeURIComponent(value)
                                        .replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[B-D])/g, decodeURIComponent)
                                    ;

                                    var cookieOptions = '';
                                    for (var option in options) {
                                        if (Object.prototype.hasOwnProperty.call(options, option)) {
                                            cookieOptions += '; ' + option;
                                            if (typeof options[option] === 'string') {
                                                cookieOptions += '=' + options[option].split(';')[0];
                                            }
                                        }
                                    }
                                    document.cookie = key + '=' + value + cookieOptions;
                                },
                                getItem: function (key) {
                                    var cookies = document.cookie.split('; ');
                                    for (var i = 0, il = cookies.length; i < il; i++) {
                                        var
                                            parts    = cookies[i].split('='),
                                            foundKey = parts[0].replace(/(%[\da-f]{2})+/gi, decodeURIComponent)
                                        ;
                                        if (key === foundKey) {
                                            return parts[1] || '';
                                        }
                                    }
                                },
                                removeItem: function (key, options) {
                                    storage.setItem(key, '', options);
                                },
                            };
                        }

                        module.error(error.noStorage);
                    },
                    storageOptions: function () {
                        var
                            options = {}
                        ;
                        if (settings.expires) {
                            options.expires = module.get.expirationDate(settings.expires);
                        }
                        if (settings.domain) {
                            options.domain = settings.domain;
                        }
                        if (settings.path) {
                            options.path = settings.path;
                        }
                        if (settings.secure) {
                            options.secure = settings.secure;
                        }
                        if (settings.samesite) {
                            options.samesite = settings.samesite;
                        }

                        return options;
                    },
                },

                clear: function () {
                    module.storage.remove(settings.key);
                },

                storage: {
                    set: function (key, value) {
                        var
                            options = module.get.storageOptions()
                        ;
                        if (storage === window.localStorage && options.expires) {
                            module.debug('Storing expiration value in localStorage', key, options.expires);
                            storage.setItem(key + settings.expirationKey, options.expires);
                        }
                        module.debug('Value stored', key, value);
                        try {
                            storage.setItem(key, value, options);
                        } catch (e) {
                            module.error(error.setItem, e);
                        }
                    },
                    get: function (key) {
                        var
                            storedValue
                        ;
                        storedValue = storage.getItem(key);
                        if (storage === window.localStorage) {
                            var expiration = storage.getItem(key + settings.expirationKey);
                            if (expiration !== null && expiration !== undefined && new Date(expiration) < new Date()) {
                                module.debug('Value in localStorage has expired. Deleting key', key);
                                module.storage.remove(key);
                                storedValue = null;
                            }
                        }
                        if (storedValue === 'undefined' || storedValue === 'null' || storedValue === undefined || storedValue === null) {
                            storedValue = undefined;
                        }

                        return storedValue;
                    },
                    remove: function (key) {
                        var
                            options = module.get.storageOptions()
                        ;
                        options.expires = module.get.expirationDate(-1);
                        if (storage === window.localStorage) {
                            storage.removeItem(key + settings.expirationKey);
                        }
                        storage.removeItem(key, options);
                    },
                },

                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.nag.settings = {

        name: 'Nag',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        namespace: 'Nag',

        // allows cookie to be overridden
        persist: false,

        // set to zero to require manually dismissal, otherwise hides on its own
        displayTime: 0,

        animation: {
            show: 'slide',
            hide: 'slide',
        },

        context: false,
        detachable: false,

        expires: 30,

        // cookie storage only options
        domain: false,
        path: '/',
        secure: false,
        samesite: false,

        // type of storage to use
        storageMethod: 'cookie',

        // value to store in dismissed localstorage/cookie
        key: 'nag',
        value: 'dismiss',

        // Key suffix to support expiration in localstorage
        expirationKey: 'ExpirationDate',

        error: {
            noStorage: 'Unsupported storage method',
            method: 'The method you called is not defined.',
            setItem: 'Unexpected error while setting value',
            expiresFormat: '"expires" must be a number of days or a Date Object',
        },

        className: {
            bottom: 'bottom',
            fixed: 'fixed',
        },

        selector: {
            close: '> .close.icon',
        },

        duration: 500,
        easing: 'easeOutQuad',

        // callback before show animation, return false to prevent show
        onShow: function () {},

        // called after show animation
        onVisible: function () {},

        // callback before hide animation, return false to prevent hide
        onHide: function () {},

        // callback after hide animation
        onHidden: function () {},

    };

    // Adds easing
    $.extend($.easing, {
        easeOutQuad: function (x) {
            return 1 - (1 - x) * (1 - x);
        },
    });
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Popup
 * 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.popup = function (parameters) {
        var
            $allModules    = $(this),
            $document      = $(document),
            $window        = $(window),
            $body          = $('body'),

            clickEvent      = 'ontouchstart' in document.documentElement
                ? 'touchstart'
                : 'click',

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },

            returnedValue
        ;
        $allModules.each(function () {
            var
                settings        = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.popup.settings, parameters)
                    : $.extend({}, $.fn.popup.settings),

                selector           = settings.selector,
                className          = settings.className,
                error              = settings.error,
                metadata           = settings.metadata,
                namespace          = settings.namespace,

                eventNamespace     = '.' + settings.namespace,
                moduleNamespace    = 'module-' + namespace,

                $module            = $(this),
                $context           = contextCheck(settings.context, window),
                $scrollContext     = contextCheck(settings.scrollContext, window),
                $boundary          = contextCheck(settings.boundary, window),
                $target            = settings.target ? contextCheck(settings.target, window) : $module,

                $popup,
                $offsetParent,

                searchDepth        = 0,
                triedPositions     = false,
                openedWithTouch    = false,

                element            = this,
                instance           = $module.data(moduleNamespace),

                documentObserver,
                elementNamespace,
                id,
                module
            ;

            module = {

                // binds events
                initialize: function () {
                    module.debug('Initializing', $module);
                    module.createID();
                    module.bind.events();
                    if (!module.exists() && settings.preserve) {
                        module.create();
                    }
                    if (settings.observeChanges) {
                        module.observeChanges();
                    }
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        documentObserver = new MutationObserver(module.event.documentChanged);
                        documentObserver.observe(document, {
                            childList: true,
                            subtree: true,
                        });
                        module.debug('Setting up mutation observer', documentObserver);
                    }
                },

                refresh: function () {
                    if (settings.popup) {
                        $popup = $document.find(settings.popup).eq(0);
                    } else {
                        if (settings.inline) {
                            $popup = $target.nextAll(selector.popup).eq(0);
                            settings.popup = $popup;
                        }
                    }
                    if (settings.popup) {
                        module.set.invisible();
                        $offsetParent = module.get.offsetParent();
                        module.remove.invisible();
                        if (settings.movePopup && module.has.popup() && module.get.offsetParent($popup)[0] !== $offsetParent[0]) {
                            module.debug('Moving popup to the same offset parent as target');
                            $popup
                                .detach()
                                .appendTo($offsetParent)
                            ;
                        }
                    } else {
                        $offsetParent = settings.inline
                            ? module.get.offsetParent($target)
                            : (module.has.popup()
                                ? module.get.offsetParent($popup)
                                : $body);
                    }
                    if ($offsetParent.is('html') && $offsetParent[0] !== $body[0]) {
                        module.debug('Setting page as offset parent');
                        $offsetParent = $body;
                    }
                    if (module.get.variation()) {
                        module.set.variation();
                    }
                },

                reposition: function () {
                    module.refresh();
                    module.set.position();
                },

                destroy: function () {
                    module.debug('Destroying previous module');
                    if (documentObserver) {
                        documentObserver.disconnect();
                    }
                    // remove element only if was created dynamically
                    if ($popup && !settings.preserve) {
                        module.removePopup();
                    }
                    // clear all timeouts
                    clearTimeout(module.hideTimer);
                    clearTimeout(module.showTimer);
                    // remove events
                    module.unbind.close();
                    module.unbind.events();
                    $module
                        .removeData(moduleNamespace)
                    ;
                },

                event: {
                    start: function (event) {
                        var
                            delay = $.isPlainObject(settings.delay)
                                ? settings.delay.show
                                : settings.delay
                        ;
                        clearTimeout(module.hideTimer);
                        if (!openedWithTouch || (openedWithTouch && settings.addTouchEvents)) {
                            module.showTimer = setTimeout(function () { module.show(); }, delay);
                        }
                    },
                    end: function () {
                        var
                            delay = $.isPlainObject(settings.delay)
                                ? settings.delay.hide
                                : settings.delay
                        ;
                        clearTimeout(module.showTimer);
                        module.hideTimer = setTimeout(function () { module.hide(); }, delay);
                    },
                    touchstart: function (event) {
                        openedWithTouch = true;
                        if (settings.addTouchEvents) {
                            module.show();
                        }
                    },
                    resize: function () {
                        if (module.is.visible()) {
                            module.set.position();
                        }
                    },
                    documentChanged: function (mutations) {
                        [].forEach.call(mutations, function (mutation) {
                            if (mutation.removedNodes) {
                                [].forEach.call(mutation.removedNodes, function (node) {
                                    if (node === element || $(node).find(element).length > 0) {
                                        module.debug('Element removed from DOM, tearing down events');
                                        module.destroy();
                                    }
                                });
                            }
                        });
                    },
                    hideGracefully: function (event) {
                        var
                            $target = $(event.target),
                            isInDOM = $.contains(document.documentElement, event.target),
                            inPopup = $target.closest(selector.popup).length > 0
                        ;
                        // don't close on clicks inside popup
                        if (event && !inPopup && isInDOM) {
                            module.debug('Click occurred outside popup hiding popup');
                            module.hide();
                        } else {
                            module.debug('Click was inside popup, keeping popup open');
                        }
                    },
                },

                // generates popup html from metadata
                create: function () {
                    var
                        targetSibling = $target.next(selector.popup),
                        contentFallback = !settings.popup && targetSibling.length === 0 ? $module.attr('title') : false,
                        html      = module.get.html(),
                        title     = module.get.title(),
                        content   = module.get.content(contentFallback)
                    ;

                    if (html || content || title) {
                        module.debug('Creating pop-up html');
                        if (!html) {
                            html = settings.templates.popup({
                                title: title,
                                content: content,
                            });
                        }
                        $popup = $('<div/>')
                            .addClass(className.popup)
                            .data(metadata.activator, $module)
                            .html(html)
                        ;
                        if (settings.inline) {
                            module.verbose('Inserting popup element inline', $popup);
                            $popup
                                .insertAfter($module)
                            ;
                        } else {
                            module.verbose('Appending popup element to body', $popup);
                            $popup
                                .appendTo($context)
                            ;
                        }
                        module.refresh();
                        module.set.variation();

                        if (settings.hoverable) {
                            module.bind.popup();
                        }
                        settings.onCreate.call($popup, element);
                    } else if (settings.popup) {
                        $document.find(settings.popup).data(metadata.activator, $module);
                        module.verbose('Used popup specified in settings');
                        module.refresh();
                        if (settings.hoverable) {
                            module.bind.popup();
                        }
                    } else if (targetSibling.length > 0) {
                        module.verbose('Pre-existing popup found');
                        settings.inline = true;
                        settings.popup = targetSibling.data(metadata.activator, $module);
                        module.refresh();
                        if (settings.hoverable) {
                            module.bind.popup();
                        }
                    } else {
                        module.debug('No content specified skipping display', element);
                    }
                },

                createID: function () {
                    id = (Math.random().toString(16) + '000000000').slice(2, 10);
                    elementNamespace = '.' + id;
                    module.verbose('Creating unique id for element', id);
                },

                // determines popup state
                toggle: function () {
                    module.debug('Toggling pop-up');
                    if (module.is.hidden()) {
                        module.debug('Popup is hidden, showing pop-up');
                        module.unbind.close();
                        module.show();
                    } else {
                        module.debug('Popup is visible, hiding pop-up');
                        module.hide();
                    }
                },

                show: function (callback) {
                    callback = callback || function () {};
                    module.debug('Showing pop-up', settings.transition);
                    if (module.is.hidden() && !(module.is.active() && module.is.dropdown())) {
                        if (!module.exists()) {
                            module.create();
                        }
                        if (settings.onShow.call($popup, element) === false) {
                            module.debug('onShow callback returned false, cancelling popup animation');

                            return;
                        }
                        if (!settings.preserve && !settings.popup) {
                            module.refresh();
                        }
                        if ($popup && module.set.position()) {
                            module.save.conditions();
                            if (settings.exclusive) {
                                module.hideAll();
                            }
                            module.animate.show(callback);
                        }
                    }
                },

                hide: function (callback) {
                    callback = callback || function () {};
                    if (module.is.visible() || module.is.animating()) {
                        if (settings.onHide.call($popup, element) === false) {
                            module.debug('onHide callback returned false, cancelling popup animation');

                            return;
                        }
                        module.remove.visible();
                        module.unbind.close();
                        module.restore.conditions();
                        module.animate.hide(callback);
                    }
                },

                hideAll: function () {
                    $document.find(selector.popup)
                        .filter('.' + className.popupVisible)
                        .each(function () {
                            $(this)
                                .data(metadata.activator)
                                .popup('hide')
                            ;
                        })
                    ;
                },
                exists: function () {
                    if (!$popup) {
                        return false;
                    }
                    if (settings.inline || settings.popup) {
                        return module.has.popup();
                    }

                    return $popup.closest($context).length > 0;
                },

                removePopup: function () {
                    if (module.has.popup() && !settings.popup) {
                        module.debug('Removing popup', $popup);
                        $popup.remove();
                        $popup = undefined;
                        settings.onRemove.call($popup, element);
                    }
                },

                save: {
                    conditions: function () {
                        module.cache = {
                            title: $module.attr('title'),
                        };
                        if (module.cache.title) {
                            $module.removeAttr('title');
                        }
                        module.verbose('Saving original attributes', module.cache.title);
                    },
                },
                restore: {
                    conditions: function () {
                        if (module.cache && module.cache.title) {
                            $module.attr('title', module.cache.title);
                            module.verbose('Restoring original attributes', module.cache.title);
                        }

                        return true;
                    },
                },
                supports: {
                    svg: function () {
                        return typeof SVGGraphicsElement !== 'undefined';
                    },
                },
                animate: {
                    show: function (callback) {
                        callback = isFunction(callback) ? callback : function () {};
                        if (settings.transition && module.can.useElement('transition')) {
                            module.set.visible();
                            $popup
                                .transition({
                                    animation: (settings.transition.showMethod || settings.transition) + ' in',
                                    queue: false,
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    duration: settings.transition.showDuration || settings.duration,
                                    onComplete: function () {
                                        module.bind.close();
                                        callback.call($popup, element);
                                        settings.onVisible.call($popup, element);
                                    },
                                })
                            ;
                        }
                    },
                    hide: function (callback) {
                        callback = isFunction(callback) ? callback : function () {};
                        module.debug('Hiding pop-up');
                        if (settings.transition && $.fn.transition !== undefined) {
                            $popup
                                .transition({
                                    animation: (settings.transition.hideMethod || settings.transition) + ' out',
                                    queue: false,
                                    duration: settings.transition.hideDuration || settings.duration,
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    onComplete: function () {
                                        module.reset();
                                        callback.call($popup, element);
                                        settings.onHidden.call($popup, element);
                                    },
                                })
                            ;
                        } else {
                            module.error(error.noTransition);
                        }
                    },
                },

                change: {
                    content: function (html) {
                        $popup.html(html);
                    },
                },

                get: {
                    html: function () {
                        $module.removeData(metadata.html);

                        return $module.data(metadata.html) || settings.html;
                    },
                    title: function () {
                        $module.removeData(metadata.title);

                        return $module.data(metadata.title) || settings.title;
                    },
                    content: function (fallback) {
                        $module.removeData(metadata.content);

                        return $module.data(metadata.content) || settings.content || fallback;
                    },
                    variation: function () {
                        $module.removeData(metadata.variation);

                        return $module.data(metadata.variation) || settings.variation;
                    },
                    popup: function () {
                        return $popup;
                    },
                    popupOffset: function () {
                        return $popup.offset();
                    },
                    calculations: function () {
                        var
                            $popupOffsetParent = module.get.offsetParent($popup),
                            targetElement      = $target[0],
                            isWindowEl         = $boundary[0] === window,
                            targetOffset       = $target.offset(),
                            parentOffset       = settings.inline || (settings.popup && settings.movePopup)
                                ? $target.offsetParent().offset()
                                : { top: 0, left: 0 },
                            screenPosition = isWindowEl
                                ? { top: 0, left: 0 }
                                : $boundary.offset(),
                            calculations   = {},
                            scroll = isWindowEl
                                ? { top: $window.scrollTop(), left: $window.scrollLeft() }
                                : { top: 0, left: 0 },
                            screen
                        ;
                        calculations = {
                            // element which is launching popup
                            target: {
                                element: $target[0],
                                width: $target.outerWidth(),
                                height: $target.outerHeight(),
                                top: targetOffset.top - parentOffset.top,
                                left: targetOffset.left - parentOffset.left,
                                margin: {},
                            },
                            // popup itself
                            popup: {
                                width: $popup.outerWidth(),
                                height: $popup.outerHeight(),
                            },
                            // offset container (or 3d context)
                            parent: {
                                width: $offsetParent.outerWidth(),
                                height: $offsetParent.outerHeight(),
                            },
                            // screen boundaries
                            screen: {
                                top: screenPosition.top,
                                left: screenPosition.left,
                                scroll: {
                                    top: scroll.top,
                                    left: scroll.left,
                                },
                                width: $boundary.width(),
                                height: $boundary.height(),
                            },
                        };

                        // if popup offset context is not same as target, then adjust calculations
                        if ($popupOffsetParent[0] !== $offsetParent[0]) {
                            var
                                popupOffset        = $popupOffsetParent.offset()
                            ;
                            calculations.target.top -= popupOffset.top;
                            calculations.target.left -= popupOffset.left;
                            calculations.parent.width = $popupOffsetParent.outerWidth();
                            calculations.parent.height = $popupOffsetParent.outerHeight();
                        }

                        // add in container calcs if fluid
                        if (settings.setFluidWidth && module.is.fluid()) {
                            calculations.container = {
                                width: $popup.parent().outerWidth(),
                            };
                            calculations.popup.width = calculations.container.width;
                        }

                        // add in margins if inline
                        calculations.target.margin.top = settings.inline
                            ? parseInt(window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10)
                            : 0;
                        calculations.target.margin.left = settings.inline
                            ? (module.is.rtl()
                                ? parseInt(window.getComputedStyle(targetElement).getPropertyValue('margin-right'), 10)
                                : parseInt(window.getComputedStyle(targetElement).getPropertyValue('margin-left'), 10))
                            : 0;
                        // calculate screen boundaries
                        screen = calculations.screen;
                        calculations.boundary = {
                            top: screen.top + screen.scroll.top,
                            bottom: screen.top + screen.scroll.top + screen.height,
                            left: screen.left + screen.scroll.left,
                            right: screen.left + screen.scroll.left + screen.width,
                        };

                        return calculations;
                    },
                    id: function () {
                        return id;
                    },
                    startEvent: function () {
                        if (settings.on === 'hover') {
                            return 'mouseenter';
                        }
                        if (settings.on === 'focus') {
                            return 'focus';
                        }

                        return false;
                    },
                    scrollEvent: function () {
                        return 'scroll';
                    },
                    endEvent: function () {
                        if (settings.on === 'hover') {
                            return 'mouseleave';
                        }
                        if (settings.on === 'focus') {
                            return 'blur';
                        }

                        return false;
                    },
                    distanceFromBoundary: function (offset, calculations) {
                        var
                            distanceFromBoundary = {},
                            popup,
                            boundary
                        ;
                        calculations = calculations || module.get.calculations();

                        // shorthand
                        popup = calculations.popup;
                        boundary = calculations.boundary;

                        if (offset) {
                            distanceFromBoundary = {
                                top: offset.top - boundary.top,
                                left: offset.left - boundary.left,
                                right: boundary.right - (offset.left + popup.width),
                                bottom: boundary.bottom - (offset.top + popup.height),
                            };
                            module.verbose('Distance from boundaries determined', offset, distanceFromBoundary);
                        }

                        return distanceFromBoundary;
                    },
                    offsetParent: function ($element) {
                        var
                            element = $element !== undefined
                                ? $element[0]
                                : $target[0],
                            parentNode = element.parentNode,
                            $node    = $(parentNode)
                        ;
                        if (parentNode) {
                            var
                                is2D     = $node.css('transform') === 'none',
                                isStatic = $node.css('position') === 'static',
                                isBody   = $node.is('body')
                            ;
                            while (parentNode && !isBody && isStatic && is2D) {
                                parentNode = parentNode.parentNode;
                                $node = $(parentNode);
                                is2D = $node.css('transform') === 'none';
                                isStatic = $node.css('position') === 'static';
                                isBody = $node.is('body');
                            }
                        }

                        return $node && $node.length > 0
                            ? $node
                            : $();
                    },
                    positions: function () {
                        return {
                            'top left': false,
                            'top center': false,
                            'top right': false,
                            'bottom left': false,
                            'bottom center': false,
                            'bottom right': false,
                            'left center': false,
                            'right center': false,
                        };
                    },
                    nextPosition: function (position) {
                        var
                            positions          = position.split(' '),
                            verticalPosition   = positions[0],
                            horizontalPosition = positions[1],
                            opposite = {
                                top: 'bottom',
                                bottom: 'top',
                                left: 'right',
                                right: 'left',
                            },
                            adjacent = {
                                left: 'center',
                                center: 'right',
                                right: 'left',
                            },
                            backup = {
                                'top left': 'top center',
                                'top center': 'top right',
                                'top right': 'right center',
                                'right center': 'bottom right',
                                'bottom right': 'bottom center',
                                'bottom center': 'bottom left',
                                'bottom left': 'left center',
                                'left center': 'top left',
                            },
                            adjacentsAvailable = verticalPosition === 'top' || verticalPosition === 'bottom',
                            oppositeTried = false,
                            adjacentTried = false,
                            nextPosition  = false
                        ;
                        if (!triedPositions) {
                            module.verbose('All available positions available');
                            triedPositions = module.get.positions();
                        }

                        module.debug('Recording last position tried', position);
                        triedPositions[position] = true;

                        if (settings.prefer === 'opposite') {
                            nextPosition = [opposite[verticalPosition], horizontalPosition];
                            nextPosition = nextPosition.join(' ');
                            oppositeTried = triedPositions[nextPosition] === true;
                            module.debug('Trying opposite strategy', nextPosition);
                        }
                        if ((settings.prefer === 'adjacent') && adjacentsAvailable) {
                            nextPosition = [verticalPosition, adjacent[horizontalPosition]];
                            nextPosition = nextPosition.join(' ');
                            adjacentTried = triedPositions[nextPosition] === true;
                            module.debug('Trying adjacent strategy', nextPosition);
                        }
                        if (adjacentTried || oppositeTried) {
                            module.debug('Using backup position', nextPosition);
                            nextPosition = backup[position];
                        }

                        return nextPosition;
                    },
                },

                set: {
                    position: function (position, calculations) {
                        // exit conditions
                        if ($target.length === 0 || $popup.length === 0) {
                            module.error(error.notFound);

                            return;
                        }
                        var
                            offset,
                            distanceAway,
                            target,
                            popup,
                            parent,
                            positioning,
                            popupOffset,
                            distanceFromBoundary
                        ;

                        calculations = calculations || module.get.calculations();
                        position = position || $module.data(metadata.position) || settings.position;

                        offset = $module.data(metadata.offset) || settings.offset;
                        distanceAway = settings.distanceAway;

                        // shorthand
                        target = calculations.target;
                        popup = calculations.popup;
                        parent = calculations.parent;

                        if (module.should.centerArrow(calculations)) {
                            module.verbose('Adjusting offset to center arrow on small target element');
                            if (position === 'top left' || position === 'bottom left') {
                                offset += target.width / 2;
                                offset -= settings.arrowPixelsFromEdge;
                            }
                            if (position === 'top right' || position === 'bottom right') {
                                offset -= target.width / 2;
                                offset += settings.arrowPixelsFromEdge;
                            }
                        }

                        if (target.width === 0 && target.height === 0 && !module.is.svg(target.element)) {
                            module.debug('Popup target is hidden, no action taken');

                            return false;
                        }

                        if (settings.inline) {
                            module.debug('Adding margin to calculation', target.margin);
                            if (position === 'left center' || position === 'right center') {
                                offset += target.margin.top;
                                distanceAway += -target.margin.left;
                            } else if (position === 'top left' || position === 'top center' || position === 'top right') {
                                offset += target.margin.left;
                                distanceAway -= target.margin.top;
                            } else {
                                offset += target.margin.left;
                                distanceAway += target.margin.top;
                            }
                        }

                        module.debug('Determining popup position from calculations', position, calculations);

                        if (module.is.rtl()) {
                            position = position.replace(/left|right/g, function (match) {
                                return match === 'left'
                                    ? 'right'
                                    : 'left';
                            });
                            module.debug('RTL: Popup position updated', position);
                        }

                        // if last attempt use specified last resort position
                        if (searchDepth === settings.maxSearchDepth && typeof settings.lastResort === 'string') {
                            position = settings.lastResort;
                        }

                        switch (position) {
                            case 'top left': {
                                positioning = {
                                    top: 'auto',
                                    bottom: parent.height - target.top + distanceAway,
                                    left: target.left + offset,
                                    right: 'auto',
                                };

                                break;
                            }
                            case 'top center': {
                                positioning = {
                                    bottom: parent.height - target.top + distanceAway,
                                    left: target.left + (target.width / 2) - (popup.width / 2) + offset,
                                    top: 'auto',
                                    right: 'auto',
                                };

                                break;
                            }
                            case 'top right': {
                                positioning = {
                                    bottom: parent.height - target.top + distanceAway,
                                    right: parent.width - target.left - target.width - offset,
                                    top: 'auto',
                                    left: 'auto',
                                };

                                break;
                            }
                            case 'left center': {
                                positioning = {
                                    top: target.top + (target.height / 2) - (popup.height / 2) + offset,
                                    right: parent.width - target.left + distanceAway,
                                    left: 'auto',
                                    bottom: 'auto',
                                };

                                break;
                            }
                            case 'right center': {
                                positioning = {
                                    top: target.top + (target.height / 2) - (popup.height / 2) + offset,
                                    left: target.left + target.width + distanceAway,
                                    bottom: 'auto',
                                    right: 'auto',
                                };

                                break;
                            }
                            case 'bottom left': {
                                positioning = {
                                    top: target.top + target.height + distanceAway,
                                    left: target.left + offset,
                                    bottom: 'auto',
                                    right: 'auto',
                                };

                                break;
                            }
                            case 'bottom center': {
                                positioning = {
                                    top: target.top + target.height + distanceAway,
                                    left: target.left + (target.width / 2) - (popup.width / 2) + offset,
                                    bottom: 'auto',
                                    right: 'auto',
                                };

                                break;
                            }
                            case 'bottom right': {
                                positioning = {
                                    top: target.top + target.height + distanceAway,
                                    right: parent.width - target.left - target.width - offset,
                                    left: 'auto',
                                    bottom: 'auto',
                                };

                                break;
                            }
                        }
                        if (positioning === undefined) {
                            module.error(error.invalidPosition, position);
                        }

                        module.debug('Calculated popup positioning values', positioning);

                        // tentatively place on stage
                        $popup
                            .css(positioning)
                            .removeClass(className.position)
                            .addClass(position)
                        ;
                        module.set.invisible();

                        popupOffset = module.get.popupOffset();

                        // see if any boundaries are surpassed with this tentative position
                        distanceFromBoundary = module.get.distanceFromBoundary(popupOffset, calculations);

                        if (!settings.forcePosition && module.is.offstage(distanceFromBoundary, position)) {
                            module.debug('Position is outside viewport', position);
                            if (searchDepth < settings.maxSearchDepth) {
                                searchDepth++;
                                position = module.get.nextPosition(position);
                                module.debug('Trying new position', position);

                                return $popup
                                    ? module.set.position(position, calculations)
                                    : false;
                            }
                            if (settings.lastResort) {
                                module.debug('No position found, showing with last position');
                            } else {
                                module.debug('Popup could not find a position to display', $popup);
                                module.error(error.cannotPlace, element);
                                module.remove.attempts();
                                module.remove.invisible();
                                module.reset();
                                settings.onUnplaceable.call($popup, element);

                                return false;
                            }
                        }
                        module.debug('Position is on stage', position);
                        module.remove.attempts();
                        module.remove.invisible();
                        if (settings.setFluidWidth && module.is.fluid()) {
                            module.set.fluidWidth(calculations);
                        }

                        return true;
                    },

                    fluidWidth: function (calculations) {
                        calculations = calculations || module.get.calculations();
                        module.debug('Automatically setting element width to parent width', calculations.parent.width);
                        $popup.css('width', calculations.container.width);
                    },

                    loading: function () {
                        $popup.addClass(className.loading);
                    },

                    invisible: function () {
                        $popup.addClass(className.invisible);
                    },

                    variation: function (variation) {
                        variation = variation || module.get.variation();
                        if (variation && module.has.popup()) {
                            module.verbose('Adding variation to popup', variation);
                            $popup.addClass(variation);
                        }
                    },

                    visible: function () {
                        $module.addClass(className.visible);
                    },
                },

                remove: {
                    loading: function () {
                        $popup.removeClass(className.loading);
                    },
                    invisible: function () {
                        $popup.removeClass(className.invisible);
                    },
                    variation: function (variation) {
                        variation = variation || module.get.variation();
                        if (variation) {
                            module.verbose('Removing variation', variation);
                            $popup.removeClass(variation);
                        }
                    },
                    visible: function () {
                        $module.removeClass(className.visible);
                    },
                    attempts: function () {
                        module.verbose('Resetting all searched positions');
                        searchDepth = 0;
                        triedPositions = false;
                    },
                },

                bind: {
                    events: function () {
                        module.debug('Binding popup events to module');
                        if (settings.on === 'click') {
                            $module
                                .on(clickEvent + eventNamespace, module.toggle)
                            ;
                        }
                        if (settings.on === 'hover') {
                            $module
                                .on('touchstart' + eventNamespace, module.event.touchstart)
                            ;
                        }
                        if (module.get.startEvent()) {
                            $module
                                .on(module.get.startEvent() + eventNamespace, module.event.start)
                                .on(module.get.endEvent() + eventNamespace, module.event.end)
                            ;
                        }
                        if (settings.target) {
                            module.debug('Target set to element', $target);
                        }
                        $window.on('resize' + elementNamespace, module.event.resize);
                    },
                    popup: function () {
                        module.verbose('Allowing hover events on popup to prevent closing');
                        if ($popup && module.has.popup()) {
                            $popup
                                .on('mouseenter' + eventNamespace, module.event.start)
                                .on('mouseleave' + eventNamespace, module.event.end)
                            ;
                        }
                    },
                    close: function () {
                        if (settings.hideOnScroll === true || (settings.hideOnScroll === 'auto' && settings.on !== 'click')) {
                            module.bind.closeOnScroll();
                        }
                        if (module.is.closable()) {
                            module.bind.clickaway();
                        } else if (settings.on === 'hover' && openedWithTouch) {
                            module.bind.touchClose();
                        }
                    },
                    closeOnScroll: function () {
                        module.verbose('Binding scroll close event to document');
                        $scrollContext
                            .one(module.get.scrollEvent() + elementNamespace, module.event.hideGracefully)
                        ;
                    },
                    touchClose: function () {
                        module.verbose('Binding popup touchclose event to document');
                        $document
                            .on('touchstart' + elementNamespace, function (event) {
                                module.verbose('Touched away from popup');
                                module.event.hideGracefully.call(element, event);
                            })
                        ;
                    },
                    clickaway: function () {
                        module.verbose('Binding popup close event to document');
                        $document
                            .on(clickEvent + elementNamespace, function (event) {
                                module.verbose('Clicked away from popup');
                                module.event.hideGracefully.call(element, event);
                            })
                        ;
                    },
                },

                unbind: {
                    events: function () {
                        $window
                            .off(elementNamespace)
                        ;
                        $module
                            .off(eventNamespace)
                        ;
                    },
                    close: function () {
                        $document
                            .off(elementNamespace)
                        ;
                        $scrollContext
                            .off(elementNamespace)
                        ;
                    },
                },

                can: {
                    useElement: function (element) {
                        if ($.fn[element] !== undefined) {
                            return true;
                        }
                        module.error(error.noElement.replace('{element}', element));

                        return false;
                    },
                },

                has: {
                    popup: function () {
                        return $popup && $popup.length > 0;
                    },
                },

                should: {
                    centerArrow: function (calculations) {
                        return !module.is.basic() && calculations.target.width <= (settings.arrowPixelsFromEdge * 2);
                    },
                },

                is: {
                    closable: function () {
                        if (settings.closable === 'auto') {
                            return settings.on !== 'hover';
                        }

                        return settings.closable;
                    },
                    offstage: function (distanceFromBoundary, position) {
                        var
                            offstage = []
                        ;
                        // return boundaries that have been surpassed
                        $.each(distanceFromBoundary, function (direction, distance) {
                            if (distance < -settings.jitter) {
                                module.debug('Position exceeds allowable distance from edge', direction, distance, position);
                                offstage.push(direction);
                            }
                        });

                        return offstage.length > 0;
                    },
                    svg: function (element) {
                        return module.supports.svg() && (element instanceof SVGGraphicsElement);
                    },
                    basic: function () {
                        return $module.hasClass(className.basic);
                    },
                    active: function () {
                        return $module.hasClass(className.active);
                    },
                    animating: function () {
                        return $popup !== undefined && $popup.hasClass(className.animating);
                    },
                    fluid: function () {
                        return $popup !== undefined && $popup.hasClass(className.fluid);
                    },
                    visible: function () {
                        return $popup !== undefined && $popup.hasClass(className.popupVisible);
                    },
                    dropdown: function () {
                        return $module.hasClass(className.dropdown);
                    },
                    hidden: function () {
                        return !module.is.visible();
                    },
                    rtl: function () {
                        return $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl' || $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl' || $context.attr('dir') === 'rtl' || $context.css('direction') === 'rtl';
                    },
                },

                reset: function () {
                    module.remove.visible();
                    if (settings.preserve) {
                        if ($.fn.transition !== undefined) {
                            $popup
                                .transition('remove transition')
                            ;
                        }
                    } else {
                        module.removePopup();
                    }
                },

                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 (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.popup.settings = {

        name: 'Popup',

        // module settings
        silent: false,
        debug: false,
        verbose: false,
        performance: true,
        namespace: 'popup',

        // whether it should use dom mutation observers
        observeChanges: true,

        // callback only when element added to dom
        onCreate: function () {},

        // callback before element removed from dom
        onRemove: function () {},

        // callback before show animation
        onShow: function () {},

        // callback after show animation
        onVisible: function () {},

        // callback before hide animation
        onHide: function () {},

        // callback when popup cannot be positioned in visible screen
        onUnplaceable: function () {},

        // callback after hide animation
        onHidden: function () {},

        // when to show popup
        on: 'hover',

        // element to use to determine if popup is out of boundary
        boundary: window,

        // whether to add touchstart events when using hover
        addTouchEvents: true,

        // default position relative to element
        position: 'top left',

        // if given position should be used regardless if popup fits
        forcePosition: false,

        // name of variation to use
        variation: '',

        // whether popup should be moved to context
        movePopup: true,

        // element which popup should be relative to
        target: false,

        // jq selector or element that should be used as popup
        popup: false,

        // popup should remain inline next to activator
        inline: false,

        // popup should be removed from page on hide
        preserve: false,

        // popup should not close when being hovered on
        hoverable: false,

        // explicitly set content
        content: false,

        // explicitly set html
        html: false,

        // explicitly set title
        title: false,

        // whether automatically close on clickaway when on click
        closable: true,

        // automatically hide on scroll
        hideOnScroll: 'auto',

        // hide other popups on show
        exclusive: false,

        // context to attach popups
        context: 'body',

        // context for binding scroll events
        scrollContext: window,

        // position to prefer when calculating new position
        prefer: 'opposite',

        // specify position to appear even if it doesn't fit
        lastResort: false,

        // number of pixels from edge of popup to pointing arrow center (used from centering)
        arrowPixelsFromEdge: 20,

        // delay used to prevent accidental refiring of animations due to user error
        delay: {
            show: 50,
            hide: 70,
        },

        // whether fluid variation should assign width explicitly
        setFluidWidth: true,

        // transition settings
        duration: 200,
        transition: 'scale',

        // distance away from activating element in px
        distanceAway: 0,

        // number of pixels an element is allowed to be "offstage" for a position to be chosen (allows for rounding)
        jitter: 2,

        // offset on aligning axis from calculated position
        offset: 0,

        // maximum times to look for a position before failing (9 positions total)
        maxSearchDepth: 15,

        error: {
            invalidPosition: 'The position you specified is not a valid position',
            cannotPlace: 'Popup does not fit within the boundaries of the viewport',
            method: 'The method you called is not defined.',
            noElement: 'This module requires ui {element}',
            notFound: 'The target or popup you specified does not exist on the page',
        },

        metadata: {
            activator: 'activator',
            content: 'content',
            html: 'html',
            offset: 'offset',
            position: 'position',
            title: 'title',
            variation: 'variation',
        },

        className: {
            active: 'active',
            basic: 'basic',
            animating: 'animating',
            dropdown: 'dropdown',
            invisible: 'invisible',
            fluid: 'fluid',
            loading: 'loading',
            popup: 'ui popup',
            position: 'top left center bottom right',
            visible: 'visible',
            popupVisible: 'visible',
        },

        selector: {
            popup: '.ui.popup',
        },

        templates: {
            escape: function (string) {
                var
                    badChars     = /["'<>`]/g,
                    shouldEscape = /["&'<>`]/,
                    escape       = {
                        '<': '&lt;',
                        '>': '&gt;',
                        '"': '&quot;',
                        "'": '&#x27;',
                        '`': '&#x60;',
                    },
                    escapedChar  = function (chr) {
                        return escape[chr];
                    }
                ;
                if (shouldEscape.test(string)) {
                    string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                    return string.replace(badChars, escapedChar);
                }

                return string;
            },
            popup: function (text) {
                var
                    html   = '',
                    escape = $.fn.popup.settings.templates.escape
                ;
                if (text !== undefined) {
                    if (text.title) {
                        text.title = escape(text.title);
                        html += '<div class="header">' + text.title + '</div>';
                    }
                    if (text.content) {
                        text.content = escape(text.content);
                        html += '<div class="content">' + text.content + '</div>';
                    }
                }

                return html;
            },
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Progress
 * 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.progress = 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.progress.settings, parameters)
                    : $.extend({}, $.fn.progress.settings),

                className       = settings.className,
                metadata        = settings.metadata,
                namespace       = settings.namespace,
                selector        = settings.selector,
                error           = settings.error,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $bars           = $(this).find(selector.bar),
                $progresses     = $(this).find(selector.progress),
                $label          = $(this).find(selector.label),

                element         = this,
                instance        = $module.data(moduleNamespace),

                animating = false,
                module
            ;
            module = {
                helper: {
                    sum: function (nums) {
                        return Array.isArray(nums) ? nums.reduce(function (left, right) {
                            return left + Number(right);
                        }, 0) : 0;
                    },
                    /**
       * Derive precision for multiple progress with total and values.
       *
       * This helper dervices a precision that is sufficiently large to show minimum value of multiple progress.
       *
       * Example1
       * - total: 1122
       * - values: [325, 111, 74, 612]
       * - min ratio: 74/1122 = 0.0659...
       * - required precision:  100
       *
       * Example2
       * - total: 10541
       * - values: [3235, 1111, 74, 6121]
       * - min ratio: 74/10541 = 0.0070...
       * - required precision:   1000
       *
       * @param min A minimum value within multiple values
       * @param total A total amount of multiple values
       * @returns {number} A precision. Could be 1, 10, 100, ... 1e+10.
       */
                    derivePrecision: function (min, total) {
                        var precisionPower = 0;
                        var precision = 1;
                        var ratio = min / total;
                        while (precisionPower < 10) {
                            ratio *= precision;
                            if (ratio > 1) {
                                break;
                            }
                            precision = Math.pow(10, precisionPower++);
                        }

                        return precision;
                    },
                    forceArray: function (element) {
                        return Array.isArray(element)
                            ? element
                            : (!isNaN(element)
                                ? [element]
                                : (typeof element === 'string' ? element.split(',') : [])); // eslint-disable-line unicorn/no-nested-ternary
                    },
                },

                initialize: function () {
                    module.set.duration();
                    module.debug(element);

                    module.read.metadata();
                    module.read.settings();

                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of progress', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },
                destroy: function () {
                    module.verbose('Destroying previous progress for', $module);
                    clearInterval(instance.interval);
                    module.remove.state();
                    $module.removeData(moduleNamespace);
                    instance = undefined;
                },

                reset: function () {
                    module.remove.nextValue();
                    module.update.progress(0);
                },

                complete: function (keepState) {
                    if (module.percent === undefined || module.percent < 100) {
                        module.remove.progressPoll();
                        if (keepState !== true) {
                            module.set.percent(100);
                        }
                    }
                },

                read: {
                    metadata: function () {
                        var
                            data = {
                                percent: module.helper.forceArray($module.data(metadata.percent)),
                                total: $module.data(metadata.total),
                                value: module.helper.forceArray($module.data(metadata.value)),
                            }
                        ;
                        if (data.total !== undefined) {
                            module.debug('Total value set from metadata', data.total);
                            module.set.total(data.total);
                        }
                        if (data.value.length > 0) {
                            module.debug('Current value set from metadata', data.value);
                            module.set.value(data.value);
                            module.set.progress(data.value);
                        }
                        if (data.percent.length > 0) {
                            module.debug('Current percent value set from metadata', data.percent);
                            module.set.percent(data.percent);
                        }
                    },
                    settings: function () {
                        if (settings.total !== false) {
                            module.debug('Current total set in settings', settings.total);
                            module.set.total(settings.total);
                        }
                        if (settings.value !== false) {
                            module.debug('Current value set in settings', settings.value);
                            module.set.value(settings.value);
                            module.set.progress(module.value);
                        }
                        if (settings.percent !== false) {
                            module.debug('Current percent set in settings', settings.percent);
                            module.set.percent(settings.percent);
                        }
                    },
                },

                bind: {
                    transitionEnd: function (callback) {
                        $bars
                            .one('transitionend' + eventNamespace, function (event) {
                                clearTimeout(module.failSafeTimer);
                                callback.call(this, event);
                            })
                        ;
                        module.failSafeTimer = setTimeout(function () {
                            $bars.triggerHandler('transitionend');
                        }, settings.duration + settings.failSafeDelay);
                        module.verbose('Adding fail safe timer', module.timer);
                    },
                },

                increment: function (incrementValue) {
                    var
                        startValue,
                        newValue
                    ;
                    if (module.has.total()) {
                        startValue = module.get.value();
                        incrementValue = incrementValue || 1;
                    } else {
                        startValue = module.get.percent();
                        incrementValue = incrementValue || module.get.randomValue();
                    }
                    newValue = startValue + incrementValue;
                    module.debug('Incrementing percentage by', startValue, newValue, incrementValue);
                    newValue = module.get.normalizedValue(newValue);
                    module.set.progress(newValue);
                },
                decrement: function (decrementValue) {
                    var
                        total     = module.get.total(),
                        startValue,
                        newValue
                    ;
                    if (total) {
                        startValue = module.get.value();
                        decrementValue = decrementValue || 1;
                        newValue = startValue - decrementValue;
                        module.debug('Decrementing value by', decrementValue, startValue);
                    } else {
                        startValue = module.get.percent();
                        decrementValue = decrementValue || module.get.randomValue();
                        newValue = startValue - decrementValue;
                        module.debug('Decrementing percentage by', decrementValue, startValue);
                    }
                    newValue = module.get.normalizedValue(newValue);
                    module.set.progress(newValue);
                },

                has: {
                    progressPoll: function () {
                        return module.progressPoll;
                    },
                    total: function () {
                        return module.get.total() !== false;
                    },
                },

                get: {
                    text: function (templateText, index) {
                        if (!index) {
                            index = 0;
                        }

                        var
                            value   = module.get.value(index),
                            total   = module.get.total(),
                            percent = animating
                                ? module.get.displayPercent(index)
                                : module.get.percent(index),
                            left = total !== false
                                ? Math.max(0, total - value)
                                : 100 - percent
                        ;
                        templateText = templateText || '';
                        templateText = templateText
                            .replace('{value}', value)
                            .replace('{total}', total || 0)
                            .replace('{left}', left)
                            .replace('{percent}', percent)
                            .replace('{bar}', settings.text.bars[index] || '')
                        ;
                        module.verbose('Adding variables to progress bar text', templateText);

                        return templateText;
                    },

                    normalizedValue: function (value) {
                        if (value < 0) {
                            module.debug('Value cannot decrement below 0');

                            return 0;
                        }
                        if (module.has.total()) {
                            if (value > module.total) {
                                module.debug('Value cannot increment above total', module.total);

                                return module.total;
                            }
                        } else if (value > 100) {
                            module.debug('Value cannot increment above 100 percent');

                            return 100;
                        }

                        return value;
                    },

                    updateInterval: function () {
                        if (settings.updateInterval === 'auto') {
                            return settings.duration;
                        }

                        return settings.updateInterval;
                    },

                    randomValue: function () {
                        module.debug('Generating random increment percentage');

                        return Math.floor((Math.random() * settings.random.max) + settings.random.min);
                    },

                    numericValue: function (value) {
                        return typeof value === 'string'
                            ? (value.replace(/[^\d.]/g, '') !== ''
                                ? +value.replace(/[^\d.]/g, '')
                                : false)
                            : value;
                    },

                    // gets current displayed percentage (if animating values this is the intermediary value)
                    displayPercent: function (index) {
                        var
                            $bar           = $($bars[index]),
                            barWidth       = $bar.width(),
                            totalWidth     = $module.width(),
                            minDisplay     = parseInt($bar.css('min-width'), 10),
                            displayPercent = barWidth > minDisplay
                                ? (barWidth / totalWidth) * 100
                                : module.percent
                        ;

                        return settings.precision > 0
                            ? Math.round(displayPercent * (10 * settings.precision)) / (10 * settings.precision)
                            : Math.round(displayPercent);
                    },

                    percent: function (index) {
                        return (module.percent && module.percent[index || 0]) || 0;
                    },
                    value: function (index) {
                        return module.nextValue || (module.value && module.value[index || 0]) || 0;
                    },
                    total: function () {
                        return module.total !== undefined ? module.total : false;
                    },
                },

                create: {
                    progressPoll: function () {
                        module.progressPoll = setTimeout(function () {
                            module.update.toNextValue();
                            module.remove.progressPoll();
                        }, module.get.updateInterval());
                    },
                },

                is: {
                    complete: function () {
                        return module.is.success() || module.is.warning() || module.is.error();
                    },
                    success: function () {
                        return $module.hasClass(className.success);
                    },
                    warning: function () {
                        return $module.hasClass(className.warning);
                    },
                    error: function () {
                        return $module.hasClass(className.error);
                    },
                    active: function () {
                        return $module.hasClass(className.active);
                    },
                    visible: function () {
                        return $module.is(':visible');
                    },
                },

                remove: {
                    progressPoll: function () {
                        module.verbose('Removing progress poll timer');
                        if (module.progressPoll) {
                            clearTimeout(module.progressPoll);
                            delete module.progressPoll;
                        }
                    },
                    nextValue: function () {
                        module.verbose('Removing progress value stored for next update');
                        delete module.nextValue;
                    },
                    state: function () {
                        module.verbose('Removing stored state');
                        delete module.total;
                        delete module.percent;
                        delete module.value;
                    },
                    active: function () {
                        module.verbose('Removing active state');
                        $module.removeClass(className.active);
                    },
                    success: function () {
                        module.verbose('Removing success state');
                        $module.removeClass(className.success);
                    },
                    warning: function () {
                        module.verbose('Removing warning state');
                        $module.removeClass(className.warning);
                    },
                    error: function () {
                        module.verbose('Removing error state');
                        $module.removeClass(className.error);
                    },
                },

                set: {
                    barWidth: function (values) {
                        module.debug('set bar width with ', values);
                        values = module.helper.forceArray(values);
                        var firstNonZeroIndex = -1;
                        var lastNonZeroIndex = -1;
                        var valuesSum = module.helper.sum(values);
                        var barCounts = $bars.length;
                        var isMultiple = barCounts > 1;
                        var percents = values.map(function (value, index) {
                            var allZero = index === barCounts - 1 && valuesSum === 0;
                            var $bar = $($bars[index]);
                            if (value === 0 && isMultiple && !allZero) {
                                $bar.css('display', 'none');
                            } else {
                                if (isMultiple && allZero) {
                                    $bar.css('background', 'transparent');
                                }
                                if (firstNonZeroIndex === -1) {
                                    firstNonZeroIndex = index;
                                }
                                lastNonZeroIndex = index;
                                $bar.css({
                                    display: 'block',
                                    width: value + '%',
                                });
                            }

                            return parseFloat(value);
                        });
                        values.forEach(function (_, index) {
                            var $bar = $($bars[index]);
                            $bar.css({
                                borderTopLeftRadius: index === firstNonZeroIndex ? '' : '0',
                                borderBottomLeftRadius: index === firstNonZeroIndex ? '' : '0',
                                borderTopRightRadius: index === lastNonZeroIndex ? '' : '0',
                                borderBottomRightRadius: index === lastNonZeroIndex ? '' : '0',
                            });
                        });
                        $module
                            .attr('data-percent', percents)
                        ;
                    },
                    duration: function (duration) {
                        duration = duration || settings.duration;
                        duration = typeof duration === 'number'
                            ? duration + 'ms'
                            : duration;
                        module.verbose('Setting progress bar transition duration', duration);
                        $bars
                            .css({
                                'transition-duration': duration,
                            })
                        ;
                    },
                    percent: function (percents) {
                        percents = module.helper.forceArray(percents).map(function (percent) {
                            percent = typeof percent === 'string'
                                ? +percent.replace('%', '')
                                : percent;

                            return settings.limitValues
                                ? Math.max(0, Math.min(100, percent))
                                : percent;
                        });
                        var hasTotal = module.has.total();
                        var totalPercent = module.helper.sum(percents);
                        var isMultipleValues = percents.length > 1 && hasTotal;
                        var sumTotal = module.helper.sum(module.helper.forceArray(module.value));
                        if (isMultipleValues && sumTotal > module.total) {
                            // Sum values instead of pecents to avoid precision issues when summing floats
                            module.error(error.sumExceedsTotal, sumTotal, module.total);
                        } else if (!isMultipleValues && totalPercent > 100) {
                            // Sum before rounding since sum of rounded may have error though sum of actual is fine
                            module.error(error.tooHigh, totalPercent);
                        } else if (totalPercent < 0) {
                            module.error(error.tooLow, totalPercent);
                        } else {
                            var autoPrecision = settings.precision > 0
                                ? settings.precision
                                : (isMultipleValues
                                    ? module.helper.derivePrecision(Math.min.apply(null, module.value), module.total)
                                    : 0);

                            // round display percentage
                            var roundedPercents = percents.map(function (percent) {
                                return autoPrecision > 0
                                    ? Math.round(percent * (10 * autoPrecision)) / (10 * autoPrecision)
                                    : Math.round(percent)
                                ;
                            });
                            module.percent = roundedPercents;
                            if (hasTotal) {
                                module.value = percents.map(function (percent) {
                                    return autoPrecision > 0
                                        ? Math.round((percent / 100) * module.total * (10 * autoPrecision)) / (10 * autoPrecision)
                                        : Math.round((percent / 100) * module.total * 10) / 10;
                                });
                            }
                            module.set.barWidth(percents);
                            module.set.labelInterval();
                        }
                        settings.onChange.call(element, percents, module.value, module.total);
                    },
                    labelInterval: function () {
                        var
                            animationCallback = function () {
                                module.verbose('Bar finished animating, removing continuous label updates');
                                clearInterval(module.interval);
                                animating = false;
                                module.set.labels();
                            }
                        ;
                        clearInterval(module.interval);
                        module.bind.transitionEnd(animationCallback);
                        animating = true;
                        module.interval = setInterval(function () {
                            var
                                isInDOM = $.contains(document.documentElement, element)
                            ;
                            if (!isInDOM) {
                                clearInterval(module.interval);
                                animating = false;
                            }
                            module.set.labels();
                        }, settings.framerate);
                    },
                    labels: function () {
                        module.verbose('Setting both bar progress and outer label text');
                        module.set.barLabel();
                        module.set.state();
                    },
                    label: function (text) {
                        if (text) {
                            text = module.get.text(text);
                            module.verbose('Setting label to text', text);
                            $label.text(text);
                        }
                    },
                    state: function (percent) {
                        percent = percent !== undefined
                            ? percent
                            : module.helper.sum(module.percent);
                        if (percent === 100) {
                            if (settings.autoSuccess && $bars.length === 1 && !(module.is.warning() || module.is.error() || module.is.success())) {
                                module.set.success();
                                module.debug('Automatically triggering success at 100%');
                            } else {
                                module.verbose('Reached 100% removing active state');
                                module.remove.active();
                                module.remove.progressPoll();
                            }
                        } else if (percent > 0) {
                            module.verbose('Adjusting active progress bar label', percent);
                            module.set.active();
                        } else {
                            module.remove.active();
                            module.remove.warning();
                            module.remove.error();
                            module.remove.success();
                            module.set.label(settings.text.active);
                        }
                    },
                    barLabel: function (text) {
                        $progresses.each(function (index, element) {
                            var $progress = $(element);
                            if (text !== undefined) {
                                $progress.text(module.get.text(text, index));
                            } else if (settings.label === 'ratio' && module.has.total()) {
                                module.verbose('Adding ratio to bar label');
                                $progress.text(module.get.text(settings.text.ratio, index));
                            } else if (settings.label === 'percent') {
                                module.verbose('Adding percentage to bar label');
                                $progress.text(module.get.text(settings.text.percent, index));
                            }
                        });
                    },
                    active: function (text) {
                        text = text || settings.text.active;
                        module.debug('Setting active state');
                        if (settings.showActivity && !module.is.active()) {
                            $module.addClass(className.active);
                        }
                        module.remove.warning();
                        module.remove.error();
                        module.remove.success();
                        text = settings.onLabelUpdate('active', text, module.value, module.total);
                        if (text) {
                            module.set.label(text);
                        }
                        module.bind.transitionEnd(function () {
                            settings.onActive.call(element, module.value, module.total);
                        });
                    },
                    success: function (text, keepState) {
                        text = text || settings.text.success || settings.text.active;
                        module.debug('Setting success state');
                        $module.addClass(className.success);
                        module.remove.active();
                        module.remove.warning();
                        module.remove.error();
                        module.complete(keepState);
                        if (settings.text.success) {
                            text = settings.onLabelUpdate('success', text, module.value, module.total);
                            module.set.label(text);
                        } else {
                            text = settings.onLabelUpdate('active', text, module.value, module.total);
                            module.set.label(text);
                        }
                        module.bind.transitionEnd(function () {
                            settings.onSuccess.call(element, module.total);
                        });
                    },
                    warning: function (text, keepState) {
                        text = text || settings.text.warning;
                        module.debug('Setting warning state');
                        $module.addClass(className.warning);
                        module.remove.active();
                        module.remove.success();
                        module.remove.error();
                        module.complete(keepState);
                        text = settings.onLabelUpdate('warning', text, module.value, module.total);
                        if (text) {
                            module.set.label(text);
                        }
                        module.bind.transitionEnd(function () {
                            settings.onWarning.call(element, module.value, module.total);
                        });
                    },
                    error: function (text, keepState) {
                        text = text || settings.text.error;
                        module.debug('Setting error state');
                        $module.addClass(className.error);
                        module.remove.active();
                        module.remove.success();
                        module.remove.warning();
                        module.complete(keepState);
                        text = settings.onLabelUpdate('error', text, module.value, module.total);
                        if (text) {
                            module.set.label(text);
                        }
                        module.bind.transitionEnd(function () {
                            settings.onError.call(element, module.value, module.total);
                        });
                    },
                    total: function (totalValue) {
                        module.total = totalValue;
                    },
                    value: function (value) {
                        module.value = module.helper.forceArray(value);
                    },
                    progress: function (value) {
                        if (!module.has.progressPoll()) {
                            module.debug('First update in progress update interval, immediately updating', value);
                            module.update.progress(value);
                            module.create.progressPoll();
                        } else {
                            module.debug('Updated within interval, setting next update to use new value', value);
                            module.set.nextValue(value);
                        }
                    },
                    nextValue: function (value) {
                        module.nextValue = value;
                    },
                },

                update: {
                    toNextValue: function () {
                        var
                            nextValue = module.nextValue
                        ;
                        if (nextValue) {
                            module.debug('Update interval complete using last updated value', nextValue);
                            module.update.progress(nextValue);
                            module.remove.nextValue();
                        }
                    },
                    progress: function (values) {
                        var hasTotal = module.has.total();
                        if (hasTotal) {
                            module.set.value(values);
                        }
                        var percentCompletes = module.helper.forceArray(values).map(function (value) {
                            var
                                percentComplete
                            ;
                            value = module.get.numericValue(value);
                            if (value === false) {
                                module.error(error.nonNumeric, value);
                            }
                            value = module.get.normalizedValue(value);
                            if (hasTotal) {
                                percentComplete = module.total > 0 ? (value / module.total) * 100 : 100;
                                module.debug('Calculating percent complete from total', percentComplete);
                            } else {
                                percentComplete = value;
                                module.debug('Setting value to exact percentage value', percentComplete);
                            }

                            return percentComplete;
                        });
                        module.set.percent(percentCompletes);
                    },
                },

                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.progress.settings = {

        name: 'Progress',
        namespace: 'progress',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        random: {
            min: 2,
            max: 5,
        },

        duration: 300,

        updateInterval: 'auto',

        autoSuccess: true,
        showActivity: true,
        limitValues: true,

        label: 'percent',
        precision: 0,
        framerate: 1000 / 30, /// 30 fps

        percent: false,
        total: false,
        value: false,

        // delay in ms for fail safe animation callback
        failSafeDelay: 100,

        onLabelUpdate: function (state, text, value, total) {
            return text;
        },
        onChange: function (percent, value, total) {},
        onSuccess: function (total) {},
        onActive: function (value, total) {},
        onError: function (value, total) {},
        onWarning: function (value, total) {},

        error: {
            method: 'The method you called is not defined.',
            nonNumeric: 'Progress value is non numeric',
            tooHigh: 'Value specified is above 100%',
            tooLow: 'Value specified is below 0%',
            sumExceedsTotal: 'Sum of multiple values exceed total',
        },

        regExp: {
            variable: /{\$*[\da-z]+}/gi,
        },

        metadata: {
            percent: 'percent',
            total: 'total',
            value: 'value',
        },

        selector: {
            bar: '> .bar',
            label: '> .label',
            progress: '.bar > .progress',
        },

        text: {
            active: false,
            error: false,
            success: false,
            warning: false,
            percent: '{percent}%',
            ratio: '{value} of {total}',
            bars: [''],
        },

        className: {
            active: 'active',
            error: 'error',
            success: 'success',
            warning: 'warning',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Slider
 * 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.slider = function (parameters) {
        var
            $allModules    = $(this),
            $document      = $(document),
            $window        = $(window),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),

            alphabet       = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],

            SINGLE_STEP     = 1,
            BIG_STEP        = 2,
            NO_STEP         = 0,
            SINGLE_BACKSTEP = -1,
            BIG_BACKSTEP    = -2,

            // Used to manage document bound events.
            // Use this so that we can distinguish between which document events are bound to which range.
            currentRange    = 0,

            returnedValue
        ;

        $allModules.each(function () {
            var
                settings        = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.slider.settings, parameters)
                    : $.extend({}, $.fn.slider.settings),

                className       = settings.className,
                metadata        = settings.metadata,
                namespace       = settings.namespace,
                error           = settings.error,
                keys            = settings.keys,
                interpretLabel  = settings.interpretLabel,

                isHover         = false,
                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $currThumb,
                touchIdentifier,
                $thumb,
                $secondThumb,
                $track,
                $trackFill,
                $labels,

                element         = this,
                instance        = $module.data(moduleNamespace),

                documentEventID,

                value,
                position,
                secondPos,
                offset,
                precision,
                gapRatio = 1,
                previousValue,

                initialPosition,
                initialLoad,
                module
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing slider', settings);
                    initialLoad = true;

                    currentRange += 1;
                    documentEventID = currentRange;

                    module.setup.layout();
                    module.setup.labels();

                    module.bind.events();

                    module.read.metadata();
                    module.read.settings();

                    initialLoad = false;
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of slider', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous slider for', $module);
                    clearInterval(instance.interval);
                    module.unbind.events();
                    module.unbind.slidingEvents();
                    $module.removeData(moduleNamespace);
                    instance = undefined;
                },

                setup: {
                    layout: function () {
                        if ($module.attr('tabindex') === undefined) {
                            $module.attr('tabindex', 0);
                        }
                        if ($module.find('.inner').length === 0) {
                            $module.append('<div class="inner">'
                                + '<div class="track"></div>'
                                + '<div class="track-fill"></div>'
                                + '<div class="thumb"></div>'
                                + '</div>');
                        }
                        precision = module.get.precision();
                        $thumb = $module.find('.thumb:not(.second)');
                        if (settings.showThumbTooltip) {
                            $thumb
                                .attr('data-position', settings.tooltipConfig.position)
                                .attr('data-variation', settings.tooltipConfig.variation)
                            ;
                        }
                        $currThumb = $thumb;
                        if (module.is.range()) {
                            if ($module.find('.thumb.second').length === 0) {
                                $module.find('.inner').append('<div class="thumb second"></div>');
                            }
                            $secondThumb = $module.find('.thumb.second');
                            if (settings.showThumbTooltip) {
                                $secondThumb
                                    .attr('data-position', settings.tooltipConfig.position)
                                    .attr('data-variation', settings.tooltipConfig.variation)
                                ;
                            }
                        }
                        $track = $module.find('.track');
                        $trackFill = $module.find('.track-fill');
                        offset = $thumb.width() / 2;
                    },
                    labels: function () {
                        if (module.is.labeled()) {
                            $labels = $module.find('.labels:not(.auto)');
                            if ($labels.length > 0) {
                                module.setup.customLabel();
                            } else {
                                module.setup.autoLabel();
                            }

                            if (settings.showLabelTicks) {
                                $module.addClass(className.ticked);
                            }
                        }
                    },
                    customLabel: function () {
                        var
                            $children   = $labels.find('.label'),
                            numChildren = $children.length,
                            min         = module.get.min(),
                            max         = module.get.max(),
                            ratio
                        ;
                        $children.each(function (index) {
                            var
                                $child = $(this),
                                attrValue = $child.attr('data-value')
                            ;
                            if (attrValue) {
                                attrValue = attrValue > max
                                    ? max
                                    : (attrValue < min ? min : attrValue);
                                ratio = (attrValue - min) / (max - min);
                            } else {
                                ratio = (index + 1) / (numChildren + 1);
                            }
                            module.update.labelPosition(ratio, $(this));
                        });
                    },
                    autoLabel: function () {
                        $labels = $module.find('.labels');
                        if ($labels.length > 0) {
                            $labels.empty();
                        } else {
                            $labels = $module.append('<ul class="auto labels"></ul>').find('.labels');
                        }
                        for (var i = 0, len = module.get.numLabels(); i <= len; i++) {
                            var
                                labelText = module.get.label(i),
                                showLabel = settings.restrictedLabels.length === 0 || settings.restrictedLabels.indexOf(labelText) >= 0,
                                $label = labelText !== '' && (showLabel || settings.showLabelTicks === 'always')
                                    ? (!(i % module.get.gapRatio())
                                        ? $('<li class="label">' + (showLabel ? labelText : '') + '</li>')
                                        : $('<li class="halftick label"></li>'))
                                    : null,
                                ratio  = i / len
                            ;
                            if ($label) {
                                module.update.labelPosition(ratio, $label);
                                $labels.append($label);
                            }
                        }
                    },
                },

                bind: {
                    events: function () {
                        module.bind.globalKeyboardEvents();
                        module.bind.keyboardEvents();
                        module.bind.mouseEvents();
                        if (settings.autoAdjustLabels) {
                            module.bind.windowEvents();
                        }
                    },
                    keyboardEvents: function () {
                        module.verbose('Binding keyboard events');
                        $module.on('keydown' + eventNamespace, module.event.keydown);
                    },
                    globalKeyboardEvents: function () {
                        $document.on('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
                    },
                    mouseEvents: function () {
                        module.verbose('Binding mouse and touch events');
                        $module.find('.track, .thumb, .inner').on('mousedown' + eventNamespace, function (event) {
                            event.stopImmediatePropagation();
                            event.preventDefault();
                            module.event.down(event);
                        });
                        $module.on('mousedown' + eventNamespace, module.event.down);
                        $module.on('mouseenter' + eventNamespace, function (event) {
                            isHover = true;
                        });
                        $module.on('mouseleave' + eventNamespace, function (event) {
                            isHover = false;
                        });
                        // All touch events are invoked on the element where the touch *started*. Thus, we can bind them all
                        // on the thumb(s) and don't need to worry about interference with other components, i.e. no dynamic binding
                        // and unbinding required.
                        $module.find('.thumb')
                            .on('touchstart' + eventNamespace, module.event.touchDown)
                            .on('touchmove' + eventNamespace, module.event.move)
                            .on('touchend' + eventNamespace, module.event.up)
                            .on('touchcancel' + eventNamespace, module.event.touchCancel)
                        ;
                    },
                    slidingEvents: function () {
                        // these don't need the identifier because we only ever want one of them to be registered with document
                        module.verbose('Binding page wide events while handle is being draged');
                        $document.on('mousemove' + eventNamespace, module.event.move);
                        $document.on('mouseup' + eventNamespace, module.event.up);
                    },
                    windowEvents: function () {
                        $window.on('resize' + eventNamespace, module.event.resize);
                    },
                },

                unbind: {
                    events: function () {
                        $module.find('.track, .thumb, .inner').off('mousedown' + eventNamespace);
                        $module.off('mousedown' + eventNamespace);
                        $module.off('mouseenter' + eventNamespace);
                        $module.off('mouseleave' + eventNamespace);
                        $module.find('.thumb')
                            .off('touchstart' + eventNamespace)
                            .off('touchmove' + eventNamespace)
                            .off('touchend' + eventNamespace)
                            .off('touchcancel' + eventNamespace)
                        ;
                        $module.off('keydown' + eventNamespace);
                        $module.off('focusout' + eventNamespace);
                        $document.off('keydown' + eventNamespace + documentEventID, module.event.activateFocus);
                        $window.off('resize' + eventNamespace);
                    },
                    slidingEvents: function () {
                        $document.off('mousemove' + eventNamespace);
                        $document.off('mouseup' + eventNamespace);
                    },
                },

                event: {
                    down: function (event) {
                        event.preventDefault();
                        if (module.is.range()) {
                            var
                                eventPos = module.determine.eventPos(event),
                                newPos = module.determine.pos(eventPos)
                            ;
                            // Special handling if range mode and both thumbs have the same value
                            if (settings.preventCrossover && module.is.range() && module.thumbVal === module.secondThumbVal) {
                                initialPosition = newPos;
                                $currThumb = undefined;
                            } else {
                                $currThumb = module.determine.closestThumb(newPos);
                            }
                            if (previousValue === undefined) {
                                previousValue = module.get.currentThumbValue();
                            }
                        } else if (previousValue === undefined) {
                            previousValue = module.get.value();
                        }

                        if (!module.is.disabled()) {
                            module.bind.slidingEvents();
                        }
                    },
                    touchDown: function (event) {
                        event.preventDefault(); // disable mouse emulation and touch-scrolling
                        event.stopImmediatePropagation();
                        if (touchIdentifier !== undefined) {
                            // ignore multiple touches on the same slider --
                            // we cannot handle changing both thumbs at once due to shared state
                            return;
                        }
                        $currThumb = $(event.target);
                        var touchEvent = event.touches ? event : event.originalEvent;
                        touchIdentifier = touchEvent.targetTouches[0].identifier;
                        if (previousValue === undefined) {
                            previousValue = module.get.currentThumbValue();
                        }
                    },
                    move: function (event) {
                        if (event.type === 'mousemove') {
                            event.preventDefault(); // prevent text selection etc.
                        }
                        if (module.is.disabled()) {
                            // touch events are always bound, so we need to prevent touch-sliding on disabled sliders here
                            return;
                        }
                        var value = module.determine.valueFromEvent(event);
                        if (event.type === 'mousemove' && $currThumb === undefined) {
                            var
                                eventPos = module.determine.eventPos(event),
                                newPos = module.determine.pos(eventPos)
                            ;
                            $currThumb = initialPosition > newPos ? $thumb : $secondThumb;
                        }
                        if (module.is.range() && (settings.minRange || settings.maxRange)) {
                            var currentRangeDiff = module.get.currentRangeDiff(value),
                                isSecondThumb = $currThumb.hasClass('second')
                            ;
                            if ((settings.minRange && currentRangeDiff < settings.minRange)
                                || (settings.maxRange && currentRangeDiff > settings.maxRange)
                                || (settings.preventCrossover && !isSecondThumb && value > module.secondThumbVal)
                                || (settings.preventCrossover && isSecondThumb && value < module.thumbVal)
                            ) {
                                return;
                            }
                        }
                        if (module.get.step() === 0 || module.is.smooth()) {
                            var
                                thumbVal = module.thumbVal,
                                secondThumbVal = module.secondThumbVal,
                                thumbSmoothVal = module.determine.smoothValueFromEvent(event)
                            ;
                            if (!$currThumb.hasClass('second')) {
                                if (settings.preventCrossover && module.is.range()) {
                                    value = Math.min(secondThumbVal, value);
                                    thumbSmoothVal = Math.min(secondThumbVal, thumbSmoothVal);
                                }
                                thumbVal = value;
                            } else {
                                if (settings.preventCrossover && module.is.range()) {
                                    value = Math.max(thumbVal, value);
                                    thumbSmoothVal = Math.max(thumbVal, thumbSmoothVal);
                                }
                                secondThumbVal = value;
                            }
                            value = Math.abs(thumbVal - (secondThumbVal || 0));
                            module.update.position(thumbSmoothVal);
                            settings.onMove.call(element, value, thumbVal, secondThumbVal);
                        } else {
                            module.update.value(value, function (value, thumbVal, secondThumbVal) {
                                settings.onMove.call(element, value, thumbVal, secondThumbVal);
                            });
                        }
                    },
                    up: function (event) {
                        event.preventDefault();
                        if (module.is.disabled()) {
                            // touch events are always bound, so we need to prevent touch-sliding on disabled sliders here
                            return;
                        }
                        var value = module.determine.valueFromEvent(event);
                        if (module.is.range() && (settings.minRange || settings.maxRange)) {
                            if ($currThumb === undefined) {
                                $currThumb = value <= module.get.currentThumbValue() ? $thumb : $secondThumb;
                            }
                            var currentRangeDiff = module.get.currentRangeDiff(value);
                            if (settings.minRange && currentRangeDiff < settings.minRange) {
                                value = module.get.edgeValue(value, settings.minRange);
                            } else if (settings.maxRange && currentRangeDiff > settings.maxRange) {
                                value = module.get.edgeValue(value, settings.maxRange);
                            }
                        }
                        module.set.value(value);
                        module.unbind.slidingEvents();
                        touchIdentifier = undefined;
                        if (previousValue !== undefined) {
                            previousValue = undefined;
                        }
                    },
                    touchCancel: function (event) {
                        event.preventDefault();
                        touchIdentifier = undefined;
                        if (previousValue !== undefined) {
                            module.update.value(previousValue);
                            previousValue = undefined;
                        }
                    },
                    keydown: function (event, first) {
                        if (module.is.disabled()) {
                            return;
                        }
                        if (settings.preventCrossover && module.is.range() && module.thumbVal === module.secondThumbVal) {
                            $currThumb = undefined;
                        }
                        if (module.is.focused()) {
                            $document.trigger(event);
                        }
                        if (first || module.is.focused()) {
                            var step = module.determine.keyMovement(event);
                            if (step !== NO_STEP) {
                                event.preventDefault();
                                switch (step) {
                                    case SINGLE_STEP: {
                                        module.takeStep();

                                        break;
                                    }
                                    case BIG_STEP: {
                                        module.takeStep(module.get.multiplier());

                                        break;
                                    }
                                    case SINGLE_BACKSTEP: {
                                        module.backStep();

                                        break;
                                    }
                                    case BIG_BACKSTEP: {
                                        module.backStep(module.get.multiplier());

                                        break;
                                    }
                                }
                            }
                        }
                    },
                    activateFocus: function (event) {
                        if (!module.is.disabled() && !module.is.focused() && module.is.hover() && module.determine.keyMovement(event) !== NO_STEP) {
                            event.preventDefault();
                            module.event.keydown(event, true);
                            $module.trigger('focus');
                        }
                    },
                    resize: function (_event) {
                        // To avoid a useless performance cost, we only call the label refresh when its necessary
                        if (gapRatio !== module.get.gapRatio()) {
                            module.setup.labels();
                            gapRatio = module.get.gapRatio();
                        }
                    },
                },

                resync: function () {
                    module.verbose('Resyncing thumb position based on value');
                    if (module.is.range()) {
                        module.update.position(module.secondThumbVal, $secondThumb);
                    }
                    module.update.position(module.thumbVal, $thumb);
                    module.setup.labels();
                },
                takeStep: function (multiplier) {
                    if (!multiplier) {
                        multiplier = 1;
                    }
                    var
                        step = module.get.step(),
                        currValue = module.get.currentThumbValue()
                    ;
                    module.verbose('Taking a step');
                    if (step > 0) {
                        module.set.value(currValue + step * multiplier);
                    } else if (step === 0) {
                        var
                            precision = module.get.precision(),
                            newValue = currValue + (multiplier / precision)
                        ;
                        module.set.value(Math.round(newValue * precision) / precision);
                    }
                },

                backStep: function (multiplier) {
                    if (!multiplier) {
                        multiplier = 1;
                    }
                    var
                        step = module.get.step(),
                        currValue = module.get.currentThumbValue()
                    ;
                    module.verbose('Going back a step');
                    if (step > 0) {
                        module.set.value(currValue - step * multiplier);
                    } else if (step === 0) {
                        var
                            precision = module.get.precision(),
                            newValue = currValue - (multiplier / precision)
                        ;
                        module.set.value(Math.round(newValue * precision) / precision);
                    }
                },

                is: {
                    range: function () {
                        var isRange = $module.hasClass(className.range);
                        if (!isRange && (settings.minRange || settings.maxRange)) {
                            $module.addClass(className.range);
                            isRange = true;
                        }

                        return isRange;
                    },
                    hover: function () {
                        return isHover;
                    },
                    focused: function () {
                        return $module.is(':focus');
                    },
                    disabled: function () {
                        return $module.hasClass(className.disabled);
                    },
                    labeled: function () {
                        var isLabeled = $module.hasClass(className.labeled);
                        if (!isLabeled && (settings.restrictedLabels.length > 0 || settings.showLabelTicks !== false)) {
                            $module.addClass(className.labeled);
                            isLabeled = true;
                        }

                        return isLabeled;
                    },
                    reversed: function () {
                        return $module.hasClass(className.reversed);
                    },
                    vertical: function () {
                        return $module.hasClass(className.vertical);
                    },
                    smooth: function () {
                        return settings.smooth || $module.hasClass(className.smooth);
                    },
                },

                get: {
                    currentRangeDiff: function (value) {
                        var currentRangeDiff;
                        if ($currThumb.hasClass('second')) {
                            currentRangeDiff = module.thumbVal < value
                                ? value - module.thumbVal
                                : module.thumbVal - value;
                        } else {
                            currentRangeDiff = module.secondThumbVal > value
                                ? module.secondThumbVal - value
                                : value - module.secondThumbVal;
                        }

                        return currentRangeDiff;
                    },
                    edgeValue: function (value, edgeValue) {
                        if ($currThumb.hasClass('second')) {
                            value = module.thumbVal < value
                                ? module.thumbVal + edgeValue
                                : module.thumbVal - edgeValue;
                        } else {
                            value = module.secondThumbVal < value
                                ? module.secondThumbVal + edgeValue
                                : module.secondThumbVal - edgeValue;
                        }

                        return value;
                    },
                    trackOffset: function () {
                        if (module.is.vertical()) {
                            return $track.offset().top;
                        }

                        return $track.offset().left;
                    },
                    trackLength: function () {
                        if (module.is.vertical()) {
                            return $track.height();
                        }

                        return $track.width();
                    },
                    trackLeft: function () {
                        if (module.is.vertical()) {
                            return $track.position().top;
                        }

                        return $track.position().left;
                    },
                    trackStartPos: function () {
                        return module.is.reversed() ? module.get.trackLeft() + module.get.trackLength() : module.get.trackLeft();
                    },
                    trackEndPos: function () {
                        return module.is.reversed() ? module.get.trackLeft() : module.get.trackLeft() + module.get.trackLength();
                    },
                    trackStartMargin: function () {
                        var margin;
                        if (module.is.vertical()) {
                            margin = module.is.reversed() ? $module.css('padding-bottom') : $module.css('padding-top');
                        } else {
                            margin = module.is.reversed() ? $module.css('padding-right') : $module.css('padding-left');
                        }

                        return margin || '0px';
                    },
                    trackEndMargin: function () {
                        var margin;
                        if (module.is.vertical()) {
                            margin = module.is.reversed() ? $module.css('padding-top') : $module.css('padding-bottom');
                        } else {
                            margin = module.is.reversed() ? $module.css('padding-left') : $module.css('padding-right');
                        }

                        return margin || '0px';
                    },
                    precision: function () {
                        var
                            decimalPlaces,
                            step = module.get.step()
                        ;
                        if (step !== 0) {
                            var split = String(step).split('.');
                            decimalPlaces = split.length === 2 ? split[1].length : 0;
                        } else {
                            decimalPlaces = settings.decimalPlaces;
                        }
                        var precision = Math.pow(10, decimalPlaces);
                        module.debug('Precision determined', precision);

                        return precision;
                    },
                    min: function () {
                        return settings.min;
                    },
                    max: function () {
                        var
                            step = module.get.step(),
                            min = module.get.min(),
                            precision = module.get.precision(),
                            quotient = step === 0 ? 0 : Math.floor(Math.round(((settings.max - min) / step) * precision) / precision),
                            remainder = step === 0 ? 0 : (settings.max - min) % step
                        ;

                        return remainder === 0 ? settings.max : min + quotient * step;
                    },
                    step: function () {
                        return settings.step;
                    },
                    numLabels: function () {
                        var step = module.get.step(),
                            precision = module.get.precision(),
                            value = Math.round(((module.get.max() - module.get.min()) / (step === 0 ? 1 : step)) * precision) / precision;
                        module.debug('Determined that there should be ' + value + ' labels');

                        return value;
                    },
                    labelType: function () {
                        return settings.labelType;
                    },
                    label: function (value) {
                        if (interpretLabel) {
                            return interpretLabel(value);
                        }

                        switch (settings.labelType) {
                            case settings.labelTypes.number: {
                                var step = module.get.step();

                                return Math.round(((value * (step === 0 ? 1 : step)) + module.get.min()) * precision) / precision;
                            }
                            case settings.labelTypes.letter: {
                                return alphabet[value % 26];
                            }
                            default: {
                                return value;
                            }
                        }
                    },
                    value: function () {
                        return value;
                    },
                    currentThumbValue: function () {
                        return $currThumb !== undefined && $currThumb.hasClass('second') ? module.secondThumbVal : module.thumbVal;
                    },
                    thumbValue: function (which) {
                        switch (which) {
                            case 'second': {
                                if (module.is.range()) {
                                    return module.secondThumbVal;
                                }

                                module.error(error.notrange);

                                break;
                            }
                            default: {
                                return module.thumbVal;
                            }
                        }
                    },
                    multiplier: function () {
                        return settings.pageMultiplier;
                    },
                    thumbPosition: function (which) {
                        switch (which) {
                            case 'second': {
                                if (module.is.range()) {
                                    return secondPos;
                                }

                                module.error(error.notrange);

                                break;
                            }
                            default: {
                                return position;
                            }
                        }
                    },
                    gapRatio: function () {
                        var gapRatio = 1;

                        if (settings.autoAdjustLabels) {
                            var
                                numLabels = module.get.numLabels(),
                                trackLength = module.get.trackLength(),
                                gapCounter = 1
                            ;

                            // While the distance between two labels is too short,
                            // we divide the number of labels at each iteration
                            // and apply only if the modulo of the operation is an odd number.
                            if (trackLength > 0) {
                                while ((trackLength / numLabels) * gapCounter < settings.labelDistance) {
                                    if (!(numLabels % gapCounter)) {
                                        gapRatio = gapCounter;
                                    }
                                    gapCounter += 1;
                                }
                            }
                        }

                        return gapRatio;
                    },
                },

                determine: {
                    pos: function (pagePos) {
                        return module.is.reversed()
                            ? module.get.trackStartPos() - pagePos + module.get.trackOffset()
                            : pagePos - module.get.trackOffset() - module.get.trackStartPos();
                    },
                    closestThumb: function (eventPos) {
                        var
                            thumbPos = parseFloat(module.determine.thumbPos($thumb)),
                            thumbDelta = Math.abs(eventPos - thumbPos),
                            secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
                            secondThumbDelta = Math.abs(eventPos - secondThumbPos)
                        ;
                        if (thumbDelta === secondThumbDelta && module.get.thumbValue() === module.get.min()) {
                            return $secondThumb;
                        }

                        return thumbDelta <= secondThumbDelta ? $thumb : $secondThumb;
                    },
                    closestThumbPos: function (eventPos) {
                        var
                            thumbPos = parseFloat(module.determine.thumbPos($thumb)),
                            thumbDelta = Math.abs(eventPos - thumbPos),
                            secondThumbPos = parseFloat(module.determine.thumbPos($secondThumb)),
                            secondThumbDelta = Math.abs(eventPos - secondThumbPos)
                        ;

                        return thumbDelta <= secondThumbDelta ? thumbPos : secondThumbPos;
                    },
                    thumbPos: function ($element) {
                        return module.is.vertical()
                            ? (module.is.reversed() ? $element.css('bottom') : $element.css('top'))
                            : (module.is.reversed() ? $element.css('right') : $element.css('left'))
                        ;
                    },
                    positionFromValue: function (val) {
                        var
                            min = module.get.min(),
                            max = module.get.max(),
                            value = val > max
                                ? max
                                : (val < min ? min : val),
                            trackLength = module.get.trackLength(),
                            ratio = (value - min) / (max - min),
                            position = Math.round(ratio * trackLength)
                        ;
                        module.verbose('Determined position: ' + position + ' from value: ' + value);

                        return position;
                    },
                    positionFromRatio: function (ratio) {
                        var
                            trackLength = module.get.trackLength(),
                            step = module.get.step(),
                            position = Math.round(ratio * trackLength),
                            adjustedPos = step === 0 ? position : Math.round(position / step) * step
                        ;
                        module.verbose('Determined position: ' + position + ' from ratio: ' + ratio);

                        return adjustedPos;
                    },
                    valueFromEvent: function (event) {
                        var
                            eventPos = module.determine.eventPos(event),
                            newPos = module.determine.pos(eventPos),
                            value
                        ;
                        if (eventPos < module.get.trackOffset()) {
                            value = module.is.reversed() ? module.get.max() : module.get.min();
                        } else if (eventPos > module.get.trackOffset() + module.get.trackLength()) {
                            value = module.is.reversed() ? module.get.min() : module.get.max();
                        } else {
                            value = module.determine.value(newPos);
                        }

                        return value;
                    },
                    smoothValueFromEvent: function (event) {
                        var
                            min = module.get.min(),
                            max = module.get.max(),
                            trackLength = module.get.trackLength(),
                            eventPos = module.determine.eventPos(event),
                            newPos = eventPos - module.get.trackOffset(),
                            ratio,
                            value
                        ;
                        newPos = newPos < 0
                            ? 0
                            : (newPos > trackLength ? trackLength : newPos);
                        ratio = newPos / trackLength;
                        if (module.is.reversed()) {
                            ratio = 1 - ratio;
                        }
                        value = ratio * (max - min) + min;

                        return value;
                    },
                    eventPos: function (event) {
                        if (event.type === 'touchmove' || event.type === 'touchend') {
                            var
                                touchEvent = event.touches ? event : event.originalEvent,
                                touch = touchEvent.changedTouches[0]; // fall back to first touch if correct touch not found
                            for (var i = 0; i < touchEvent.touches.length; i++) {
                                if (touchEvent.touches[i].identifier === touchIdentifier) {
                                    touch = touchEvent.touches[i];

                                    break;
                                }
                            }
                            var
                                touchY = touch.pageY,
                                touchX = touch.pageX
                            ;

                            return module.is.vertical() ? touchY : touchX;
                        }
                        var
                            clickY = event.pageY || event.originalEvent.pageY,
                            clickX = event.pageX || event.originalEvent.pageX
                        ;

                        return module.is.vertical() ? clickY : clickX;
                    },
                    value: function (position) {
                        var
                            startPos = module.is.reversed() ? module.get.trackEndPos() : module.get.trackStartPos(),
                            endPos = module.is.reversed() ? module.get.trackStartPos() : module.get.trackEndPos(),
                            ratio = (position - startPos) / (endPos - startPos),
                            range = module.get.max() - module.get.min(),
                            step = module.get.step(),
                            value = ratio * range,
                            difference = step === 0 ? value : Math.round(value / step) * step
                        ;
                        module.verbose('Determined value based upon position: ' + position + ' as: ' + value);
                        if (value !== difference) {
                            module.verbose('Rounding value to closest step: ' + difference);
                        }
                        // Use precision to avoid ugly Javascript floating point rounding issues
                        // (like 35 * .01 = 0.35000000000000003)
                        module.verbose('Cutting off additional decimal places');

                        return Math.round((difference + module.get.min()) * precision) / precision;
                    },
                    keyMovement: function (event) {
                        var
                            key = event.which,
                            downArrow = module.is.vertical()
                                ? (module.is.reversed() ? keys.downArrow : keys.upArrow)
                                : keys.downArrow,
                            upArrow = module.is.vertical()
                                ? (module.is.reversed() ? keys.upArrow : keys.downArrow)
                                : keys.upArrow,
                            leftArrow = !module.is.vertical()
                                ? (module.is.reversed() ? keys.rightArrow : keys.leftArrow)
                                : keys.leftArrow,
                            rightArrow = !module.is.vertical()
                                ? (module.is.reversed() ? keys.leftArrow : keys.rightArrow)
                                : keys.rightArrow
                        ;
                        if (key === downArrow || key === leftArrow) {
                            return SINGLE_BACKSTEP;
                        }
                        if (key === upArrow || key === rightArrow) {
                            return SINGLE_STEP;
                        }
                        if (key === keys.pageDown) {
                            return BIG_BACKSTEP;
                        }
                        if (key === keys.pageUp) {
                            return BIG_STEP;
                        }

                        return NO_STEP;
                    },
                },

                handleNewValuePosition: function (val) {
                    var
                        min = module.get.min(),
                        max = module.get.max(),
                        newPos
                    ;
                    if (val <= min) {
                        val = min;
                    } else if (val >= max) {
                        val = max;
                    }
                    newPos = module.determine.positionFromValue(val);

                    return newPos;
                },

                set: {
                    value: function (newValue, fireChange) {
                        fireChange = fireChange !== false;
                        var toReset = previousValue === undefined;
                        previousValue = previousValue === undefined ? module.get.value() : previousValue;
                        module.update.value(newValue, function (value, thumbVal, secondThumbVal) {
                            if ((!initialLoad || settings.fireOnInit) && fireChange) {
                                if (newValue !== previousValue) {
                                    settings.onChange.call(element, value, thumbVal, secondThumbVal);
                                }
                                settings.onMove.call(element, value, thumbVal, secondThumbVal);
                            }
                            if (toReset) {
                                previousValue = undefined;
                            }
                        });
                    },
                    rangeValue: function (first, second, fireChange) {
                        fireChange = fireChange !== false;
                        if (module.is.range()) {
                            var
                                min = module.get.min(),
                                max = module.get.max(),
                                toReset = previousValue === undefined
                            ;
                            previousValue = previousValue === undefined ? module.get.value() : previousValue;
                            if (first <= min) {
                                first = min;
                            } else if (first >= max) {
                                first = max;
                            }
                            if (second <= min) {
                                second = min;
                            } else if (second >= max) {
                                second = max;
                            }
                            module.thumbVal = first;
                            module.secondThumbVal = second;
                            value = Math.abs(module.thumbVal - module.secondThumbVal);
                            module.update.position(module.thumbVal, $thumb);
                            module.update.position(module.secondThumbVal, $secondThumb);
                            if ((!initialLoad || settings.fireOnInit) && fireChange) {
                                if (value !== previousValue) {
                                    settings.onChange.call(element, value, module.thumbVal, module.secondThumbVal);
                                }
                                settings.onMove.call(element, value, module.thumbVal, module.secondThumbVal);
                            }
                            if (toReset) {
                                previousValue = undefined;
                            }
                        } else {
                            module.error(error.notrange);
                        }
                    },
                    position: function (position, which) {
                        var thumbVal = module.determine.value(position);
                        if (which === 'second') {
                            module.secondThumbVal = thumbVal;
                            module.update.position(thumbVal, $secondThumb);
                        } else {
                            module.thumbVal = thumbVal;
                            module.update.position(thumbVal, $thumb);
                        }
                        value = Math.abs(module.thumbVal - (module.secondThumbVal || 0));
                        module.set.value(value);
                    },
                },

                update: {
                    value: function (newValue, callback) {
                        var
                            min = module.get.min(),
                            max = module.get.max()
                        ;
                        if (newValue <= min) {
                            newValue = min;
                        } else if (newValue >= max) {
                            newValue = max;
                        }
                        if (!module.is.range()) {
                            value = newValue;
                            module.thumbVal = value;
                        } else {
                            if ($currThumb === undefined) {
                                $currThumb = newValue <= module.get.currentThumbValue() ? $thumb : $secondThumb;
                            }
                            if (!$currThumb.hasClass('second')) {
                                if (settings.preventCrossover && module.is.range()) {
                                    newValue = Math.min(module.secondThumbVal - (settings.minRange || 0), newValue);
                                }
                                module.thumbVal = newValue;
                            } else {
                                if (settings.preventCrossover && module.is.range()) {
                                    newValue = Math.max(module.thumbVal + (settings.minRange || 0), newValue);
                                }
                                module.secondThumbVal = newValue;
                            }
                            value = Math.abs(module.thumbVal - module.secondThumbVal);
                        }
                        module.update.position(newValue);
                        module.debug('Setting slider value to ' + value);
                        if (typeof callback === 'function') {
                            callback(value, module.thumbVal, module.secondThumbVal);
                        }
                    },
                    position: function (newValue, $element) {
                        var
                            newPos = module.handleNewValuePosition(newValue),
                            $targetThumb = $element || $currThumb,
                            thumbVal = module.thumbVal || module.get.min(),
                            secondThumbVal = module.secondThumbVal || module.get.min()
                        ;
                        if (settings.showThumbTooltip) {
                            var precision = module.get.precision();
                            $targetThumb.attr('data-tooltip', Math.round(newValue * precision) / precision);
                        }
                        if (module.is.range()) {
                            if (!$targetThumb.hasClass('second')) {
                                position = newPos;
                                thumbVal = newValue;
                            } else {
                                secondPos = newPos;
                                secondThumbVal = newValue;
                            }
                        } else {
                            position = newPos;
                            thumbVal = newValue;
                        }
                        var
                            trackPosValue,
                            thumbPosValue,
                            min = module.get.min(),
                            max = module.get.max(),
                            thumbPosPercent = 100 * ((newValue - min) / (max - min)),
                            trackStartPosPercent = 100 * ((Math.min(thumbVal, secondThumbVal) - min) / (max - min)),
                            trackEndPosPercent = 100 * (1 - (Math.max(thumbVal, secondThumbVal) - min) / (max - min))
                        ;
                        if (module.is.vertical()) {
                            if (module.is.reversed()) {
                                thumbPosValue = { bottom: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', top: 'auto' };
                                trackPosValue = { bottom: trackStartPosPercent + '%', top: trackEndPosPercent + '%' };
                            } else {
                                thumbPosValue = { top: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', bottom: 'auto' };
                                trackPosValue = { top: trackStartPosPercent + '%', bottom: trackEndPosPercent + '%' };
                            }
                        } else {
                            if (module.is.reversed()) {
                                thumbPosValue = { right: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', left: 'auto' };
                                trackPosValue = { right: trackStartPosPercent + '%', left: trackEndPosPercent + '%' };
                            } else {
                                thumbPosValue = { left: 'calc(' + thumbPosPercent + '% - ' + offset + 'px)', right: 'auto' };
                                trackPosValue = { left: trackStartPosPercent + '%', right: trackEndPosPercent + '%' };
                            }
                        }
                        $targetThumb.css(thumbPosValue);
                        $trackFill.css(trackPosValue);
                        module.debug('Setting slider position to ' + newPos);
                    },
                    labelPosition: function (ratio, $label) {
                        var
                            startMargin = module.get.trackStartMargin(),
                            endMargin   = module.get.trackEndMargin(),
                            posDir = module.is.vertical()
                                ? (module.is.reversed() ? 'bottom' : 'top')
                                : (module.is.reversed() ? 'right' : 'left'),
                            startMarginMod = module.is.reversed() && !module.is.vertical()
                                ? ' - '
                                : ' + '
                        ;
                        var position = '(100% - ' + startMargin + ' - ' + endMargin + ') * ' + ratio;
                        $label.css(posDir, 'calc(' + position + startMarginMod + startMargin + ')');
                    },
                },

                goto: {
                    max: function () {
                        module.set.value(module.get.max());
                    },
                    min: function () {
                        module.set.value(module.get.min());
                    },
                },

                read: {
                    metadata: function () {
                        var
                            data = {
                                thumbVal: $module.data(metadata.thumbVal),
                                secondThumbVal: $module.data(metadata.secondThumbVal),
                            }
                        ;
                        if (data.thumbVal) {
                            if (module.is.range() && data.secondThumbVal) {
                                module.debug('Current value set from metadata', data.thumbVal, data.secondThumbVal);
                                module.set.rangeValue(data.thumbVal, data.secondThumbVal);
                            } else {
                                module.debug('Current value set from metadata', data.thumbVal);
                                module.set.value(data.thumbVal);
                            }
                        }
                    },
                    settings: function () {
                        if (settings.start !== false) {
                            if (module.is.range()) {
                                var rangeDiff = settings.end - settings.start;
                                if (rangeDiff < 0
                                    || (settings.minRange && rangeDiff < settings.minRange)
                                    || (settings.maxRange && rangeDiff > settings.maxRange)
                                    || (settings.minRange && settings.maxRange && settings.minRange > settings.maxRange)
                                ) {
                                    module.error(error.invalidRanges, settings.start, settings.end, settings.minRange, settings.maxRange);
                                }
                                module.debug('Start position set from settings', settings.start, settings.end);
                                module.set.rangeValue(settings.start, settings.end);
                            } else {
                                module.debug('Start position set from settings', settings.start);
                                module.set.value(settings.start);
                            }
                        }
                    },
                },

                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.slider.settings = {

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        name: 'Slider',
        namespace: 'slider',

        error: {
            method: 'The method you called is not defined.',
            notrange: 'This slider is not a range slider',
            invalidRanges: 'Invalid range settings (start/end/minRange/maxRange)',
        },

        metadata: {
            thumbVal: 'thumbVal',
            secondThumbVal: 'secondThumbVal',
        },

        min: 0,
        max: 20,
        step: 1,
        start: 0,
        end: 20,
        minRange: false,
        maxRange: false,
        labelType: 'number',
        showLabelTicks: false,
        smooth: false,
        autoAdjustLabels: true,
        labelDistance: 100,
        preventCrossover: true,
        fireOnInit: false,
        interpretLabel: false,

        // the decimal place to round to if step is undefined
        decimalPlaces: 2,

        // page up/down multiplier. How many more times the steps to take on page up/down press
        pageMultiplier: 2,

        selector: {},

        className: {
            reversed: 'reversed',
            disabled: 'disabled',
            labeled: 'labeled',
            ticked: 'ticked',
            vertical: 'vertical',
            range: 'range',
            smooth: 'smooth',
        },

        keys: {
            pageUp: 33,
            pageDown: 34,
            leftArrow: 37,
            upArrow: 38,
            rightArrow: 39,
            downArrow: 40,
        },

        restrictedLabels: [],
        showThumbTooltip: false,
        tooltipConfig: {
            position: 'top center',
            variation: 'tiny black',
        },

        labelTypes: {
            number: 'number',
            letter: 'letter',
        },

        onChange: function (value, thumbVal, secondThumbVal) {},
        onMove: function (value, thumbVal, secondThumbVal) {},
    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Rating
 * 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.rating = 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.rating.settings, parameters)
                    : $.extend({}, $.fn.rating.settings),

                namespace       = settings.namespace,
                className       = settings.className,
                error           = settings.error,
                metadata        = settings.metadata,
                selector        = settings.selector,
                cssVars         = settings.cssVars,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                element         = this,
                instance        = $(this).data(moduleNamespace),

                $module         = $(this),
                $icon           = $module.find(selector.icon),

                initialLoad,
                module
            ;

            module = {

                initialize: function () {
                    module.verbose('Initializing rating module', settings);

                    if ($icon.length === 0) {
                        module.setup.layout();
                    }

                    if (settings.interactive && !module.is.disabled()) {
                        module.enable();
                    } else {
                        module.disable();
                    }
                    module.set.initialLoad();
                    module.set.rating(module.get.initialRating());
                    module.remove.initialLoad();
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Instantiating module', settings);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous instance', instance);
                    module.remove.events();
                    $module
                        .removeData(moduleNamespace)
                    ;
                },

                refresh: function () {
                    $icon = $module.find(selector.icon);
                },

                setup: {
                    layout: function () {
                        var
                            maxRating = module.get.maxRating(),
                            icon      = module.get.icon(),
                            html      = $.fn.rating.settings.templates.icon(maxRating, icon)
                        ;
                        module.debug('Generating icon html dynamically');
                        $module
                            .html(html)
                        ;
                        module.refresh();
                    },
                },

                event: {
                    mouseenter: function () {
                        var
                            $activeIcon = $(this)
                        ;
                        $activeIcon
                            .nextAll()
                            .removeClass(className.selected)
                        ;
                        $module
                            .addClass(className.selected)
                        ;
                        $activeIcon
                            .addClass(className.selected)
                            .prevAll()
                            .addClass(className.selected)
                        ;
                    },
                    mouseleave: function () {
                        $module
                            .removeClass(className.selected)
                        ;
                        $icon
                            .removeClass(className.selected)
                        ;
                    },
                    click: function () {
                        var
                            $activeIcon   = $(this),
                            currentRating = module.get.rating(),
                            rating        = $icon.index($activeIcon) + 1,
                            canClear      = settings.clearable === 'auto'
                                ? $icon.length === 1
                                : settings.clearable
                        ;
                        if (canClear && currentRating === rating) {
                            module.clearRating();
                        } else {
                            module.set.rating(rating);
                        }
                    },
                },

                clearRating: function () {
                    module.debug('Clearing current rating');
                    module.set.rating(0);
                },

                bind: {
                    events: function () {
                        module.verbose('Binding events');
                        $module
                            .on('mouseenter' + eventNamespace, selector.icon, module.event.mouseenter)
                            .on('mouseleave' + eventNamespace, selector.icon, module.event.mouseleave)
                            .on('click' + eventNamespace, selector.icon, module.event.click)
                        ;
                    },
                },

                remove: {
                    events: function () {
                        module.verbose('Removing events');
                        $module
                            .off(eventNamespace)
                        ;
                    },
                    initialLoad: function () {
                        initialLoad = false;
                    },
                },

                enable: function () {
                    module.debug('Setting rating to interactive mode');
                    module.bind.events();
                    $module
                        .removeClass(className.disabled)
                    ;
                },

                disable: function () {
                    module.debug('Setting rating to read-only mode');
                    module.remove.events();
                    $module
                        .addClass(className.disabled)
                    ;
                },

                is: {
                    initialLoad: function () {
                        return initialLoad;
                    },
                    disabled: function () {
                        return $module.hasClass(className.disabled);
                    },
                },

                get: {
                    icon: function () {
                        var icon = $module.data(metadata.icon);
                        if (icon) {
                            $module.removeData(metadata.icon);
                        }

                        return icon || settings.icon;
                    },
                    initialRating: function () {
                        if ($module.data(metadata.rating) !== undefined) {
                            $module.removeData(metadata.rating);

                            return $module.data(metadata.rating);
                        }

                        return settings.initialRating;
                    },
                    maxRating: function () {
                        if ($module.data(metadata.maxRating) !== undefined) {
                            $module.removeData(metadata.maxRating);

                            return $module.data(metadata.maxRating);
                        }

                        return settings.maxRating;
                    },
                    rating: function () {
                        var
                            currentRating = $icon.filter('.' + className.active).length
                        ;
                        module.verbose('Current rating retrieved', currentRating);

                        return currentRating;
                    },
                },

                set: {
                    rating: function (rating) {
                        var
                            ratingIndex = Math.floor(
                                rating - 1 >= 0
                                    ? rating - 1
                                    : 0
                            ),
                            $activeIcon = $icon.eq(ratingIndex),
                            $partialActiveIcon = rating <= 1
                                ? $activeIcon
                                : $activeIcon.next(),
                            filledPercentage = (rating % 1) * 100
                        ;
                        $module
                            .removeClass(className.selected)
                        ;
                        $icon
                            .removeClass(className.selected)
                            .removeClass(className.active)
                            .removeClass(className.partiallyActive)
                        ;
                        if (rating > 0) {
                            module.verbose('Setting current rating to', rating);
                            $activeIcon
                                .prevAll()
                                .addBack()
                                .addClass(className.active)
                            ;
                            if ($activeIcon.next() && rating % 1 !== 0) {
                                $partialActiveIcon
                                    .addClass(className.partiallyActive)
                                    .addClass(className.active)
                                ;
                                $partialActiveIcon
                                    .css(cssVars.filledCustomPropName, filledPercentage + '%')
                                ;
                                if ($partialActiveIcon.css('backgroundColor') === 'transparent') {
                                    $partialActiveIcon
                                        .removeClass(className.partiallyActive)
                                        .removeClass(className.active)
                                    ;
                                }
                            }
                        }
                        if (!module.is.initialLoad()) {
                            settings.onRate.call(element, rating);
                        }
                    },
                    initialLoad: function () {
                        initialLoad = true;
                    },
                },

                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 ($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;
                },
            };
            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.rating.settings = {

        name: 'Rating',
        namespace: 'rating',

        icon: 'star',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        initialRating: 0,
        interactive: true,
        maxRating: 4,
        clearable: 'auto',

        fireOnInit: false,

        onRate: function (rating) {},

        error: {
            method: 'The method you called is not defined',
        },

        metadata: {
            rating: 'rating',
            maxRating: 'maxRating',
            icon: 'icon',
        },

        className: {
            active: 'active',
            disabled: 'disabled',
            selected: 'selected',
            loading: 'loading',
            partiallyActive: 'partial',
        },

        cssVars: {
            filledCustomPropName: '--full',
        },

        selector: {
            icon: '.icon',
        },

        templates: {
            deQuote: function (string, encode) {
                return String(string).replace(/"/g, encode ? '&quot;' : '');
            },
            icon: function (maxRating, iconClass) {
                var
                    icon = 1,
                    html = '',
                    deQuote = $.fn.rating.settings.templates.deQuote
                ;
                while (icon <= maxRating) {
                    html += '<i class="' + deQuote(iconClass) + ' icon"></i>';
                    icon++;
                }

                return html;
            },
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Search
 * 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.search = 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.search.settings, parameters)
                    : $.extend({}, $.fn.search.settings),

                className        = settings.className,
                metadata         = settings.metadata,
                regExp           = settings.regExp,
                fields           = settings.fields,
                selector         = settings.selector,
                error            = settings.error,
                namespace        = settings.namespace,

                eventNamespace   = '.' + namespace,
                moduleNamespace  = namespace + '-module',

                $module          = $(this),
                $prompt          = $module.find(selector.prompt),
                $searchButton    = $module.find(selector.searchButton),
                $results         = $module.find(selector.results),
                $result          = $module.find(selector.result),
                $category        = $module.find(selector.category),

                element          = this,
                instance         = $module.data(moduleNamespace),

                disabledBubbled  = false,
                resultsDismissed = false,

                module
            ;

            module = {

                initialize: function () {
                    module.verbose('Initializing module');
                    module.get.settings();
                    module.determine.searchFields();
                    module.bind.events();
                    module.set.type();
                    module.create.results();
                    module.instantiate();
                },
                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },
                destroy: function () {
                    module.verbose('Destroying instance');
                    $module
                        .off(eventNamespace)
                        .removeData(moduleNamespace)
                    ;
                },

                refresh: function () {
                    module.debug('Refreshing selector cache');
                    $prompt = $module.find(selector.prompt);
                    $searchButton = $module.find(selector.searchButton);
                    $category = $module.find(selector.category);
                    $results = $module.find(selector.results);
                    $result = $module.find(selector.result);
                },

                refreshResults: function () {
                    $results = $module.find(selector.results);
                    $result = $module.find(selector.result);
                },

                bind: {
                    events: function () {
                        module.verbose('Binding events to search');
                        if (settings.automatic) {
                            $module
                                .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
                            ;
                            $prompt
                                .attr('autocomplete', module.is.chrome() ? 'fomantic-search' : 'off')
                            ;
                        }
                        $module
                            // prompt
                            .on('focus' + eventNamespace, selector.prompt, module.event.focus)
                            .on('blur' + eventNamespace, selector.prompt, module.event.blur)
                            .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
                            // search button
                            .on('click' + eventNamespace, selector.searchButton, module.query)
                            // results
                            .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
                            .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
                            .on('click' + eventNamespace, selector.result, module.event.result.click)
                        ;
                    },
                },

                determine: {
                    searchFields: function () {
                        // this makes sure $.extend does not add specified search fields to default fields
                        // this is the only setting which should not extend defaults
                        if (parameters && parameters.searchFields !== undefined) {
                            settings.searchFields = parameters.searchFields;
                        }
                    },
                },

                event: {
                    input: function () {
                        if (settings.searchDelay) {
                            clearTimeout(module.timer);
                            module.timer = setTimeout(function () {
                                if (module.is.focused()) {
                                    module.query();
                                }
                            }, settings.searchDelay);
                        } else {
                            module.query();
                        }
                    },
                    focus: function () {
                        module.set.focus();
                        if (settings.searchOnFocus && module.has.minimumCharacters()) {
                            module.query(function () {
                                if (module.can.show()) {
                                    module.showResults();
                                }
                            });
                        }
                    },
                    blur: function (event) {
                        var
                            pageLostFocus = document.activeElement === this,
                            callback      = function () {
                                module.cancel.query();
                                module.remove.focus();
                                module.timer = setTimeout(function () { module.hideResults(); }, settings.hideDelay);
                            }
                        ;
                        if (pageLostFocus) {
                            return;
                        }
                        resultsDismissed = false;
                        if (module.resultsClicked) {
                            module.debug('Determining if user action caused search to close');
                            $module
                                .one('click.close' + eventNamespace, selector.results, function (event) {
                                    if (module.is.inMessage(event) || disabledBubbled) {
                                        $prompt.trigger('focus');

                                        return;
                                    }
                                    disabledBubbled = false;
                                    if (!module.is.animating() && !module.is.hidden()) {
                                        callback();
                                    }
                                })
                            ;
                        } else {
                            module.debug('Input blurred without user action, closing results');
                            callback();
                        }
                    },
                    result: {
                        mousedown: function () {
                            module.resultsClicked = true;
                        },
                        mouseup: function () {
                            module.resultsClicked = false;
                        },
                        click: function (event) {
                            module.debug('Search result selected');
                            var
                                $result = $(this),
                                $title  = $result.find(selector.title).eq(0),
                                $link   = $result.is('a[href]')
                                    ? $result
                                    : $result.find('a[href]').eq(0),
                                href    = $link.attr('href') || false,
                                target  = $link.attr('target') || false,
                                // title is used for result lookup
                                value   = $title.length > 0
                                    ? $title.text()
                                    : false,
                                results = module.get.results(),
                                result  = $result.data(metadata.result) || module.get.result(value, results)
                            ;
                            var oldValue = module.get.value();
                            if (isFunction(settings.onSelect)) {
                                if (settings.onSelect.call(element, result, results) === false) {
                                    module.debug('Custom onSelect callback cancelled default select action');
                                    disabledBubbled = true;

                                    return;
                                }
                            }
                            module.hideResults();
                            if (value && module.get.value() === oldValue) {
                                module.set.value(value);
                            }
                            if (href) {
                                event.preventDefault();
                                module.verbose('Opening search link found in result', $link);
                                if (target === '_blank' || event.ctrlKey) {
                                    window.open(href);
                                } else {
                                    window.location.href = href;
                                }
                            }
                        },
                    },
                },
                ensureVisible: function ($el) {
                    var
                        elTop,
                        elBottom,
                        resultsScrollTop,
                        resultsHeight
                    ;
                    if ($el.length === 0) {
                        return;
                    }
                    elTop = $el.position().top;
                    elBottom = elTop + $el.outerHeight(true);

                    resultsScrollTop = $results.scrollTop();
                    resultsHeight = $results.height();

                    if (elTop < 0) {
                        $results.scrollTop(resultsScrollTop + elTop);
                    } else if (resultsHeight < elBottom) {
                        $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
                    }
                },
                handleKeyboard: function (event) {
                    var
                        // force selector refresh
                        $result         = $module.find(selector.result),
                        $category       = $module.find(selector.category),
                        $activeResult   = $result.filter('.' + className.active),
                        currentIndex    = $result.index($activeResult),
                        resultSize      = $result.length,
                        hasActiveResult = $activeResult.length > 0,

                        keyCode         = event.which,
                        keys            = {
                            backspace: 8,
                            enter: 13,
                            escape: 27,
                            upArrow: 38,
                            downArrow: 40,
                        },
                        newIndex
                    ;
                    // search shortcuts
                    if (keyCode === keys.escape) {
                        if (!module.is.visible()) {
                            module.verbose('Escape key pressed, blurring search field');
                            $prompt.trigger('blur');
                        } else {
                            module.hideResults();
                        }
                        event.stopPropagation();
                        resultsDismissed = true;
                    }
                    if (module.is.visible()) {
                        if (keyCode === keys.enter) {
                            module.verbose('Enter key pressed, selecting active result');
                            if ($result.filter('.' + className.active).length > 0) {
                                module.event.result.click.call($result.filter('.' + className.active), event);
                                event.preventDefault();

                                return false;
                            }
                        } else if (keyCode === keys.upArrow && hasActiveResult) {
                            module.verbose('Up key pressed, changing active result');
                            newIndex = currentIndex - 1 < 0
                                ? currentIndex
                                : currentIndex - 1;
                            $category
                                .removeClass(className.active)
                            ;
                            $result
                                .removeClass(className.active)
                                .eq(newIndex)
                                .addClass(className.active)
                                .closest($category)
                                .addClass(className.active)
                            ;
                            module.ensureVisible($result.eq(newIndex));
                            event.preventDefault();
                        } else if (keyCode === keys.downArrow) {
                            module.verbose('Down key pressed, changing active result');
                            newIndex = currentIndex + 1 >= resultSize
                                ? currentIndex
                                : currentIndex + 1;
                            $category
                                .removeClass(className.active)
                            ;
                            $result
                                .removeClass(className.active)
                                .eq(newIndex)
                                .addClass(className.active)
                                .closest($category)
                                .addClass(className.active)
                            ;
                            module.ensureVisible($result.eq(newIndex));
                            event.preventDefault();
                        }
                    } else {
                        // query shortcuts
                        if (keyCode === keys.enter) {
                            module.verbose('Enter key pressed, executing query');
                            module.query();
                            module.set.buttonPressed();
                            $prompt.one('keyup', module.remove.buttonFocus);
                        }
                    }
                },

                setup: {
                    api: function (searchTerm, callback) {
                        var
                            apiSettings = {
                                debug: settings.debug,
                                on: false,
                                cache: settings.cache,
                                action: 'search',
                                urlData: {
                                    query: searchTerm,
                                },
                            },
                            apiCallbacks = {
                                onSuccess: function (response, $module, xhr) {
                                    module.parse.response.call(element, response, searchTerm);
                                    callback();
                                    if (settings.apiSettings && typeof settings.apiSettings.onSuccess === 'function') {
                                        settings.apiSettings.onSuccess.call(this, response, $module, xhr);
                                    }
                                },
                                onFailure: function (response, $module, xhr) {
                                    module.displayMessage(error.serverError);
                                    callback();
                                    if (settings.apiSettings && typeof settings.apiSettings.onFailure === 'function') {
                                        settings.apiSettings.onFailure.call(this, response, $module, xhr);
                                    }
                                },
                                onAbort: function (status, $module, xhr) {
                                    if (settings.apiSettings && typeof settings.apiSettings.onAbort === 'function') {
                                        settings.apiSettings.onAbort.call(this, status, $module, xhr);
                                    }
                                },
                                onError: function (errorMessage, $module, xhr) {
                                    module.error();
                                    if (settings.apiSettings && typeof settings.apiSettings.onError === 'function') {
                                        settings.apiSettings.onError.call(this, errorMessage, $module, xhr);
                                    }
                                },
                            }
                        ;
                        $.extend(true, apiSettings, settings.apiSettings, apiCallbacks);
                        module.verbose('Setting up API request', apiSettings);
                        $module.api(apiSettings);
                    },
                },

                can: {
                    useAPI: function () {
                        return $.fn.api !== undefined;
                    },
                    show: function () {
                        return module.is.focused() && !module.is.visible() && !module.is.empty();
                    },
                    transition: function () {
                        return settings.transition && $.fn.transition !== undefined;
                    },
                },

                is: {
                    animating: function () {
                        return $results.hasClass(className.animating);
                    },
                    chrome: function () {
                        return !!window.chrome && !window.StyleMedia;
                    },
                    hidden: function () {
                        return $results.hasClass(className.hidden);
                    },
                    inMessage: function (event) {
                        if (!event.target) {
                            return;
                        }
                        var
                            $target = $(event.target),
                            isInDOM = $.contains(document.documentElement, event.target)
                        ;

                        return isInDOM && $target.closest(selector.message).length > 0;
                    },
                    empty: function () {
                        return $results.html() === '';
                    },
                    visible: function () {
                        return $results.filter(':visible').length > 0;
                    },
                    focused: function () {
                        return $prompt.filter(':focus').length > 0;
                    },
                },

                get: {
                    settings: function () {
                        if ($.isPlainObject(parameters) && parameters.searchFullText) {
                            settings.fullTextSearch = parameters.searchFullText;
                            module.error(settings.error.oldSearchSyntax, element);
                        }
                        if (settings.ignoreDiacritics && !String.prototype.normalize) {
                            settings.ignoreDiacritics = false;
                            module.error(error.noNormalize, element);
                        }
                    },
                    inputEvent: function () {
                        var
                            prompt = $prompt[0],
                            inputEvent   = prompt !== undefined && prompt.oninput !== undefined
                                ? 'input'
                                : (prompt !== undefined && prompt.onpropertychange !== undefined
                                    ? 'propertychange'
                                    : 'keyup')
                        ;

                        return inputEvent;
                    },
                    value: function () {
                        return $prompt.val();
                    },
                    results: function () {
                        return $module.data(metadata.results);
                    },
                    result: function (value, results) {
                        var
                            result       = false
                        ;
                        value = value !== undefined
                            ? value
                            : module.get.value();
                        results = results !== undefined
                            ? results
                            : module.get.results();
                        if (settings.type === 'category') {
                            module.debug('Finding result that matches', value);
                            $.each(results, function (index, category) {
                                if (Array.isArray(category.results)) {
                                    result = module.search.object(value, category.results)[0];
                                    // don't continue searching if a result is found
                                    if (result) {
                                        return false;
                                    }
                                }
                            });
                        } else {
                            module.debug('Finding result in results object', value);
                            result = module.search.object(value, results)[0];
                        }

                        return result || false;
                    },
                },

                select: {
                    firstResult: function () {
                        module.verbose('Selecting first result');
                        $result.first().addClass(className.active);
                    },
                },

                set: {
                    focus: function () {
                        $module.addClass(className.focus);
                    },
                    loading: function () {
                        $module.addClass(className.loading);
                    },
                    value: function (value) {
                        module.verbose('Setting search input value', value);
                        $prompt
                            .val(value)
                        ;
                    },
                    type: function (type) {
                        type = type || settings.type;
                        if (className[type]) {
                            $module.addClass(className[type]);
                        }
                    },
                    buttonPressed: function () {
                        $searchButton.addClass(className.pressed);
                    },
                },

                remove: {
                    loading: function () {
                        $module.removeClass(className.loading);
                    },
                    focus: function () {
                        $module.removeClass(className.focus);
                    },
                    buttonPressed: function () {
                        $searchButton.removeClass(className.pressed);
                    },
                    diacritics: function (text) {
                        return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036F]/g, '') : text;
                    },
                },

                query: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    var
                        searchTerm = module.get.value(),
                        cache = module.read.cache(searchTerm)
                    ;
                    callback = callback || function () {};
                    if (module.has.minimumCharacters()) {
                        if (cache) {
                            module.debug('Reading result from cache', searchTerm);
                            module.save.results(cache.results);
                            settings.onResults.call(element, cache.results, true);
                            module.addResults(cache.html);
                            module.inject.id(cache.results);
                            callback();
                        } else {
                            module.debug('Querying for', searchTerm);
                            if ($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
                                module.search.local(searchTerm);
                                callback();
                            } else if (module.can.useAPI()) {
                                module.search.remote(searchTerm, callback);
                            } else {
                                module.error(error.source);
                                callback();
                            }
                        }
                        settings.onSearchQuery.call(element, searchTerm);
                    } else {
                        module.hideResults();
                    }
                },

                search: {
                    local: function (searchTerm) {
                        var
                            results = module.search.object(searchTerm, settings.source),
                            searchHTML
                        ;
                        module.set.loading();
                        module.save.results(results);
                        module.debug('Returned full local search results', results);
                        if (settings.maxResults > 0) {
                            module.debug('Using specified max results', results);
                            results = results.slice(0, settings.maxResults);
                        }
                        if (settings.type === 'category') {
                            results = module.create.categoryResults(results);
                        }
                        searchHTML = module.generateResults({
                            results: results,
                        });
                        module.remove.loading();
                        module.addResults(searchHTML);
                        module.inject.id(results);
                        module.write.cache(searchTerm, {
                            html: searchHTML,
                            results: results,
                        });
                    },
                    remote: function (searchTerm, callback) {
                        callback = isFunction(callback)
                            ? callback
                            : function () {};
                        if ($module.api('is loading')) {
                            $module.api('abort');
                        }
                        module.setup.api(searchTerm, callback);
                        $module
                            .api('query')
                        ;
                    },
                    object: function (searchTerm, source, searchFields) {
                        searchTerm = module.remove.diacritics(String(searchTerm));
                        var
                            results      = [],
                            exactResults = [],
                            fuzzyResults = [],
                            searchExp    = searchTerm.replace(regExp.escape, '\\$&'),
                            matchRegExp  = new RegExp(regExp.beginsWith + searchExp, 'i'),

                            // avoid duplicates when pushing results
                            addResult = function (array, result) {
                                var
                                    notResult      = $.inArray(result, results) === -1,
                                    notFuzzyResult = $.inArray(result, fuzzyResults) === -1,
                                    notExactResults = $.inArray(result, exactResults) === -1
                                ;
                                if (notResult && notFuzzyResult && notExactResults) {
                                    array.push(result);
                                }
                            }
                        ;
                        source = source || settings.source;
                        searchFields = searchFields !== undefined
                            ? searchFields
                            : settings.searchFields;

                        // search fields should be array to loop correctly
                        if (!Array.isArray(searchFields)) {
                            searchFields = [searchFields];
                        }

                        // exit conditions if no source
                        if (source === undefined || source === false) {
                            module.error(error.source);

                            return [];
                        }
                        // iterate through search fields looking for matches
                        var lastSearchFieldIndex = searchFields.length - 1;
                        $.each(source, function (label, content) {
                            var concatenatedContent = [];
                            $.each(searchFields, function (index, field) {
                                var
                                    fieldExists = (typeof content[field] === 'string') || (typeof content[field] === 'number')
                                ;
                                if (fieldExists) {
                                    var text;
                                    text = typeof content[field] === 'string'
                                        ? module.remove.diacritics(content[field])
                                        : content[field].toString();
                                    if (settings.fullTextSearch === 'all') {
                                        concatenatedContent.push(text);
                                        if (index < lastSearchFieldIndex) {
                                            return true;
                                        }
                                        text = concatenatedContent.join(' ');
                                    }
                                    if (settings.fullTextSearch !== 'all' && text.search(matchRegExp) !== -1) {
                                        // content starts with value (first in results)
                                        addResult(results, content);
                                    } else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
                                        addResult(exactResults, content);
                                    } else if (settings.fullTextSearch === 'some' && module.wordSearch(searchTerm, text)) {
                                        addResult(exactResults, content);
                                    } else if (settings.fullTextSearch === 'all' && module.wordSearch(searchTerm, text, true)) {
                                        addResult(exactResults, content);
                                    } else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
                                        // content fuzzy matches (last in results)
                                        addResult(fuzzyResults, content);
                                    }
                                }
                            });
                        });
                        $.merge(exactResults, fuzzyResults);
                        $.merge(results, exactResults);

                        return results;
                    },
                },
                exactSearch: function (query, term) {
                    query = query.toLowerCase();
                    term = term.toLowerCase();

                    return term.indexOf(query) > -1;
                },
                wordSearch: function (query, term, matchAll) {
                    var allWords = query.split(/\s+/),
                        w,
                        wL = allWords.length,
                        found = false
                    ;
                    for (w = 0; w < wL; w++) {
                        found = module.exactSearch(allWords[w], term);
                        if ((!found && matchAll) || (found && !matchAll)) {
                            break;
                        }
                    }

                    return found;
                },
                fuzzySearch: function (query, term) {
                    var
                        termLength  = term.length,
                        queryLength = query.length
                    ;
                    if (typeof query !== 'string') {
                        return false;
                    }
                    query = query.toLowerCase();
                    term = term.toLowerCase();
                    if (queryLength > termLength) {
                        return false;
                    }
                    if (queryLength === termLength) {
                        return query === term;
                    }
                    for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
                        var
                            continueSearch = false,
                            queryCharacter = query.charCodeAt(characterIndex)
                        ;
                        while (nextCharacterIndex < termLength) {
                            if (term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
                                continueSearch = true;

                                break;
                            }
                        }

                        if (!continueSearch) {
                            return false;
                        }
                    }

                    return true;
                },

                parse: {
                    response: function (response, searchTerm) {
                        if (Array.isArray(response)) {
                            var o = {};
                            o[fields.results] = response;
                            response = o;
                        }
                        var
                            searchHTML = module.generateResults(response)
                        ;
                        module.verbose('Parsing server response', response);
                        if (response !== undefined) {
                            if (searchTerm !== undefined && response[fields.results] !== undefined) {
                                module.addResults(searchHTML);
                                module.inject.id(response[fields.results]);
                                module.write.cache(searchTerm, {
                                    html: searchHTML,
                                    results: response[fields.results],
                                });
                                module.save.results(response[fields.results]);
                            }
                        }
                    },
                },

                cancel: {
                    query: function () {
                        if (module.can.useAPI()) {
                            $module.api('abort');
                        }
                    },
                },

                has: {
                    minimumCharacters: function () {
                        var
                            searchTerm    = module.get.value(),
                            numCharacters = searchTerm.length
                        ;

                        return numCharacters >= settings.minCharacters;
                    },
                    results: function () {
                        if ($results.length === 0) {
                            return false;
                        }
                        var
                            html = $results.html()
                        ;

                        return html !== '';
                    },
                },

                clear: {
                    cache: function (value) {
                        var
                            cache = $module.data(metadata.cache)
                        ;
                        if (!value) {
                            module.debug('Clearing cache', value);
                            $module.removeData(metadata.cache);
                        } else if (value && cache && cache[value]) {
                            module.debug('Removing value from cache', value);
                            delete cache[value];
                            $module.data(metadata.cache, cache);
                        }
                    },
                },

                read: {
                    cache: function (name) {
                        var
                            cache = $module.data(metadata.cache)
                        ;
                        if (settings.cache) {
                            module.verbose('Checking cache for generated html for query', name);

                            return (typeof cache === 'object') && (cache[name] !== undefined)
                                ? cache[name]
                                : false;
                        }

                        return false;
                    },
                },

                create: {
                    categoryResults: function (results) {
                        var
                            categoryResults = {}
                        ;
                        $.each(results, function (index, result) {
                            if (!result.category) {
                                return;
                            }
                            if (categoryResults[result.category] === undefined) {
                                module.verbose('Creating new category of results', result.category);
                                categoryResults[result.category] = {
                                    name: result.category,
                                    results: [result],
                                };
                            } else {
                                categoryResults[result.category].results.push(result);
                            }
                        });

                        return categoryResults;
                    },
                    id: function (resultIndex, categoryIndex) {
                        var
                            resultID      = resultIndex + 1, // not zero indexed
                            letterID,
                            id
                        ;
                        if (categoryIndex !== undefined) {
                            // start char code for "A"
                            letterID = String.fromCharCode(97 + categoryIndex);
                            id = letterID + resultID;
                            module.verbose('Creating category result id', id);
                        } else {
                            id = resultID;
                            module.verbose('Creating result id', id);
                        }

                        return id;
                    },
                    results: function () {
                        if ($results.length === 0) {
                            $results = $('<div />')
                                .addClass(className.results)
                                .appendTo($module)
                            ;
                        }
                    },
                },

                inject: {
                    result: function (result, resultIndex, categoryIndex) {
                        module.verbose('Injecting result into results');
                        var
                            $selectedResult = categoryIndex !== undefined
                                ? $results
                                    .children().eq(categoryIndex)
                                    .children(selector.results)
                                    .first()
                                    .children(selector.result)
                                    .eq(resultIndex)
                                : $results
                                    .children(selector.result).eq(resultIndex)
                        ;
                        module.verbose('Injecting results metadata', $selectedResult);
                        $selectedResult
                            .data(metadata.result, result)
                        ;
                    },
                    id: function (results) {
                        module.debug('Injecting unique ids into results');
                        var
                            // since results may be object, we must use counters
                            categoryIndex = 0,
                            resultIndex   = 0
                        ;
                        if (settings.type === 'category') {
                            // iterate through each category result
                            $.each(results, function (index, category) {
                                if (category.results.length > 0) {
                                    resultIndex = 0;
                                    $.each(category.results, function (index, result) {
                                        if (result.id === undefined) {
                                            result.id = module.create.id(resultIndex, categoryIndex);
                                        }
                                        module.inject.result(result, resultIndex, categoryIndex);
                                        resultIndex++;
                                    });
                                    categoryIndex++;
                                }
                            });
                        } else {
                            // top level
                            $.each(results, function (index, result) {
                                if (result.id === undefined) {
                                    result.id = module.create.id(resultIndex);
                                }
                                module.inject.result(result, resultIndex);
                                resultIndex++;
                            });
                        }

                        return results;
                    },
                },

                save: {
                    results: function (results) {
                        module.verbose('Saving current search results to metadata', results);
                        $module.data(metadata.results, results);
                    },
                },

                write: {
                    cache: function (name, value) {
                        var
                            cache = $module.data(metadata.cache) !== undefined
                                ? $module.data(metadata.cache)
                                : {}
                        ;
                        if (settings.cache) {
                            module.verbose('Writing generated html to cache', name, value);
                            cache[name] = value;
                            $module
                                .data(metadata.cache, cache)
                            ;
                        }
                    },
                },

                addResults: function (html) {
                    if (isFunction(settings.onResultsAdd)) {
                        if (settings.onResultsAdd.call($results, html) === false) {
                            module.debug('onResultsAdd callback cancelled default action');

                            return false;
                        }
                    }
                    if (html) {
                        $results
                            .html(html)
                        ;
                        module.refreshResults();
                        if (settings.selectFirstResult) {
                            module.select.firstResult();
                        }
                        module.showResults();
                    } else {
                        module.hideResults(function () {
                            $results.empty();
                        });
                    }
                },

                showResults: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (resultsDismissed) {
                        return;
                    }
                    if (!module.is.visible() && module.has.results()) {
                        if (module.can.transition()) {
                            module.debug('Showing results with css animations');
                            $results
                                .transition({
                                    animation: settings.transition + ' in',
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    duration: settings.duration,
                                    onShow: function () {
                                        var $firstResult = $module.find(selector.result).eq(0);
                                        module.ensureVisible($firstResult);
                                    },
                                    onComplete: function () {
                                        callback();
                                    },
                                    queue: true,
                                })
                            ;
                        } else {
                            module.debug('Showing results with javascript');
                            $results
                                .stop()
                                .fadeIn(settings.duration, settings.easing)
                            ;
                        }
                        settings.onResultsOpen.call($results);
                    }
                },
                hideResults: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.visible()) {
                        if (module.can.transition()) {
                            module.debug('Hiding results with css animations');
                            $results
                                .transition({
                                    animation: settings.transition + ' out',
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    duration: settings.duration,
                                    onComplete: function () {
                                        callback();
                                    },
                                    queue: true,
                                })
                            ;
                        } else {
                            module.debug('Hiding results with javascript');
                            $results
                                .stop()
                                .fadeOut(settings.duration, settings.easing)
                            ;
                        }
                        settings.onResultsClose.call($results);
                    }
                },

                generateResults: function (response) {
                    module.debug('Generating html from response', response);
                    var
                        template       = settings.templates[settings.type],
                        isProperObject = $.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results]),
                        isProperArray  = Array.isArray(response[fields.results]) && response[fields.results].length > 0,
                        html           = ''
                    ;
                    if (isProperObject || isProperArray) {
                        if (settings.maxResults > 0) {
                            if (isProperObject) {
                                if (settings.type === 'standard') {
                                    module.error(error.maxResults);
                                }
                            } else {
                                response[fields.results] = response[fields.results].slice(0, settings.maxResults);
                            }
                        }
                        if (isFunction(template)) {
                            html = template(response, fields, settings.preserveHTML);
                        } else {
                            module.error(error.noTemplate, false);
                        }
                    } else if (settings.showNoResults) {
                        html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
                    }
                    settings.onResults.call(element, response);

                    return html;
                },

                displayMessage: function (text, type, header) {
                    type = type || 'standard';
                    module.debug('Displaying message', text, type, header);
                    module.addResults(settings.templates.message(text, type, header));

                    return settings.templates.message(text, type, header);
                },

                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;
                },
            };
            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.search.settings = {

        name: 'Search',
        namespace: 'search',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        // template to use (specified in settings.templates)
        type: 'standard',

        // minimum characters required to search
        minCharacters: 1,

        // whether to select first result after searching automatically
        selectFirstResult: false,

        // API config
        apiSettings: false,

        // object to search
        source: false,

        // Whether search should query current term on focus
        searchOnFocus: true,

        // fields to search
        searchFields: [
            'id',
            'title',
            'description',
        ],

        // field to display in standard results template
        displayField: '',

        // search anywhere in value (set to 'exact' to require exact matches
        fullTextSearch: 'exact',

        // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
        ignoreDiacritics: false,

        // whether to add events to prompt automatically
        automatic: true,

        // delay before hiding menu after blur
        hideDelay: 0,

        // delay before searching
        searchDelay: 200,

        // maximum results returned from search
        maxResults: 7,

        // whether to store lookups in local cache
        cache: true,

        // whether no results errors should be shown
        showNoResults: true,

        // preserve possible html of resultset values
        preserveHTML: true,

        // transition settings
        transition: 'scale',
        duration: 200,
        easing: 'easeOutExpo',

        // callbacks
        onSelect: false,
        onResultsAdd: false,

        onSearchQuery: function (query) {},
        onResults: function (response, fromCache) {},

        onResultsOpen: function () {},
        onResultsClose: function () {},

        className: {
            animating: 'animating',
            active: 'active',
            category: 'category',
            empty: 'empty',
            focus: 'focus',
            hidden: 'hidden',
            loading: 'loading',
            results: 'results',
            pressed: 'down',
        },

        error: {
            source: 'Cannot search. No source used, and Fomantic API module was not included',
            noResultsHeader: 'No Results',
            noResults: 'Your search returned no results',
            noTemplate: 'A valid template name was not specified.',
            oldSearchSyntax: 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
            serverError: 'There was an issue querying the server.',
            maxResults: 'Results must be an array to use maxResults setting',
            method: 'The method you called is not defined.',
            noNormalize: '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.',
        },

        metadata: {
            cache: 'cache',
            results: 'results',
            result: 'result',
        },

        regExp: {
            escape: /[$()*+./?[\\\]^{|}-]/g,
            beginsWith: '(?:\\s|^)',
        },

        // maps api response attributes to internal representation
        fields: {
            categories: 'results', // array of categories (category view)
            categoryName: 'name', // name of category (category view)
            categoryResults: 'results', // array of results (category view)
            description: 'description', // result description
            image: 'image', // result image
            price: 'price', // result price
            results: 'results', // array of results (standard)
            title: 'title', // result title
            url: 'url', // result url
            action: 'action', // "view more" object name
            actionText: 'text', // "view more" text
            actionURL: 'url', // "view more" url
        },

        selector: {
            prompt: '.prompt',
            searchButton: '.search.button',
            results: '.results',
            message: '.results > .message',
            category: '.category',
            result: '.result',
            title: '.title, .name',
        },

        templates: {
            escape: function (string, preserveHTML) {
                if (preserveHTML) {
                    return string;
                }
                var
                    badChars     = /["'<>`]/g,
                    shouldEscape = /["&'<>`]/,
                    escape       = {
                        '<': '&lt;',
                        '>': '&gt;',
                        '"': '&quot;',
                        "'": '&#x27;',
                        '`': '&#x60;',
                    },
                    escapedChar  = function (chr) {
                        return escape[chr];
                    };
                if (shouldEscape.test(string)) {
                    string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                    return string.replace(badChars, escapedChar);
                }

                return string;
            },
            message: function (message, type, header) {
                var
                    html = ''
                ;
                if (message !== undefined && type !== undefined) {
                    html += ''
                        + '<div class="message ' + type + '">';
                    if (header) {
                        html += ''
                            + '<div class="header">' + header + '</div>';
                    }
                    html += ' <div class="description">' + message + '</div>';
                    html += '</div>';
                }

                return html;
            },
            category: function (response, fields, preserveHTML) {
                var
                    html = '',
                    escape = $.fn.search.settings.templates.escape
                ;
                if (response[fields.categoryResults] !== undefined) {
                    // each category
                    $.each(response[fields.categoryResults], function (index, category) {
                        if (category[fields.results] !== undefined && category.results.length > 0) {
                            html += '<div class="category">';

                            if (category[fields.categoryName] !== undefined) {
                                html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
                            }

                            // each item inside category
                            html += '<div class="results">';
                            $.each(category.results, function (index, result) {
                                html += result[fields.url]
                                    ? '<a class="result" href="' + result[fields.url].replace(/"/g, '') + '">'
                                    : '<a class="result">';
                                if (result[fields.image] !== undefined) {
                                    html += ''
                                        + '<div class="image">'
                                        + ' <img src="' + result[fields.image].replace(/"/g, '') + '">'
                                        + '</div>';
                                }
                                html += '<div class="content">';
                                if (result[fields.price] !== undefined) {
                                    html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
                                }
                                if (result[fields.title] !== undefined) {
                                    html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
                                }
                                if (result[fields.description] !== undefined) {
                                    html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
                                }
                                html += ''
                                    + '</div>';
                                html += '</a>';
                            });
                            html += '</div>';
                            html += ''
                                + '</div>';
                        }
                    });
                    if (response[fields.action]) {
                        html += fields.actionURL === false
                            ? ''
                                + '<div class="action">'
                                + escape(response[fields.action][fields.actionText], preserveHTML)
                                + '</div>'
                            : ''
                                + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g, '') + '" class="action">'
                                + escape(response[fields.action][fields.actionText], preserveHTML)
                                + '</a>';
                    }

                    return html;
                }

                return false;
            },
            standard: function (response, fields, preserveHTML) {
                var
                    html = '',
                    escape = $.fn.search.settings.templates.escape
                ;
                if (response[fields.results] !== undefined) {
                    // each result
                    $.each(response[fields.results], function (index, result) {
                        html += result[fields.url]
                            ? '<a class="result" href="' + result[fields.url].replace(/"/g, '') + '">'
                            : '<a class="result">';
                        if (result[fields.image] !== undefined) {
                            html += ''
                                + '<div class="image">'
                                + ' <img src="' + result[fields.image].replace(/"/g, '') + '">'
                                + '</div>';
                        }
                        html += '<div class="content">';
                        if (result[fields.price] !== undefined) {
                            html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
                        }
                        if (result[fields.title] !== undefined) {
                            html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
                        }
                        if (result[fields.description] !== undefined) {
                            html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
                        }
                        html += ''
                            + '</div>';
                        html += '</a>';
                    });
                    if (response[fields.action]) {
                        html += fields.actionURL === false
                            ? ''
                                + '<div class="action">'
                                + escape(response[fields.action][fields.actionText], preserveHTML)
                                + '</div>'
                            : ''
                                + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g, '') + '" class="action">'
                                + escape(response[fields.action][fields.actionText], preserveHTML)
                                + '</a>';
                    }

                    return html;
                }

                return false;
            },
        },
    };

    $.extend($.easing, {
        easeOutExpo: function (x) {
            return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
        },
    });
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Shape
 * 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.shape = 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.shape.settings, parameters)
                    : $.extend({}, $.fn.shape.settings),

                // internal aliases
                namespace     = settings.namespace,
                selector      = settings.selector,
                error         = settings.error,
                className     = settings.className,

                // define namespaces for modules
                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                // selector cache
                $module       = $(this),
                $sides        = $module.find('>' + selector.sides),
                $side         = $sides.find('>' + selector.side),

                // private variables
                nextIndex = false,
                $activeSide,
                $nextSide,

                // standard module
                element       = this,
                instance      = $module.data(moduleNamespace),
                module
            ;

            module = {

                initialize: function () {
                    module.verbose('Initializing module for', element);
                    module.set.defaultSide();
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous module for', element);
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                },

                refresh: function () {
                    module.verbose('Refreshing selector cache for', element);
                    $module = $(element);
                    $sides = $(this).find(selector.sides);
                    $side = $(this).find(selector.side);
                },

                repaint: function () {
                    module.verbose('Forcing repaint event');
                    var
                        shape          = $sides[0] || document.createElement('div'),
                        fakeAssignment = shape.offsetWidth
                    ;
                },

                animate: function (propertyObject, callback) {
                    module.verbose('Animating box with properties', propertyObject);
                    callback = callback || function (event) {
                        module.verbose('Executing animation callback');
                        if (event !== undefined) {
                            event.stopPropagation();
                        }
                        module.reset();
                        module.set.active();
                    };
                    settings.onBeforeChange.call($nextSide[0]);
                    module.verbose('Starting CSS animation');
                    $module
                        .addClass(className.animating)
                    ;
                    $sides
                        .css(propertyObject)
                        .one('transitionend', callback)
                    ;
                    module.set.duration(settings.duration);
                    requestAnimationFrame(function () {
                        $module
                            .addClass(className.animating)
                        ;
                        $activeSide
                            .addClass(className.hidden)
                        ;
                    });
                },

                queue: function (method) {
                    module.debug('Queueing animation of', method);
                    $sides
                        .one('transitionend', function () {
                            module.debug('Executing queued animation');
                            setTimeout(function () {
                                $module.shape(method);
                            }, 0);
                        })
                    ;
                },

                reset: function () {
                    module.verbose('Animating states reset');
                    $module
                        .removeClass(className.animating)
                        .attr('style', '')
                        .removeAttr('style')
                    ;
                    // removeAttr style does not consistently work in safari
                    $sides
                        .attr('style', '')
                        .removeAttr('style')
                    ;
                    $side
                        .attr('style', '')
                        .removeAttr('style')
                        .removeClass(className.hidden)
                    ;
                    $nextSide
                        .removeClass(className.animating)
                        .attr('style', '')
                        .removeAttr('style')
                    ;
                },

                is: {
                    complete: function () {
                        return $side.filter('.' + className.active)[0] === $nextSide[0];
                    },
                    animating: function () {
                        return $module.hasClass(className.animating);
                    },
                    hidden: function () {
                        return $module.closest(':hidden').length > 0;
                    },
                },

                set: {

                    defaultSide: function () {
                        $activeSide = $side.filter('.' + settings.className.active);
                        $nextSide = $activeSide.next(selector.side).length > 0
                            ? $activeSide.next(selector.side)
                            : $side.first();
                        nextIndex = false;
                        module.verbose('Active side set to', $activeSide);
                        module.verbose('Next side set to', $nextSide);
                    },

                    duration: function (duration) {
                        duration = duration || settings.duration;
                        duration = typeof duration === 'number'
                            ? duration + 'ms'
                            : duration;
                        module.verbose('Setting animation duration', duration);
                        if (settings.duration || settings.duration === 0) {
                            $sides.add($side)
                                .css({
                                    'transition-duration': duration,
                                })
                            ;
                        }
                    },

                    currentStageSize: function () {
                        var
                            $activeSide = $side.filter('.' + settings.className.active),
                            width       = $activeSide.outerWidth(true),
                            height      = $activeSide.outerHeight(true)
                        ;
                        $module
                            .css({
                                width: width,
                                height: height,
                            })
                        ;
                    },

                    stageSize: function () {
                        var
                            $clone      = $module.clone().addClass(className.loading),
                            $side       = $clone.find('>' + selector.sides + '>' + selector.side),
                            $activeSide = $side.filter('.' + settings.className.active),
                            $nextSide   = nextIndex
                                ? $side.eq(nextIndex)
                                : ($activeSide.next(selector.side).length > 0
                                    ? $activeSide.next(selector.side)
                                    : $side.first()),
                            newWidth    = settings.width === 'next'
                                ? $nextSide.outerWidth(true)
                                : (settings.width === 'initial'
                                    ? $module.width()
                                    : settings.width),
                            newHeight    = settings.height === 'next'
                                ? $nextSide.outerHeight(true)
                                : (settings.height === 'initial'
                                    ? $module.height()
                                    : settings.height)
                        ;
                        $activeSide.removeClass(className.active);
                        $nextSide.addClass(className.active);
                        $clone.insertAfter($module);
                        $clone.remove();
                        if (settings.width !== 'auto') {
                            $module.css('width', newWidth + settings.jitter);
                            module.verbose('Specifying width during animation', newWidth);
                        }
                        if (settings.height !== 'auto') {
                            $module.css('height', newHeight + settings.jitter);
                            module.verbose('Specifying height during animation', newHeight);
                        }
                    },

                    nextSide: function (selector) {
                        nextIndex = selector;
                        $nextSide = $side.filter(selector);
                        nextIndex = $side.index($nextSide);
                        if ($nextSide.length === 0) {
                            module.set.defaultSide();
                            module.error(error.side);
                        }
                        module.verbose('Next side manually set to', $nextSide);
                    },

                    active: function () {
                        module.verbose('Setting new side to active', $nextSide);
                        $side
                            .removeClass(className.active)
                        ;
                        $nextSide
                            .addClass(className.active)
                        ;
                        settings.onChange.call($nextSide[0]);
                        module.set.defaultSide();
                    },
                },

                flip: {
                    to: function (type, stage) {
                        if (module.is.hidden()) {
                            module.debug('Module not visible', $nextSide);

                            return;
                        }
                        if (module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
                            module.debug('Side already visible', $nextSide);

                            return;
                        }
                        var
                            transform = module.get.transform[type]()
                        ;
                        if (!module.is.animating()) {
                            module.debug('Flipping ' + type, $nextSide);
                            module.set.stageSize();
                            module.stage[stage]();
                            module.animate(transform);
                        } else {
                            module.queue('flip ' + type);
                        }
                    },

                    up: function () {
                        module.flip.to('up', 'above');
                    },

                    down: function () {
                        module.flip.to('down', 'below');
                    },

                    left: function () {
                        module.flip.to('left', 'left');
                    },

                    right: function () {
                        module.flip.to('right', 'right');
                    },

                    over: function () {
                        module.flip.to('over', 'behind');
                    },

                    back: function () {
                        module.flip.to('back', 'behind');
                    },

                },

                get: {

                    transform: {
                        up: function () {
                            var
                                translateZ = $activeSide.outerHeight(true) / 2,
                                translateY = $nextSide.outerHeight(true) - translateZ
                            ;

                            return {
                                transform: 'translateY(' + translateY + 'px) translateZ(-' + translateZ + 'px) rotateX(-90deg)',
                            };
                        },

                        down: function () {
                            var
                                translate = {
                                    z: $activeSide.outerHeight(true) / 2,
                                }
                            ;

                            return {
                                transform: 'translateY(-' + translate.z + 'px) translateZ(-' + translate.z + 'px) rotateX(90deg)',
                            };
                        },

                        left: function () {
                            var
                                translateZ = $activeSide.outerWidth(true) / 2,
                                translateX = $nextSide.outerWidth(true) - translateZ
                            ;

                            return {
                                transform: 'translateX(' + translateX + 'px) translateZ(-' + translateZ + 'px) rotateY(90deg)',
                            };
                        },

                        right: function () {
                            var
                                translate = {
                                    z: $activeSide.outerWidth(true) / 2,
                                }
                            ;

                            return {
                                transform: 'translateX(-' + translate.z + 'px) translateZ(-' + translate.z + 'px) rotateY(-90deg)',
                            };
                        },

                        over: function () {
                            var
                                translate = {
                                    x: -(($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) / 2),
                                }
                            ;

                            return {
                                transform: 'translateX(' + translate.x + 'px) rotateY(180deg)',
                            };
                        },

                        back: function () {
                            var
                                translate = {
                                    x: -(($activeSide.outerWidth(true) - $nextSide.outerWidth(true)) / 2),
                                }
                            ;

                            return {
                                transform: 'translateX(' + translate.x + 'px) rotateY(-180deg)',
                            };
                        },
                    },

                    nextSide: function () {
                        return $activeSide.next(selector.side).length > 0
                            ? $activeSide.next(selector.side)
                            : $side.first();
                    },

                },

                stage: {

                    above: function () {
                        var
                            box = {
                                origin: ($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) / 2,
                                depth: {
                                    active: $nextSide.outerHeight(true) / 2,
                                    next: $activeSide.outerHeight(true) / 2,
                                },
                            }
                        ;
                        module.verbose('Setting the initial animation position as above', $nextSide, box);
                        $activeSide
                            .css({
                                transform: 'rotateX(0deg)',
                            })
                        ;
                        $nextSide
                            .addClass(className.animating)
                            .css({
                                top: box.origin + 'px',
                                transform: 'rotateX(90deg) translateZ(' + box.depth.next + 'px) translateY(-' + box.depth.active + 'px)',
                            })
                        ;
                    },

                    below: function () {
                        var
                            box = {
                                origin: ($activeSide.outerHeight(true) - $nextSide.outerHeight(true)) / 2,
                                depth: {
                                    active: $nextSide.outerHeight(true) / 2,
                                    next: $activeSide.outerHeight(true) / 2,
                                },
                            }
                        ;
                        module.verbose('Setting the initial animation position as below', $nextSide, box);
                        $activeSide
                            .css({
                                transform: 'rotateX(0deg)',
                            })
                        ;
                        $nextSide
                            .addClass(className.animating)
                            .css({
                                top: box.origin + 'px',
                                transform: 'rotateX(-90deg) translateZ(' + box.depth.next + 'px) translateY(' + box.depth.active + 'px)',
                            })
                        ;
                    },

                    left: function () {
                        var
                            height = {
                                active: $activeSide.outerWidth(true),
                                next: $nextSide.outerWidth(true),
                            },
                            box = {
                                origin: (height.active - height.next) / 2,
                                depth: {
                                    active: height.next / 2,
                                    next: height.active / 2,
                                },
                            }
                        ;
                        module.verbose('Setting the initial animation position as left', $nextSide, box);
                        $activeSide
                            .css({
                                transform: 'rotateY(0deg)',
                            })
                        ;
                        $nextSide
                            .addClass(className.animating)
                            .css({
                                left: box.origin + 'px',
                                transform: 'rotateY(-90deg) translateZ(' + box.depth.next + 'px) translateX(-' + box.depth.active + 'px)',
                            })
                        ;
                    },

                    right: function () {
                        var
                            height = {
                                active: $activeSide.outerWidth(true),
                                next: $nextSide.outerWidth(true),
                            },
                            box = {
                                origin: (height.active - height.next) / 2,
                                depth: {
                                    active: height.next / 2,
                                    next: height.active / 2,
                                },
                            }
                        ;
                        module.verbose('Setting the initial animation position as right', $nextSide, box);
                        $activeSide
                            .css({
                                transform: 'rotateY(0deg)',
                            })
                        ;
                        $nextSide
                            .addClass(className.animating)
                            .css({
                                left: box.origin + 'px',
                                transform: 'rotateY(90deg) translateZ(' + box.depth.next + 'px) translateX(' + box.depth.active + 'px)',
                            })
                        ;
                    },

                    behind: function () {
                        var
                            height = {
                                active: $activeSide.outerWidth(true),
                                next: $nextSide.outerWidth(true),
                            },
                            box = {
                                origin: (height.active - height.next) / 2,
                                depth: {
                                    active: height.next / 2,
                                    next: height.active / 2,
                                },
                            }
                        ;
                        module.verbose('Setting the initial animation position as behind', $nextSide, box);
                        $activeSide
                            .css({
                                transform: 'rotateY(0deg)',
                            })
                        ;
                        $nextSide
                            .addClass(className.animating)
                            .css({
                                left: box.origin + 'px',
                                transform: 'rotateY(-180deg)',
                            })
                        ;
                    },
                },
                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 ($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;
                },
            };

            if (methodInvoked) {
                if (instance === undefined) {
                    module.initialize();
                }
                var $inputs = $module.find('input');
                if ($inputs.length > 0) {
                    $inputs.trigger('blur');
                    setTimeout(function () {
                        module.invoke(query);
                    }, 150);
                } else {
                    module.invoke(query);
                }
            } else {
                if (instance !== undefined) {
                    instance.invoke('destroy');
                }
                module.initialize();
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };

    $.fn.shape.settings = {

        // module info
        name: 'Shape',

        // hide all debug content
        silent: false,

        // debug content outputted to console
        debug: false,

        // verbose debug output
        verbose: false,

        // fudge factor in pixels when swapping from 2d to 3d (can be useful to correct rounding errors)
        jitter: 0,

        // performance data output
        performance: true,

        // event namespace
        namespace: 'shape',

        // width during animation, can be set to 'auto', initial', 'next' or pixel amount
        width: 'initial',

        // height during animation, can be set to 'auto', 'initial', 'next' or pixel amount
        height: 'initial',

        // callback occurs on side change
        onBeforeChange: function () {},
        onChange: function () {},

        // allow animation to same side
        allowRepeats: false,

        // animation duration
        duration: false,

        // possible errors
        error: {
            side: 'You tried to switch to a side that does not exist.',
            method: 'The method you called is not defined',
        },

        // classnames used
        className: {
            animating: 'animating',
            hidden: 'hidden',
            loading: 'loading',
            active: 'active',
        },

        // selectors used
        selector: {
            sides: '.sides',
            side: '.side',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Sidebar
 * 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.sidebar = function (parameters) {
        var
            $allModules     = $(this),
            $window         = $(window),
            $document       = $(document),
            $body           = $('body'),
            $html           = $('html'),
            $head           = $('head'),

            time            = Date.now(),
            performance     = [],

            query           = arguments[0],
            methodInvoked   = typeof query === 'string',
            queryArguments  = [].slice.call(arguments, 1),
            contextCheck    = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $body;
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },
            returnedValue;

        $allModules.each(function () {
            var
                settings        = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.sidebar.settings, parameters)
                    : $.extend({}, $.fn.sidebar.settings),

                selector        = settings.selector,
                className       = settings.className,
                namespace       = settings.namespace,
                regExp          = settings.regExp,
                error           = settings.error,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $module         = $(this),
                $context        = contextCheck(settings.context, window),
                isBody          = $context[0] === $body[0],

                $sidebars       = $module.children(selector.sidebar),
                $fixed          = $context.children(selector.fixed),
                $pusher         = $context.children(selector.pusher),
                $style,

                element         = this,
                instance        = $module.data(moduleNamespace),

                elementNamespace,
                id,
                currentScroll,
                initialBodyMargin = '',
                tempBodyMargin = '',
                hadScrollbar = false,

                module
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing sidebar', parameters);

                    module.create.id();

                    // avoids locking rendering if initialized in onReady
                    if (settings.delaySetup) {
                        requestAnimationFrame(module.setup.layout);
                    } else {
                        module.setup.layout();
                    }

                    requestAnimationFrame(function () {
                        module.setup.cache();
                    });

                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                create: {
                    id: function () {
                        id = (Math.random().toString(16) + '000000000').slice(2, 10);
                        elementNamespace = '.' + id;
                        module.verbose('Creating unique id for element', id);
                    },
                },

                destroy: function () {
                    module.verbose('Destroying previous module for', $module);
                    $module
                        .off(eventNamespace)
                        .removeData(moduleNamespace)
                    ;
                    // bound by uuid
                    $context.off(elementNamespace);
                    $window.off(elementNamespace);
                    $document.off(elementNamespace);
                },

                event: {
                    clickaway: function (event) {
                        if (settings.closable) {
                            var
                                clickedInPusher = $pusher.find(event.target).length > 0 || $pusher.is(event.target),
                                clickedContext  = $context.is(event.target)
                            ;
                            if (clickedInPusher) {
                                module.verbose('User clicked on dimmed page');
                                module.hide();
                            }
                            if (clickedContext) {
                                module.verbose('User clicked on dimmable context (scaled out page)');
                                module.hide();
                            }
                        }
                    },
                    touch: function (event) {
                        // event.stopPropagation();
                    },
                    containScroll: function (event) {
                        if (element.scrollTop <= 0) {
                            element.scrollTop = 1;
                        }
                        if ((element.scrollTop + element.offsetHeight) >= element.scrollHeight) {
                            element.scrollTop = element.scrollHeight - element.offsetHeight - 1;
                        }
                    },
                    scroll: function (event) {
                        if ($(event.target).closest(selector.sidebar).length === 0) {
                            event.preventDefault();
                        }
                    },
                },

                bind: {
                    clickaway: function () {
                        module.verbose('Adding clickaway events to context', $context);
                        $context
                            .on('click' + elementNamespace, module.event.clickaway)
                            .on('touchend' + elementNamespace, module.event.clickaway)
                        ;
                    },
                    scrollLock: function () {
                        if (settings.scrollLock) {
                            module.debug('Disabling page scroll');
                            hadScrollbar = module.has.scrollbar();
                            if (hadScrollbar) {
                                module.save.bodyMargin();
                                module.set.bodyMargin();
                            }
                            $context.addClass(className.locked);
                        }
                        module.verbose('Adding events to contain sidebar scroll');
                        $document
                            .on('touchmove' + elementNamespace, module.event.touch)
                        ;
                        $module
                            .on('scroll' + eventNamespace, module.event.containScroll)
                        ;
                    },
                },
                unbind: {
                    clickaway: function () {
                        module.verbose('Removing clickaway events from context', $context);
                        $context.off(elementNamespace);
                    },
                    scrollLock: function () {
                        module.verbose('Removing scroll lock from page');
                        if (hadScrollbar) {
                            module.restore.bodyMargin();
                        }
                        $context.removeClass(className.locked);
                        $document.off(elementNamespace);
                        $module.off('scroll' + eventNamespace);
                    },
                },

                add: {
                    inlineCSS: function () {
                        var
                            width     = module.cache.width || $module.outerWidth(),
                            height    = module.cache.height || $module.outerHeight(),
                            isRTL     = module.is.rtl(),
                            direction = module.get.direction(),
                            distance  = {
                                left: width,
                                right: -width,
                                top: height,
                                bottom: -height,
                            },
                            style
                        ;

                        if (isRTL) {
                            module.verbose('RTL detected, flipping widths');
                            distance.left = -width;
                            distance.right = width;
                        }

                        style = '<style>';

                        if (direction === 'left' || direction === 'right') {
                            module.debug('Adding CSS rules for animation distance', width);
                            style += ''
                                + ' .ui.visible.' + direction + '.sidebar ~ .fixed,'
                                + ' .ui.visible.' + direction + '.sidebar ~ .pusher {'
                                + '           transform: translate3d(' + distance[direction] + 'px, 0, 0);'
                                + ' }';
                        } else if (direction === 'top' || direction === 'bottom') {
                            style += ''
                                + ' .ui.visible.' + direction + '.sidebar ~ .fixed,'
                                + ' .ui.visible.' + direction + '.sidebar ~ .pusher {'
                                + '           transform: translate3d(0, ' + distance[direction] + 'px, 0);'
                                + ' }';
                        }

                        /* IE is only browser not to create context with transforms */
                        /* https://www.w3.org/Bugs/Public/show_bug.cgi?id=16328 */
                        if (module.is.ie()) {
                            if (direction === 'left' || direction === 'right') {
                                module.debug('Adding CSS rules for animation distance', width);
                                style += ''
                                    + ' body.pushable > .ui.visible.' + direction + '.sidebar ~ .pusher::after {'
                                    + '           transform: translate3d(' + distance[direction] + 'px, 0, 0);'
                                    + ' }';
                            } else if (direction === 'top' || direction === 'bottom') {
                                style += ''
                                    + ' body.pushable > .ui.visible.' + direction + '.sidebar ~ .pusher::after {'
                                    + '           transform: translate3d(0, ' + distance[direction] + 'px, 0);'
                                    + ' }';
                            }
                            /* opposite sides visible forces content overlay */
                            style += ''
                                + ' body.pushable > .ui.visible.left.sidebar ~ .ui.visible.right.sidebar ~ .pusher::after,'
                                + ' body.pushable > .ui.visible.right.sidebar ~ .ui.visible.left.sidebar ~ .pusher::after {'
                                + '           transform: translate3d(0, 0, 0);'
                                + ' }';
                        }
                        style += '</style>';
                        $style = $(style)
                            .appendTo($head)
                        ;
                        module.debug('Adding sizing css to head', $style);
                    },
                },

                refresh: function () {
                    module.verbose('Refreshing selector cache');
                    $context = contextCheck(settings.context, window);
                    module.refreshSidebars();
                    $pusher = $context.children(selector.pusher);
                    $fixed = $context.children(selector.fixed);
                    module.clear.cache();
                },

                refreshSidebars: function () {
                    module.verbose('Refreshing other sidebars');
                    $sidebars = $context.children(selector.sidebar);
                },

                repaint: function () {
                    module.verbose('Forcing repaint event');
                    element.style.display = 'none';
                    var ignored = element.offsetHeight;
                    element.scrollTop = element.scrollTop; // eslint-disable-line no-self-assign
                    element.style.display = '';
                },

                setup: {
                    cache: function () {
                        module.cache = {
                            width: $module.outerWidth(),
                            height: $module.outerHeight(),
                        };
                    },
                    layout: function () {
                        if ($context.children(selector.pusher).length === 0) {
                            module.debug('Adding wrapper element for sidebar');
                            module.error(error.pusher);
                            $pusher = $('<div class="pusher" />');
                            $context
                                .children()
                                .not(selector.omitted)
                                .not($sidebars)
                                .wrapAll($pusher)
                            ;
                            module.refresh();
                        }
                        if ($module.nextAll(selector.pusher).length === 0 || $module.nextAll(selector.pusher)[0] !== $pusher[0]) {
                            module.debug('Moved sidebar to correct parent element');
                            module.error(error.movedSidebar, element);
                            $module.detach().prependTo($context);
                            module.refresh();
                        }
                        module.clear.cache();
                        module.set.pushable();
                        module.set.direction();
                    },
                },

                attachEvents: function (selector, event) {
                    var
                        $toggle = $(selector)
                    ;
                    event = isFunction(module[event])
                        ? module[event]
                        : module.toggle;
                    if ($toggle.length > 0) {
                        module.debug('Attaching sidebar events to element', selector, event);
                        $toggle
                            .on('click' + eventNamespace, event)
                        ;
                    } else {
                        module.error(error.notFound, selector);
                    }
                },
                can: {
                    leftBodyScrollbar: function () {
                        if (module.cache.leftBodyScrollbar === undefined) {
                            module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
                        }

                        return module.cache.leftBodyScrollbar;
                    },
                },
                save: {
                    bodyMargin: function () {
                        initialBodyMargin = $context.css((isBody ? 'margin-' : 'padding-') + (module.can.leftBodyScrollbar() ? 'left' : 'right'));
                        var
                            bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, ''), 10),
                            bodyScrollbarWidth = isBody ? window.innerWidth - document.documentElement.clientWidth : $context[0].offsetWidth - $context[0].clientWidth
                        ;
                        tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
                    },
                },
                show: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (module.is.hidden()) {
                        if (settings.onShow.call(element) === false) {
                            module.verbose('Show callback returned false cancelling show');

                            return;
                        }
                        if (settings.overlay) {
                            module.error(error.overlay);
                            settings.transition = 'overlay';
                        }
                        module.refresh();
                        if (module.othersActive()) {
                            module.debug('Other sidebars currently visible');
                            if (settings.exclusive) {
                                // if not overlay queue animation after hide
                                if (settings.transition !== 'overlay') {
                                    module.hideOthers(module.show);

                                    return;
                                }

                                module.hideOthers();
                            } else {
                                settings.transition = 'overlay';
                            }
                        }
                        module.set.dimmerStyles();
                        module.pushPage(function () {
                            callback.call(element);
                            settings.onVisible.call(element);
                        });
                        settings.onChange.call(element);
                    } else {
                        module.debug('Sidebar is already visible');
                    }
                },

                hide: function (callback) {
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if ((module.is.visible() || module.is.animating()) && settings.onHide.call(element) !== false) {
                        module.debug('Hiding sidebar', callback);
                        module.refreshSidebars();
                        module.pullPage(function () {
                            callback.call(element);
                            settings.onHidden.call(element);
                        });
                        settings.onChange.call(element);
                    }
                },

                othersAnimating: function () {
                    return $sidebars.not($module).filter('.' + className.animating).length > 0;
                },
                othersVisible: function () {
                    return $sidebars.not($module).filter('.' + className.visible).length > 0;
                },
                othersActive: function () {
                    return module.othersVisible() || module.othersAnimating();
                },

                hideOthers: function (callback) {
                    var
                        $otherSidebars = $sidebars.not($module).filter('.' + className.visible),
                        sidebarCount   = $otherSidebars.length,
                        callbackCount  = 0
                    ;
                    callback = callback || function () {};
                    $otherSidebars
                        .sidebar('hide', function () {
                            callbackCount++;
                            if (callbackCount === sidebarCount) {
                                callback();
                            }
                        })
                    ;
                },

                toggle: function () {
                    module.verbose('Determining toggled direction');
                    if (module.is.hidden()) {
                        module.show();
                    } else {
                        module.hide();
                    }
                },

                pushPage: function (callback) {
                    var
                        transition = module.get.transition(),
                        $transition = transition === 'overlay' || module.othersActive()
                            ? $module
                            : $pusher,
                        animate,
                        dim,
                        transitionEnd
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    if (settings.returnScroll) {
                        currentScroll = (isBody ? $window : $context).scrollTop();
                    }
                    if (settings.transition === 'scale down') {
                        module.scrollToTop();
                    }
                    module.bind.scrollLock();
                    module.set.transition(transition);
                    module.repaint();
                    animate = function () {
                        module.bind.clickaway();
                        module.add.inlineCSS();
                        module.set.animating();
                        module.set.visible();
                    };
                    dim = function () {
                        module.set.dimmed();
                    };
                    transitionEnd = function (event) {
                        if (event.target === $transition[0]) {
                            $transition.off('transitionend' + elementNamespace, transitionEnd);
                            module.remove.animating();
                            callback.call(element);
                        }
                    };
                    $transition.off('transitionend' + elementNamespace);
                    $transition.on('transitionend' + elementNamespace, transitionEnd);
                    requestAnimationFrame(animate);
                    if (settings.dimPage && !module.othersVisible()) {
                        requestAnimationFrame(dim);
                    }
                },

                pullPage: function (callback) {
                    var
                        transition = module.get.transition(),
                        $transition = transition === 'overlay' || module.othersActive()
                            ? $module
                            : $pusher,
                        animate,
                        transitionEnd
                    ;
                    callback = isFunction(callback)
                        ? callback
                        : function () {};
                    module.verbose('Removing context push state', module.get.direction());

                    module.unbind.clickaway();
                    module.unbind.scrollLock();

                    animate = function () {
                        module.set.transition(transition);
                        module.set.animating();
                        if (settings.dimPage && !module.othersVisible()) {
                            module.set.closing();
                        }
                        module.remove.visible();
                    };
                    transitionEnd = function (event) {
                        if (event.target === $transition[0]) {
                            $transition.off('transitionend' + elementNamespace, transitionEnd);
                            module.remove.animating();
                            module.remove.closing();
                            module.remove.transition();
                            module.remove.inlineCSS();
                            if (transition === 'scale down' || settings.returnScroll) {
                                module.scrollBack();
                            }
                            if (settings.dimPage && !module.othersVisible()) {
                                $pusher.removeClass(className.dimmed);
                            }
                            callback.call(element);
                        }
                    };
                    $transition.off('transitionend' + elementNamespace);
                    $transition.on('transitionend' + elementNamespace, transitionEnd);
                    requestAnimationFrame(animate);
                },

                scrollToTop: function () {
                    module.verbose('Scrolling to top of page to avoid animation issues');
                    $module.scrollTop(0);
                    (isBody ? $window : $context)[0].scrollTo(0, 0);
                },

                scrollBack: function () {
                    module.verbose('Scrolling back to original page position');
                    (isBody ? $window : $context)[0].scrollTo(0, currentScroll);
                },

                clear: {
                    cache: function () {
                        module.verbose('Clearing cached dimensions');
                        module.cache = {};
                    },
                },

                set: {
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        $context.css((isBody ? 'margin-' : 'padding-') + position, tempBodyMargin + 'px');
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, 'calc(' + el.css(attribute) + ' + ' + tempBodyMargin + 'px)');
                        });
                    },
                    dimmerStyles: function () {
                        if (settings.blurring) {
                            $pusher.addClass(className.blurring);
                        } else {
                            $pusher.removeClass(className.blurring);
                        }
                    },

                    // container
                    pushed: function () {
                        $context.addClass(className.pushed);
                    },
                    pushable: function () {
                        $context.addClass(className.pushable);
                    },

                    // pusher
                    dimmed: function () {
                        $pusher.addClass(className.dimmed);
                    },

                    // sidebar
                    active: function () {
                        $module.addClass(className.active);
                    },
                    animating: function () {
                        $module.addClass(className.animating);
                    },
                    closing: function () {
                        $pusher.addClass(className.closing);
                    },
                    transition: function (transition) {
                        transition = transition || module.get.transition();
                        $module.addClass(transition);
                    },
                    direction: function (direction) {
                        direction = direction || module.get.direction();
                        $module.addClass(className[direction]);
                    },
                    visible: function () {
                        $module.addClass(className.visible);
                    },
                    overlay: function () {
                        $module.addClass(className.overlay);
                    },
                },
                remove: {

                    inlineCSS: function () {
                        module.debug('Removing inline css styles', $style);
                        if ($style && $style.length > 0) {
                            $style.remove();
                        }
                    },

                    // context
                    pushed: function () {
                        $context.removeClass(className.pushed);
                    },
                    pushable: function () {
                        $context.removeClass(className.pushable);
                    },

                    // sidebar
                    active: function () {
                        $module.removeClass(className.active);
                    },
                    animating: function () {
                        $module.removeClass(className.animating);
                    },
                    closing: function () {
                        $pusher.removeClass(className.closing);
                    },
                    transition: function (transition) {
                        transition = transition || module.get.transition();
                        $module.removeClass(transition);
                    },
                    direction: function (direction) {
                        direction = direction || module.get.direction();
                        $module.removeClass(className[direction]);
                    },
                    visible: function () {
                        $module.removeClass(className.visible);
                    },
                    overlay: function () {
                        $module.removeClass(className.overlay);
                    },
                },
                restore: {
                    bodyMargin: function () {
                        var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
                        $context.css((isBody ? 'margin-' : 'padding-') + position, initialBodyMargin);
                        $context.find(selector.bodyFixed.replace('right', position)).each(function () {
                            var
                                el = $(this),
                                attribute = el.css('position') === 'fixed' ? 'padding-' + position : position
                            ;
                            el.css(attribute, '');
                        });
                    },
                },
                get: {
                    direction: function () {
                        if ($module.hasClass(className.top)) {
                            return className.top;
                        }
                        if ($module.hasClass(className.right)) {
                            return className.right;
                        }
                        if ($module.hasClass(className.bottom)) {
                            return className.bottom;
                        }

                        return className.left;
                    },
                    transition: function () {
                        var
                            direction = module.get.direction(),
                            transition
                        ;
                        transition = module.is.mobile()
                            ? (settings.mobileTransition === 'auto'
                                ? settings.defaultTransition.mobile[direction]
                                : settings.mobileTransition)
                            : (settings.transition === 'auto'
                                ? settings.defaultTransition.computer[direction]
                                : settings.transition);
                        module.verbose('Determined transition', transition);

                        return transition;
                    },
                },
                has: {
                    scrollbar: function () {
                        return isBody || $context.css('overflow-y') !== 'hidden';
                    },
                },
                is: {
                    safari: function () {
                        if (module.cache.isSafari === undefined) {
                            module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
                        }

                        return module.cache.isSafari;
                    },
                    edge: function () {
                        if (module.cache.isEdge === undefined) {
                            module.cache.isEdge = !!window.setImmediate && !module.is.ie();
                        }

                        return module.cache.isEdge;
                    },
                    firefox: function () {
                        if (module.cache.isFirefox === undefined) {
                            module.cache.isFirefox = !!window.InstallTrigger;
                        }

                        return module.cache.isFirefox;
                    },
                    iframe: function () {
                        return !(self === top);
                    },
                    ie: function () {
                        if (module.cache.isIE === undefined) {
                            var
                                isIE11 = !window.ActiveXObject && 'ActiveXObject' in window,
                                isIE = 'ActiveXObject' in window
                            ;
                            module.cache.isIE = isIE11 || isIE;
                        }

                        return module.cache.isIE;
                    },

                    mobile: function () {
                        var
                            userAgent    = navigator.userAgent,
                            isMobile     = userAgent.match(regExp.mobile)
                        ;
                        if (isMobile) {
                            module.verbose('Browser was found to be mobile', userAgent);

                            return true;
                        }

                        module.verbose('Browser is not mobile, using regular transition', userAgent);

                        return false;
                    },
                    hidden: function () {
                        return !module.is.visible();
                    },
                    visible: function () {
                        return $module.hasClass(className.visible);
                    },
                    // alias
                    open: function () {
                        return module.is.visible();
                    },
                    closed: function () {
                        return module.is.hidden();
                    },
                    vertical: function () {
                        return $module.hasClass(className.top);
                    },
                    animating: function () {
                        return $context.hasClass(className.animating);
                    },
                    rtl: function () {
                        if (module.cache.isRTL === undefined) {
                            module.cache.isRTL = $module.attr('dir') === 'rtl' || $module.css('direction') === 'rtl' || $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl' || $context.attr('dir') === 'rtl' || $context.css('direction') === 'rtl';
                        }

                        return module.cache.isRTL;
                    },
                },

                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) {
                    module.invoke('destroy');
                }
                module.initialize();
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };

    $.fn.sidebar.settings = {

        name: 'Sidebar',
        namespace: 'sidebar',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        transition: 'auto',
        mobileTransition: 'auto',

        defaultTransition: {
            computer: {
                left: 'uncover',
                right: 'uncover',
                top: 'overlay',
                bottom: 'overlay',
            },
            mobile: {
                left: 'uncover',
                right: 'uncover',
                top: 'overlay',
                bottom: 'overlay',
            },
        },

        context: 'body',
        exclusive: false,
        closable: true,
        dimPage: true,
        scrollLock: false,
        returnScroll: false,
        delaySetup: false,

        onChange: function () {},
        onShow: function () {},
        onHide: function () {},

        onHidden: function () {},
        onVisible: function () {},

        className: {
            active: 'active',
            animating: 'animating',
            blurring: 'blurring',
            closing: 'closing',
            dimmed: 'dimmed',
            locked: 'locked',
            pushable: 'pushable',
            pushed: 'pushed',
            right: 'right',
            top: 'top',
            left: 'left',
            bottom: 'bottom',
            visible: 'visible',
        },

        selector: {
            bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar, > .ui.fixed.nag, > .ui.fixed.nag > .close',
            fixed: '.fixed',
            omitted: 'script, link, style, .ui.modal, .ui.dimmer, .ui.nag, .ui.fixed',
            pusher: '.pusher',
            sidebar: '.ui.sidebar',
        },

        regExp: {
            mobile: /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/g,
        },

        error: {
            method: 'The method you called is not defined.',
            pusher: 'Had to add pusher element. For optimal performance make sure body content is inside a pusher element',
            movedSidebar: 'Had to move sidebar. For optimal performance make sure sidebar and pusher are direct children of your body tag',
            overlay: 'The overlay setting is no longer supported, use animation: overlay',
            notFound: 'There were no elements that matched the specified selector',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Sticky
 * 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.sticky = function (parameters) {
        var
            $allModules    = $(this),
            $document      = $(document),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            returnedValue
        ;

        $allModules.each(function () {
            var
                settings              = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.sticky.settings, parameters)
                    : $.extend({}, $.fn.sticky.settings),

                className             = settings.className,
                namespace             = settings.namespace,
                error                 = settings.error,

                eventNamespace        = '.' + namespace,
                moduleNamespace       = 'module-' + namespace,

                $module               = $(this),
                $window               = $(window),
                $scroll               = contextCheck(settings.scrollContext, window),
                $container,
                $context,

                instance              = $module.data(moduleNamespace),

                element         = this,

                documentObserver,
                observer,
                module
            ;

            module = {

                initialize: function () {
                    module.determineContainer();
                    module.determineContext();
                    module.verbose('Initializing sticky', settings, $container);

                    module.save.positions();
                    module.checkErrors();
                    module.bind.events();

                    if (settings.observeChanges) {
                        module.observeChanges();
                    }
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous instance');
                    module.reset();
                    if (documentObserver) {
                        documentObserver.disconnect();
                    }
                    if (observer) {
                        observer.disconnect();
                    }
                    $window
                        .off('load' + eventNamespace, module.event.load)
                        .off('resize' + eventNamespace, module.event.resize)
                    ;
                    $scroll
                        .off('scrollchange' + eventNamespace, module.event.scrollchange)
                    ;
                    $module.removeData(moduleNamespace);
                },

                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        documentObserver = new MutationObserver(module.event.documentChanged);
                        observer = new MutationObserver(module.event.changed);
                        documentObserver.observe(document, {
                            childList: true,
                            subtree: true,
                        });
                        observer.observe(element, {
                            childList: true,
                            subtree: true,
                        });
                        observer.observe($context[0], {
                            childList: true,
                            subtree: true,
                        });
                        module.debug('Setting up mutation observer', observer);
                    }
                },

                determineContainer: function () {
                    $container = settings.container ? contextCheck(settings.container, window) : $module.offsetParent();
                },

                determineContext: function () {
                    $context = settings.context ? contextCheck(settings.context, window) : $container;
                    if ($context.length === 0) {
                        module.error(error.invalidContext, settings.context, $module);
                    }
                },

                checkErrors: function () {
                    if (module.is.hidden()) {
                        module.error(error.visible, $module);
                    }
                    if (module.cache.element.height > module.cache.context.height) {
                        module.reset();
                        module.error(error.elementSize, $module);
                    }
                },

                bind: {
                    events: function () {
                        $window
                            .on('load' + eventNamespace, module.event.load)
                            .on('resize' + eventNamespace, module.event.resize)
                        ;
                        // pub/sub pattern
                        $scroll
                            .off('scroll' + eventNamespace)
                            .on('scroll' + eventNamespace, module.event.scroll)
                            .on('scrollchange' + eventNamespace, module.event.scrollchange)
                        ;
                    },
                },

                event: {
                    changed: function (mutations) {
                        clearTimeout(module.timer);
                        module.timer = setTimeout(function () {
                            module.verbose('DOM tree modified, updating sticky menu', mutations);
                            module.refresh();
                        }, 100);
                    },
                    documentChanged: function (mutations) {
                        [].forEach.call(mutations, function (mutation) {
                            if (mutation.removedNodes) {
                                [].forEach.call(mutation.removedNodes, function (node) {
                                    if (node === element || $(node).find(element).length > 0) {
                                        module.debug('Element removed from DOM, tearing down events');
                                        module.destroy();
                                    }
                                });
                            }
                        });
                    },
                    load: function () {
                        module.verbose('Page contents finished loading');
                        requestAnimationFrame(module.refresh);
                    },
                    resize: function () {
                        module.verbose('Window resized');
                        requestAnimationFrame(module.refresh);
                    },
                    scroll: function () {
                        requestAnimationFrame(function () {
                            $scroll.triggerHandler('scrollchange' + eventNamespace, $scroll.scrollTop());
                        });
                    },
                    scrollchange: function (event, scrollPosition) {
                        module.stick(scrollPosition);
                        settings.onScroll.call(element);
                    },
                },

                refresh: function (hardRefresh) {
                    module.reset();
                    if (!settings.context) {
                        module.determineContext();
                    }
                    if (hardRefresh) {
                        module.determineContainer();
                    }
                    module.save.positions();
                    module.stick();
                    settings.onReposition.call(element);
                },

                supports: {
                    sticky: function () {
                        var
                            $element = $('<div/>')
                        ;
                        $element.addClass(className.supported);

                        return $element.css('position').match('sticky');
                    },
                },

                save: {
                    lastScroll: function (scroll) {
                        module.lastScroll = scroll;
                    },
                    elementScroll: function (scroll) {
                        module.elementScroll = scroll;
                    },
                    positions: function () {
                        var
                            scrollContext = {
                                height: $scroll.height(),
                            },
                            element = {
                                margin: {
                                    top: parseInt($module.css('margin-top'), 10),
                                    bottom: parseInt($module.css('margin-bottom'), 10),
                                },
                                offset: $module.offset(),
                                width: $module.outerWidth(),
                                height: $module.outerHeight(),
                            },
                            context = {
                                offset: $context.offset(),
                                height: $context.outerHeight(),
                            }
                        ;
                        if (!module.is.standardScroll()) {
                            module.debug('Non-standard scroll. Removing scroll offset from element offset');

                            scrollContext.top = $scroll.scrollTop();
                            scrollContext.left = $scroll.scrollLeft();

                            element.offset.top += scrollContext.top;
                            context.offset.top += scrollContext.top;
                            element.offset.left += scrollContext.left;
                            context.offset.left += scrollContext.left;
                        }
                        module.cache = {
                            fits: (element.height + settings.offset) <= scrollContext.height,
                            sameHeight: element.height === context.height,
                            scrollContext: {
                                height: scrollContext.height,
                            },
                            element: {
                                margin: element.margin,
                                top: element.offset.top - element.margin.top,
                                left: element.offset.left,
                                width: element.width,
                                height: element.height,
                                bottom: element.offset.top + element.height,
                            },
                            context: {
                                top: context.offset.top,
                                height: context.height,
                                bottom: context.offset.top + context.height,
                            },
                        };
                        module.set.containerSize();

                        module.stick();
                        module.debug('Caching element positions', module.cache);
                    },
                },

                get: {
                    direction: function (scroll) {
                        var
                            direction = 'down'
                        ;
                        scroll = scroll || $scroll.scrollTop();
                        if (module.lastScroll && module.lastScroll > scroll) {
                            direction = 'up';
                        }

                        return direction;
                    },
                    scrollChange: function (scroll) {
                        scroll = scroll || $scroll.scrollTop();

                        return module.lastScroll
                            ? scroll - module.lastScroll
                            : 0;
                    },
                    currentElementScroll: function () {
                        if (module.elementScroll) {
                            return module.elementScroll;
                        }

                        return module.is.top()
                            ? Math.abs(parseInt($module.css('top'), 10)) || 0
                            : Math.abs(parseInt($module.css('bottom'), 10)) || 0;
                    },

                    elementScroll: function (scroll) {
                        scroll = scroll || $scroll.scrollTop();
                        var
                            element        = module.cache.element,
                            scrollContext  = module.cache.scrollContext,
                            delta          = module.get.scrollChange(scroll),
                            maxScroll      = element.height - scrollContext.height + settings.offset,
                            elementScroll  = module.get.currentElementScroll(),
                            possibleScroll = elementScroll + delta
                        ;
                        if (module.cache.fits || possibleScroll < 0) {
                            elementScroll = 0;
                        } else if (possibleScroll > maxScroll) {
                            elementScroll = maxScroll;
                        } else {
                            elementScroll = possibleScroll;
                        }

                        return elementScroll;
                    },
                },

                remove: {
                    lastScroll: function () {
                        delete module.lastScroll;
                    },
                    elementScroll: function () {
                        delete module.elementScroll;
                    },
                    minimumSize: function () {
                        $container
                            .css('min-height', '')
                        ;
                    },
                    offset: function () {
                        $module.css('margin-top', '');
                    },
                },

                set: {
                    offset: function () {
                        module.verbose('Setting offset on element', settings.offset);
                        $module
                            .css('margin-top', settings.offset)
                        ;
                    },
                    containerSize: function () {
                        var
                            tagName = $container[0].tagName
                        ;
                        if (tagName === 'HTML' || tagName === 'body') {
                            module.determineContainer();
                        } else {
                            var tallestHeight = Math.max(module.cache.context.height, module.cache.element.height);
                            if (tallestHeight - $container.outerHeight() > settings.jitter) {
                                module.debug('Context is taller than container. Specifying exact height for container', module.cache.context.height);
                                $container.css({
                                    height: tallestHeight,
                                });
                            } else {
                                $container.css({
                                    height: '',
                                });
                            }
                            if (Math.abs($container.outerHeight() - module.cache.context.height) > settings.jitter) {
                                module.debug('Context has padding, specifying exact height for container', module.cache.context.height);
                                $container.css({
                                    height: module.cache.context.height,
                                });
                            }
                        }
                    },
                    minimumSize: function () {
                        var
                            element   = module.cache.element
                        ;
                        $container
                            .css('min-height', element.height)
                        ;
                    },
                    scroll: function (scroll) {
                        module.debug('Setting scroll on element', scroll);
                        if (module.elementScroll === scroll) {
                            return;
                        }
                        if (module.is.top()) {
                            $module
                                .css('bottom', '')
                                .css('top', -scroll + 'px')
                            ;
                        }
                        if (module.is.bottom()) {
                            $module
                                .css('top', '')
                                .css('bottom', scroll + 'px')
                            ;
                        }
                    },
                    size: function () {
                        if (module.cache.element.height !== 0 && module.cache.element.width !== 0) {
                            element.style.setProperty('width', module.cache.element.width + 'px', 'important');
                            element.style.setProperty('height', module.cache.element.height + 'px', 'important');
                        }
                    },
                },

                is: {
                    standardScroll: function () {
                        return $scroll[0] === window;
                    },
                    top: function () {
                        return $module.hasClass(className.top);
                    },
                    bottom: function () {
                        return $module.hasClass(className.bottom);
                    },
                    initialPosition: function () {
                        return !module.is.fixed() && !module.is.bound();
                    },
                    hidden: function () {
                        return !$module.is(':visible');
                    },
                    bound: function () {
                        return $module.hasClass(className.bound);
                    },
                    fixed: function () {
                        return $module.hasClass(className.fixed);
                    },
                },

                stick: function (scrollPosition) {
                    var
                        cachedPosition = scrollPosition || $scroll.scrollTop(),
                        cache          = module.cache,
                        fits           = cache.fits,
                        sameHeight     = cache.sameHeight,
                        element        = cache.element,
                        scrollContext  = cache.scrollContext,
                        context        = cache.context,
                        offset         = module.is.bottom() && settings.pushing
                            ? settings.bottomOffset
                            : settings.offset,
                        scroll         = {
                            top: cachedPosition + offset,
                            bottom: cachedPosition + offset + scrollContext.height,
                        },
                        elementScroll  = fits
                            ? 0
                            : module.get.elementScroll(scroll.top),

                        // shorthand
                        doesntFit      = !fits,
                        elementVisible = element.height !== 0
                    ;
                    if (elementVisible && !sameHeight) {
                        if (module.is.initialPosition()) {
                            if (scroll.top >= context.bottom) {
                                module.debug('Initial element position is bottom of container');
                                module.bindBottom();
                            } else if (scroll.top > element.top) {
                                if ((element.height + scroll.top - elementScroll) >= context.bottom && element.height < context.height) {
                                    module.debug('Initial element position is bottom of container');
                                    module.bindBottom();
                                } else {
                                    module.debug('Initial element position is fixed');
                                    module.fixTop();
                                }
                            }
                        } else if (module.is.fixed()) {
                            if (module.is.top()) {
                                if (scroll.top <= element.top) {
                                    module.debug('Fixed element reached top of container');
                                    module.setInitialPosition();
                                } else if ((element.height + scroll.top - elementScroll) >= context.bottom) {
                                    module.debug('Fixed element reached bottom of container');
                                    module.bindBottom();
                                } else if (doesntFit) { // scroll element if larger than screen
                                    module.set.scroll(elementScroll);
                                    module.save.lastScroll(scroll.top);
                                    module.save.elementScroll(elementScroll);
                                }
                            } else if (module.is.bottom()) {
                                if ((scroll.bottom - element.height) <= element.top) { // top edge
                                    module.debug('Bottom fixed rail has reached top of container');
                                    module.setInitialPosition();
                                } else if (scroll.bottom >= context.bottom) { // bottom edge
                                    module.debug('Bottom fixed rail has reached bottom of container');
                                    module.bindBottom();
                                } else if (doesntFit) { // scroll element if larger than screen
                                    module.set.scroll(elementScroll);
                                    module.save.lastScroll(scroll.top);
                                    module.save.elementScroll(elementScroll);
                                }
                            }
                        } else if (module.is.bottom()) {
                            if (scroll.top <= element.top) {
                                module.debug('Jumped from bottom fixed to top fixed, most likely used home/end button');
                                module.setInitialPosition();
                            } else {
                                if (settings.pushing) {
                                    if (module.is.bound() && scroll.bottom <= context.bottom) {
                                        module.debug('Fixing bottom attached element to bottom of browser.');
                                        module.fixBottom();
                                    }
                                } else {
                                    if (module.is.bound() && (scroll.top <= context.bottom - element.height)) {
                                        module.debug('Fixing bottom attached element to top of browser.');
                                        module.fixTop();
                                    }
                                }
                            }
                        }
                    }
                },

                bindTop: function () {
                    module.debug('Binding element to top of parent container');
                    module.remove.offset();
                    if (settings.setSize) {
                        module.set.size();
                    }
                    $module
                        .css({
                            left: '',
                            top: '',
                            marginBottom: '',
                        })
                        .removeClass(className.fixed)
                        .removeClass(className.bottom)
                        .addClass(className.bound)
                        .addClass(className.top)
                    ;
                    settings.onTop.call(element);
                    settings.onUnstick.call(element);
                },
                bindBottom: function () {
                    module.debug('Binding element to bottom of parent container');
                    module.remove.offset();
                    if (settings.setSize) {
                        module.set.size();
                    }
                    $module
                        .css({
                            left: '',
                            top: '',
                        })
                        .removeClass(className.fixed)
                        .removeClass(className.top)
                        .addClass(className.bound)
                        .addClass(className.bottom)
                    ;
                    settings.onBottom.call(element);
                    settings.onUnstick.call(element);
                },

                setInitialPosition: function () {
                    module.debug('Returning to initial position');
                    module.unfix();
                    module.unbind();
                },

                fixTop: function () {
                    module.debug('Fixing element to top of page');
                    if (settings.setSize) {
                        module.set.size();
                    }
                    module.set.minimumSize();
                    module.set.offset();
                    $module
                        .css({
                            left: module.cache.element.left,
                            bottom: '',
                            marginBottom: '',
                        })
                        .removeClass(className.bound)
                        .removeClass(className.bottom)
                        .addClass(className.fixed)
                        .addClass(className.top)
                    ;
                    settings.onStick.call(element);
                },

                fixBottom: function () {
                    module.debug('Sticking element to bottom of page');
                    if (settings.setSize) {
                        module.set.size();
                    }
                    module.set.minimumSize();
                    module.set.offset();
                    $module
                        .css({
                            left: module.cache.element.left,
                            bottom: '',
                            marginBottom: '',
                        })
                        .removeClass(className.bound)
                        .removeClass(className.top)
                        .addClass(className.fixed)
                        .addClass(className.bottom)
                    ;
                    settings.onStick.call(element);
                },

                unbind: function () {
                    if (module.is.bound()) {
                        module.debug('Removing container bound position on element');
                        module.remove.offset();
                        $module
                            .removeClass(className.bound)
                            .removeClass(className.top)
                            .removeClass(className.bottom)
                        ;
                    }
                },

                unfix: function () {
                    if (module.is.fixed()) {
                        module.debug('Removing fixed position on element');
                        module.remove.minimumSize();
                        module.remove.offset();
                        $module
                            .removeClass(className.fixed)
                            .removeClass(className.top)
                            .removeClass(className.bottom)
                        ;
                        settings.onUnstick.call(element);
                    }
                },

                reset: function () {
                    module.debug('Resetting elements position');
                    module.unbind();
                    module.unfix();
                    module.resetCSS();
                    module.remove.offset();
                    module.remove.lastScroll();
                },

                resetCSS: function () {
                    $module
                        .css({
                            width: '',
                            height: '',
                        })
                    ;
                    $container
                        .css({
                            height: '',
                        })
                    ;
                },

                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(); }, 0);
                    },
                    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.sticky.settings = {

        name: 'Sticky',
        namespace: 'sticky',

        silent: false,
        debug: false,
        verbose: true,
        performance: true,

        // whether to stick in the opposite direction on scroll up
        pushing: false,

        context: false,
        container: false,

        // Context to watch scroll events
        scrollContext: window,

        // Offset to adjust scroll
        offset: 0,

        // Offset to adjust scroll when attached to bottom of screen
        bottomOffset: 0,

        // will only set container height if difference between context and container is larger than this number
        jitter: 5,

        // set width of sticky element when it is fixed to page (used to make sure 100% width is maintained if no fixed size set)
        setSize: true,

        // Whether to automatically observe changes with Mutation Observers
        observeChanges: false,

        // Called when position is recalculated
        onReposition: function () {},

        // Called on each scroll
        onScroll: function () {},

        // Called when element is stuck to viewport
        onStick: function () {},

        // Called when element is unstuck from viewport
        onUnstick: function () {},

        // Called when element reaches top of context
        onTop: function () {},

        // Called when element reaches bottom of context
        onBottom: function () {},

        error: {
            visible: 'Element is hidden, you must call refresh after element becomes visible. Use silent setting to suppress this warning in production.',
            method: 'The method you called is not defined.',
            invalidContext: 'Context specified does not exist',
            elementSize: 'Sticky element is larger than its container, cannot create sticky.',
        },

        className: {
            bound: 'bound',
            fixed: 'fixed',
            supported: 'native',
            top: 'top',
            bottom: 'bottom',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Tab
 * https://github.com/fomantic/Fomantic-UI/
 *
 *
 * Released under the MIT license
 * https://opensource.org/licenses/MIT
 *
 */

(function ($, window, document) {
    'use strict';

    function isWindow(obj) {
        return obj !== null && obj === obj.window;
    }

    function isFunction(obj) {
        return typeof obj === 'function' && typeof obj.nodeType !== 'number';
    }

    window = window !== undefined && window.Math === Math
        ? window
        : globalThis;

    $.fn.tab = function (parameters) {
        var
            // use window context if none specified
            $allModules     = isFunction(this)
                ? $(window)
                : $(this),
            $document      = $(document),
            time            = Date.now(),
            performance     = [],

            query           = arguments[0],
            methodInvoked   = typeof query === 'string',
            queryArguments  = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            initializedHistory = false,
            returnedValue
        ;

        $allModules.each(function () {
            var
                settings        = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.tab.settings, parameters)
                    : $.extend({}, $.fn.tab.settings),

                className       = settings.className,
                metadata        = settings.metadata,
                selector        = settings.selector,
                error           = settings.error,
                regExp          = settings.regExp,

                eventNamespace  = '.' + settings.namespace,
                moduleNamespace = 'module-' + settings.namespace,

                $module         = $(this),
                $context,
                $tabs,

                cache           = {},
                firstLoad       = true,
                recursionDepth  = 0,
                element         = this,
                instance        = $module.data(moduleNamespace),

                activeTabPath,
                parameterArray,
                module,

                historyEvent
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing tab menu item', $module);
                    module.determineTabs();

                    module.debug('Determining tabs', settings.context, $tabs);
                    // set up automatic routing
                    if (settings.auto) {
                        module.set.auto();
                    }
                    module.bind.events();

                    if (settings.history && !initializedHistory) {
                        settings.history = module.initializeHistory();
                        initializedHistory = true;
                    }

                    var activeTab = module.determine.activeTab();
                    if (settings.autoTabActivation && instance === undefined && activeTab === null) {
                        activeTab = settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation;
                        module.debug('No active tab detected, setting tab active', activeTab);
                        module.changeTab(activeTab);
                    }
                    if (activeTab !== null && settings.history && settings.historyType === 'state') {
                        var autoUpdate = $.address.autoUpdate();
                        $.address.autoUpdate(false);
                        $.address.value(activeTab);
                        $.address.autoUpdate(autoUpdate);
                    }

                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, module)
                    ;
                },

                destroy: function () {
                    module.debug('Destroying tabs', $module);
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                },

                bind: {
                    events: function () {
                        // if using $.tab don't add events
                        if (!isWindow(element)) {
                            module.debug('Attaching tab activation events to element', $module);
                            $module
                                .on('click' + eventNamespace, module.event.click)
                            ;
                        }
                    },
                },

                determineTabs: function () {
                    var
                        $reference
                    ;

                    // determine tab context
                    if (settings.context === 'parent') {
                        if ($module.closest(selector.ui).length > 0) {
                            $reference = $module.closest(selector.ui);
                            module.verbose('Using closest UI element as parent', $reference);
                        } else {
                            $reference = $module;
                        }
                        $context = $reference.parent();
                        module.verbose('Determined parent element for creating context', $context);
                    } else if (settings.context) {
                        $context = contextCheck(settings.context, window);
                        module.verbose('Using selector for tab context', settings.context, $context);
                    } else {
                        $context = $('body');
                    }
                    // find tabs
                    if (settings.childrenOnly) {
                        $tabs = $context.children(selector.tabs);
                        module.debug('Searching tab context children for tabs', $context, $tabs);
                    } else {
                        $tabs = $context.find(selector.tabs);
                        module.debug('Searching tab context for tabs', $context, $tabs);
                    }
                },

                initializeHistory: function () {
                    module.debug('Initializing page state');
                    if ($.address === undefined) {
                        module.error(error.state);

                        return false;
                    }
                    if (settings.historyType === 'state') {
                        module.debug('Using HTML5 to manage state');
                        if (settings.path !== false) {
                            $.address
                                .history(true)
                                .state(settings.path)
                            ;
                            $(window).trigger('popstate');
                        } else {
                            module.error(error.path);

                            return false;
                        }
                    }
                    $.address
                        .bind('change', module.event.history.change)
                    ;

                    return true;
                },

                event: {
                    click: function (event) {
                        var
                            tabPath = $(this).data(metadata.tab)
                        ;
                        if (tabPath !== undefined) {
                            if (settings.history) {
                                module.verbose('Updating page state', event);
                                $.address.value(tabPath);
                            } else {
                                module.verbose('Changing tab', event);
                                module.changeTab(tabPath);
                            }
                            event.preventDefault();
                        } else {
                            module.debug('No tab specified');
                        }
                    },
                    history: {
                        change: function (event) {
                            var
                                tabPath   = event.pathNames.join('/') || module.get.initialPath(),
                                pageTitle = settings.templates.determineTitle(tabPath) || false
                            ;
                            module.performance.display();
                            module.debug('History change event', tabPath, event);
                            historyEvent = event;
                            if (tabPath !== undefined) {
                                module.changeTab(tabPath);
                            }
                            if (pageTitle) {
                                $.address.title(pageTitle);
                            }
                        },
                    },
                },

                refresh: function () {
                    if (activeTabPath) {
                        module.debug('Refreshing tab', activeTabPath);
                        module.changeTab(activeTabPath);
                    }
                },

                cache: {

                    read: function (cacheKey) {
                        return cacheKey !== undefined
                            ? cache[cacheKey]
                            : false;
                    },
                    add: function (cacheKey, content) {
                        cacheKey = cacheKey || activeTabPath;
                        module.debug('Adding cached content for', cacheKey);
                        cache[cacheKey] = content;
                    },
                    remove: function (cacheKey) {
                        cacheKey = cacheKey || activeTabPath;
                        module.debug('Removing cached content for', cacheKey);
                        delete cache[cacheKey];
                    },
                },

                escape: {
                    string: function (text) {
                        text = String(text);

                        return text.replace(regExp.escape, '\\$&');
                    },
                },

                set: {
                    auto: function () {
                        var
                            url = typeof settings.path === 'string'
                                ? settings.path.replace(/\/$/, '') + '/{$tab}'
                                : '/{$tab}'
                        ;
                        module.verbose('Setting up automatic tab retrieval from server', url);
                        if ($.isPlainObject(settings.apiSettings)) {
                            settings.apiSettings.url = url;
                        } else {
                            settings.apiSettings = {
                                url: url,
                            };
                        }
                    },
                    loading: function (tabPath) {
                        var
                            $tab      = module.get.tabElement(tabPath),
                            isLoading = $tab.hasClass(className.loading)
                        ;
                        if (!isLoading) {
                            module.verbose('Setting loading state for', $tab);
                            $tab
                                .addClass(className.loading)
                                .siblings($tabs)
                                .removeClass(className.active + ' ' + className.loading)
                            ;
                            if ($tab.length > 0) {
                                settings.onRequest.call($tab[0], tabPath);
                            }
                        }
                    },
                    state: function (state) {
                        $.address.value(state);
                    },
                },

                changeTab: function (tabPath) {
                    var
                        pushStateAvailable = window.history && window.history.pushState,
                        shouldIgnoreLoad   = pushStateAvailable && settings.ignoreFirstLoad && firstLoad,
                        remoteContent      = settings.auto || $.isPlainObject(settings.apiSettings),
                        // only add default path if not remote content
                        pathArray = remoteContent && !shouldIgnoreLoad
                            ? module.utilities.pathToArray(tabPath)
                            : module.get.defaultPathArray(tabPath)
                    ;
                    tabPath = module.utilities.arrayToPath(pathArray);
                    $.each(pathArray, function (index, tab) {
                        var
                            currentPathArray   = pathArray.slice(0, index + 1),
                            currentPath        = module.utilities.arrayToPath(currentPathArray),

                            isTab              = module.is.tab(currentPath),
                            isLastIndex        = index + 1 === pathArray.length,

                            $tab               = module.get.tabElement(currentPath),
                            $anchor,
                            nextPathArray,
                            nextPath,
                            isLastTab
                        ;
                        module.verbose('Looking for tab', tab);
                        if (isTab) {
                            module.verbose('Tab was found', tab);
                            // scope up
                            activeTabPath = currentPath;
                            parameterArray = module.utilities.filterArray(pathArray, currentPathArray);

                            if (isLastIndex) {
                                isLastTab = true;
                            } else {
                                nextPathArray = pathArray.slice(0, index + 2);
                                nextPath = module.utilities.arrayToPath(nextPathArray);
                                isLastTab = !module.is.tab(nextPath);
                                if (isLastTab) {
                                    module.verbose('Tab parameters found', nextPathArray);
                                }
                            }
                            if (settings.onBeforeChange.call(element, currentPath) === false) {
                                module.debug('onBeforeChange returned false, cancelling tab change', $tab);

                                return false;
                            }
                            if (isLastTab && remoteContent) {
                                if (!shouldIgnoreLoad) {
                                    module.activate.navigation(currentPath);
                                    module.fetch.content(currentPath, tabPath);
                                } else {
                                    module.debug('Ignoring remote content on first tab load', currentPath);
                                    firstLoad = false;
                                    module.cache.add(tabPath, $tab.html());
                                    module.activate.all(currentPath);
                                    settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
                                    settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
                                }

                                return false;
                            }

                            module.debug('Opened local tab', currentPath);
                            module.activate.all(currentPath);
                            if (!module.cache.read(currentPath)) {
                                module.cache.add(currentPath, true);
                                module.debug('First time tab loaded calling tab init');
                                settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
                            }
                            settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
                        } else if (tabPath.search('/') === -1 && tabPath !== '') {
                            // look for in page anchor
                            tabPath = module.escape.string(tabPath);
                            $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
                            currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
                            $tab = module.get.tabElement(currentPath);
                            // if anchor exists use parent tab
                            if ($anchor && $anchor.length > 0 && currentPath) {
                                module.debug('Anchor link used, opening parent tab', $tab, $anchor);
                                if (settings.onBeforeChange.call(element, currentPath) === false) {
                                    module.debug('onBeforeChange returned false, cancelling tab change', $tab);

                                    return false;
                                }
                                if (!$tab.hasClass(className.active)) {
                                    setTimeout(function () {
                                        module.scrollTo($anchor);
                                    }, 0);
                                }
                                module.activate.all(currentPath);
                                if (!module.cache.read(currentPath)) {
                                    module.cache.add(currentPath, true);
                                    module.debug('First time tab loaded calling tab init');
                                    settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
                                }
                                settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);

                                return false;
                            }
                        } else {
                            module.error(error.missingTab, $module, $context, currentPath);

                            return false;
                        }
                    });
                },

                scrollTo: function ($element) {
                    var
                        scrollOffset = $element && $element.length > 0
                            ? $element.offset().top
                            : false
                    ;
                    if (scrollOffset !== false) {
                        module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
                        $document.scrollTop(scrollOffset);
                    }
                },

                update: {
                    content: function (tabPath, html, evaluateScripts) {
                        var
                            $tab = module.get.tabElement(tabPath),
                            tab  = $tab[0]
                        ;
                        evaluateScripts = evaluateScripts !== undefined
                            ? evaluateScripts
                            : settings.evaluateScripts;
                        if (typeof settings.cacheType === 'string' && settings.cacheType.toLowerCase() === 'dom' && typeof html !== 'string') {
                            $tab
                                .empty()
                                .append($(html).clone(true))
                            ;
                        } else {
                            if (evaluateScripts) {
                                module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
                                $tab.html(html);
                            } else {
                                module.debug('Updating HTML', tabPath, html);
                                tab.innerHTML = html;
                            }
                        }
                    },
                },

                fetch: {

                    content: function (tabPath, fullTabPath) {
                        var
                            $tab        = module.get.tabElement(tabPath),
                            apiSettings = {
                                dataType: 'html',
                                encodeParameters: false,
                                on: 'now',
                                cache: settings.alwaysRefresh,
                                headers: {
                                    'X-Remote': true,
                                },
                                onSuccess: function (response) {
                                    if (settings.cacheType === 'response') {
                                        module.cache.add(fullTabPath, response);
                                    }
                                    module.update.content(tabPath, response);
                                    if (tabPath == activeTabPath) {
                                        module.debug('Content loaded', tabPath);
                                        module.activate.tab(tabPath);
                                    } else {
                                        module.debug('Content loaded in background', tabPath);
                                    }
                                    settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
                                    settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);

                                    if (settings.loadOnce) {
                                        module.cache.add(fullTabPath, true);
                                    } else if (typeof settings.cacheType === 'string' && settings.cacheType.toLowerCase() === 'dom' && $tab.children().length > 0) {
                                        setTimeout(function () {
                                            var
                                                $clone = $tab.children().clone(true)
                                            ;
                                            $clone = $clone.not('script');
                                            module.cache.add(fullTabPath, $clone);
                                        }, 0);
                                    } else {
                                        module.cache.add(fullTabPath, $tab.html());
                                    }
                                },
                                urlData: {
                                    tab: fullTabPath,
                                },
                            },
                            request         = $tab.api('get request') || false,
                            existingRequest = request && request.state() === 'pending',
                            requestSettings,
                            cachedContent
                        ;

                        fullTabPath = fullTabPath || tabPath;
                        cachedContent = module.cache.read(fullTabPath);

                        if (settings.cache && cachedContent) {
                            module.activate.tab(tabPath);
                            module.debug('Adding cached content', fullTabPath);
                            if (!settings.loadOnce) {
                                if (settings.evaluateScripts === 'once') {
                                    module.update.content(tabPath, cachedContent, false);
                                } else {
                                    module.update.content(tabPath, cachedContent);
                                }
                            }
                            settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
                        } else if (existingRequest) {
                            module.set.loading(tabPath);
                            module.debug('Content is already loading', fullTabPath);
                        } else if ($.api !== undefined) {
                            requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
                            module.debug('Retrieving remote content', fullTabPath, requestSettings);
                            module.set.loading(tabPath);
                            $tab.api(requestSettings);
                        } else {
                            module.error(error.api);
                        }
                    },
                },

                activate: {
                    all: function (tabPath) {
                        module.activate.tab(tabPath);
                        module.activate.navigation(tabPath);
                    },
                    tab: function (tabPath) {
                        var
                            $tab          = module.get.tabElement(tabPath),
                            $deactiveTabs = settings.deactivate === 'siblings'
                                ? $tab.siblings($tabs)
                                : $tabs.not($tab),
                            isActive      = $tab.hasClass(className.active)
                        ;
                        module.verbose('Showing tab content for', $tab);
                        if (!isActive) {
                            $tab
                                .addClass(className.active)
                            ;
                            $deactiveTabs
                                .removeClass(className.active + ' ' + className.loading)
                            ;
                            if ($tab.length > 0) {
                                settings.onVisible.call($tab[0], tabPath);
                            }
                        }
                    },
                    navigation: function (tabPath) {
                        var
                            $navigation         = module.get.navElement(tabPath),
                            $deactiveNavigation = settings.deactivate === 'siblings'
                                ? $navigation.siblings($allModules)
                                : $allModules.not($navigation),
                            isActive    = $navigation.hasClass(className.active)
                        ;
                        module.verbose('Activating tab navigation for', $navigation, tabPath);
                        if (!isActive) {
                            $navigation
                                .addClass(className.active)
                            ;
                            $deactiveNavigation
                                .removeClass(className.active + ' ' + className.loading)
                            ;
                        }
                    },
                },

                deactivate: {
                    all: function () {
                        module.deactivate.navigation();
                        module.deactivate.tabs();
                    },
                    navigation: function () {
                        $allModules
                            .removeClass(className.active)
                        ;
                    },
                    tabs: function () {
                        $tabs
                            .removeClass(className.active + ' ' + className.loading)
                        ;
                    },
                },

                is: {
                    tab: function (tabName) {
                        return tabName !== undefined
                            ? module.get.tabElement(tabName).length > 0
                            : false;
                    },
                },

                get: {
                    initialPath: function () {
                        return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
                    },
                    path: function () {
                        return $.address.value();
                    },
                    // adds default tabs to tab path
                    defaultPathArray: function (tabPath) {
                        return module.utilities.pathToArray(module.get.defaultPath(tabPath));
                    },
                    defaultPath: function (tabPath) {
                        var
                            $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
                            defaultTab  = $defaultNav.data(metadata.tab) || false
                        ;
                        if (defaultTab) {
                            module.debug('Found default tab', defaultTab);
                            if (recursionDepth < settings.maxDepth) {
                                recursionDepth++;

                                return module.get.defaultPath(defaultTab);
                            }
                            module.error(error.recursion);
                        } else {
                            module.debug('No default tabs found for', tabPath, $tabs);
                        }
                        recursionDepth = 0;

                        return tabPath;
                    },
                    navElement: function (tabPath) {
                        tabPath = tabPath || activeTabPath;

                        return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
                    },
                    tabElement: function (tabPath) {
                        var
                            $fullPathTab,
                            $simplePathTab,
                            tabPathArray,
                            lastTab
                        ;
                        tabPath = tabPath || activeTabPath;
                        tabPathArray = module.utilities.pathToArray(tabPath);
                        lastTab = module.utilities.last(tabPathArray);
                        $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
                        $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');

                        return $fullPathTab.length > 0
                            ? $fullPathTab
                            : $simplePathTab;
                    },
                    tab: function () {
                        return activeTabPath;
                    },
                },

                determine: {
                    activeTab: function () {
                        var activeTab = null;

                        $tabs.each(function (_index, tab) {
                            var $tab = $(tab);

                            if ($tab.hasClass(className.active)) {
                                var
                                    tabPath = $(this).data(metadata.tab),
                                    $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
                                ;

                                if ($anchor.hasClass(className.active)) {
                                    activeTab = tabPath;
                                }
                            }
                        });

                        return activeTab;
                    },
                },

                utilities: {
                    filterArray: function (keepArray, removeArray) {
                        return $.grep(keepArray, function (keepValue) {
                            return $.inArray(keepValue, removeArray) === -1;
                        });
                    },
                    last: function (array) {
                        return Array.isArray(array)
                            ? array[array.length - 1]
                            : false;
                    },
                    pathToArray: function (pathName) {
                        if (pathName === undefined) {
                            pathName = activeTabPath;
                        }

                        return typeof pathName === 'string'
                            ? pathName.split('/')
                            : [pathName];
                    },
                    arrayToPath: function (pathArray) {
                        return Array.isArray(pathArray)
                            ? pathArray.join('/')
                            : false;
                    },
                },

                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;
    };

    // shortcut for tabbed content with no defined navigation
    $.tab = function () {
        $(window).tab.apply(this, arguments);
    };

    $.fn.tab.settings = {

        name: 'Tab',
        namespace: 'tab',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        auto: false, // uses pjax style endpoints fetching content from same url with remote-content headers
        history: false, // use browser history
        historyType: 'hash', // #/ or html5 state
        path: false, // base path of url

        context: false, // specify a context that tabs must appear inside
        childrenOnly: false, // use only tabs that are children of context
        maxDepth: 25, // max depth a tab can be nested

        deactivate: 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together

        alwaysRefresh: false, // load tab content new every tab click
        cache: true, // cache the content requests to pull locally
        loadOnce: false, // Whether tab data should only be loaded once when using remote content
        cacheType: 'response', // Whether to cache exact response, or to html cache contents after scripts execute
        ignoreFirstLoad: false, // don't load remote content on first load

        apiSettings: false, // settings for api call
        evaluateScripts: 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
        autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab

        onFirstLoad: function (tabPath, parameterArray, historyEvent) {}, // called first time loaded
        onLoad: function (tabPath, parameterArray, historyEvent) {}, // called on every load
        onVisible: function (tabPath, parameterArray, historyEvent) {}, // called every time tab visible
        onRequest: function (tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
        onBeforeChange: function (tabPath) {}, // called before a tab is about to be changed. Returning false will cancel the tab change

        templates: {
            determineTitle: function (tabArray) {}, // returns page title for path
        },

        error: {
            api: 'You attempted to load content without API module',
            method: 'The method you called is not defined',
            missingTab: 'Activated tab cannot be found. Tabs are case-sensitive.',
            noContent: 'The tab you specified is missing a content url.',
            path: 'History enabled, but no path was specified',
            recursion: 'Max recursive depth reached',
            state: 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>',
        },

        regExp: {
            escape: /[\s#$()*+,.:=?@[\\\]^{|}-]/g,
        },

        metadata: {
            tab: 'tab',
            loaded: 'loaded',
            promise: 'promise',
        },

        className: {
            loading: 'loading',
            active: 'active',
        },

        selector: {
            tabs: '.ui.tab',
            ui: '.ui',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Toast
 * 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.toast = function (parameters) {
        var
            $allModules    = $(this),
            $body          = $('body'),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : $body;
                    }
                }

                return $context;
            },
            returnedValue
        ;
        $allModules.each(function () {
            var
                settings          = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.toast.settings, parameters)
                    : $.extend({}, $.fn.toast.settings),

                className        = settings.className,
                selector         = settings.selector,
                error            = settings.error,
                namespace        = settings.namespace,
                fields           = settings.fields,

                eventNamespace   = '.' + namespace,
                moduleNamespace  = namespace + '-module',

                $module          = $(this),
                $toastBox,
                $toast,
                $actions,
                $progress,
                $progressBar,
                $animationObject,
                $close,
                $context         = settings.context ? contextCheck(settings.context, window) : $body,

                isToastComponent = $module.hasClass('toast') || $module.hasClass('message') || $module.hasClass('card'),

                element          = this,
                instance         = isToastComponent ? $module.data(moduleNamespace) : undefined,

                id,
                module
            ;
            module = {

                initialize: function () {
                    module.verbose('Initializing element');
                    module.create.id();
                    if (!module.has.container()) {
                        module.create.container();
                    }
                    if (isToastComponent || settings.message !== '' || settings.title !== '' || module.get.iconClass() !== '' || settings.showImage || module.has.configActions()) {
                        if (typeof settings.showProgress !== 'string' || [className.top, className.bottom].indexOf(settings.showProgress) === -1) {
                            settings.showProgress = false;
                        }
                        module.create.toast();
                        if (settings.closeOnClick && (settings.closeIcon || $($toast).find(selector.input).length > 0 || module.has.configActions())) {
                            settings.closeOnClick = false;
                        }
                        if (!settings.closeOnClick) {
                            $toastBox.addClass(className.unclickable);
                        }
                        module.bind.events();
                    }
                    module.instantiate();
                    if ($toastBox) {
                        module.show();
                    }
                },

                instantiate: function () {
                    module.verbose('Storing instance of toast');
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                destroy: function () {
                    if ($toastBox) {
                        module.debug('Removing toast', $toastBox);
                        module.unbind.events();
                        settings.onRemove.call($toastBox, element);
                        $toastBox.remove();
                        $toastBox = undefined;
                        $toast = undefined;
                        $animationObject = undefined;
                        $progress = undefined;
                        $progressBar = undefined;
                        $close = undefined;
                    }
                    $module
                        .removeData(moduleNamespace)
                    ;
                },

                show: function (callback) {
                    if (settings.onShow.call($toastBox, element) === false) {
                        module.debug('onShow callback returned false, cancelling toast animation');

                        return;
                    }
                    callback = callback || function () {};
                    module.debug('Showing toast');
                    module.animate.show(callback);
                },

                close: function (callback) {
                    if (settings.onHide.call($toastBox, element) === false) {
                        module.debug('onHide callback returned false, cancelling toast animation');

                        return;
                    }
                    callback = callback || function () {};
                    module.debug('Closing toast');
                    module.remove.visible();
                    module.unbind.events();
                    module.animate.close(callback);
                },

                create: {
                    container: function () {
                        module.verbose('Creating container');
                        $context.append($('<div/>', {
                            class: settings.position + ' ' + className.container + ' '
                            + (settings.horizontal ? className.horizontal : '') + ' '
                            + (settings.context && settings.context !== 'body' ? className.absolute : ''),
                        }));
                    },
                    id: function () {
                        id = (Math.random().toString(16) + '000000000').slice(2, 10);
                        module.verbose('Creating unique id for element', id);
                    },
                    toast: function () {
                        $toastBox = $('<div/>', { class: className.box });
                        var iconClass = module.get.iconClass();
                        if (!isToastComponent) {
                            module.verbose('Creating toast');
                            $toast = $('<div/>', { role: 'alert' });
                            var $content = $('<div/>', { class: className.content });
                            if (iconClass !== '') {
                                $toast.append($('<i/>', { class: iconClass + ' ' + className.icon }));
                            }

                            if (settings.showImage) {
                                $toast.append($('<img>', {
                                    class: className.image + ' ' + settings.classImage,
                                    src: settings.showImage,
                                }));
                            }
                            if (settings.title !== '') {
                                var titleId = '_' + module.get.id() + 'title';
                                $toast.attr('aria-labelledby', titleId);
                                $content.append($('<div/>', {
                                    class: className.title,
                                    id: titleId,
                                    html: module.helpers.escape(settings.title, settings.preserveHTML),
                                }));
                            }
                            var descId = '_' + module.get.id() + 'desc';
                            $toast.attr('aria-describedby', descId);
                            $content.append($('<div/>', {
                                class: className.message,
                                id: descId,
                                html: module.helpers.escape(settings.message, settings.preserveHTML),
                            }));

                            $toast
                                .addClass(settings.class + ' ' + className.toast)
                                .append($content)
                            ;
                            $toast.css('opacity', String(settings.opacity));
                            if (settings.closeIcon) {
                                $close = $('<i/>', {
                                    class: className.close + ' ' + (typeof settings.closeIcon === 'string' ? settings.closeIcon : ''),
                                    role: 'button',
                                    tabindex: 0,
                                    'aria-label': settings.text.close,
                                });
                                if ($close.hasClass(className.left)) {
                                    $toast.prepend($close);
                                } else {
                                    $toast.append($close);
                                }
                            }
                        } else {
                            $toast = settings.cloneModule ? $module.clone().removeAttr('id') : $module;
                            $close = $toast.find('> i' + module.helpers.toClass(className.close));
                            settings.closeIcon = $close.length > 0;
                            if (iconClass !== '') {
                                $toast.find(selector.icon).attr('class', iconClass + ' ' + className.icon);
                            }
                            if (settings.showImage) {
                                $toast.find(selector.image).attr('src', settings.showImage);
                            }
                            if (settings.title !== '') {
                                $toast.find(selector.title).html(module.helpers.escape(settings.title, settings.preserveHTML));
                            }
                            if (settings.message !== '') {
                                $toast.find(selector.message).html(module.helpers.escape(settings.message, settings.preserveHTML));
                            }
                        }
                        if ($toast.hasClass(className.compact)) {
                            settings.compact = true;
                        }
                        if ($toast.hasClass('card')) {
                            settings.compact = false;
                        }
                        $actions = $toast.find('.actions');
                        if (module.has.configActions()) {
                            if ($actions.length === 0) {
                                $actions = $('<div/>', { class: className.actions + ' ' + (settings.classActions || '') }).appendTo($toast);
                            }
                            if ($toast.hasClass('card') && !$actions.hasClass(className.attached)) {
                                $actions.addClass(className.extraContent);
                                if ($actions.hasClass(className.vertical)) {
                                    $actions.removeClass(className.vertical);
                                    module.error(error.verticalCard);
                                }
                            }
                            settings.actions.forEach(function (el) {
                                var
                                    icon = el[fields.icon]
                                        ? '<i ' + (el[fields.text] ? 'aria-hidden="true"' : '')
                                            + ' class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>'
                                        : '',
                                    text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
                                    cls = module.helpers.deQuote(el[fields.class] || ''),
                                    click = el[fields.click] && isFunction(el[fields.click])
                                        ? el[fields.click]
                                        : function () {}
                                ;
                                $actions.append($('<button/>', {
                                    html: icon + text,
                                    'aria-label': (el[fields.text] || el[fields.icon] || '').replace(/<[^>]+(>|$)/g, ''),
                                    class: className.button + ' ' + cls,
                                    on: {
                                        click: function () {
                                            var $button = $(this);
                                            if ($button.is(selector.approve) || $button.is(selector.deny) || click.call(element, $module) === false) {
                                                return;
                                            }
                                            module.close();
                                        },
                                    },
                                }));
                            });
                        }
                        if ($actions && $actions.hasClass(className.vertical)) {
                            $toast.addClass(className.vertical);
                        }
                        if ($actions.length > 0 && !$actions.hasClass(className.attached)) {
                            if ($actions && (!$actions.hasClass(className.basic) || $actions.hasClass(className.left))) {
                                $toast.addClass(className.actions);
                            }
                        }
                        if (settings.displayTime === 'auto') {
                            settings.displayTime = Math.max(settings.minDisplayTime, ($toast.text().split(' ').length / settings.wordsPerMinute) * 60000);
                        }
                        $toastBox.append($toast);

                        if ($actions.length > 0 && $actions.hasClass(className.attached)) {
                            $actions.addClass(className.buttons);
                            $actions.detach();
                            $toast.addClass(className.attached);
                            if (!$actions.hasClass(className.vertical)) {
                                if ($actions.hasClass(className.top)) {
                                    $toastBox.prepend($actions);
                                    $toast.addClass(className.bottom);
                                } else {
                                    $toastBox.append($actions);
                                    $toast.addClass(className.top);
                                }
                            } else {
                                $toast.wrap(
                                    $('<div/>', {
                                        class: className.vertical + ' '
                                            + className.attached + ' '
                                            + (settings.compact ? className.compact : ''),
                                    })
                                );
                                if ($actions.hasClass(className.left)) {
                                    $toast.addClass(className.left).parent().addClass(className.left).prepend($actions);
                                } else {
                                    $toast.parent().append($actions);
                                }
                            }
                        }
                        if ($module !== $toast) {
                            $module = $toast;
                            element = $toast[0];
                        }
                        if (settings.displayTime > 0) {
                            var progressingClass = className.progressing + ' ' + (settings.pauseOnHover ? className.pausable : '');
                            if (settings.showProgress) {
                                $progress = $('<div/>', {
                                    class: className.progress + ' ' + (settings.classProgress || settings.class),
                                    'data-percent': '',
                                });
                                if (!settings.classProgress) {
                                    if ($toast.hasClass('toast') && !$toast.hasClass(className.inverted)) {
                                        $progress.addClass(className.inverted);
                                    } else {
                                        $progress.removeClass(className.inverted);
                                    }
                                }
                                $progressBar = $('<div/>', { class: 'bar ' + (settings.progressUp ? 'up ' : 'down ') + progressingClass });
                                $progress
                                    .addClass(settings.showProgress)
                                    .append($progressBar)
                                ;
                                if ($progress.hasClass(className.top)) {
                                    $toastBox.prepend($progress);
                                } else {
                                    $toastBox.append($progress);
                                }
                                $progressBar.css('animation-duration', settings.displayTime / 1000 + 's');
                            }
                            $animationObject = $('<span/>', { class: 'wait ' + progressingClass });
                            $animationObject.css('animation-duration', settings.displayTime / 1000 + 's');
                            $animationObject.appendTo($toast);
                        }
                        if (settings.compact) {
                            $toastBox.addClass(className.compact);
                            $toast.addClass(className.compact);
                            if ($progress) {
                                $progress.addClass(className.compact);
                            }
                        }
                        if (settings.newestOnTop) {
                            $toastBox.prependTo(module.get.container());
                        } else {
                            $toastBox.appendTo(module.get.container());
                        }
                    },
                },

                bind: {
                    events: function () {
                        module.debug('Binding events to toast');
                        if (settings.closeIcon) {
                            $close.on('click' + eventNamespace, module.event.close);
                        }
                        $toast.on('click' + eventNamespace, module.event.click);
                        if ($animationObject) {
                            $animationObject.on('animationend' + eventNamespace, module.event.close);
                        }
                        $toastBox
                            .on('click' + eventNamespace, selector.approve, module.event.approve)
                            .on('click' + eventNamespace, selector.deny, module.event.deny)
                        ;
                    },
                },

                unbind: {
                    events: function () {
                        module.debug('Unbinding events to toast');
                        if (settings.closeIcon) {
                            $close.off('click' + eventNamespace);
                        }
                        $toast.off('click' + eventNamespace);
                        if ($animationObject) {
                            $animationObject.off('animationend' + eventNamespace);
                        }
                        $toastBox
                            .off('click' + eventNamespace)
                        ;
                    },
                },

                animate: {
                    show: function (callback) {
                        callback = isFunction(callback) ? callback : function () {};
                        if (settings.transition && module.can.useElement('transition')) {
                            module.set.visible();
                            $toastBox
                                .transition({
                                    animation: settings.transition.showMethod + ' in',
                                    queue: false,
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    duration: settings.transition.showDuration,
                                    onComplete: function () {
                                        callback.call($toastBox, element);
                                        settings.onVisible.call($toastBox, element);
                                    },
                                })
                            ;
                        }
                    },
                    close: function (callback) {
                        callback = isFunction(callback) ? callback : function () {};
                        if (settings.transition && $.fn.transition !== undefined) {
                            $toastBox
                                .transition({
                                    animation: settings.transition.hideMethod + ' out',
                                    queue: false,
                                    duration: settings.transition.hideDuration,
                                    debug: settings.debug,
                                    verbose: settings.verbose,
                                    silent: settings.silent,
                                    interval: 50,

                                    onBeforeHide: function (callback) {
                                        callback = isFunction(callback) ? callback : function () {};
                                        if (settings.transition.closeEasing !== '') {
                                            if ($toastBox) {
                                                $toastBox.css('opacity', '0');
                                                $toastBox.wrap('<div/>').parent().hide(settings.transition.closeDuration, settings.transition.closeEasing, function () {
                                                    if ($toastBox) {
                                                        $toastBox.parent().remove();
                                                        callback.call($toastBox);
                                                    }
                                                });
                                            }
                                        } else {
                                            callback.call($toastBox);
                                        }
                                    },
                                    onComplete: function () {
                                        callback.call($toastBox, element);
                                        settings.onHidden.call($toastBox, element);
                                        module.destroy();
                                    },
                                })
                            ;
                        } else {
                            module.error(error.noTransition);
                        }
                    },
                    pause: function () {
                        $animationObject.css('animationPlayState', 'paused');
                        if ($progressBar) {
                            $progressBar.css('animationPlayState', 'paused');
                        }
                    },
                    continue: function () {
                        $animationObject.css('animationPlayState', 'running');
                        if ($progressBar) {
                            $progressBar.css('animationPlayState', 'running');
                        }
                    },
                },

                has: {
                    container: function () {
                        module.verbose('Determining if there is already a container');

                        return module.get.containers().length > 0;
                    },
                    toast: function () {
                        return !!module.get.toast();
                    },
                    toasts: function () {
                        return module.get.toasts().length > 0;
                    },
                    configActions: function () {
                        return Array.isArray(settings.actions) && settings.actions.length > 0;
                    },
                },

                get: {
                    id: function () {
                        return id;
                    },
                    containers: function () {
                        return $context.children(module.helpers.toClass(settings.position) + selector.container
                            + (settings.horizontal
                                ? module.helpers.toClass(className.horizontal)
                                : ':not(' + module.helpers.toClass(className.horizontal) + ')')
                            + (settings.context && settings.context !== 'body'
                                ? module.helpers.toClass(className.absolute)
                                : ':not(' + module.helpers.toClass(className.absolute) + ')'));
                    },
                    container: function () {
                        return module.get.containers()[0];
                    },
                    toastBox: function () {
                        return $toastBox || null;
                    },
                    toast: function () {
                        return $toast || null;
                    },
                    toasts: function () {
                        return $(module.get.container()).find(selector.box);
                    },
                    iconClass: function () {
                        return typeof settings.showIcon === 'string'
                            ? settings.showIcon
                            : (settings.showIcon && settings.icons[settings.class]
                                ? settings.icons[settings.class]
                                : '');
                    },
                    remainingTime: function () {
                        return $animationObject ? $animationObject.css('opacity') * settings.displayTime : 0;
                    },
                },

                set: {
                    visible: function () {
                        $toast.addClass(className.visible);
                    },
                },

                remove: {
                    visible: function () {
                        $toast.removeClass(className.visible);
                    },
                },

                event: {
                    close: function () {
                        module.close();
                    },
                    click: function (event) {
                        if ($(event.target).closest(selector.clickable).length === 0) {
                            if (settings.onClick.call($toastBox, element) === false || !settings.closeOnClick) {
                                module.verbose('Click callback returned false or close denied by setting cancelling close');

                                return;
                            }
                            module.close();
                        }
                    },
                    approve: function () {
                        if (settings.onApprove.call(element, $module) === false) {
                            module.verbose('Approve callback returned false cancelling close');

                            return;
                        }
                        module.close();
                    },
                    deny: function () {
                        if (settings.onDeny.call(element, $module) === false) {
                            module.verbose('Deny callback returned false cancelling close');

                            return;
                        }
                        module.close();
                    },
                },

                helpers: {
                    toClass: function (selector) {
                        var
                            classes = selector.trim().split(/\s+/),
                            result = ''
                        ;

                        classes.forEach(function (element) {
                            result += '.' + element;
                        });

                        return result;
                    },
                    deQuote: function (string) {
                        return String(string).replace(/"/g, '');
                    },
                    escape: function (string, preserveHTML) {
                        if (preserveHTML) {
                            return string;
                        }
                        var
                            badChars     = /["'<>`]/g,
                            shouldEscape = /["&'<>`]/,
                            escape       = {
                                '<': '&lt;',
                                '>': '&gt;',
                                '"': '&quot;',
                                "'": '&#x27;',
                                '`': '&#x60;',
                            },
                            escapedChar  = function (chr) {
                                return escape[chr];
                            }
                        ;
                        if (shouldEscape.test(string)) {
                            string = string.replace(/&(?![\d#a-z]{1,12};)/gi, '&amp;');

                            return string.replace(badChars, escapedChar);
                        }

                        return string;
                    },
                },

                can: {
                    useElement: function (element) {
                        if ($.fn[element] !== undefined) {
                            return true;
                        }
                        module.error(error.noElement.replace('{element}', element));

                        return false;
                    },
                },

                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();
                returnedValue = $module;
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };
    $.toast = $.fn.toast;

    $.fn.toast.settings = {

        name: 'Toast',
        namespace: 'toast',

        silent: false,
        debug: false,
        verbose: false,
        performance: true,

        context: 'body',

        position: 'top right',
        horizontal: false,
        class: 'neutral',
        classProgress: false,
        classActions: false,
        classImage: 'mini',

        title: '',
        message: '',
        displayTime: 3000, // set to zero to require manually dismissal, otherwise hides on its own
        minDisplayTime: 1000, // minimum displaytime in case displayTime is set to 'auto'
        wordsPerMinute: 120,
        showIcon: false,
        newestOnTop: false,
        showProgress: false,
        pauseOnHover: true,
        progressUp: false, // if true, the bar will start at 0% and increase to 100%
        opacity: 1,
        compact: true,
        closeIcon: false,
        closeOnClick: true,
        cloneModule: true,
        actions: false,
        preserveHTML: true,
        showImage: false,

        // transition settings
        transition: {
            showMethod: 'scale',
            showDuration: 500,
            hideMethod: 'scale',
            hideDuration: 500,
            closeEasing: 'easeOutCubic', // Set to empty string to stack the closed toast area immediately (old behaviour)
            closeDuration: 500,
        },

        error: {
            method: 'The method you called is not defined.',
            noElement: 'This module requires ui {element}',
            verticalCard: 'Vertical but not attached actions are not supported for card layout',
        },

        className: {
            container: 'ui toast-container',
            absolute: 'absolute',
            box: 'floating toast-box',
            progress: 'ui attached active progress',
            toast: 'ui toast',
            icon: 'centered icon',
            visible: 'visible',
            content: 'content',
            title: 'ui header',
            message: 'message',
            actions: 'actions',
            extraContent: 'extra content',
            button: 'ui button',
            buttons: 'ui buttons',
            close: 'close icon',
            image: 'ui image',
            vertical: 'vertical',
            horizontal: 'horizontal',
            attached: 'attached',
            inverted: 'inverted',
            compact: 'compact',
            pausable: 'pausable',
            progressing: 'progressing',
            top: 'top',
            bottom: 'bottom',
            left: 'left',
            basic: 'basic',
            unclickable: 'unclickable',
        },

        text: {
            close: 'Close',
        },

        icons: {
            info: 'info',
            success: 'checkmark',
            warning: 'warning',
            error: 'times',
        },

        selector: {
            container: '.ui.toast-container',
            box: '.toast-box',
            toast: '.ui.toast',
            title: '.header',
            message: '.message:not(.ui)',
            image: '> img.image, > .image > img',
            icon: '> i.icon',
            input: 'input:not([type="hidden"]), textarea, select, button, .ui.button, ui.dropdown',
            clickable: 'a, details, .ui.accordion',
            approve: '.actions .positive, .actions .approve, .actions .ok',
            deny: '.actions .negative, .actions .deny, .actions .cancel',
        },

        fields: {
            class: 'class',
            text: 'text',
            icon: 'icon',
            click: 'click',
        },

        // callbacks
        onShow: function () {},
        onVisible: function () {},
        onClick: function () {},
        onHide: function () {},
        onHidden: function () {},
        onRemove: function () {},
        onApprove: function () {},
        onDeny: function () {},
    };

    $.extend($.easing, {
        easeOutBounce: function (x) {
            var
                n1 = 7.5625,
                d1 = 2.75
            ;
            if (x < 1 / d1) {
                return n1 * x * x;
            }
            if (x < 2 / d1) {
                x -= 1.5 / d1;

                return n1 * x * x + 0.75;
            }
            if (x < 2.5 / d1) {
                x -= 2.25 / d1;

                return n1 * x * x + 0.9375;
            }
            x -= 2.625 / d1;

            return n1 * x * x + 0.984375;
        },
        easeOutCubic: function (t) {
            return --t * t * t + 1;
        },
    });
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Transition
 * 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.transition = function () {
        var
            $allModules     = $(this),

            time            = Date.now(),
            performance     = [],

            moduleArguments = arguments,
            query           = moduleArguments[0],
            queryArguments  = [].slice.call(arguments, 1),
            methodInvoked   = typeof query === 'string',

            returnedValue
        ;
        $allModules.each(function (index) {
            var
                $module  = $(this),
                element  = this,

                // set at run time
                settings,
                instance,

                error,
                className,
                metadata,

                moduleNamespace,
                eventNamespace,
                module
            ;

            module = {

                initialize: function () {
                    // get full settings
                    settings = module.get.settings.apply(element, moduleArguments);

                    // shorthand
                    className = settings.className;
                    error = settings.error;
                    metadata = settings.metadata;

                    // define namespace
                    eventNamespace = '.' + settings.namespace;
                    moduleNamespace = 'module-' + settings.namespace;
                    instance = $module.data(moduleNamespace) || module;

                    if (methodInvoked) {
                        methodInvoked = module.invoke(query);
                    }

                    // method not invoked, lets run an animation
                    if (methodInvoked === false) {
                        module.verbose('Converted arguments into settings object', settings);
                        if (settings.interval) {
                            module.delay(settings.interval);
                        } else {
                            module.animate();
                        }
                        module.instantiate();
                    }
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous module for', element);
                    $module
                        .removeData(moduleNamespace)
                    ;
                },

                refresh: function () {
                    module.verbose('Refreshing display type on next animation');
                    delete module.displayType;
                },

                forceRepaint: function () {
                    module.verbose('Forcing element repaint');
                    var
                        $parentElement = $module.parent(),
                        $nextElement = $module.next()
                    ;
                    if ($nextElement.length === 0) {
                        $module.detach().appendTo($parentElement);
                    } else {
                        $module.detach().insertBefore($nextElement);
                    }
                },

                repaint: function () {
                    module.verbose('Repainting element');
                    var
                        fakeAssignment = element.offsetWidth
                    ;
                },

                delay: function (interval) {
                    var
                        direction = module.get.animationDirection(),
                        shouldReverse,
                        delay
                    ;
                    if (!direction) {
                        direction = module.can.transition()
                            ? module.get.direction()
                            : 'static';
                    }
                    interval = interval !== undefined
                        ? interval
                        : settings.interval;
                    shouldReverse = settings.reverse === 'auto' && direction === className.outward;
                    delay = shouldReverse || settings.reverse === true
                        ? ($allModules.length - index) * interval
                        : index * interval;
                    module.debug('Delaying animation by', delay);
                    setTimeout(function () { module.animate(); }, delay);
                },

                animate: function (overrideSettings) {
                    settings = overrideSettings || settings;

                    module.debug('Preparing animation', settings.animation);
                    if (module.is.animating()) {
                        if (settings.queue) {
                            if (!settings.allowRepeats && module.has.direction() && module.is.occurring() && module.queuing !== true) {
                                module.debug('Animation is currently occurring, preventing queueing same animation', settings.animation);
                            } else {
                                module.queue(settings.animation);
                            }

                            return false;
                        }
                        if (!settings.allowRepeats && module.is.occurring()) {
                            module.debug('Animation is already occurring, will not execute repeated animation', settings.animation);

                            return false;
                        }

                        module.debug('New animation started, completing previous early', settings.animation);
                        instance.complete();
                    }
                    if (module.can.animate()) {
                        module.set.animating(settings.animation);
                    } else {
                        module.error(error.noAnimation, settings.animation, element);
                    }
                },

                reset: function () {
                    module.debug('Resetting animation to beginning conditions');
                    module.remove.animationCallbacks();
                    module.restore.conditions();
                    module.remove.animating();
                },

                queue: function (animation) {
                    module.debug('Queueing animation of', animation);
                    module.queuing = true;
                    $module
                        .one('animationend.queue' + eventNamespace, function () {
                            module.queuing = false;
                            module.repaint();
                            module.animate.apply(this, settings);
                        })
                    ;
                },

                complete: function (event) {
                    if (event && event.target === element) {
                        event.stopPropagation();
                    }
                    module.debug('Animation complete', settings.animation);
                    module.remove.completeCallback();
                    module.remove.failSafe();
                    if (!module.is.looping()) {
                        if (module.is.outward()) {
                            module.verbose('Animation is outward, hiding element');
                            module.restore.conditions();
                            module.hide();
                        } else if (module.is.inward()) {
                            module.verbose('Animation is inward, showing element');
                            module.restore.conditions();
                            module.show();
                        } else {
                            module.verbose('Static animation completed');
                            module.restore.conditions();
                            settings.onComplete.call(element);
                        }
                    }
                },

                force: {
                    visible: function () {
                        var
                            style          = $module.attr('style'),
                            userStyle      = module.get.userStyle(style),
                            displayType    = module.get.displayType(),
                            overrideStyle  = userStyle + 'display: ' + displayType + ' !important;',
                            inlineDisplay  = $module[0].style.display,
                            mustStayHidden = !displayType || (inlineDisplay === 'none' && settings.skipInlineHidden) || $module[0].tagName.match(/(script|link|style)/i)
                        ;
                        if (mustStayHidden) {
                            module.remove.transition();

                            return false;
                        }
                        module.verbose('Overriding default display to show element', displayType);
                        $module
                            .attr('style', overrideStyle)
                        ;

                        return true;
                    },
                    hidden: function () {
                        var
                            style          = $module.attr('style'),
                            currentDisplay = $module.css('display'),
                            emptyStyle     = style === undefined || style === ''
                        ;
                        if (currentDisplay !== 'none' && !module.is.hidden()) {
                            module.verbose('Overriding default display to hide element');
                            $module
                                .css('display', 'none')
                            ;
                        } else if (emptyStyle) {
                            $module
                                .removeAttr('style')
                            ;
                        }
                    },
                },

                has: {
                    direction: function (animation) {
                        var
                            hasDirection = false
                        ;
                        animation = animation || settings.animation;
                        if (typeof animation === 'string') {
                            animation = animation.split(' ');
                            $.each(animation, function (index, word) {
                                if (word === className.inward || word === className.outward) {
                                    hasDirection = true;
                                }
                            });
                        }

                        return hasDirection;
                    },
                    inlineDisplay: function () {
                        var
                            style = $module.attr('style') || ''
                        ;

                        return Array.isArray(style.match(/display.*?;/, ''));
                    },
                },

                set: {
                    animating: function (animation) {
                        // remove previous callbacks
                        module.remove.completeCallback();

                        // determine exact animation
                        animation = animation || settings.animation;
                        var animationClass = module.get.animationClass(animation);

                        // save animation class in cache to restore class names
                        module.save.animation(animationClass);

                        if (module.force.visible()) {
                            module.remove.hidden();
                            module.remove.direction();

                            module.start.animation(animationClass);
                        }
                    },
                    duration: function (animationName, duration) {
                        duration = duration || settings.duration;
                        duration = typeof duration === 'number'
                            ? duration + 'ms'
                            : duration;
                        if (duration || duration === 0) {
                            module.verbose('Setting animation duration', duration);
                            $module
                                .css({
                                    'animation-duration': duration,
                                })
                            ;
                        }
                    },
                    direction: function (direction) {
                        direction = direction || module.get.direction();
                        if (direction === className.inward) {
                            module.set.inward();
                        } else {
                            module.set.outward();
                        }
                    },
                    looping: function () {
                        module.debug('Transition set to loop');
                        $module
                            .addClass(className.looping)
                        ;
                    },
                    hidden: function () {
                        $module
                            .addClass(className.transition)
                            .addClass(className.hidden)
                        ;
                    },
                    inward: function () {
                        module.debug('Setting direction to inward');
                        $module
                            .removeClass(className.outward)
                            .addClass(className.inward)
                        ;
                    },
                    outward: function () {
                        module.debug('Setting direction to outward');
                        $module
                            .removeClass(className.inward)
                            .addClass(className.outward)
                        ;
                    },
                    visible: function () {
                        $module
                            .addClass(className.transition)
                            .addClass(className.visible)
                        ;
                    },
                },

                start: {
                    animation: function (animationClass) {
                        animationClass = animationClass || module.get.animationClass();
                        module.debug('Starting tween', animationClass);
                        $module
                            .addClass(animationClass)
                            .one('animationend.complete' + eventNamespace, module.complete)
                        ;
                        if (settings.useFailSafe) {
                            module.add.failSafe();
                        }
                        module.set.duration(settings.duration);
                        settings.onStart.call(element);
                    },
                },

                save: {
                    animation: function (animation) {
                        if (!module.cache) {
                            module.cache = {};
                        }
                        module.cache.animation = animation;
                    },
                    displayType: function (displayType) {
                        if (displayType !== 'none') {
                            $module.data(metadata.displayType, displayType);
                        }
                    },
                    transitionExists: function (animation, exists) {
                        $.fn.transition.exists[animation] = exists;
                        module.verbose('Saving existence of transition', animation, exists);
                    },
                },

                restore: {
                    conditions: function () {
                        var
                            animation = module.get.currentAnimation()
                        ;
                        if (animation) {
                            $module
                                .removeClass(animation)
                            ;
                            module.verbose('Removing animation class', module.cache);
                        }
                        module.remove.duration();
                    },
                },

                add: {
                    failSafe: function () {
                        var
                            duration = module.get.duration()
                        ;
                        module.timer = setTimeout(function () {
                            $module.triggerHandler('animationend');
                        }, duration + settings.failSafeDelay);
                        module.verbose('Adding fail safe timer', module.timer);
                    },
                },

                remove: {
                    animating: function () {
                        $module.removeClass(className.animating);
                    },
                    animationCallbacks: function () {
                        module.remove.queueCallback();
                        module.remove.completeCallback();
                    },
                    queueCallback: function () {
                        $module.off('.queue' + eventNamespace);
                    },
                    completeCallback: function () {
                        $module.off('.complete' + eventNamespace);
                    },
                    display: function () {
                        $module.css('display', '');
                    },
                    direction: function () {
                        $module
                            .removeClass(className.inward)
                            .removeClass(className.outward)
                        ;
                    },
                    duration: function () {
                        $module
                            .css('animation-duration', '')
                        ;
                    },
                    failSafe: function () {
                        module.verbose('Removing fail safe timer', module.timer);
                        if (module.timer) {
                            clearTimeout(module.timer);
                        }
                    },
                    hidden: function () {
                        $module.removeClass(className.hidden);
                    },
                    visible: function () {
                        $module.removeClass(className.visible);
                    },
                    looping: function () {
                        module.debug('Transitions are no longer looping');
                        if (module.is.looping()) {
                            module.reset();
                            $module
                                .removeClass(className.looping)
                            ;
                        }
                    },
                    transition: function () {
                        $module
                            .removeClass(className.transition)
                            .removeClass(className.visible)
                            .removeClass(className.hidden)
                        ;
                    },
                },
                get: {
                    settings: function (animation, duration, onComplete) {
                        if (typeof animation === 'object') { // single settings object
                            return $.extend(true, {}, $.fn.transition.settings, animation);
                        }
                        if (typeof onComplete === 'function') { // all arguments provided
                            return $.extend({}, $.fn.transition.settings, {
                                animation: animation,
                                onComplete: onComplete,
                                duration: duration,
                            });
                        }
                        if (typeof duration === 'string' || typeof duration === 'number') { // only duration provided
                            return $.extend({}, $.fn.transition.settings, {
                                animation: animation,
                                duration: duration,
                            });
                        }
                        if (typeof duration === 'object') { // duration is actually settings object
                            return $.extend({}, $.fn.transition.settings, duration, {
                                animation: animation,
                            });
                        }
                        if (typeof duration === 'function') { // duration is actually callback
                            return $.extend({}, $.fn.transition.settings, {
                                animation: animation,
                                onComplete: duration,
                            });
                        }

                        // only animation provided
                        return $.extend({}, $.fn.transition.settings, {
                            animation: animation,
                        });
                    },
                    animationClass: function (animation) {
                        var
                            animationClass = animation || settings.animation,
                            directionClass = module.can.transition() && !module.has.direction()
                                ? module.get.direction() + ' '
                                : ''
                        ;

                        return className.animating + ' '
                            + className.transition + ' '
                            + directionClass
                            + animationClass;
                    },
                    currentAnimation: function () {
                        return module.cache && module.cache.animation !== undefined
                            ? module.cache.animation
                            : false;
                    },
                    currentDirection: function () {
                        return module.is.inward()
                            ? className.inward
                            : className.outward;
                    },
                    direction: function () {
                        return module.is.hidden() || !module.is.visible()
                            ? className.inward
                            : className.outward;
                    },
                    animationDirection: function (animation) {
                        var
                            direction
                        ;
                        animation = animation || settings.animation;
                        if (typeof animation === 'string') {
                            animation = animation.split(' ');
                            // search animation name for out/in class
                            $.each(animation, function (index, word) {
                                if (word === className.inward) {
                                    direction = className.inward;
                                } else if (word === className.outward) {
                                    direction = className.outward;
                                }
                            });
                        }
                        // return found direction
                        if (direction) {
                            return direction;
                        }

                        return false;
                    },
                    duration: function (duration) {
                        duration = duration || settings.duration;
                        if (duration === false) {
                            duration = $module.css('animation-duration') || 0;
                        }

                        return typeof duration === 'string'
                            ? (duration.indexOf('ms') > -1
                                ? parseFloat(duration)
                                : parseFloat(duration) * 1000)
                            : duration;
                    },
                    displayType: function (shouldDetermine) {
                        shouldDetermine = shouldDetermine !== undefined
                            ? shouldDetermine
                            : true;
                        if (settings.displayType) {
                            return settings.displayType;
                        }
                        if (shouldDetermine && $module.data(metadata.displayType) === undefined) {
                            var currentDisplay = $module.css('display');
                            if (currentDisplay === '' || currentDisplay === 'none') {
                                // create fake element to determine display state
                                module.can.transition(true);
                            } else {
                                module.save.displayType(currentDisplay);
                            }
                        }

                        return $module.data(metadata.displayType);
                    },
                    userStyle: function (style) {
                        style = style || $module.attr('style') || '';

                        return style.replace(/display.*?;/, '');
                    },
                    transitionExists: function (animation) {
                        return $.fn.transition.exists[animation];
                    },
                },

                can: {
                    transition: function (forced) {
                        var
                            animation         = settings.animation,
                            transitionExists  = module.get.transitionExists(animation),
                            displayType       = module.get.displayType(false),
                            elementClass,
                            tagName,
                            $clone,
                            currentAnimation,
                            inAnimation,
                            directionExists
                        ;
                        if (transitionExists === undefined || forced) {
                            module.verbose('Determining whether animation exists');
                            elementClass = $module.attr('class');
                            tagName = $module.prop('tagName');

                            $clone = $('<' + tagName + ' />').addClass(elementClass).insertAfter($module);
                            currentAnimation = $clone
                                .addClass(animation)
                                .removeClass(className.inward)
                                .removeClass(className.outward)
                                .addClass(className.animating)
                                .addClass(className.transition)
                                .css('animationName')
                            ;
                            $clone.detach().insertAfter($module);
                            inAnimation = $clone
                                .addClass(className.inward)
                                .css('animationName')
                            ;
                            if (!displayType) {
                                $clone.detach().insertAfter($module);
                                displayType = $clone
                                    .attr('class', elementClass)
                                    .removeAttr('style')
                                    .removeClass(className.hidden)
                                    .removeClass(className.visible)
                                    .show()
                                    .css('display')
                                ;
                                module.verbose('Determining final display state', displayType);
                                module.save.displayType(displayType);
                            }

                            $clone.remove();
                            if (currentAnimation !== inAnimation) {
                                module.debug('Direction exists for animation', animation);
                                directionExists = true;
                            } else if (currentAnimation === 'none' || !currentAnimation) {
                                module.debug('No animation defined in css', animation);

                                return;
                            } else {
                                module.debug('Static animation found', animation, displayType);
                                directionExists = false;
                            }
                            module.save.transitionExists(animation, directionExists);
                        }

                        return transitionExists !== undefined
                            ? transitionExists
                            : directionExists;
                    },
                    animate: function () {
                        // can transition does not return a value if animation does not exist
                        return module.can.transition() !== undefined;
                    },
                },

                is: {
                    animating: function () {
                        return $module.hasClass(className.animating);
                    },
                    inward: function () {
                        return $module.hasClass(className.inward);
                    },
                    outward: function () {
                        return $module.hasClass(className.outward);
                    },
                    looping: function () {
                        return $module.hasClass(className.looping);
                    },
                    occurring: function (animation) {
                        animation = animation || settings.animation;
                        animation = '.' + animation.replace(' ', '.');

                        return $module.filter(animation).length > 0;
                    },
                    visible: function () {
                        return $module.is(':visible');
                    },
                    hidden: function () {
                        return $module.css('visibility') === 'hidden';
                    },
                    supported: function () {
                        // keep method for backward compatibility until 2.10.0
                        return true;
                    },
                },

                hide: function () {
                    if (settings.onHide.call(element) === false) {
                        module.verbose('Hide callback returned false cancelling hide');

                        return false;
                    }
                    module.verbose('Hiding element');
                    if (module.is.animating()) {
                        module.reset();
                    }
                    element.blur(); // IE will trigger focus change if element is not blurred before hiding
                    module.remove.display();
                    module.remove.visible();
                    settings.onBeforeHide.call(element, module.hideNow);
                },

                hideNow: function () {
                    module.set.hidden();
                    module.force.hidden();
                    settings.onHidden.call(element);
                    settings.onComplete.call(element);
                },

                show: function (display) {
                    if (module.force.visible() && settings.onShow.call(element) !== false) {
                        module.verbose('Showing element', display);
                        module.remove.hidden();
                        settings.onBeforeShow.call(element, module.showNow);
                    }
                },

                showNow: function () {
                    module.set.visible();
                    settings.onVisible.call(element);
                    settings.onComplete.call(element);
                },

                toggle: function () {
                    if (module.is.visible()) {
                        module.hide();
                    } else {
                        module.show();
                    }
                },

                stop: function () {
                    module.debug('Stopping current animation');
                    $module.triggerHandler('animationend');
                },

                stopAll: function () {
                    module.debug('Stopping all animation');
                    module.remove.queueCallback();
                    $module.triggerHandler('animationend');
                },

                clear: {
                    queue: function () {
                        module.debug('Clearing animation queue');
                        module.remove.queueCallback();
                    },
                },

                enable: function () {
                    module.verbose('Starting animation');
                    $module.removeClass(className.disabled);
                },

                disable: function () {
                    module.debug('Stopping animation');
                    $module.addClass(className.disabled);
                },

                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 ($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 = [];
                    },
                },
                // modified for transition to return invoke success
                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 {
                                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 !== undefined
                        ? found
                        : false;
                },
            };
            module.initialize();
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };

    // Records if CSS transition is available
    $.fn.transition.exists = {};

    $.fn.transition.settings = {

        // module info
        name: 'Transition',

        // hide all output from this component regardless of other settings
        silent: false,

        // debug content outputted to console
        debug: false,

        // verbose debug output
        verbose: false,

        // performance data output
        performance: true,

        // event namespace
        namespace: 'transition',

        // delay between animations in group
        interval: 0,

        // whether group animations should be reversed
        reverse: 'auto',

        // animation callback event
        onStart: function () {},
        onComplete: function () {},
        onShow: function () {},
        onBeforeShow: function (callback) {
            callback.call(this);
        },
        onVisible: function () {},
        onHide: function () {},
        onHidden: function () {},
        onBeforeHide: function (callback) {
            callback.call(this);
        },

        // whether timeout should be used to ensure callback fires in cases animationend does not
        useFailSafe: true,

        // delay in ms for fail safe
        failSafeDelay: 100,

        // whether EXACT animation can occur twice in a row
        allowRepeats: false,

        // Override final display type on visible
        displayType: false,

        // animation duration
        animation: 'fade',
        duration: false,

        // new animations will occur after previous ones
        queue: true,

        // whether initially inline hidden objects should be skipped for transition
        skipInlineHidden: false,

        metadata: {
            displayType: 'display',
        },

        className: {
            animating: 'animating',
            disabled: 'disabled',
            hidden: 'hidden',
            inward: 'in',
            loading: 'loading',
            looping: 'looping',
            outward: 'out',
            transition: 'transition',
            visible: 'visible',
        },

        // possible errors
        error: {
            noAnimation: 'Element is no longer attached to DOM. Unable to animate.  Use silent setting to suppress this warning in production.',
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - API
 * https://github.com/fomantic/Fomantic-UI/
 *
 *
 * Released under the MIT license
 * https://opensource.org/licenses/MIT
 *
 */

(function ($, window, document) {
    'use strict';

    function isWindow(obj) {
        return obj !== null && obj === obj.window;
    }

    function isFunction(obj) {
        return typeof obj === 'function' && typeof obj.nodeType !== 'number';
    }

    window = window !== undefined && window.Math === Math
        ? window
        : globalThis;

    $.fn.api = function (parameters) {
        var
            // use window context if none specified
            $allModules     = isFunction(this)
                ? $(window)
                : $(this),
            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            returnedValue
        ;

        $allModules.each(function () {
            var
                settings          = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.api.settings, parameters)
                    : $.extend({}, $.fn.api.settings),

                // internal aliases
                regExp          = settings.regExp,
                namespace       = settings.namespace,
                metadata        = settings.metadata,
                selector        = settings.selector,
                error           = settings.error,
                className       = settings.className,

                // define namespaces for modules
                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                // element that creates request
                $module         = $(this),
                $form           = $module.closest(selector.form),

                // context used for state
                $context        = settings.stateContext ? contextCheck(settings.stateContext, window) : $module,

                // request details
                ajaxSettings,
                requestSettings,
                url,
                data,
                requestStartTime,
                originalData,

                // standard module
                element         = this,
                context         = $context[0],
                instance        = $module.data(moduleNamespace),
                module
            ;

            module = {

                initialize: function () {
                    if (!methodInvoked) {
                        originalData = settings.data;
                        module.bind.events();
                    }
                    module.instantiate();
                },

                instantiate: function () {
                    module.verbose('Storing instance of module', module);
                    instance = module;
                    $module
                        .data(moduleNamespace, instance)
                    ;
                },

                destroy: function () {
                    module.verbose('Destroying previous module for', element);
                    $module
                        .removeData(moduleNamespace)
                        .off(eventNamespace)
                    ;
                },

                bind: {
                    events: function () {
                        var
                            triggerEvent = module.get.event()
                        ;
                        if (triggerEvent) {
                            module.verbose('Attaching API events to element', triggerEvent);
                            $module
                                .on(triggerEvent + eventNamespace, module.event.trigger)
                            ;
                        } else if (settings.on === 'now') {
                            module.debug('Querying API endpoint immediately');
                            module.query();
                        }
                    },
                },

                decode: {
                    json: function (response) {
                        if (response !== undefined && typeof response === 'string') {
                            try {
                                response = JSON.parse(response);
                            } catch (e) {
                                // isn't json string
                            }
                        }

                        return response;
                    },
                },

                read: {
                    cachedResponse: function (url) {
                        var
                            response
                        ;
                        if (window.Storage === undefined) {
                            module.error(error.noStorage);

                            return;
                        }
                        response = sessionStorage.getItem(url + module.get.normalizedData());
                        module.debug('Using cached response', url, settings.data, response);
                        response = module.decode.json(response);

                        return response;
                    },
                },
                write: {
                    cachedResponse: function (url, response) {
                        if (window.Storage === undefined) {
                            module.error(error.noStorage);

                            return;
                        }
                        if ($.isPlainObject(response)) {
                            response = JSON.stringify(response);
                        }
                        sessionStorage.setItem(url + module.get.normalizedData(), response);
                        module.verbose('Storing cached response for url', url, settings.data, response);
                    },
                },

                query: function () {
                    if (module.is.disabled()) {
                        module.debug('Element is disabled API request aborted');

                        return;
                    }

                    if (module.is.loading()) {
                        if (settings.interruptRequests) {
                            module.debug('Interrupting previous request');
                            module.abort();
                        } else {
                            module.debug('Cancelling request, previous request is still pending');

                            return;
                        }
                    }

                    // pass element metadata to url (value, text)
                    if (settings.defaultData) {
                        $.extend(true, settings.urlData, module.get.defaultData());
                    }

                    // Add form content
                    if (settings.serializeForm) {
                        settings.data = module.add.formData(originalData || settings.data);
                    }

                    // call beforesend and get any settings changes
                    requestSettings = module.get.settings();

                    // check if before send cancelled request
                    if (requestSettings === false) {
                        module.cancelled = true;
                        module.error(error.beforeSend);

                        return;
                    }

                    module.cancelled = false;

                    // get url
                    url = module.get.templatedURL();

                    if (!url && !module.is.mocked()) {
                        module.error(error.missingURL);

                        return;
                    }

                    // replace variables
                    url = module.add.urlData(url);
                    // missing url parameters
                    if (!url && !module.is.mocked()) {
                        return;
                    }

                    requestSettings.url = settings.base + url;

                    // look for jQuery ajax parameters in settings
                    ajaxSettings = $.extend(true, {}, settings, {
                        type: settings.method || settings.type,
                        data: data,
                        url: settings.base + url,
                        beforeSend: settings.beforeXHR,
                        success: function () {},
                        failure: function () {},
                        complete: function () {},
                    });

                    module.debug('Querying URL', ajaxSettings.url);
                    module.verbose('Using AJAX settings', ajaxSettings);
                    if (settings.cache === 'local' && module.read.cachedResponse(url)) {
                        module.debug('Response returned from local cache');
                        module.request = module.create.request();
                        module.request.resolveWith(context, [module.read.cachedResponse(url)]);

                        return;
                    }

                    if (!settings.throttle) {
                        module.debug('Sending request', data, ajaxSettings.method);
                        module.send.request();
                    } else {
                        if (!settings.throttleFirstRequest && !module.timer) {
                            module.debug('Sending request', data, ajaxSettings.method);
                            module.send.request();
                            module.timer = setTimeout(function () {}, settings.throttle);
                        } else {
                            module.debug('Throttling request', settings.throttle);
                            clearTimeout(module.timer);
                            module.timer = setTimeout(function () {
                                if (module.timer) {
                                    delete module.timer;
                                }
                                module.debug('Sending throttled request', data, ajaxSettings.method);
                                module.send.request();
                            }, settings.throttle);
                        }
                    }
                },

                should: {
                    removeError: function () {
                        return settings.hideError === true || (settings.hideError === 'auto' && !module.is.form());
                    },
                },

                is: {
                    disabled: function () {
                        return $module.filter(selector.disabled).length > 0;
                    },
                    expectingJSON: function () {
                        return settings.dataType === 'json' || settings.dataType === 'jsonp';
                    },
                    form: function () {
                        return $module.is('form') || $context.is('form');
                    },
                    mocked: function () {
                        return settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync;
                    },
                    input: function () {
                        return $module.is('input');
                    },
                    loading: function () {
                        return module.request
                            ? module.request.state() === 'pending'
                            : false;
                    },
                    abortedRequest: function (xhr) {
                        if (xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
                            module.verbose('XHR request determined to be aborted');

                            return true;
                        }

                        module.verbose('XHR request was not aborted');

                        return false;
                    },
                    validResponse: function (response) {
                        if (!module.is.expectingJSON() || !isFunction(settings.successTest)) {
                            module.verbose('Response is not JSON, skipping validation', settings.successTest, response);

                            return true;
                        }
                        module.debug('Checking JSON returned success', settings.successTest, response);
                        if (settings.successTest(response)) {
                            module.debug('Response passed success test', response);

                            return true;
                        }

                        module.debug('Response failed success test', response);

                        return false;
                    },
                },

                was: {
                    cancelled: function () {
                        return module.cancelled || false;
                    },
                    successful: function () {
                        return module.request && module.request.state() === 'resolved';
                    },
                    failure: function () {
                        return module.request && module.request.state() === 'rejected';
                    },
                    complete: function () {
                        return module.request && (module.request.state() === 'resolved' || module.request.state() === 'rejected');
                    },
                },

                add: {
                    urlData: function (url, urlData) {
                        var
                            requiredVariables,
                            optionalVariables
                        ;
                        if (url) {
                            requiredVariables = url.match(regExp.required);
                            optionalVariables = url.match(regExp.optional);
                            urlData = urlData || settings.urlData;
                            if (requiredVariables) {
                                module.debug('Looking for required URL variables', requiredVariables);
                                $.each(requiredVariables, function (index, templatedString) {
                                    var
                                        // allow legacy {$var} style
                                        variable = templatedString.indexOf('$') !== -1
                                            ? templatedString.slice(2, -1)
                                            : templatedString.slice(1, -1),
                                        value   = $.isPlainObject(urlData) && urlData[variable] !== undefined
                                            ? urlData[variable]
                                            : ($module.data(variable) !== undefined
                                                ? $module.data(variable)
                                                : ($context.data(variable) !== undefined // eslint-disable-line unicorn/no-nested-ternary
                                                    ? $context.data(variable)
                                                    : urlData[variable]))
                                    ;
                                    // remove value
                                    if (value === undefined) {
                                        module.error(error.requiredParameter, variable, url);
                                        url = false;

                                        return false;
                                    }

                                    module.verbose('Found required variable', variable, value);
                                    value = settings.encodeParameters
                                        ? module.get.urlEncodedValue(value)
                                        : value;
                                    url = url.replace(templatedString, value);
                                });
                            }
                            if (optionalVariables) {
                                module.debug('Looking for optional URL variables', requiredVariables);
                                $.each(optionalVariables, function (index, templatedString) {
                                    var
                                        // allow legacy {/$var} style
                                        variable = templatedString.indexOf('$') !== -1
                                            ? templatedString.slice(3, -1)
                                            : templatedString.slice(2, -1),
                                        value   = $.isPlainObject(urlData) && urlData[variable] !== undefined
                                            ? urlData[variable]
                                            : ($module.data(variable) !== undefined
                                                ? $module.data(variable)
                                                : ($context.data(variable) !== undefined // eslint-disable-line unicorn/no-nested-ternary
                                                    ? $context.data(variable)
                                                    : urlData[variable]))
                                    ;
                                    // optional replacement
                                    if (value !== undefined) {
                                        module.verbose('Optional variable Found', variable, value);
                                        url = url.replace(templatedString, value);
                                    } else {
                                        module.verbose('Optional variable not found', variable);
                                        // remove preceding slash if set
                                        url = url.indexOf('/' + templatedString) !== -1
                                            ? url.replace('/' + templatedString, '')
                                            : url.replace(templatedString, '');
                                    }
                                });
                            }
                        }

                        return url;
                    },
                    formData: function (data) {
                        var
                            formData = {},
                            hasOtherData,
                            useFormDataApi = settings.serializeForm === 'formdata'
                        ;
                        data = data || originalData || settings.data;
                        hasOtherData = $.isPlainObject(data);

                        if (useFormDataApi) {
                            formData = new FormData($form[0]);
                            settings.processData = settings.processData !== undefined ? settings.processData : false;
                            settings.contentType = settings.contentType !== undefined ? settings.contentType : false;
                        } else {
                            var
                                formArray = $form.serializeArray(),
                                pushes = {},
                                pushValues = {},
                                build = function (base, key, value) {
                                    base[key] = value;

                                    return base;
                                }
                            ;
                            // add files
                            $.each($('input[type="file"]', $form), function (i, tag) {
                                $.each($(tag)[0].files, function (j, file) {
                                    formArray.push({ name: tag.name, value: file });
                                });
                            });
                            $.each(formArray, function (i, el) {
                                if (!regExp.validate.test(el.name)) {
                                    return;
                                }
                                var
                                    isCheckbox = $('[name="' + el.name + '"]', $form).attr('type') === 'checkbox',
                                    floatValue = parseFloat(el.value),
                                    value = (isCheckbox && el.value === 'on')
                                        || el.value === 'true'
                                        || (String(floatValue) === el.value
                                            ? floatValue
                                            : (el.value === 'false' ? false : el.value)),
                                    nameKeys = el.name.match(regExp.key) || [],
                                    pushKey = el.name.replace(/\[]$/, '')
                                ;
                                if (!(pushKey in pushes)) {
                                    pushes[pushKey] = 0;
                                    pushValues[pushKey] = value;
                                } else if (Array.isArray(pushValues[pushKey])) {
                                    pushValues[pushKey].push(value);
                                } else {
                                    pushValues[pushKey] = [pushValues[pushKey], value];
                                }
                                if (pushKey.indexOf('[]') === -1) {
                                    value = pushValues[pushKey];
                                }

                                while (nameKeys.length > 0) {
                                    var k = nameKeys.pop();

                                    if (k === '' && !Array.isArray(value)) { // foo[]
                                        value = build([], pushes[pushKey]++, value);
                                    } else if (regExp.fixed.test(k)) { // foo[n]
                                        value = build([], k, value);
                                    } else if (regExp.named.test(k)) { // foo; foo[bar]
                                        value = build({}, k, value);
                                    }
                                }
                                formData = $.extend(true, formData, value);
                            });
                        }

                        if (hasOtherData) {
                            module.debug('Extending existing data with form data', data, formData);
                            if (useFormDataApi) {
                                $.each(Object.keys(data), function (i, el) {
                                    formData.append(el, data[el]);
                                });
                                data = formData;
                            } else {
                                data = $.extend(true, {}, data, formData);
                            }
                        } else {
                            module.debug('Adding form data', formData);
                            data = formData;
                        }

                        return data;
                    },
                },

                send: {
                    request: function () {
                        module.set.loading();
                        module.request = module.create.request();
                        if (module.is.mocked()) {
                            module.mockedXHR = module.create.mockedXHR();
                        } else {
                            module.xhr = module.create.xhr();
                        }
                        settings.onRequest.call(context, module.request, module.xhr);
                    },
                },

                event: {
                    trigger: function (event) {
                        module.query();
                        if (event.type === 'submit' || event.type === 'click') {
                            event.preventDefault();
                        }
                    },
                    xhr: {
                        always: function () {
                            // nothing special
                        },
                        done: function (response, textStatus, xhr) {
                            var
                                context            = this,
                                elapsedTime        = Date.now() - requestStartTime,
                                timeLeft           = settings.loadingDuration - elapsedTime,
                                translatedResponse = isFunction(settings.onResponse)
                                    ? (module.is.expectingJSON() && !settings.rawResponse
                                        ? settings.onResponse.call(context, $.extend(true, {}, response))
                                        : settings.onResponse.call(context, response))
                                    : false
                            ;
                            timeLeft = timeLeft > 0
                                ? timeLeft
                                : 0;
                            if (translatedResponse) {
                                module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
                                response = translatedResponse;
                            }
                            if (timeLeft > 0) {
                                module.debug('Response completed early delaying state change by', timeLeft);
                            }
                            setTimeout(function () {
                                if (module.is.validResponse(response)) {
                                    module.request.resolveWith(context, [response, xhr]);
                                } else {
                                    module.request.rejectWith(context, [xhr, 'invalid']);
                                }
                            }, timeLeft);
                        },
                        fail: function (xhr, status, httpMessage) {
                            var
                                context     = this,
                                elapsedTime = Date.now() - requestStartTime,
                                timeLeft    = settings.loadingDuration - elapsedTime
                            ;
                            timeLeft = timeLeft > 0
                                ? timeLeft
                                : 0;
                            if (timeLeft > 0) {
                                module.debug('Response completed early delaying state change by', timeLeft);
                            }
                            setTimeout(function () {
                                if (module.is.abortedRequest(xhr)) {
                                    module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
                                } else {
                                    module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
                                }
                            }, timeLeft);
                        },
                    },
                    request: {
                        done: function (response, xhr) {
                            module.debug('Successful API Response', response);
                            if (settings.cache === 'local' && url) {
                                module.write.cachedResponse(url, response);
                                module.debug('Saving server response locally', module.cache);
                            }
                            settings.onSuccess.call(context, response, $module, xhr);
                        },
                        complete: function (firstParameter, secondParameter) {
                            var
                                xhr,
                                response
                            ;
                            // have to guess callback parameters based on request success
                            if (module.was.successful()) {
                                response = firstParameter;
                                xhr = secondParameter;
                            } else {
                                xhr = firstParameter;
                                response = module.get.responseFromXHR(xhr);
                            }
                            module.remove.loading();
                            settings.onComplete.call(context, response, $module, xhr);
                        },
                        fail: function (xhr, status, httpMessage) {
                            var
                                // pull response from xhr if available
                                response     = module.get.responseFromXHR(xhr),
                                errorMessage = module.get.errorFromRequest(response, status, httpMessage)
                            ;
                            if (status === 'aborted') {
                                module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
                                settings.onAbort.call(context, status, $module, xhr);

                                return true;
                            }
                            if (status === 'invalid') {
                                module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
                            } else if (status === 'error') {
                                if (xhr !== undefined) {
                                    module.debug('XHR produced a server error', status, httpMessage);
                                    // make sure we have an error to display to console
                                    if ((xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
                                        module.error(error.statusMessage + httpMessage, ajaxSettings.url);
                                    }
                                    settings.onError.call(context, errorMessage, $module, xhr);
                                }
                            }

                            if (settings.errorDuration && status !== 'aborted') {
                                module.debug('Adding error state');
                                module.set.error();
                                if (module.should.removeError()) {
                                    setTimeout(function () { module.remove.error(); }, settings.errorDuration);
                                }
                            }
                            module.debug('API Request failed', errorMessage, xhr);
                            settings.onFailure.call(context, response, $module, xhr);
                        },
                    },
                },

                create: {

                    request: function () {
                        // api request promise
                        return $.Deferred()
                            .always(module.event.request.complete)
                            .done(module.event.request.done)
                            .fail(module.event.request.fail)
                        ;
                    },

                    mockedXHR: function () {
                        var
                            // xhr does not simulate these properties of xhr but must return them
                            textStatus     = false,
                            status         = false,
                            httpMessage    = false,
                            responder      = settings.mockResponse || settings.response,
                            asyncResponder = settings.mockResponseAsync || settings.responseAsync,
                            asyncCallback,
                            response,
                            mockedXHR
                        ;

                        mockedXHR = $.Deferred()
                            .always(module.event.xhr.complete)
                            .done(module.event.xhr.done)
                            .fail(module.event.xhr.fail)
                        ;

                        if (responder) {
                            if (isFunction(responder)) {
                                module.debug('Using specified synchronous callback', responder);
                                response = responder.call(context, requestSettings);
                            } else {
                                module.debug('Using settings specified response', responder);
                                response = responder;
                            }
                            // simulating response
                            mockedXHR.resolveWith(context, [response, textStatus, { responseText: response }]);
                        } else if (isFunction(asyncResponder)) {
                            asyncCallback = function (response) {
                                module.debug('Async callback returned response', response);

                                if (response) {
                                    mockedXHR.resolveWith(context, [response, textStatus, { responseText: response }]);
                                } else {
                                    mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
                                }
                            };
                            module.debug('Using specified async response callback', asyncResponder);
                            asyncResponder.call(context, requestSettings, asyncCallback);
                        }

                        return mockedXHR;
                    },

                    xhr: function () {
                        var
                            xhr
                        ;
                        // ajax request promise
                        xhr = $.ajax(ajaxSettings)
                            .always(module.event.xhr.always)
                            .done(module.event.xhr.done)
                            .fail(module.event.xhr.fail)
                        ;
                        module.verbose('Created server request', xhr, ajaxSettings);

                        return xhr;
                    },
                },

                set: {
                    error: function () {
                        module.verbose('Adding error state to element', $context);
                        $context.addClass(className.error);
                    },
                    loading: function () {
                        module.verbose('Adding loading state to element', $context);
                        $context.addClass(className.loading);
                        requestStartTime = Date.now();
                    },
                },

                remove: {
                    error: function () {
                        module.verbose('Removing error state from element', $context);
                        $context.removeClass(className.error);
                    },
                    loading: function () {
                        module.verbose('Removing loading state from element', $context);
                        $context.removeClass(className.loading);
                    },
                },

                get: {
                    normalizedData: function () {
                        return typeof settings.data === 'string' ? settings.data : JSON.stringify(settings.data, Object.keys(settings.data).sort());
                    },
                    responseFromXHR: function (xhr) {
                        return $.isPlainObject(xhr)
                            ? (module.is.expectingJSON()
                                ? module.decode.json(xhr.responseText)
                                : xhr.responseText)
                            : false;
                    },
                    errorFromRequest: function (response, status, httpMessage) {
                        return $.isPlainObject(response) && response.error !== undefined
                            ? response.error // use json error message
                            : (settings.error[status] !== undefined // use server error message
                                ? settings.error[status]
                                : httpMessage);
                    },
                    request: function () {
                        return module.request || false;
                    },
                    xhr: function () {
                        return module.xhr || false;
                    },
                    settings: function () {
                        var
                            runSettings
                        ;
                        runSettings = settings.beforeSend.call($module, settings);
                        if (runSettings) {
                            if (runSettings.success !== undefined) {
                                module.debug('Legacy success callback detected', runSettings);
                                module.error(error.legacyParameters, runSettings.success);
                                runSettings.onSuccess = runSettings.success;
                            }
                            if (runSettings.failure !== undefined) {
                                module.debug('Legacy failure callback detected', runSettings);
                                module.error(error.legacyParameters, runSettings.failure);
                                runSettings.onFailure = runSettings.failure;
                            }
                            if (runSettings.complete !== undefined) {
                                module.debug('Legacy complete callback detected', runSettings);
                                module.error(error.legacyParameters, runSettings.complete);
                                runSettings.onComplete = runSettings.complete;
                            }
                        }
                        if (runSettings === undefined) {
                            module.error(error.noReturnedValue);
                        }
                        if (runSettings === false) {
                            return runSettings;
                        }

                        return runSettings !== undefined
                            ? $.extend(true, {}, runSettings)
                            : $.extend(true, {}, settings);
                    },
                    urlEncodedValue: function (value) {
                        var
                            decodedValue   = window.decodeURIComponent(value),
                            encodedValue   = window.encodeURIComponent(value),
                            alreadyEncoded = decodedValue !== value
                        ;
                        if (alreadyEncoded) {
                            module.debug('URL value is already encoded, avoiding double encoding', value);

                            return value;
                        }
                        module.verbose('Encoding value using encodeURIComponent', value, encodedValue);

                        return encodedValue;
                    },
                    defaultData: function () {
                        var
                            data = {}
                        ;
                        if (!isWindow(element)) {
                            if (module.is.input()) {
                                data.value = $module.val();
                            } else if (!module.is.form()) {
                                data.text = $module.text();
                            }
                        }

                        return data;
                    },
                    event: function () {
                        if (isWindow(element) || settings.on === 'now') {
                            module.debug('API called without element, no events attached');

                            return false;
                        }
                        if (settings.on === 'auto') {
                            if ($module.is('input')) {
                                return element.oninput !== undefined
                                    ? 'input'
                                    : (element.onpropertychange !== undefined
                                        ? 'propertychange'
                                        : 'keyup');
                            }
                            if ($module.is('form')) {
                                return 'submit';
                            }

                            return 'click';
                        }

                        return settings.on;
                    },
                    templatedURL: function (action) {
                        action = action || settings.action || $module.data(metadata.action) || false;
                        url = settings.url || $module.data(metadata.url) || false;
                        if (url) {
                            module.debug('Using specified url', url);

                            return url;
                        }
                        if (action) {
                            module.debug('Looking up url for action', action, settings.api);
                            if (settings.api[action] === undefined && !module.is.mocked()) {
                                module.error(error.missingAction, settings.action, settings.api);

                                return;
                            }
                            url = settings.api[action];
                        } else if (module.is.form()) {
                            url = $module.attr('action') || $context.attr('action') || false;
                            module.debug('No url or action specified, defaulting to form action', url);
                        }

                        return url;
                    },
                },

                abort: function () {
                    var
                        xhr = module.get.xhr()
                    ;
                    if (xhr && xhr.state() !== 'resolved') {
                        module.debug('Cancelling API request');
                        xhr.abort();
                    }
                },

                // reset state
                reset: function () {
                    module.remove.error();
                    module.remove.loading();
                },

                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;
    };
    $.api = $.fn.api;

    $.api.settings = {

        name: 'API',
        namespace: 'api',

        debug: false,
        verbose: false,
        performance: true,

        // object containing all templates endpoints
        api: {},

        // whether to cache responses
        cache: true,

        // whether new requests should abort previous requests
        interruptRequests: true,

        // event binding
        on: 'auto',

        // context for applying state classes
        stateContext: false,

        // duration for loading state
        loadingDuration: 0,

        // whether to hide errors after a period of time
        hideError: 'auto',

        // duration for error state
        errorDuration: 2000,

        // whether parameters should be encoded with encodeURIComponent
        encodeParameters: true,

        // API action to use
        action: false,

        // templated URL to use
        url: false,

        // base URL to apply to all endpoints
        base: '',

        // data that will
        urlData: {},

        // whether to add default data to url data
        defaultData: true,

        // whether to serialize closest form
        // use true to convert complex named keys like a[b][1][c][] into a nested object
        // use 'formdata' for formdata web api
        serializeForm: false,

        // how long to wait before request should occur
        throttle: 0,

        // whether to throttle first request or only repeated
        throttleFirstRequest: true,

        // standard ajax settings
        method: 'get',
        data: {},
        dataType: 'json',

        // mock response
        mockResponse: false,
        mockResponseAsync: false,

        // aliases for mock
        response: false,
        responseAsync: false,

        // whether onResponse should work with response value without force converting into an object
        rawResponse: true,

        // callbacks before request
        beforeSend: function (settings) {
            return settings;
        },
        beforeXHR: function (xhr) {},
        onRequest: function (promise, xhr) {},

        // after request
        onResponse: false, // function(response) { },

        // response was successful, if JSON passed validation
        onSuccess: function (response, $module) {},

        // request finished without aborting
        onComplete: function (response, $module) {},

        // failed JSON success test
        onFailure: function (response, $module) {},

        // server error
        onError: function (errorMessage, $module) {},

        // request aborted
        onAbort: function (errorMessage, $module) {},

        successTest: false,

        // errors
        error: {
            beforeSend: 'The before send function has aborted the request',
            error: 'There was an error with your request',
            exitConditions: 'API Request Aborted. Exit conditions met',
            JSONParse: 'JSON could not be parsed during error handling',
            legacyParameters: 'You are using legacy API success callback names',
            method: 'The method you called is not defined',
            missingAction: 'API action used but no url was defined',
            missingURL: 'No URL specified for api event',
            noReturnedValue: 'The beforeSend callback must return a settings object, beforeSend ignored.',
            noStorage: 'Caching responses locally requires session storage',
            parseError: 'There was an error parsing your request',
            requiredParameter: 'Missing a required URL parameter: ',
            statusMessage: 'Server gave an error: ',
            timeout: 'Your request timed out',
        },

        regExp: {
            required: /{\$*[\da-z]+}/gi,
            optional: /{\/\$*[\da-z]+}/gi,
            validate: /^[_a-z][\w-]*(?:\[[\w-]*])*$/i,
            key: /[\w-]+|(?=\[])/gi,
            push: /^$/,
            fixed: /^\d+$/,
            named: /^[\w-]+$/i,
        },

        className: {
            loading: 'loading',
            error: 'error',
        },

        selector: {
            disabled: '.disabled',
            form: 'form',
        },

        metadata: {
            action: 'action',
            url: 'url',
        },
    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - State
 * 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.state = function (parameters) {
        var
            $allModules     = $(this),

            time            = Date.now(),
            performance     = [],

            query           = arguments[0],
            methodInvoked   = typeof query === 'string',
            queryArguments  = [].slice.call(arguments, 1),
            contextCheck    = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            returnedValue
        ;
        $allModules.each(function () {
            var
                settings          = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.state.settings, parameters)
                    : $.extend({}, $.fn.state.settings),

                error           = settings.error,
                metadata        = settings.metadata,
                className       = settings.className,
                namespace       = settings.namespace,
                states          = settings.states,
                text            = settings.text,

                eventNamespace  = '.' + namespace,
                moduleNamespace = namespace + '-module',

                $module         = $(this),
                $context        = settings.context ? contextCheck(settings.context, window) : $module,

                element         = this,
                instance        = $module.data(moduleNamespace),

                module
            ;
            module = {

                initialize: function () {
                    module.verbose('Initializing module');

                    // allow module to guess desired state based on element
                    if (settings.automatic) {
                        module.add.defaults();
                    }

                    // bind events with delegated events
                    $context
                        .on('mouseenter' + eventNamespace, module.change.text)
                        .on('mouseleave' + eventNamespace, module.reset.text)
                        .on('click' + eventNamespace, module.toggle.state)
                    ;
                    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);
                    $context
                        .off(eventNamespace)
                    ;
                    $module
                        .removeData(metadata.storedText)
                        .removeData(moduleNamespace)
                    ;
                },

                refresh: function () {
                    module.verbose('Refreshing selector cache');
                    $module = $(element);
                },

                add: {
                    defaults: function () {
                        var
                            userStates = parameters && $.isPlainObject(parameters.states)
                                ? parameters.states
                                : {}
                        ;
                        $.each(settings.defaults, function (type, typeStates) {
                            if (module.is[type] !== undefined && module.is[type]()) {
                                module.verbose('Adding default states', type, element);
                                $.extend(settings.states, typeStates, userStates);
                            }
                        });
                    },
                },

                is: {

                    active: function () {
                        return $module.hasClass(className.active);
                    },
                    loading: function () {
                        return $module.hasClass(className.loading);
                    },
                    inactive: function () {
                        return !$module.hasClass(className.active);
                    },
                    state: function (state) {
                        if (className[state] === undefined) {
                            return false;
                        }

                        return $module.hasClass(className[state]);
                    },

                    enabled: function () {
                        return !$module.is(settings.filter.active);
                    },
                    disabled: function () {
                        return $module.is(settings.filter.active);
                    },
                    textEnabled: function () {
                        return !$module.is(settings.filter.text);
                    },

                    // definitions for automatic type detection
                    button: function () {
                        return $module.is('.button:not(a, .submit)');
                    },
                    input: function () {
                        return $module.is('input');
                    },
                    progress: function () {
                        return $module.is('.ui.progress');
                    },
                },

                allow: function (state) {
                    module.debug('Now allowing state', state);
                    states[state] = true;
                },
                disallow: function (state) {
                    module.debug('No longer allowing', state);
                    states[state] = false;
                },

                allows: function (state) {
                    return states[state] || false;
                },

                enable: function () {
                    $module.removeClass(className.disabled);
                },

                disable: function () {
                    $module.addClass(className.disabled);
                },

                setState: function (state) {
                    if (module.allows(state)) {
                        $module.addClass(className[state]);
                    }
                },

                removeState: function (state) {
                    if (module.allows(state)) {
                        $module.removeClass(className[state]);
                    }
                },

                toggle: {
                    state: function () {
                        var
                            apiRequest,
                            requestCancelled
                        ;
                        if (module.allows('active') && module.is.enabled()) {
                            module.refresh();
                            if ($.fn.api !== undefined) {
                                apiRequest = $module.api('get request');
                                requestCancelled = $module.api('was cancelled');
                                if (requestCancelled) {
                                    module.debug('API Request cancelled by beforesend');
                                    settings.activateTest = function () {
                                        return false;
                                    };
                                    settings.deactivateTest = function () {
                                        return false;
                                    };
                                } else if (apiRequest) {
                                    module.listenTo(apiRequest);

                                    return;
                                }
                            }
                            module.change.state();
                        }
                    },
                },

                listenTo: function (apiRequest) {
                    module.debug('API request detected, waiting for state signal', apiRequest);
                    if (apiRequest) {
                        if (text.loading) {
                            module.update.text(text.loading);
                        }
                        $.when(apiRequest)
                            .then(function () {
                                if (apiRequest.state() === 'resolved') {
                                    module.debug('API request succeeded');
                                    settings.activateTest = function () {
                                        return true;
                                    };
                                    settings.deactivateTest = function () {
                                        return true;
                                    };
                                } else {
                                    module.debug('API request failed');
                                    settings.activateTest = function () {
                                        return false;
                                    };
                                    settings.deactivateTest = function () {
                                        return false;
                                    };
                                }
                                module.change.state();
                            })
                        ;
                    }
                },

                // checks whether active/inactive state can be given
                change: {

                    state: function () {
                        module.debug('Determining state change direction');
                        // inactive to active change
                        if (module.is.inactive()) {
                            module.activate();
                        } else {
                            module.deactivate();
                        }
                        if (settings.sync) {
                            module.sync();
                        }
                        settings.onChange.call(element);
                    },

                    text: function () {
                        if (module.is.textEnabled()) {
                            if (module.is.disabled()) {
                                module.verbose('Changing text to disabled text', text.hover);
                                module.update.text(text.disabled);
                            } else if (module.is.active()) {
                                if (text.hover) {
                                    module.verbose('Changing text to hover text', text.hover);
                                    module.update.text(text.hover);
                                } else if (text.deactivate) {
                                    module.verbose('Changing text to deactivating text', text.deactivate);
                                    module.update.text(text.deactivate);
                                }
                            } else {
                                if (text.hover) {
                                    module.verbose('Changing text to hover text', text.hover);
                                    module.update.text(text.hover);
                                } else if (text.activate) {
                                    module.verbose('Changing text to activating text', text.activate);
                                    module.update.text(text.activate);
                                }
                            }
                        }
                    },

                },

                activate: function () {
                    if (settings.activateTest.call(element)) {
                        module.debug('Setting state to active');
                        $module
                            .addClass(className.active)
                        ;
                        module.update.text(text.active);
                        settings.onActivate.call(element);
                    }
                },

                deactivate: function () {
                    if (settings.deactivateTest.call(element)) {
                        module.debug('Setting state to inactive');
                        $module
                            .removeClass(className.active)
                        ;
                        module.update.text(text.inactive);
                        settings.onDeactivate.call(element);
                    }
                },

                sync: function () {
                    module.verbose('Syncing other buttons to current state');
                    if (module.is.active()) {
                        $allModules
                            .not($module)
                            .state('activate')
                        ;
                    } else {
                        $allModules
                            .not($module)
                            .state('deactivate')
                        ;
                    }
                },

                get: {
                    text: function () {
                        return settings.selector.text
                            ? $module.find(settings.selector.text).text()
                            : $module.html();
                    },
                    textFor: function (state) {
                        return text[state] || false;
                    },
                },

                flash: {
                    text: function (text, duration, callback) {
                        var
                            previousText = module.get.text();
                        module.debug('Flashing text message', text, duration);
                        text = text || settings.text.flash;
                        duration = duration || settings.flashDuration;
                        callback = callback || function () {};
                        module.update.text(text);
                        setTimeout(function () {
                            module.update.text(previousText);
                            callback.call(element);
                        }, duration);
                    },
                },

                reset: {
                    // on mouseout sets text to previous value
                    text: function () {
                        var
                            activeText   = text.active || $module.data(metadata.storedText),
                            inactiveText = text.inactive || $module.data(metadata.storedText)
                        ;
                        if (module.is.textEnabled()) {
                            if (module.is.active() && activeText) {
                                module.verbose('Resetting active text', activeText);
                                module.update.text(activeText);
                            } else if (inactiveText) {
                                module.verbose('Resetting inactive text', activeText);
                                module.update.text(inactiveText);
                            }
                        }
                    },
                },

                update: {
                    text: function (text) {
                        var
                            currentText = module.get.text();
                        if (text && text !== currentText) {
                            module.debug('Updating text', text);
                            if (settings.selector.text) {
                                $module
                                    .data(metadata.storedText, text)
                                    .find(settings.selector.text)
                                    .text(text)
                                ;
                            } else {
                                $module
                                    .data(metadata.storedText, text)
                                    .html(text)
                                ;
                            }
                        } else {
                            module.debug('Text is already set, ignoring update', text);
                        }
                    },
                },

                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.state.settings = {

        // module info
        name: 'State',

        // debug output
        debug: false,

        // verbose debug output
        verbose: false,

        // namespace for events
        namespace: 'state',

        // debug data includes performance
        performance: true,

        // callback occurs on state change
        onActivate: function () {},
        onDeactivate: function () {},
        onChange: function () {},

        // state test functions
        activateTest: function () {
            return true;
        },
        deactivateTest: function () {
            return true;
        },

        // whether to automatically map default states
        automatic: true,

        // activate / deactivate changes all elements instantiated at same time
        sync: false,

        // default flash text duration, used for temporarily changing text of an element
        flashDuration: 1000,

        // selector filter
        filter: {
            text: '.loading, .disabled',
            active: '.disabled',
        },

        context: false,

        // error
        error: {
            method: 'The method you called is not defined.',
        },

        // metadata
        metadata: {
            promise: 'promise',
            storedText: 'stored-text',
        },

        // change class on state
        className: {
            active: 'active',
            disabled: 'disabled',
            error: 'error',
            loading: 'loading',
            success: 'success',
            warning: 'warning',
        },

        selector: {
            // selector for text node
            text: false,
        },

        defaults: {
            input: {
                disabled: true,
                loading: true,
                active: true,
            },
            button: {
                disabled: true,
                loading: true,
                active: true,
            },
            progress: {
                active: true,
                success: true,
                warning: true,
                error: true,
            },
        },

        states: {
            active: true,
            disabled: true,
            error: true,
            loading: true,
            success: true,
            warning: true,
        },

        text: {
            disabled: false,
            flash: false,
            hover: false,
            active: false,
            inactive: false,
            activate: false,
            deactivate: false,
        },

    };
})(jQuery, window, document);

/*!
 * # Fomantic-UI 2.9.3 - Visibility
 * 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.visibility = function (parameters) {
        var
            $allModules    = $(this),

            time           = Date.now(),
            performance    = [],

            query          = arguments[0],
            methodInvoked  = typeof query === 'string',
            queryArguments = [].slice.call(arguments, 1),
            contextCheck   = function (context, win) {
                var $context;
                if ([window, document].indexOf(context) >= 0) {
                    $context = $(context);
                } else {
                    $context = $(win.document).find(context);
                    if ($context.length === 0) {
                        $context = win.frameElement ? contextCheck(context, win.parent) : window;
                    }
                }

                return $context;
            },
            returnedValue,

            moduleCount    = $allModules.length,
            loadedCount    = 0
        ;

        $allModules.each(function () {
            var
                settings        = $.isPlainObject(parameters)
                    ? $.extend(true, {}, $.fn.visibility.settings, parameters)
                    : $.extend({}, $.fn.visibility.settings),

                className       = settings.className,
                namespace       = settings.namespace,
                error           = settings.error,
                metadata        = settings.metadata,

                eventNamespace  = '.' + namespace,
                moduleNamespace = 'module-' + namespace,

                $window         = $(window),

                $module         = $(this),
                $context        = contextCheck(settings.context, window),

                $placeholder,

                instance        = $module.data(moduleNamespace),

                element         = this,
                disabled        = false,

                contextObserver,
                observer,
                module
            ;

            module = {

                initialize: function () {
                    module.debug('Initializing', settings);

                    module.setup.cache();

                    if (module.should.trackChanges()) {
                        if (settings.type === 'image') {
                            module.setup.image();
                        }
                        if (settings.type === 'fixed') {
                            module.setup.fixed();
                        }

                        if (settings.observeChanges) {
                            module.observeChanges();
                        }
                        module.bind.events();
                    }

                    module.save.position();
                    if (!module.is.visible()) {
                        module.error(error.visible, $module);
                    }

                    if (settings.initialCheck) {
                        module.checkVisibility();
                    }
                    module.instantiate();
                },

                instantiate: function () {
                    module.debug('Storing instance', module);
                    $module
                        .data(moduleNamespace, module)
                    ;
                    instance = module;
                },

                destroy: function () {
                    module.verbose('Destroying previous module');
                    if (observer) {
                        observer.disconnect();
                    }
                    if (contextObserver) {
                        contextObserver.disconnect();
                    }
                    $window
                        .off('load' + eventNamespace, module.event.load)
                        .off('resize' + eventNamespace, module.event.resize)
                    ;
                    $context
                        .off('scroll' + eventNamespace, module.event.scroll)
                        .off('scrollchange' + eventNamespace, module.event.scrollchange)
                    ;
                    if (settings.type === 'fixed') {
                        module.resetFixed();
                        module.remove.placeholder();
                    }
                    $module
                        .off(eventNamespace)
                        .removeData(moduleNamespace)
                    ;
                },

                observeChanges: function () {
                    if ('MutationObserver' in window) {
                        contextObserver = new MutationObserver(module.event.contextChanged);
                        observer = new MutationObserver(module.event.changed);
                        contextObserver.observe(document, {
                            childList: true,
                            subtree: true,
                        });
                        observer.observe(element, {
                            childList: true,
                            subtree: true,
                        });
                        module.debug('Setting up mutation observer', observer);
                    }
                },

                bind: {
                    events: function () {
                        module.verbose('Binding visibility events to scroll and resize');
                        if (settings.refreshOnLoad) {
                            $window
                                .on('load' + eventNamespace, module.event.load)
                            ;
                        }
                        $window
                            .on('resize' + eventNamespace, module.event.resize)
                        ;
                        // pub/sub pattern
                        $context
                            .off('scroll' + eventNamespace)
                            .on('scroll' + eventNamespace, module.event.scroll)
                            .on('scrollchange' + eventNamespace, module.event.scrollchange)
                        ;
                    },
                },

                event: {
                    changed: function (mutations) {
                        module.verbose('DOM tree modified, updating visibility calculations');
                        module.timer = setTimeout(function () {
                            module.verbose('DOM tree modified, updating sticky menu');
                            module.refresh();
                        }, 100);
                    },
                    contextChanged: function (mutations) {
                        [].forEach.call(mutations, function (mutation) {
                            if (mutation.removedNodes) {
                                [].forEach.call(mutation.removedNodes, function (node) {
                                    if (node === element || $(node).find(element).length > 0) {
                                        module.debug('Element removed from DOM, tearing down events');
                                        module.destroy();
                                    }
                                });
                            }
                        });
                    },
                    resize: function () {
                        module.debug('Window resized');
                        if (settings.refreshOnResize) {
                            requestAnimationFrame(module.refresh);
                        }
                    },
                    load: function () {
                        module.debug('Page finished loading');
                        requestAnimationFrame(module.refresh);
                    },
                    // publishes scrollchange event on one scroll
                    scroll: function () {
                        if (settings.throttle) {
                            clearTimeout(module.timer);
                            module.timer = setTimeout(function () {
                                $context.triggerHandler('scrollchange' + eventNamespace, [$context.scrollTop()]);
                            }, settings.throttle);
                        } else {
                            requestAnimationFrame(function () {
                                $context.triggerHandler('scrollchange' + eventNamespace, [$context.scrollTop()]);
                            });
                        }
                    },
                    // subscribes to scrollchange
                    scrollchange: function (event, scrollPosition) {
                        module.checkVisibility(scrollPosition);
                    },
                },

                precache: function (images, callback) {
                    if (!Array.isArray(images)) {
                        images = [images];
                    }
                    var
                        imagesLength  = images.length,
                        loadedCounter = 0,
                        cache         = [],
                        cacheImage    = document.createElement('img'),
                        handleLoad    = function () {
                            loadedCounter++;
                            if (loadedCounter >= images.length) {
                                if (isFunction(callback)) {
                                    callback();
                                }
                            }
                        }
                    ;
                    while (imagesLength--) {
                        cacheImage = document.createElement('img');
                        cacheImage.addEventListener('load', handleLoad);
                        cacheImage.addEventListener('error', handleLoad);
                        cacheImage.src = images[imagesLength];
                        cache.push(cacheImage);
                    }
                },

                enableCallbacks: function () {
                    module.debug('Allowing callbacks to occur');
                    disabled = false;
                },

                disableCallbacks: function () {
                    module.debug('Disabling all callbacks temporarily');
                    disabled = true;
                },

                should: {
                    trackChanges: function () {
                        if (methodInvoked) {
                            module.debug('One time query, no need to bind events');

                            return false;
                        }
                        module.debug('Callbacks being attached');

                        return true;
                    },
                },

                setup: {
                    cache: function () {
                        module.cache = {
                            occurred: {},
                            screen: {},
                            element: {},
                        };
                    },
                    image: function () {
                        var
                            src = $module.data(metadata.src)
                        ;
                        if (src) {
                            module.verbose('Lazy loading image', src);
                            settings.once = true;
                            settings.observeChanges = false;

                            // show when top visible
                            settings.onOnScreen = function () {
                                module.debug('Image on screen', element);
                                module.precache(src, function () {
                                    module.set.image(src, function () {
                                        loadedCount++;
                                        if (loadedCount === moduleCount) {
                                            settings.onAllLoaded.call(this);
                                        }
                                        settings.onLoad.call(this);
                                    });
                                });
                            };
                        }
                    },
                    fixed: function () {
                        module.debug('Setting up fixed');
                        settings.once = false;
                        settings.observeChanges = false;
                        settings.initialCheck = true;
                        settings.refreshOnLoad = true;
                        if (!parameters.transition) {
                            settings.transition = false;
                        }
                        module.create.placeholder();
                        module.debug('Added placeholder', $placeholder);
                        settings.onTopPassed = function () {
                            module.debug('Element passed, adding fixed position', $module);
                            module.show.placeholder();
                            module.set.fixed();
                            if (settings.transition) {
                                if ($.fn.transition !== undefined) {
                                    $module.transition(settings.transition, settings.duration);
                                }
                            }
                        };
                        settings.onTopPassedReverse = function () {
                            module.debug('Element returned to position, removing fixed', $module);
                            module.hide.placeholder();
                            module.remove.fixed();
                        };
                    },
                },

                create: {
                    placeholder: function () {
                        module.verbose('Creating fixed position placeholder');
                        $placeholder = $module
                            .clone(false)
                            .css('display', 'none')
                            .addClass(className.placeholder)
                            .insertAfter($module)
                        ;
                    },
                },

                show: {
                    placeholder: function () {
                        module.verbose('Showing placeholder');
                        $placeholder
                            .css('display', 'block')
                            .css('visibility', 'hidden')
                        ;
                    },
                },
                hide: {
                    placeholder: function () {
                        module.verbose('Hiding placeholder');
                        $placeholder
                            .css('display', 'none')
                            .css('visibility', '')
                        ;
                    },
                },

                set: {
                    fixed: function () {
                        module.verbose('Setting element to fixed position');
                        $module
                            .addClass(className.fixed)
                            .css({
                                position: 'fixed',
                                top: settings.offset + 'px',
                                left: 'auto',
                                zIndex: settings.zIndex,
                            })
                        ;
                        settings.onFixed.call(element);
                    },
                    image: function (src, callback) {
                        $module
                            .attr('src', src)
                        ;
                        if (settings.transition) {
                            if ($.fn.transition !== undefined) {
                                if ($module.hasClass(className.visible)) {
                                    module.debug('Transition already occurred on this image, skipping animation');

                                    return;
                                }
                                $module.transition(settings.transition, settings.duration, callback);
                            } else {
                                $module.fadeIn(settings.duration, callback);
                            }
                        } else {
                            $module.show();
                        }
                    },
                },

                is: {
                    onScreen: function () {
                        var
                            calculations   = module.get.elementCalculations()
                        ;

                        return calculations.onScreen;
                    },
                    offScreen: function () {
                        var
                            calculations   = module.get.elementCalculations()
                        ;

                        return calculations.offScreen;
                    },
                    visible: function () {
                        if (module.cache && module.cache.element) {
                            return !(module.cache.element.width === 0 && module.cache.element.offset.top === 0);
                        }

                        return false;
                    },
                    verticallyScrollableContext: function () {
                        var
                            overflowY = $context[0] !== window
                                ? $context.css('overflow-y')
                                : false
                        ;

                        return overflowY === 'auto' || overflowY === 'scroll';
                    },
                    horizontallyScrollableContext: function () {
                        var
                            overflowX = $context[0] !== window
                                ? $context.css('overflow-x')
                                : false
                        ;

                        return overflowX === 'auto' || overflowX === 'scroll';
                    },
                },

                refresh: function () {
                    module.debug('Refreshing constants (width/height)');
                    if (settings.type === 'fixed') {
                        module.resetFixed();
                    }
                    module.reset();
                    module.save.position();
                    if (settings.checkOnRefresh) {
                        module.checkVisibility();
                    }
                    settings.onRefresh.call(element);
                },

                resetFixed: function () {
                    module.remove.fixed();
                    module.remove.occurred();
                },

                reset: function () {
                    module.verbose('Resetting all cached values');
                    if ($.isPlainObject(module.cache)) {
                        module.cache.screen = {};
                        module.cache.element = {};
                    }
                },

                checkVisibility: function (scroll) {
                    module.verbose('Checking visibility of element', module.cache.element);

                    if (!disabled && module.is.visible()) {
                        // save scroll position
                        module.save.scroll(scroll);

                        // update calculations derived from scroll
                        module.save.calculations();

                        // percentage
                        module.passed();

                        // reverse (must be first)
                        module.passingReverse();
                        module.topVisibleReverse();
                        module.bottomVisibleReverse();
                        module.topPassedReverse();
                        module.bottomPassedReverse();

                        // one time
                        module.onScreen();
                        module.offScreen();
                        module.passing();
                        module.topVisible();
                        module.bottomVisible();
                        module.topPassed();
                        module.bottomPassed();

                        // on update callback
                        if (settings.onUpdate) {
                            settings.onUpdate.call(element, module.get.elementCalculations());
                        }
                    }
                },

                passed: function (amount, newCallback) {
                    var
                        calculations   = module.get.elementCalculations()
                    ;
                    // assign callback
                    if (amount && newCallback) {
                        settings.onPassed[amount] = newCallback;
                    } else if (amount !== undefined) {
                        return module.get.pixelsPassed(amount) > calculations.pixelsPassed;
                    } else if (calculations.passing) {
                        $.each(settings.onPassed, function (amount, callback) {
                            if (calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
                                module.execute(callback, amount);
                            } else if (!settings.once) {
                                module.remove.occurred(callback);
                            }
                        });
                    }
                },

                onScreen: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onOnScreen,
                        callbackName = 'onScreen'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for onScreen', newCallback);
                        settings.onOnScreen = newCallback;
                    }
                    if (calculations.onScreen) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback !== undefined) {
                        return calculations.onOnScreen;
                    }
                },

                offScreen: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onOffScreen,
                        callbackName = 'offScreen'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for offScreen', newCallback);
                        settings.onOffScreen = newCallback;
                    }
                    if (calculations.offScreen) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback !== undefined) {
                        return calculations.onOffScreen;
                    }
                },

                passing: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onPassing,
                        callbackName = 'passing'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for passing', newCallback);
                        settings.onPassing = newCallback;
                    }
                    if (calculations.passing) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback !== undefined) {
                        return calculations.passing;
                    }
                },

                topVisible: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onTopVisible,
                        callbackName = 'topVisible'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for top visible', newCallback);
                        settings.onTopVisible = newCallback;
                    }
                    if (calculations.topVisible) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return calculations.topVisible;
                    }
                },

                bottomVisible: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onBottomVisible,
                        callbackName = 'bottomVisible'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for bottom visible', newCallback);
                        settings.onBottomVisible = newCallback;
                    }
                    if (calculations.bottomVisible) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return calculations.bottomVisible;
                    }
                },

                topPassed: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onTopPassed,
                        callbackName = 'topPassed'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for top passed', newCallback);
                        settings.onTopPassed = newCallback;
                    }
                    if (calculations.topPassed) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return calculations.topPassed;
                    }
                },

                bottomPassed: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onBottomPassed,
                        callbackName = 'bottomPassed'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for bottom passed', newCallback);
                        settings.onBottomPassed = newCallback;
                    }
                    if (calculations.bottomPassed) {
                        module.execute(callback, callbackName);
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return calculations.bottomPassed;
                    }
                },

                passingReverse: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onPassingReverse,
                        callbackName = 'passingReverse'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for passing reverse', newCallback);
                        settings.onPassingReverse = newCallback;
                    }
                    if (!calculations.passing) {
                        if (module.get.occurred('passing')) {
                            module.execute(callback, callbackName);
                        }
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback !== undefined) {
                        return !calculations.passing;
                    }
                },

                topVisibleReverse: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onTopVisibleReverse,
                        callbackName = 'topVisibleReverse'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for top visible reverse', newCallback);
                        settings.onTopVisibleReverse = newCallback;
                    }
                    if (!calculations.topVisible) {
                        if (module.get.occurred('topVisible')) {
                            module.execute(callback, callbackName);
                        }
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return !calculations.topVisible;
                    }
                },

                bottomVisibleReverse: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onBottomVisibleReverse,
                        callbackName = 'bottomVisibleReverse'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for bottom visible reverse', newCallback);
                        settings.onBottomVisibleReverse = newCallback;
                    }
                    if (!calculations.bottomVisible) {
                        if (module.get.occurred('bottomVisible')) {
                            module.execute(callback, callbackName);
                        }
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return !calculations.bottomVisible;
                    }
                },

                topPassedReverse: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onTopPassedReverse,
                        callbackName = 'topPassedReverse'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for top passed reverse', newCallback);
                        settings.onTopPassedReverse = newCallback;
                    }
                    if (!calculations.topPassed) {
                        if (module.get.occurred('topPassed')) {
                            module.execute(callback, callbackName);
                        }
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return !calculations.onTopPassed;
                    }
                },

                bottomPassedReverse: function (newCallback) {
                    var
                        calculations = module.get.elementCalculations(),
                        callback     = newCallback || settings.onBottomPassedReverse,
                        callbackName = 'bottomPassedReverse'
                    ;
                    if (newCallback) {
                        module.debug('Adding callback for bottom passed reverse', newCallback);
                        settings.onBottomPassedReverse = newCallback;
                    }
                    if (!calculations.bottomPassed) {
                        if (module.get.occurred('bottomPassed')) {
                            module.execute(callback, callbackName);
                        }
                    } else if (!settings.once) {
                        module.remove.occurred(callbackName);
                    }
                    if (newCallback === undefined) {
                        return !calculations.bottomPassed;
                    }
                },

                execute: function (callback, callbackName) {
                    var
                        calculations = module.get.elementCalculations(),
                        screen       = module.get.screenCalculations()
                    ;
                    callback = callback || false;
                    if (callback) {
                        if (settings.continuous) {
                            module.debug('Callback being called continuously', callbackName, calculations);
                            callback.call(element, calculations, screen);
                        } else if (!module.get.occurred(callbackName)) {
                            module.debug('Conditions met', callbackName, calculations);
                            callback.call(element, calculations, screen);
                        }
                    }
                    module.save.occurred(callbackName);
                },

                remove: {
                    fixed: function () {
                        module.debug('Removing fixed position');
                        $module
                            .removeClass(className.fixed)
                            .css({
                                position: '',
                                top: '',
                                left: '',
                                zIndex: '',
                            })
                        ;
                        settings.onUnfixed.call(element);
                    },
                    placeholder: function () {
                        module.debug('Removing placeholder content');
                        if ($placeholder) {
                            $placeholder.remove();
                        }
                    },
                    occurred: function (callback) {
                        if (callback) {
                            var
                                occurred = module.cache.occurred
                            ;
                            if (occurred[callback] !== undefined && occurred[callback] === true) {
                                module.debug('Callback can now be called again', callback);
                                module.cache.occurred[callback] = false;
                            }
                        } else {
                            module.cache.occurred = {};
                        }
                    },
                },

                save: {
                    calculations: function () {
                        module.verbose('Saving all calculations necessary to determine positioning');
                        module.save.direction();
                        module.save.screenCalculations();
                        module.save.elementCalculations();
                    },
                    occurred: function (callback) {
                        if (callback) {
                            if (module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
                                module.verbose('Saving callback occurred', callback);
                                module.cache.occurred[callback] = true;
                            }
                        }
                    },
                    scroll: function (scrollPosition) {
                        scrollPosition = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
                        module.cache.scroll = scrollPosition;
                    },
                    direction: function () {
                        var
                            scroll     = module.get.scroll(),
                            lastScroll = module.get.lastScroll(),
                            direction
                        ;
                        if (scroll > lastScroll && lastScroll) {
                            direction = 'down';
                        } else if (scroll < lastScroll && lastScroll) {
                            direction = 'up';
                        } else {
                            direction = 'static';
                        }
                        module.cache.direction = direction;

                        return module.cache.direction;
                    },
                    elementPosition: function () {
                        var
                            element = module.cache.element,
                            screen  = module.get.screenSize()
                        ;
                        module.verbose('Saving element position');
                        // (quicker than $.extend)
                        element.fits = element.height < screen.height;
                        element.offset = $module.offset();
                        element.width = $module.outerWidth();
                        element.height = $module.outerHeight();
                        // compensate for scroll in context
                        if (module.is.verticallyScrollableContext()) {
                            element.offset.top += $context.scrollTop() - $context.offset().top;
                        }
                        if (module.is.horizontallyScrollableContext()) {
                            element.offset.left += $context.scrollLeft() - $context.offset().left;
                        }
                        // store
                        module.cache.element = element;

                        return element;
                    },
                    elementCalculations: function () {
                        var
                            screen     = module.get.screenCalculations(),
                            element    = module.get.elementPosition()
                        ;
                        // offset
                        if (settings.includeMargin) {
                            element.margin = {};
                            element.margin.top = parseInt($module.css('margin-top'), 10);
                            element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
                            element.top = element.offset.top - element.margin.top;
                            element.bottom = element.offset.top + element.height + element.margin.bottom;
                        } else {
                            element.top = element.offset.top;
                            element.bottom = element.offset.top + element.height;
                        }

                        // visibility
                        element.topPassed = screen.top >= element.top;
                        element.bottomPassed = screen.top >= element.bottom;
                        element.topVisible = (screen.bottom >= element.top) && !element.topPassed;
                        element.bottomVisible = (screen.bottom >= element.bottom) && !element.bottomPassed;
                        element.pixelsPassed = 0;
                        element.percentagePassed = 0;

                        // meta calculations
                        element.onScreen = (element.topVisible || element.passing) && !element.bottomPassed;
                        element.passing = element.topPassed && !element.bottomPassed;
                        element.offScreen = !element.onScreen;

                        // passing calculations
                        if (element.passing) {
                            element.pixelsPassed = screen.top - element.top;
                            element.percentagePassed = (screen.top - element.top) / element.height;
                        }
                        module.cache.element = element;
                        module.verbose('Updated element calculations', element);

                        return element;
                    },
                    screenCalculations: function () {
                        var
                            scroll = module.get.scroll()
                        ;
                        module.save.direction();
                        module.cache.screen.top = scroll;
                        module.cache.screen.bottom = scroll + module.cache.screen.height;

                        return module.cache.screen;
                    },
                    screenSize: function () {
                        module.verbose('Saving window position');
                        module.cache.screen = {
                            height: $context.height(),
                        };
                    },
                    position: function () {
                        module.save.screenSize();
                        module.save.elementPosition();
                    },
                },

                get: {
                    pixelsPassed: function (amount) {
                        var
                            element = module.get.elementCalculations()
                        ;
                        if (amount.search('%') > -1) {
                            return element.height * (parseInt(amount, 10) / 100);
                        }

                        return parseInt(amount, 10);
                    },
                    occurred: function (callback) {
                        return module.cache.occurred !== undefined
                            ? module.cache.occurred[callback] || false
                            : false;
                    },
                    direction: function () {
                        if (module.cache.direction === undefined) {
                            module.save.direction();
                        }

                        return module.cache.direction;
                    },
                    elementPosition: function () {
                        if (module.cache.element === undefined) {
                            module.save.elementPosition();
                        }

                        return module.cache.element;
                    },
                    elementCalculations: function () {
                        if (module.cache.element === undefined) {
                            module.save.elementCalculations();
                        }

                        return module.cache.element;
                    },
                    screenCalculations: function () {
                        if (module.cache.screen === undefined) {
                            module.save.screenCalculations();
                        }

                        return module.cache.screen;
                    },
                    screenSize: function () {
                        if (module.cache.screen === undefined) {
                            module.save.screenSize();
                        }

                        return module.cache.screen;
                    },
                    scroll: function () {
                        if (module.cache.scroll === undefined) {
                            module.save.scroll();
                        }

                        return module.cache.scroll;
                    },
                    lastScroll: function () {
                        if (module.cache.screen === undefined) {
                            module.debug('First scroll event, no last scroll could be found');

                            return false;
                        }

                        return module.cache.screen.top;
                    },
                },

                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 (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();
                }
                instance.save.scroll();
                instance.save.calculations();
                module.invoke(query);
            } else {
                if (instance !== undefined) {
                    instance.invoke('destroy');
                }
                module.initialize();
            }
        });

        return returnedValue !== undefined
            ? returnedValue
            : this;
    };

    $.fn.visibility.settings = {

        name: 'Visibility',
        namespace: 'visibility',

        debug: false,
        verbose: false,
        performance: true,

        // whether to use mutation observers to follow changes
        observeChanges: true,

        // check position immediately on init
        initialCheck: true,

        // whether to refresh calculations after all page images load
        refreshOnLoad: true,

        // whether to refresh calculations after page resize event
        refreshOnResize: true,

        // should call callbacks on refresh event (resize, etc)
        checkOnRefresh: true,

        // callback should only occur one time
        once: true,

        // callback should fire continuously when evaluates to true
        continuous: false,

        // offset to use with scroll top
        offset: 0,

        // whether to include margin in elements position
        includeMargin: false,

        // scroll context for visibility checks
        context: window,

        // visibility check delay in ms (defaults to animationFrame)
        throttle: false,

        // special visibility type (image, fixed)
        type: false,

        // z-index to use with visibility 'fixed'
        zIndex: '10',

        // image only animation settings
        transition: 'fade in',
        duration: 1000,

        // array of callbacks for percentage
        onPassed: {},

        // standard callbacks
        onOnScreen: false,
        onOffScreen: false,
        onPassing: false,
        onTopVisible: false,
        onBottomVisible: false,
        onTopPassed: false,
        onBottomPassed: false,

        // reverse callbacks
        onPassingReverse: false,
        onTopVisibleReverse: false,
        onBottomVisibleReverse: false,
        onTopPassedReverse: false,
        onBottomPassedReverse: false,

        // special callbacks for image
        onLoad: function () {},
        onAllLoaded: function () {},

        // special callbacks for fixed position
        onFixed: function () {},
        onUnfixed: function () {},

        // utility callbacks
        onUpdate: false, // disabled by default for performance
        onRefresh: function () {},

        metadata: {
            src: 'src',
        },

        className: {
            fixed: 'fixed',
            placeholder: 'constraint',
            visible: 'visible',
        },

        error: {
            method: 'The method you called is not defined.',
            visible: 'Element is hidden, you must call refresh after element becomes visible',
        },

    };
})(jQuery, window, document);
