/*!
 * # Fomantic-UI - 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 (...args) {
        const $allModules = $(this);

        let time = Date.now();
        let performance = [];

        const parameters = args[0];
        const methodInvoked = typeof parameters === 'string';
        const queryArguments = args.slice(1);

        let returnedValue;

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

            const className = settings.className;
            const metadata = settings.metadata;
            const namespace = settings.namespace;
            const selector = settings.selector;
            const error = settings.error;

            const eventNamespace = '.' + namespace;
            const moduleNamespace = 'module-' + namespace;

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

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

            let animating = false;
            const 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 large enough to show the 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) {
                        let precisionPower = 0;
                        let precision = 1;
                        let ratio = min / total;
                        while (precisionPower < 10) {
                            ratio *= precision;
                            if (ratio > 1) {
                                break;
                            }
                            precision = 10 ** precisionPower++;
                        }

                        return precision;
                    },
                    forceArray: function (element) {
                        return Array.isArray(element)
                            ? element
                            : (!Number.isNaN(Number(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 () {
                        const 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) {
                    let startValue;
                    let 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) {
                    const total = module.get.total();
                    let startValue;
                    let 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 = 0) {
                        const value = module.get.value(index);
                        const total = module.get.total();
                        const percent = animating
                            ? module.get.displayPercent(index)
                            : module.get.percent(index);
                        const left = total !== false
                            ? Math.max(0, total - value)
                            : 100 - percent;
                        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) {
                        const $bar = $($bars[index]);
                        const barWidth = $bar.width();
                        const totalWidth = $module.width();
                        const minDisplay = Number.parseInt($bar.css('min-width'), 10);
                        const 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);
                        let firstNonZeroIndex = -1;
                        let lastNonZeroIndex = -1;
                        const valuesSum = module.helper.sum(values);
                        const barCounts = $bars.length;
                        const isMultiple = barCounts > 1;
                        const percents = values.map(function (value, index) {
                            const allZero = index === barCounts - 1 && valuesSum === 0;
                            const $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 Number.parseFloat(value);
                        });
                        for (const [index, _] of values.entries()) {
                            const $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 = 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;
                        });
                        const hasTotal = module.has.total();
                        const totalPercent = module.helper.sum(percents);
                        const isMultipleValues = percents.length > 1 && hasTotal;
                        const sumTotal = module.helper.sum(module.helper.forceArray(module.value));
                        if (isMultipleValues && sumTotal > module.total) {
                            // Sum values instead of percents 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 {
                            const autoPrecision = settings.precision > 0
                                ? settings.precision
                                : (isMultipleValues
                                    ? module.helper.derivePrecision(Math.min.apply(null, module.value), module.total)
                                    : 0);

                            // round display percentage
                            const 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 () {
                        const 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 () {
                            const isInDOM = document.documentElement.contains(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 = 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) {
                            const $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 = 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 = settings.text.success || settings.text.active, keepState = false) {
                        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 = settings.text.warning, keepState = false) {
                        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 = settings.text.error, keepState = false) {
                        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 () {
                        const 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) {
                        const hasTotal = module.has.total();
                        if (hasTotal) {
                            module.set.value(values);
                        }
                        const percentCompletes = module.helper.forceArray(values).map(function (value) {
                            let 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 (...args) {
                    if (!settings.silent && settings.debug) {
                        if (settings.performance) {
                            module.performance.log(args);
                        } else {
                            module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
                            module.debug.apply(console, args);
                        }
                    }
                },
                verbose: function (...args) {
                    if (!settings.silent && settings.verbose && settings.debug) {
                        if (settings.performance) {
                            module.performance.log(args);
                        } else {
                            module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
                            module.verbose.apply(console, args);
                        }
                    }
                },
                error: function (...args) {
                    if (!settings.silent) {
                        module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
                        module.error.apply(console, args);
                    }
                },
                performance: {
                    log: function (message) {
                        let currentTime;
                        let executionTime;
                        let previousTime;
                        if (settings.performance) {
                            currentTime = Date.now();
                            previousTime = time || currentTime;
                            executionTime = currentTime - previousTime;
                            time = currentTime;
                            performance.push({
                                Name: message[0],
                                Arguments: message.slice(1),
                                Element: element,
                                'Execution Time': executionTime,
                            });
                        }
                        clearTimeout(module.performance.timer);
                        module.performance.timer = setTimeout(function () {
                            module.performance.display();
                        }, 500);
                    },
                    display: function () {
                        let title = settings.name + ':';
                        let 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);
                            console.table(performance);
                            console.groupEnd();
                        }
                        performance = [];
                    },
                },
                invoke: function (query, passedArguments = queryArguments, context = element) {
                    let object = instance;
                    let maxDepth;
                    let found;
                    let response;
                    if (typeof query === 'string' && object !== undefined) {
                        query = query.split(/[ .]/);
                        maxDepth = query.length - 1;
                        $.each(query, function (depth, value) {
                            const 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(parameters);
            } 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);
