Spamworldpro Mini Shell
Spamworldpro


Server : Apache
System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64
User : corals ( 1002)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/corals/cartforge.co/pub/static/frontend/Magento/luma/en_US/js/bundle/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/corals/cartforge.co/pub/static/frontend/Magento/luma/en_US/js/bundle/bundle2.js
require.config({"config": {
        "jsbuild":{"mage/menu.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'matchMedia',\n    'jquery-ui-modules/menu',\n    'mage/translate'\n], function ($, mediaCheck) {\n    'use strict';\n\n    /**\n     * Menu Widget - this widget is a wrapper for the jQuery UI Menu\n     */\n    $.widget('mage.menu', $.ui.menu, {\n        options: {\n            responsive: false,\n            expanded: false,\n            showDelay: 42,\n            hideDelay: 300,\n            delay: 0,\n            mediaBreakpoint: '(max-width: 768px)'\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            var self = this;\n\n            this.delay = this.options.delay;\n\n            this._super();\n            $(window).on('resize', function () {\n                self.element.find('.submenu-reverse').removeClass('submenu-reverse');\n            });\n        },\n\n        /**\n         * @private\n         */\n        _init: function () {\n            this._super();\n\n            if (this.options.expanded === true) {\n                this.isExpanded();\n            }\n\n            if (this.options.responsive === true) {\n                mediaCheck({\n                    media: this.options.mediaBreakpoint,\n                    entry: $.proxy(function () {\n                        this._toggleMobileMode();\n                    }, this),\n                    exit: $.proxy(function () {\n                        this._toggleDesktopMode();\n                    }, this)\n                });\n            }\n\n            this._assignControls()._listen();\n            this._setActiveMenu();\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _assignControls: function () {\n            this.controls = {\n                toggleBtn: $('[data-action=\"toggle-nav\"]')\n            };\n\n            return this;\n        },\n\n        /**\n         * @private\n         */\n        _listen: function () {\n            var controls = this.controls,\n                toggle = this.toggle;\n\n            controls.toggleBtn.off('click');\n            controls.toggleBtn.on('click', toggle.bind(this));\n        },\n\n        /**\n         * Toggle.\n         */\n        toggle: function () {\n            var html = $('html');\n\n            if (html.hasClass('nav-open')) {\n                html.removeClass('nav-open');\n                setTimeout(function () {\n                    html.removeClass('nav-before-open');\n                }, this.options.hideDelay);\n            } else {\n                html.addClass('nav-before-open');\n                setTimeout(function () {\n                    html.addClass('nav-open');\n                }, this.options.showDelay);\n            }\n        },\n\n        /**\n         * Tries to figure out the active category for current page and add appropriate classes:\n         *  - 'active' class for active category\n         *  - 'has-active' class for all parents of active category\n         *\n         *  First, checks whether current URL is URL of category page,\n         *  otherwise tries to retrieve category URL in case of current URL is product view page URL\n         *  which has category tree path in it.\n         *\n         * @return void\n         * @private\n         */\n        _setActiveMenu: function () {\n            var currentUrl = window.location.href.split('?')[0];\n\n            if (!this._setActiveMenuForCategory(currentUrl)) {\n                this._setActiveMenuForProduct(currentUrl);\n            }\n        },\n\n        /**\n         * Looks for category with provided URL and adds 'active' CSS class to it if it was not set before.\n         * If menu item has parent categories, sets 'has-active' class to all af them.\n         *\n         * @param {String} url - possible category URL\n         * @returns {Boolean} - true if active category was founded by provided URL, otherwise return false\n         * @private\n         */\n        _setActiveMenuForCategory: function (url) {\n            var activeCategoryLink = this.element.find('a[href=\"' + url + '\"]'),\n                classes,\n                classNav;\n\n            if (!activeCategoryLink || !activeCategoryLink.hasClass('ui-menu-item-wrapper')) {\n\n                //category was not found by provided URL\n                return false;\n            } else if (!activeCategoryLink.parent().hasClass('active')) {\n                activeCategoryLink.parent().addClass('active');\n                classes = activeCategoryLink.parent().attr('class');\n                classNav = classes.match(/(nav\\-)[0-9]+(\\-[0-9]+)+/gi);\n\n                if (classNav) {\n                    this._setActiveParent(classNav[0]);\n                }\n            }\n\n            return true;\n        },\n\n        /**\n         * Sets 'has-active' CSS class to all parent categories which have part of provided class in childClassName\n         *\n         * @example\n         *  childClassName - 'nav-1-2-3'\n         *  CSS class 'has-active' will be added to categories have 'nav-1-2' and 'nav-1' classes\n         *\n         * @param {String} childClassName - Class name of active category <li> element\n         * @return void\n         * @private\n         */\n        _setActiveParent: function (childClassName) {\n            var parentElement,\n                parentClass = childClassName.substr(0, childClassName.lastIndexOf('-'));\n\n            if (parentClass.lastIndexOf('-') !== -1) {\n                parentElement = this.element.find('.' + parentClass);\n\n                if (parentElement) {\n                    parentElement.addClass('has-active');\n                }\n                this._setActiveParent(parentClass);\n            }\n        },\n\n        /**\n         * Tries to retrieve category URL from current URL and mark this category as active\n         * @see _setActiveMenuForCategory(url)\n         *\n         * @example\n         *  currentUrl - http://magento.com/category1/category12/product.html,\n         *  category URLs has extensions .phtml - http://magento.com/category1.phtml\n         *  method sets active category which has URL http://magento.com/category1/category12.phtml\n         *\n         * @param {String} currentUrl - current page URL without parameters\n         * @return void\n         * @private\n         */\n        _setActiveMenuForProduct: function (currentUrl) {\n            var categoryUrlExtension,\n                lastUrlSection,\n                possibleCategoryUrl,\n                //retrieve first category URL to know what extension is used for category URLs\n                firstCategoryUrl = this.element.find('> li a').attr('href');\n\n            if (firstCategoryUrl) {\n                lastUrlSection = firstCategoryUrl.substr(firstCategoryUrl.lastIndexOf('/'));\n                categoryUrlExtension = lastUrlSection.lastIndexOf('.') !== -1 ?\n                    lastUrlSection.substr(lastUrlSection.lastIndexOf('.')) : '';\n\n                possibleCategoryUrl = currentUrl.substr(0, currentUrl.lastIndexOf('/')) + categoryUrlExtension;\n                this._setActiveMenuForCategory(possibleCategoryUrl);\n            }\n        },\n\n        /**\n         * Add class for expanded option.\n         */\n        isExpanded: function () {\n            var subMenus = this.element.find(this.options.menus),\n                expandedMenus = subMenus.find(this.options.menus);\n\n            expandedMenus.addClass('expanded');\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _activate: function (event) {\n            window.location.href = this.active.find('> a').attr('href');\n            this.collapseAll(event);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _keydown: function (event) {\n            var match, prev, character, skip, regex,\n                preventDefault = true;\n\n            /* eslint-disable max-depth */\n            /**\n             * @param {String} value\n             */\n            function escape(value) {\n                return value.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, '\\\\$&');\n            }\n\n            if (this.active.closest(this.options.menus).attr('aria-expanded') != 'true') { //eslint-disable-line eqeqeq\n\n                switch (event.keyCode) {\n                    case $.ui.keyCode.PAGE_UP:\n                        this.previousPage(event);\n                        break;\n\n                    case $.ui.keyCode.PAGE_DOWN:\n                        this.nextPage(event);\n                        break;\n\n                    case $.ui.keyCode.HOME:\n                        this._move('first', 'first', event);\n                        break;\n\n                    case $.ui.keyCode.END:\n                        this._move('last', 'last', event);\n                        break;\n\n                    case $.ui.keyCode.UP:\n                        this.previous(event);\n                        break;\n\n                    case $.ui.keyCode.DOWN:\n                        if (this.active && !this.active.is('.ui-state-disabled')) {\n                            this.expand(event);\n                        }\n                        break;\n\n                    case $.ui.keyCode.LEFT:\n                        this.previous(event);\n                        break;\n\n                    case $.ui.keyCode.RIGHT:\n                        this.next(event);\n                        break;\n\n                    case $.ui.keyCode.ENTER:\n                    case $.ui.keyCode.SPACE:\n                        this._activate(event);\n                        break;\n\n                    case $.ui.keyCode.ESCAPE:\n                        this.collapse(event);\n                        break;\n                    default:\n                        preventDefault = false;\n                        prev = this.previousFilter || '';\n                        character = String.fromCharCode(event.keyCode);\n                        skip = false;\n\n                        clearTimeout(this.filterTimer);\n\n                        if (character === prev) {\n                            skip = true;\n                        } else {\n                            character = prev + character;\n                        }\n\n                        regex = new RegExp('^' + escape(character), 'i');\n                        match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                            return regex.test($(this).children('a').text());\n                        });\n                        match = skip && match.index(this.active.next()) !== -1 ?\n                            this.active.nextAll('.ui-menu-item') :\n                            match;\n\n                        // If no matches on the current filter, reset to the last character pressed\n                        // to move down the menu to the first item that starts with that character\n                        if (!match.length) {\n                            character = String.fromCharCode(event.keyCode);\n                            regex = new RegExp('^' + escape(character), 'i');\n                            match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                                return regex.test($(this).children('a').text());\n                            });\n                        }\n\n                        if (match.length) {\n                            this.focus(event, match);\n\n                            if (match.length > 1) {\n                                this.previousFilter = character;\n                                this.filterTimer = this._delay(function () {\n                                    delete this.previousFilter;\n                                }, 1000);\n                            } else {\n                                delete this.previousFilter;\n                            }\n                        } else {\n                            delete this.previousFilter;\n                        }\n                }\n            } else {\n                switch (event.keyCode) {\n                    case $.ui.keyCode.DOWN:\n                        this.next(event);\n                        break;\n\n                    case $.ui.keyCode.UP:\n                        this.previous(event);\n                        break;\n\n                    case $.ui.keyCode.RIGHT:\n                        if (this.active && !this.active.is('.ui-state-disabled')) {\n                            this.expand(event);\n                        }\n                        break;\n\n                    case $.ui.keyCode.ENTER:\n                    case $.ui.keyCode.SPACE:\n                        this._activate(event);\n                        break;\n\n                    case $.ui.keyCode.LEFT:\n                    case $.ui.keyCode.ESCAPE:\n                        this.collapse(event);\n                        break;\n                    default:\n                        preventDefault = false;\n                        prev = this.previousFilter || '';\n                        character = String.fromCharCode(event.keyCode);\n                        skip = false;\n\n                        clearTimeout(this.filterTimer);\n\n                        if (character === prev) {\n                            skip = true;\n                        } else {\n                            character = prev + character;\n                        }\n\n                        regex = new RegExp('^' + escape(character), 'i');\n                        match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                            return regex.test($(this).children('a').text());\n                        });\n                        match = skip && match.index(this.active.next()) !== -1 ?\n                            this.active.nextAll('.ui-menu-item') :\n                            match;\n\n                        // If no matches on the current filter, reset to the last character pressed\n                        // to move down the menu to the first item that starts with that character\n                        if (!match.length) {\n                            character = String.fromCharCode(event.keyCode);\n                            regex = new RegExp('^' + escape(character), 'i');\n                            match = this.activeMenu.children('.ui-menu-item').filter(function () {\n                                return regex.test($(this).children('a').text());\n                            });\n                        }\n\n                        if (match.length) {\n                            this.focus(event, match);\n\n                            if (match.length > 1) {\n                                this.previousFilter = character;\n                                this.filterTimer = this._delay(function () {\n                                    delete this.previousFilter;\n                                }, 1000);\n                            } else {\n                                delete this.previousFilter;\n                            }\n                        } else {\n                            delete this.previousFilter;\n                        }\n                }\n            }\n\n            /* eslint-enable max-depth */\n            if (preventDefault) {\n                event.preventDefault();\n            }\n        },\n\n        /**\n         * @private\n         */\n        _toggleMobileMode: function () {\n            var subMenus;\n\n            $(this.element).off('mouseenter mouseleave');\n            this._on({\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'click .ui-menu-item:has(a)': function (event) {\n                    var target;\n\n                    event.preventDefault();\n                    target = $(event.target).closest('.ui-menu-item');\n                    target.get(0).scrollIntoView();\n\n                    // Open submenu on click\n                    if (target.has('.ui-menu').length) {\n                        this.expand(event);\n                    } else if (!this.element.is(':focus') &&\n                        $(this.document[0].activeElement).closest('.ui-menu').length\n                    ) {\n                        // Redirect focus to the menu\n                        this.element.trigger('focus', [true]);\n\n                        // If the active item is on the top level, let it stay active.\n                        // Otherwise, blur the active item since it is no longer visible.\n                        if (this.active && this.active.parents('.ui-menu').length === 1) { //eslint-disable-line\n                            clearTimeout(this.timer);\n                        }\n                    }\n\n                    if (!target.hasClass('level-top') || !target.has('.ui-menu').length) {\n                        window.location.href = target.find('> a').attr('href');\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'click .ui-menu-item:has(.ui-state-active)': function (event) {\n                    this.collapseAll(event, true);\n                }\n            });\n\n            subMenus = this.element.find('.level-top');\n            $.each(subMenus, $.proxy(function (index, item) {\n                var category = $(item).find('> a span').not('.ui-menu-icon').text(),\n                    categoryUrl = $(item).find('> a').attr('href'),\n                    menu = $(item).find('> .ui-menu');\n\n                this.categoryLink = $('<a>')\n                    .attr('href', categoryUrl)\n                    .text($.mage.__('All %1').replace('%1', category));\n\n                this.categoryParent = $('<li>')\n                    .addClass('ui-menu-item all-category')\n                    .html(this.categoryLink);\n\n                if (menu.find('.all-category').length === 0) {\n                    menu.prepend(this.categoryParent);\n                }\n\n            }, this));\n        },\n\n        /**\n         * @private\n         */\n        _toggleDesktopMode: function () {\n            var categoryParent, html;\n\n            $(this.element).off('click mousedown mouseenter mouseleave');\n            this._on({\n\n                /**\n                 * Prevent focus from sticking to links inside menu after clicking\n                 * them (focus should always stay on UL during navigation).\n                 */\n                'mousedown .ui-menu-item > a': function (event) {\n                    event.preventDefault();\n                },\n\n                /**\n                 * Prevent focus from sticking to links inside menu after clicking\n                 * them (focus should always stay on UL during navigation).\n                 */\n                'click .ui-state-disabled > a': function (event) {\n                    event.preventDefault();\n                },\n\n                /**\n                 * @param {jQuer.Event} event\n                 */\n                'click .ui-menu-item:has(a)': function (event) {\n                    var target = $(event.target).closest('.ui-menu-item');\n\n                    if (!this.mouseHandled && target.not('.ui-state-disabled').length) {\n                        this.select(event);\n\n                        // Only set the mouseHandled flag if the event will bubble, see #9469.\n                        if (!event.isPropagationStopped()) {\n                            this.mouseHandled = true;\n                        }\n\n                        // Open submenu on click\n                        if (target.has('.ui-menu').length) {\n                            this.expand(event);\n                        } else if (!this.element.is(':focus') &&\n                            $(this.document[0].activeElement).closest('.ui-menu').length\n                        ) {\n                            // Redirect focus to the menu\n                            this.element.trigger('focus', [true]);\n\n                            // If the active item is on the top level, let it stay active.\n                            // Otherwise, blur the active item since it is no longer visible.\n                            if (this.active && this.active.parents('.ui-menu').length === 1) { //eslint-disable-line\n                                clearTimeout(this.timer);\n                            }\n                        }\n                    }\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'mouseenter .ui-menu-item': function (event) {\n                    var target = $(event.currentTarget),\n                        submenu = this.options.menus,\n                        ulElement,\n                        ulElementWidth,\n                        width,\n                        targetPageX,\n                        rightBound;\n\n                    if (target.has(submenu)) {\n                        ulElement = target.find(submenu);\n                        ulElementWidth = ulElement.outerWidth(true);\n                        width = target.outerWidth() * 2;\n                        targetPageX = target.offset().left;\n                        rightBound = $(window).width();\n\n                        if (ulElementWidth + width + targetPageX > rightBound) {\n                            ulElement.addClass('submenu-reverse');\n                        }\n\n                        if (targetPageX - ulElementWidth < 0) {\n                            ulElement.removeClass('submenu-reverse');\n                        }\n                    }\n\n                    // Remove ui-state-active class from siblings of the newly focused menu item\n                    // to avoid a jump caused by adjacent elements both having a class with a border\n                    target.siblings().children('.ui-state-active').removeClass('ui-state-active');\n                    this.focus(event, target);\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'mouseleave': function (event) {\n                    this.collapseAll(event, true);\n                },\n\n                /**\n                 * Mouse leave.\n                 */\n                'mouseleave .ui-menu': 'collapseAll'\n            });\n\n            categoryParent = this.element.find('.all-category');\n            html = $('html');\n\n            categoryParent.remove();\n\n            if (html.hasClass('nav-open')) {\n                html.removeClass('nav-open');\n                setTimeout(function () {\n                    html.removeClass('nav-before-open');\n                }, this.options.hideDelay);\n            }\n        },\n\n        /**\n         * @param {*} handler\n         * @param {Number} delay\n         * @return {Number}\n         * @private\n         */\n        _delay: function (handler, delay) {\n            var instance = this,\n\n                /**\n                 * @return {*}\n                 */\n                handlerProxy = function () {\n                    return (typeof handler === 'string' ? instance[handler] : handler).apply(instance, arguments);\n                };\n\n            return setTimeout(handlerProxy, delay || 0);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        expand: function (event) {\n            var newItem = this.active &&\n                this.active\n                    .children('.ui-menu')\n                    .children('.ui-menu-item')\n                    .first();\n\n            if (newItem && newItem.length) {\n                if (newItem.closest('.ui-menu').is(':visible') &&\n                    newItem.closest('.ui-menu').has('.all-categories')\n                ) {\n                    return;\n                }\n\n                // remove the active state class from the siblings\n                this.active.siblings().children('.ui-state-active').removeClass('ui-state-active');\n\n                this._open(newItem.parent());\n\n                // Delay so Firefox will not hide activedescendant change in expanding submenu from AT\n                this._delay(function () {\n                    this.focus(event, newItem);\n                });\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         */\n        select: function (event) {\n            var ui;\n\n            this.active = this.active || $(event.target).closest('.ui-menu-item');\n\n            if (this.active.is('.all-category')) {\n                this.active = $(event.target).closest('.ui-menu-item');\n            }\n            ui = {\n                item: this.active\n            };\n\n            if (!this.active.has('.ui-menu').length) {\n                this.collapseAll(event, true);\n            }\n            this._trigger('select', event, ui);\n        }\n    });\n\n    $.widget('mage.navigation', $.mage.menu, {\n        options: {\n            responsiveAction: 'wrap', //option for responsive handling\n            maxItems: null, //option to set max number of menu items\n            container: '#menu', //container to check against navigation length\n            moreText: $.mage.__('more'),\n            breakpoint: 768\n        },\n\n        /**\n         * @private\n         */\n        _init: function () {\n            var that, responsive;\n\n            this._super();\n\n            that = this;\n            responsive = this.options.responsiveAction;\n\n            this.element\n                .addClass('ui-menu-responsive')\n                .attr('responsive', 'main');\n\n            this.setupMoreMenu();\n            this.setMaxItems();\n\n            //check responsive option\n            if (responsive == 'onResize') { //eslint-disable-line eqeqeq\n                $(window).on('resize', function () {\n                    if ($(window).width() > that.options.breakpoint) {\n                        that._responsive();\n                        $('[responsive=more]').show();\n                    } else {\n                        that.element.children().show();\n                        $('[responsive=more]').hide();\n                    }\n                });\n            } else if (responsive == 'onReload') { //eslint-disable-line eqeqeq\n                this._responsive();\n            }\n        },\n\n        /**\n         * Setup more menu.\n         */\n        setupMoreMenu: function () {\n            var moreListItems = this.element.children().clone(),\n                moreLink = $('<a>' + this.options.moreText + '</a>');\n\n            moreListItems.hide();\n\n            moreLink.attr('href', '#');\n\n            this.moreItemsList = $('<ul>')\n                .append(moreListItems);\n\n            this.moreListContainer = $('<li>')\n                .append(moreLink)\n                .append(this.moreItemsList);\n\n            this.responsiveMenu = $('<ul>')\n                .addClass('ui-menu-more')\n                .attr('responsive', 'more')\n                .append(this.moreListContainer)\n                .menu({\n                    position: {\n                        my: 'right top',\n                        at: 'right bottom'\n                    }\n                })\n                .insertAfter(this.element);\n        },\n\n        /**\n         * @private\n         */\n        _responsive: function () {\n            var container = $(this.options.container),\n                containerSize = container.width(),\n                width = 0,\n                items = this.element.children('li'),\n                more = $('.ui-menu-more > li > ul > li a');\n\n            items = items.map(function () {\n                var item = {};\n\n                item.item = $(this);\n                item.itemSize = $(this).outerWidth();\n\n                return item;\n            });\n\n            $.each(items, function (index) {\n                var itemText = items[index].item\n                    .find('a:first')\n                    .text();\n\n                width += parseInt(items[index].itemSize, null); //eslint-disable-line radix\n\n                if (width < containerSize) {\n                    items[index].item.show();\n\n                    more.each(function () {\n                        var text = $(this).text();\n\n                        if (text === itemText) {\n                            $(this).parent().hide();\n                        }\n                    });\n                } else if (width > containerSize) {\n                    items[index].item.hide();\n\n                    more.each(function () {\n                        var text = $(this).text();\n\n                        if (text === itemText) {\n                            $(this).parent().show();\n                        }\n                    });\n                }\n            });\n        },\n\n        /**\n         * Set max items.\n         */\n        setMaxItems: function () {\n            var items = this.element.children('li'),\n                itemsCount = items.length,\n                maxItems = this.options.maxItems,\n                overflow = itemsCount - maxItems,\n                overflowItems = items.slice(overflow);\n\n            overflowItems.hide();\n\n            overflowItems.each(function () {\n                var itemText = $(this).find('a:first').text();\n\n                $(this).hide();\n\n                $('.ui-menu-more > li > ul > li a').each(function () {\n                    var text = $(this).text();\n\n                    if (text === itemText) {\n                        $(this).parent().show();\n                    }\n                });\n            });\n        }\n    });\n\n    return {\n        menu: $.mage.menu,\n        navigation: $.mage.navigation\n    };\n});\n","mage/smart-keyboard-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    /**\n     * @return {Object}\n     * @constructor\n     */\n    function KeyboardHandler() {\n        var body = $('body'),\n            focusState = false,\n            tabFocusClass = '_keyfocus',\n            productsGrid = '[data-container=\"product-grid\"]',\n            catalogProductsGrid = $(productsGrid),\n            CODE_TAB = 9;\n\n        /**\n         * Handle logic, when onTabKeyPress fired at first.\n         * Then it changes state.\n         */\n        function onFocusInHandler() {\n            focusState = true;\n            body.addClass(tabFocusClass)\n                .off('focusin.keyboardHandler', onFocusInHandler);\n        }\n\n        /**\n         * Handle logic to remove state after onTabKeyPress to normal.\n         */\n        function onClickHandler() {\n            focusState = false;\n            body.removeClass(tabFocusClass)\n                .off('click', onClickHandler);\n        }\n\n        /**\n         * Tab key onKeypress handler. Apply main logic:\n         *  - call differ actions onTabKeyPress and onClick\n         */\n        function smartKeyboardFocus() {\n            $(document).on('keydown keypress', function (event) {\n                if (event.which === CODE_TAB && !focusState) {\n                    body\n                        .on('focusin.keyboardHandler', onFocusInHandler)\n                        .on('click', onClickHandler);\n                }\n            });\n\n            // ARIA support for catalog grid products\n            if (catalogProductsGrid.length) {\n                body.on('focusin.gridProducts', productsGrid, function () {\n                    if (body.hasClass(tabFocusClass)) {\n                        $(this).addClass('active');\n                    }\n                });\n                body.on('focusout.gridProducts', productsGrid, function () {\n                    $(this).removeClass('active');\n                });\n            }\n        }\n\n        /**\n         * Attach smart focus on specific element.\n         * @param {jQuery} element\n         */\n        function handleFocus(element) {\n            element.on('focusin.emulateTabFocus', function () {\n                focusState = true;\n                body.addClass(tabFocusClass);\n                element.off();\n            });\n\n            element.on('focusout.emulateTabFocus', function () {\n                focusState = false;\n                body.removeClass(tabFocusClass);\n                element.off();\n            });\n        }\n\n        return {\n            apply: smartKeyboardFocus,\n            focus: handleFocus\n        };\n    }\n\n    return new KeyboardHandler;\n});\n","mage/validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'moment',\n    'mageUtils',\n    'jquery-ui-modules/widget',\n    'jquery/validate',\n    'mage/translate'\n], function ($, moment, utils) {\n    'use strict';\n\n    var creditCartTypes, rules, showLabel, originValidateDelegate;\n\n    $.extend(true, $, {\n        // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions\n        mage: {\n            /**\n             * Check if string is empty with trim\n             * @param {String} value\n             */\n            isEmpty: function (value) {\n                return value === '' || value === undefined ||\n                    value == null || value.length === 0 || /^\\s+$/.test(value);\n            },\n\n            /**\n             * Check if string is empty no trim\n             * @param {String} value\n             */\n            isEmptyNoTrim: function (value) {\n                return value === '' || value == null || value.length === 0;\n            },\n\n            /**\n             * Checks if {value} is between numbers {from} and {to}\n             * @param {String} value\n             * @param {String} from\n             * @param {String} to\n             * @returns {Boolean}\n             */\n            isBetween: function (value, from, to) {\n                return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&\n                    ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));\n            },\n\n            /**\n             * Parse price string\n             * @param {String} value\n             */\n            parseNumber: function (value) {\n                var isDot, isComa;\n\n                if (typeof value !== 'string') {\n                    return parseFloat(value);\n                }\n                isDot = value.indexOf('.');\n                isComa = value.indexOf(',');\n\n                if (isDot !== -1 && isComa !== -1) {\n                    if (isComa > isDot) {\n                        value = value.replace('.', '').replace(',', '.');\n                    } else {\n                        value = value.replace(',', '');\n                    }\n                } else if (isComa !== -1) {\n                    value = value.replace(',', '.');\n                }\n\n                return parseFloat(value);\n            },\n\n            /**\n             * Removes HTML tags and space characters, numbers and punctuation.\n             *\n             * @param {String} value - Value being stripped.\n             * @return {String}\n             */\n            stripHtml: function (value) {\n                return value.replace(/<.[^<>]*?>/g, ' ').replace(/&nbsp;|&#160;/gi, ' ')\n                    .replace(/[0-9.(),;:!?%#$'\"_+=\\/-]*/g, '');\n            }\n        }\n    });\n\n    /**\n     * @param {String} name\n     * @param {*} method\n     * @param {*} message\n     * @param {*} dontSkip\n     */\n    $.validator.addMethod = function (name, method, message, dontSkip) {\n        $.validator.methods[name] = method;\n        $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];\n\n        if (method.length < 3 || dontSkip) {\n            $.validator.addClassRules(name, $.validator.normalizeRule(name));\n        }\n    };\n\n    /**\n     * Javascript object with credit card types\n     * 0 - regexp for card number\n     * 1 - regexp for cvn\n     * 2 - check or not credit card number trough Luhn algorithm by\n     */\n    creditCartTypes = {\n        'SO': [\n            new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'),\n            new RegExp('^([0-9]{3}|[0-9]{4})?$'),\n            true\n        ],\n        'SM': [\n            new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|' +\n                '(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|' +\n                '(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|' +\n                '(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|' +\n                '(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|' +\n                '(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'),\n            true\n        ],\n        'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],\n        'MC': [\n            new RegExp('^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$'),\n            new RegExp('^[0-9]{3}$'),\n            true\n        ],\n        'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],\n        'DI': [new RegExp('^(6011(0|[2-4]|74|7[7-9]|8[6-9]|9)|6(4[4-9]|5))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n        'JCB': [new RegExp('^35(2[8-9]|[3-8])\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n        'DN': [new RegExp('^(3(0[0-5]|095|6|[8-9]))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n        'UN': [\n            new RegExp('^(622(1(2[6-9]|[3-9])|[3-8]|9([[0-1]|2[0-5]))|62[4-6]|628([2-8]))\\\\d*?$'),\n            new RegExp('^[0-9]{3}$'),\n            true\n        ],\n        'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n        'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\\\d*$'), new RegExp('^[0-9]{3}$'), true]\n    };\n\n    /**\n     * validate credit card number using mod10\n     * @param {String} s\n     * @return {Boolean}\n     */\n    function validateCreditCard(s) {\n        // remove non-numerics\n        var v = '0123456789',\n            w = '',\n            i, j, k, m, c, a, x;\n\n        for (i = 0; i < s.length; i++) {\n            x = s.charAt(i);\n\n            if (v.indexOf(x, 0) !== -1) {\n                w += x;\n            }\n        }\n        // validate number\n        j = w.length / 2;\n        k = Math.floor(j);\n        m = Math.ceil(j) - k;\n        c = 0;\n\n        for (i = 0; i < k; i++) {\n            a = w.charAt(i * 2 + m) * 2;\n            c += a > 9 ? Math.floor(a / 10 + a % 10) : a;\n        }\n\n        for (i = 0; i < k + m; i++) {\n            c += w.charAt(i * 2 + 1 - m) * 1;\n        }\n\n        return c % 10 === 0;\n    }\n\n    /**\n     * validate all table required inputs at once, using single hidden input\n     * @param {String} value\n     * @param {HTMLElement} element\n     *\n     * @return {Boolean}\n     */\n    function tableSingleValidation(value, element) {\n        var empty = $(element).closest('table')\n            .find('input.required-option:visible')\n            .filter(function (i, el) {\n                if ($(el).is('disabled')) {\n                    return $.mage.isEmpty(el.value);\n                }\n            })\n            .length;\n\n        return empty === 0;\n    }\n\n    /**\n     *\n     * @param {float} qty\n     * @param {float} qtyIncrements\n     * @returns {float}\n     */\n    function resolveModulo(qty, qtyIncrements) {\n        var divideEpsilon = 10000,\n            epsilon,\n            remainder;\n\n        while (qtyIncrements < 1) {\n            qty *= 10;\n            qtyIncrements *= 10;\n        }\n\n        epsilon = qtyIncrements / divideEpsilon;\n        remainder = qty % qtyIncrements;\n\n        if (Math.abs(remainder - qtyIncrements) < epsilon ||\n            Math.abs(remainder) < epsilon) {\n            remainder = 0;\n        }\n\n        return remainder;\n    }\n\n    /**\n     * Collection of validation rules including rules from additional-methods.js\n     * @type {Object}\n     */\n    rules = {\n        'max-words': [\n            function (value, element, params) {\n                return this.optional(element) || $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length <= params;\n            },\n            $.mage.__('Please enter {0} words or less.')\n        ],\n        'min-words': [\n            function (value, element, params) {\n                return this.optional(element) || $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length >= params;\n            },\n            $.mage.__('Please enter at least {0} words.')\n        ],\n        'range-words': [\n            function (value, element, params) {\n                return this.optional(element) ||\n                    $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length >= params[0] &&\n                    value.match(/bw+b/g).length < params[1];\n            },\n            $.mage.__('Please enter between {0} and {1} words.')\n        ],\n        'letters-with-basic-punc': [\n            function (value, element) {\n                return this.optional(element) || /^[a-z\\-.,()'\\\"\\s]+$/i.test(value);\n            },\n            $.mage.__('Letters or punctuation only please')\n        ],\n        'alphanumeric': [\n            function (value, element) {\n                return this.optional(element) || /^\\w+$/i.test(value);\n            },\n            $.mage.__('Letters, numbers, spaces or underscores only please')\n        ],\n        'letters-only': [\n            function (value, element) {\n                return this.optional(element) || /^[a-z]+$/i.test(value);\n            },\n            $.mage.__('Letters only please')\n        ],\n        'no-whitespace': [\n            function (value, element) {\n                return this.optional(element) || /^\\S+$/i.test(value);\n            },\n            $.mage.__('No white space please')\n        ],\n        'no-marginal-whitespace': [\n            function (value, element) {\n                return this.optional(element) || !/^\\s+|\\s+$/i.test(value);\n            },\n            $.mage.__('No marginal white space please')\n        ],\n        'zip-range': [\n            function (value, element) {\n                return this.optional(element) || /^90[2-5]-\\d{2}-\\d{4}$/.test(value);\n            },\n            $.mage.__('Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx')\n        ],\n        'integer': [\n            function (value, element) {\n                return this.optional(element) || /^-?\\d+$/.test(value);\n            },\n            $.mage.__('A positive or negative non-decimal number please')\n        ],\n        'vinUS': [\n            function (v) {\n                var i, n, d, f, cd, cdv, LL, VL, FL, rs;\n\n                /* eslint-disable max-depth */\n                if (v.length !== 17) {\n                    return false;\n                }\n\n                LL = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L',\n                    'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];\n                VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];\n                FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];\n                rs = 0;\n\n                for (i = 0; i < 17; i++) {\n                    f = FL[i];\n                    d = v.slice(i, i + 1);\n\n                    if (i === 8) {\n                        cdv = d;\n                    }\n\n                    if (!isNaN(d)) {\n                        d *= f;\n                    } else {\n                        for (n = 0; n < LL.length; n++) {\n                            if (d.toUpperCase() === LL[n]) {\n                                d = VL[n];\n                                d *= f;\n\n                                if (isNaN(cdv) && n === 8) {\n                                    cdv = LL[n];\n                                }\n                                break;\n                            }\n                        }\n                    }\n                    rs += d;\n                }\n\n                /* eslint-enable max-depth */\n                cd = rs % 11;\n\n                if (cd === 10) {\n                    cd = 'X';\n                }\n\n                if (cd === cdv) {\n                    return true;\n                }\n\n                return false;\n            },\n            $.mage.__('The specified vehicle identification number (VIN) is invalid.')\n        ],\n        'dateITA': [\n            function (value, element) {\n                var check = false,\n                    re = /^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/,\n                    adata, gg, mm, aaaa, xdata;\n\n                if (re.test(value)) {\n                    adata = value.split('/');\n                    gg = parseInt(adata[0], 10);\n                    mm = parseInt(adata[1], 10);\n                    aaaa = parseInt(adata[2], 10);\n                    xdata = new Date(aaaa, mm - 1, gg);\n\n                    if (xdata.getFullYear() === aaaa &&\n                        xdata.getMonth() === mm - 1 &&\n                        xdata.getDate() === gg\n                    ) {\n                        check = true;\n                    } else {\n                        check = false;\n                    }\n                } else {\n                    check = false;\n                }\n\n                return this.optional(element) || check;\n            },\n            $.mage.__('Please enter a correct date')\n        ],\n        'dateNL': [\n            function (value, element) {\n                return this.optional(element) || /^\\d\\d?[\\.\\/-]\\d\\d?[\\.\\/-]\\d\\d\\d?\\d?$/.test(value);\n            },\n            'Vul hier een geldige datum in.'\n        ],\n        'time': [\n            function (value, element) {\n                return this.optional(element) || /^([01]\\d|2[0-3])(:[0-5]\\d){0,2}$/.test(value);\n            },\n            $.mage.__('Please enter a valid time, between 00:00 and 23:59')\n        ],\n        'time12h': [\n            function (value, element) {\n                return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\\d){0,2}(\\s[AP]M))$/i.test(value);\n            },\n            $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm')\n        ],\n        'phoneUS': [\n            function (phoneNumber, element) {\n                phoneNumber = phoneNumber.replace(/\\s+/g, '');\n\n                return this.optional(element) || phoneNumber.length > 9 &&\n                    phoneNumber.match(/^(1-?)?(\\([2-9]\\d{2}\\)|[2-9]\\d{2})-?[2-9]\\d{2}-?\\d{4}$/);\n            },\n            $.mage.__('Please specify a valid phone number')\n        ],\n        'phoneUK': [\n            function (phoneNumber, element) {\n                return this.optional(element) || phoneNumber.length > 9 &&\n                    phoneNumber.match(/^(\\(?(0|\\+44)[1-9]{1}\\d{1,4}?\\)?\\s?\\d{3,4}\\s?\\d{3,4})$/);\n            },\n            $.mage.__('Please specify a valid phone number')\n        ],\n        'mobileUK': [\n            function (phoneNumber, element) {\n                return this.optional(element) || phoneNumber.length > 9 &&\n                    phoneNumber.match(/^((0|\\+44)7\\d{3}\\s?\\d{6})$/);\n            },\n            $.mage.__('Please specify a valid mobile number')\n        ],\n        'stripped-min-length': [\n            function (value, element, param) {\n                return value.length >= param;\n            },\n            $.mage.__('Please enter at least {0} characters')\n        ],\n\n        /* detect chars that would require more than 3 bytes */\n        'validate-no-utf8mb4-characters': [\n            function (value) {\n                var validator = this,\n                    message = $.mage.__('Please remove invalid characters: {0}.'),\n                    matches = value.match(/(?:[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])/g),\n                    result = matches === null;\n\n                if (!result) {\n                    validator.charErrorMessage = message.replace('{0}', matches.join());\n                }\n\n                return result;\n            }, function () {\n                return this.charErrorMessage;\n            }\n        ],\n\n        /* eslint-disable max-len */\n        'email2': [\n            function (value, element) {\n                return this.optional(element) ||\n                    /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)*(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$/i.test(value);\n            },\n            $.validator.messages.email\n        ],\n        'url2': [\n            function (value, element) {\n                return this.optional(element) || /^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)*(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(\\#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i.test(value);\n            },\n            $.validator.messages.url\n        ],\n\n        /* eslint-enable max-len */\n        'credit-card-types': [\n            function (value, element, param) {\n                var validTypes;\n\n                if (/[^0-9-]+/.test(value)) {\n                    return false;\n                }\n                value = value.replace(/\\D/g, '');\n\n                validTypes = 0x0000;\n\n                if (param.mastercard) {\n                    validTypes |= 0x0001;\n                }\n\n                if (param.visa) {\n                    validTypes |= 0x0002;\n                }\n\n                if (param.amex) {\n                    validTypes |= 0x0004;\n                }\n\n                if (param.dinersclub) {\n                    validTypes |= 0x0008;\n                }\n\n                if (param.enroute) {\n                    validTypes |= 0x0010;\n                }\n\n                if (param.discover) {\n                    validTypes |= 0x0020;\n                }\n\n                if (param.jcb) {\n                    validTypes |= 0x0040;\n                }\n\n                if (param.unknown) {\n                    validTypes |= 0x0080;\n                }\n\n                if (param.all) {\n                    validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;\n                }\n\n                if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard\n                    return value.length === 16;\n                }\n\n                if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa\n                    return value.length === 16;\n                }\n\n                if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex\n                    return value.length === 15;\n                }\n\n                if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub\n                    return value.length === 14;\n                }\n\n                if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute\n                    return value.length === 15;\n                }\n\n                if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover\n                    return value.length === 16;\n                }\n\n                if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb\n                    return value.length === 16;\n                }\n\n                if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb\n                    return value.length === 15;\n                }\n\n                if (validTypes & 0x0080) { //unknown\n                    return true;\n                }\n\n                return false;\n            },\n            $.mage.__('Please enter a valid credit card number.')\n        ],\n\n        /* eslint-disable max-len */\n        'ipv4': [\n            function (value, element) {\n                return this.optional(element) ||\n                    /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);\n            },\n            $.mage.__('Please enter a valid IP v4 address.')\n        ],\n        'ipv6': [\n            function (value, element) {\n                return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);\n            },\n            $.mage.__('Please enter a valid IP v6 address.')\n        ],\n\n        /* eslint-enable max-len */\n        'pattern': [\n            function (value, element, param) {\n                return this.optional(element) || new RegExp(param).test(value);\n            },\n            $.mage.__('Invalid format.')\n        ],\n        'allow-container-className': [\n            function (element) {\n                if (element.type === 'radio' || element.type === 'checkbox') {\n                    return $(element).hasClass('change-container-classname');\n                }\n            },\n            ''\n        ],\n        'validate-no-html-tags': [\n            function (value) {\n                return !/<(\\/)?\\w+/.test(value);\n            },\n            $.mage.__('HTML tags are not allowed.')\n        ],\n        'validate-select': [\n            function (value) {\n                return value !== 'none' && value != null && value.length !== 0;\n            },\n            $.mage.__('Please select an option.')\n        ],\n        'validate-no-empty': [\n            function (value) {\n                return !$.mage.isEmpty(value);\n            },\n            $.mage.__('Empty Value.')\n        ],\n        'validate-alphanum-with-spaces': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);\n            },\n            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.')\n        ],\n        'validate-data': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);\n            },\n            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len\n        ],\n        'validate-street': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[ \\w]{3,}([A-Za-z]\\.)?([ \\w]*\\#\\d+)?(\\r\\n| )[ \\w]{3,}/.test(v);\n            },\n            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), spaces and \"#\" in this field.')\n        ],\n        'validate-phoneStrict': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(v);\n            },\n            $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n        ],\n        'validate-phoneLax': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) ||\n                    /^((\\d[\\-. ]?)?((\\(\\d{3}\\))|\\d{3}))?[\\-. ]?\\d{3}[\\-. ]?\\d{4}$/.test(v);\n            },\n            $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n        ],\n        'validate-fax': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(v);\n            },\n            $.mage.__('Please enter a valid fax number (Ex: 123-456-7890).')\n        ],\n        'validate-email': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*@([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*\\.(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]){2,})$/i.test(v); //eslint-disable-line max-len\n            },\n            $.mage.__('Please enter a valid email address (Ex: [email protected]).')\n        ],\n        'validate-emailSender': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[\\S ]+$/.test(v);\n            },\n            $.mage.__('Please enter a valid email address (Ex: [email protected]).')\n        ],\n        'validate-password': [\n            function (v) {\n                var pass;\n\n                if (v == null) {\n                    return false;\n                }\n                //strip leading and trailing spaces\n                pass = v.trim();\n\n                if (!pass.length) {\n                    return true;\n                }\n\n                return !(pass.length > 0 && pass.length < 6);\n            },\n            $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')\n        ],\n        'validate-admin-password': [\n            function (v) {\n                var pass;\n\n                if (v == null) {\n                    return false;\n                }\n                pass = v.trim();\n                // strip leading and trailing spaces\n                if (pass.length === 0) {\n                    return true;\n                }\n\n                if (!/[a-z]/i.test(v) || !/[0-9]/.test(v)) {\n                    return false;\n                }\n\n                if (pass.length < 7) {\n                    return false;\n                }\n\n                return true;\n            },\n            $.mage.__('Please enter 7 or more characters, using both numeric and alphabetic.')\n        ],\n        'validate-customer-password': [\n            function (v, elm) {\n                var validator = this,\n                    counter = 0,\n                    passwordMinLength = $(elm).data('password-min-length'),\n                    passwordMinCharacterSets = $(elm).data('password-min-character-sets'),\n                    pass = v.trim(),\n                    result = pass.length >= passwordMinLength;\n\n                if (result === false) {\n                    validator.passwordErrorMessage = $.mage.__('Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.').replace('%1', passwordMinLength); //eslint-disable-line max-len\n\n                    return result;\n                }\n\n                if (pass.match(/\\d+/)) {\n                    counter++;\n                }\n\n                if (pass.match(/[a-z]+/)) {\n                    counter++;\n                }\n\n                if (pass.match(/[A-Z]+/)) {\n                    counter++;\n                }\n\n                if (pass.match(/[^a-zA-Z0-9]+/)) {\n                    counter++;\n                }\n\n                if (counter < passwordMinCharacterSets) {\n                    result = false;\n                    validator.passwordErrorMessage = $.mage.__('Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.').replace('%1', passwordMinCharacterSets); //eslint-disable-line max-len\n                }\n\n                return result;\n            }, function () {\n                return this.passwordErrorMessage;\n            }\n        ],\n        'validate-url': [\n            function (v) {\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n                v = (v || '').replace(/^\\s+/, '').replace(/\\s+$/, '');\n\n                return (/^(http|https|ftp):\\/\\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\\d+))?(\\/[A-Z0-9~](([A-Z0-9_~-]|\\.)*[A-Z0-9~]|))*\\/?(.*)?$/i).test(v); //eslint-disable-line max-len\n\n            },\n            $.mage.__('Please enter a valid URL. Protocol is required (http://, https:// or ftp://).')\n        ],\n        'validate-clean-url': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\\/\\/(([A-Z0-9][A-Z0-9_-]*)(\\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\\d+))?\\/?/i.test(v) || /^(www)((\\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\\d+))?\\/?/i.test(v); //eslint-disable-line max-len\n\n            },\n            $.mage.__('Please enter a valid URL. For example http://www.example.com or www.example.com.')\n        ],\n        'validate-xml-identifier': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\\/-]*$/i.test(v);\n\n            },\n            $.mage.__('Please enter a valid XML-identifier (Ex: something_1, block5, id-4).')\n        ],\n        'validate-ssn': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^\\d{3}-?\\d{2}-?\\d{4}$/.test(v);\n\n            },\n            $.mage.__('Please enter a valid social security number (Ex: 123-45-6789).')\n        ],\n        'validate-zip-us': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /(^\\d{5}$)|(^\\d{5}-\\d{4}$)/.test(v);\n\n            },\n            $.mage.__('Please enter a valid zip code (Ex: 90602 or 90602-1234).')\n        ],\n        'validate-date-au': [\n            function (v) {\n                var regex, d;\n\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n                regex = /^(\\d{2})\\/(\\d{2})\\/(\\d{4})$/;\n\n                if ($.mage.isEmpty(v) || !regex.test(v)) {\n                    return false;\n                }\n                d = new Date(v.replace(regex, '$2/$1/$3'));\n\n                return parseInt(RegExp.$2, 10) === 1 + d.getMonth() &&\n                    parseInt(RegExp.$1, 10) === d.getDate() &&\n                    parseInt(RegExp.$3, 10) === d.getFullYear();\n\n            },\n            $.mage.__('Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.')\n        ],\n        'validate-currency-dollar': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^\\$?\\-?([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}\\d*(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$/.test(v); //eslint-disable-line max-len\n\n            },\n            $.mage.__('Please enter a valid $ amount. For example $100.00.')\n        ],\n        'validate-not-negative-number': [\n            function (v) {\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n                v = $.mage.parseNumber(v);\n\n                return !isNaN(v) && v >= 0;\n\n            },\n            $.mage.__('Please enter a number 0 or greater in this field.')\n        ],\n        // validate-not-negative-number should be replaced in all places with this one and then removed\n        'validate-zero-or-greater': [\n            function (v) {\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n                v = $.mage.parseNumber(v);\n\n                return !isNaN(v) && v >= 0;\n\n            },\n            $.mage.__('Please enter a number 0 or greater in this field.')\n        ],\n        'validate-greater-than-zero': [\n            function (v) {\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n                v = $.mage.parseNumber(v);\n\n                return !isNaN(v) && v > 0;\n            },\n            $.mage.__('Please enter a number greater than 0 in this field.')\n        ],\n        'validate-css-length': [\n            function (v) {\n                if (v !== '') {\n                    return (/^[0-9]*\\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);\n                }\n\n                return true;\n            },\n            $.mage.__('Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).')\n        ],\n        // Additional methods\n        'validate-number': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || !isNaN($.mage.parseNumber(v)) && /^\\s*-?\\d*(\\.\\d*)?\\s*$/.test(v);\n            },\n            $.mage.__('Please enter a valid number in this field.')\n        ],\n        'required-number': [\n            function (v) {\n                return !!v.length;\n            },\n            $.mage.__('Please enter a valid number in this field.')\n        ],\n        'validate-number-range': [\n            function (v, elm, param) {\n                var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;\n\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n\n                numValue = $.mage.parseNumber(v);\n\n                if (isNaN(numValue)) {\n                    return false;\n                }\n\n                dataAttrRange = /^(-?[\\d.,]+)?-(-?[\\d.,]+)?$/;\n                classNameRange = /^number-range-(-?[\\d.,]+)?-(-?[\\d.,]+)?$/;\n                result = true;\n                range = param;\n\n                if (typeof range === 'string') {\n                    m = dataAttrRange.exec(range);\n\n                    if (m) {\n                        result = result && $.mage.isBetween(numValue, m[1], m[2]);\n                    } else {\n                        result = false;\n                    }\n                } else if (elm && elm.className) {\n                    classes = elm.className.split(' ');\n                    ii = classes.length;\n\n                    while (ii--) {\n                        range = classes[ii];\n                        m = classNameRange.exec(range);\n\n                        if (m) { //eslint-disable-line max-depth\n                            result = result && $.mage.isBetween(numValue, m[1], m[2]);\n                            break;\n                        }\n                    }\n                }\n\n                return result;\n            },\n            $.mage.__('The value is not within the specified range.'),\n            true\n        ],\n        'validate-digits': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || !/[^\\d]/.test(v);\n            },\n            $.mage.__('Please enter a valid number in this field.')\n        ],\n        'validate-forbidden-extensions': [\n            function (v, elem) {\n                var forbiddenExtensions = $(elem).attr('data-validation-params'),\n                    forbiddenExtensionsArray = forbiddenExtensions.split(','),\n                    extensionsArray = v.split(','),\n                    result = true;\n\n                this.validateExtensionsMessage = $.mage.__('Forbidden extensions has been used. Avoid usage of ') +\n                    forbiddenExtensions;\n\n                $.each(extensionsArray, function (key, extension) {\n                    if (forbiddenExtensionsArray.indexOf(extension) !== -1) {\n                        result = false;\n                    }\n                });\n\n                return result;\n            }, function () {\n                return this.validateExtensionsMessage;\n            }\n        ],\n        'validate-digits-range': [\n            function (v, elm, param) {\n                var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;\n\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n\n                numValue = $.mage.parseNumber(v);\n\n                if (isNaN(numValue)) {\n                    return false;\n                }\n\n                dataAttrRange = /^(-?\\d+)?-(-?\\d+)?$/;\n                classNameRange = /^digits-range-(-?\\d+)?-(-?\\d+)?$/;\n                result = true;\n                range = param;\n\n                if (typeof range === 'string') {\n                    m = dataAttrRange.exec(range);\n\n                    if (m) {\n                        result = result && $.mage.isBetween(numValue, m[1], m[2]);\n                    } else {\n                        result = false;\n                    }\n                } else if (elm && elm.className) {\n                    classes = elm.className.split(' ');\n                    ii = classes.length;\n\n                    while (ii--) {\n                        range = classes[ii];\n                        m = classNameRange.exec(range);\n\n                        if (m) { //eslint-disable-line max-depth\n                            result = result && $.mage.isBetween(numValue, m[1], m[2]);\n                            break;\n                        }\n                    }\n                }\n\n                return result;\n            },\n            $.mage.__('The value is not within the specified range.'),\n            true\n        ],\n        'validate-range': [\n            function (v, elm) {\n                var minValue, maxValue, ranges, reRange, result, values,\n                    i, name, validRange, minValidRange, maxValidRange;\n\n                if ($.mage.isEmptyNoTrim(v)) {\n                    return true;\n                } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {\n                    minValue = maxValue = $.mage.parseNumber(v);\n                } else {\n                    ranges = /^(-?\\d+)?-(-?\\d+)?$/.exec(v);\n\n                    if (ranges) {\n                        minValue = $.mage.parseNumber(ranges[1]);\n                        maxValue = $.mage.parseNumber(ranges[2]);\n\n                        if (minValue > maxValue) { //eslint-disable-line max-depth\n                            return false;\n                        }\n                    } else {\n                        return false;\n                    }\n                }\n                reRange = /^range-(-?\\d+)?-(-?\\d+)?$/;\n                result = true;\n                values = $(elm).prop('class').split(' ');\n\n                for (i = values.length - 1; i >= 0; i--) {\n                    name = values[i];\n                    validRange = reRange.exec(name);\n\n                    if (validRange) {\n                        minValidRange = $.mage.parseNumber(validRange[1]);\n                        maxValidRange = $.mage.parseNumber(validRange[2]);\n                        result = result &&\n                            (isNaN(minValidRange) || minValue >= minValidRange) &&\n                            (isNaN(maxValidRange) || maxValue <= maxValidRange);\n                    }\n                }\n\n                return result;\n            },\n            $.mage.__('The value is not within the specified range.')\n        ],\n        'validate-alpha': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);\n            },\n            $.mage.__('Please use letters only (a-z or A-Z) in this field.')\n        ],\n        'validate-code': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+[a-zA-Z0-9_]+$/.test(v);\n            },\n            $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len\n        ],\n        'validate-alphanum': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);\n            },\n            $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') //eslint-disable-line max-len\n        ],\n        'validate-not-number-first': [\n            function (value) {\n                return $.mage.isEmptyNoTrim(value) || /^[^0-9-\\.].*$/.test(value.trim());\n            },\n            $.mage.__('First character must be letter.')\n        ],\n        'validate-date': [\n            function (value, params, additionalParams) {\n                var test = moment(value, utils.convertToMomentFormat(additionalParams.dateFormat));\n\n                return $.mage.isEmptyNoTrim(value) || test.isValid();\n            },\n            $.mage.__('Please enter a valid date.')\n\n        ],\n        'validate-date-range': [\n            function (v, elm) {\n                var m = /\\bdate-range-(\\w+)-(\\w+)\\b/.exec(elm.className),\n                    currentYear, normalizedTime, dependentElements;\n\n                if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {\n                    return true;\n                }\n\n                currentYear = new Date().getFullYear() + '';\n\n                /**\n                 * @param {String} vd\n                 * @return {Number}\n                 */\n                normalizedTime = function (vd) {\n                    vd = vd.split(/[.\\/]/);\n\n                    if (vd[2] && vd[2].length < 4) {\n                        vd[2] = currentYear.substr(0, vd[2].length) + vd[2];\n                    }\n\n                    return new Date(vd.join('/')).getTime();\n                };\n\n                dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');\n\n                return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||\n                    normalizedTime(v) <= normalizedTime(dependentElements[0].value);\n            },\n            $.mage.__('Make sure the To Date is later than or the same as the From Date.')\n        ],\n        'validate-cpassword': [\n            function () {\n                var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]),\n                    pass = false,\n                    passwordElements, i, passwordElement;\n\n                if ($('#password')) {\n                    pass = $('#password');\n                }\n                passwordElements = $('.validate-password');\n\n                for (i = 0; i < passwordElements.length; i++) {\n                    passwordElement = $(passwordElements[i]);\n\n                    if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {\n                        pass = passwordElement;\n                    }\n                }\n\n                if ($('.validate-admin-password').length) {\n                    pass = $($('.validate-admin-password')[0]);\n                }\n\n                return pass.val() === conf.val();\n            },\n            $.mage.__('Please make sure your passwords match.')\n        ],\n        'validate-identifier': [\n            function (v) {\n                return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\\/-]+(\\.[a-z0-9_-]+)?$/.test(v);\n            },\n            $.mage.__('Please enter a valid URL Key (Ex: \"example-page\", \"example-page.html\" or \"anotherlevel/example-page\").') //eslint-disable-line max-len\n        ],\n        'validate-zip-international': [\n\n            /*function(v) {\n             // @TODO: Cleanup\n             return Validation.get('IsEmpty').test(v) ||\n             /(^[A-z0-9]{2,10}([\\s]{0,1}|[\\-]{0,1})[A-z0-9]{2,10}$)/.test(v);\n             }*/\n            function () {\n                return true;\n            },\n            $.mage.__('Please enter a valid zip code.')\n        ],\n        'validate-one-required': [\n            function (v, elm) {\n                var p = $(elm).parent(),\n                    options = p.find('input');\n\n                return options.map(function (el) {\n                    return $(el).val();\n                }).length > 0;\n            },\n            $.mage.__('Please select one of the options above.')\n        ],\n        'validate-state': [\n            function (v) {\n                return v !== 0;\n            },\n            $.mage.__('Please select State/Province.')\n        ],\n        'required-file': [\n            function (v, elm) {\n                var result = !$.mage.isEmptyNoTrim(v),\n                    ovId;\n\n                if (!result) {\n                    ovId = $('#' + $(elm).attr('id') + '_value');\n\n                    if (ovId.length > 0) {\n                        result = !$.mage.isEmptyNoTrim(ovId.val());\n                    }\n                }\n\n                return result;\n            },\n            $.mage.__('Please select a file.')\n        ],\n        'validate-ajax-error': [\n            function (v, element) {\n                element = $(element);\n                element.on('change.ajaxError', function () {\n                    element.removeClass('validate-ajax-error');\n                    element.off('change.ajaxError');\n                });\n\n                return !element.hasClass('validate-ajax-error');\n            },\n            ''\n        ],\n        'validate-optional-datetime': [\n            function (v, elm, param) {\n                var dateTimeParts = $('.datetime-picker[id^=\"options_' + param + '\"]'),\n                    hasWithValue = false,\n                    hasWithNoValue = false,\n                    pattern = /day_part$/i,\n                    i;\n\n                for (i = 0; i < dateTimeParts.length; i++) {\n                    if (!pattern.test($(dateTimeParts[i]).attr('id'))) {\n                        if ($(dateTimeParts[i]).val() === 's') { //eslint-disable-line max-depth\n                            hasWithValue = true;\n                        } else {\n                            hasWithNoValue = true;\n                        }\n                    }\n                }\n\n                return hasWithValue ^ hasWithNoValue;\n            },\n            $.mage.__('The field isn\\'t complete.')\n        ],\n        'validate-required-datetime': [\n            function (v, elm, param) {\n                var dateTimeParts = $('.datetime-picker[id^=\"options_' + param + '\"]'),\n                    i;\n\n                for (i = 0; i < dateTimeParts.length; i++) {\n                    if (dateTimeParts[i].value === '') {\n                        return false;\n                    }\n                }\n\n                return true;\n            },\n            $.mage.__('This is a required field.')\n        ],\n        'validate-one-required-by-name': [\n            function (v, elm, selector) {\n                var name = elm.name.replace(/([\\\\\"])/g, '\\\\$1'),\n                    container = this.currentForm;\n\n                selector = selector === true ? 'input[name=\"' + name + '\"]:checked' : selector;\n\n                return !!container.querySelectorAll(selector).length;\n            },\n            $.mage.__('Please select one of the options.')\n        ],\n        'less-than-equals-to': [\n            function (value, element, params) {\n                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {\n                    this.lteToVal = $(params).val();\n\n                    return parseFloat(value) <= parseFloat($(params).val());\n                }\n\n                return true;\n            },\n            function () {\n                var message = $.mage.__('Please enter a value less than or equal to %s.');\n\n                return message.replace('%s', this.lteToVal);\n            }\n        ],\n        'greater-than-equals-to': [\n            function (value, element, params) {\n                if ($.isNumeric($(params).val()) && $.isNumeric(value)) {\n                    this.gteToVal = $(params).val();\n\n                    return parseFloat(value) >= parseFloat($(params).val());\n                }\n\n                return true;\n            },\n            function () {\n                var message = $.mage.__('Please enter a value greater than or equal to %s.');\n\n                return message.replace('%s', this.gteToVal);\n            }\n        ],\n        'validate-emails': [\n            function (value) {\n                var validRegexp, emails, i;\n\n                if ($.mage.isEmpty(value)) {\n                    return true;\n                }\n                validRegexp = /^([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*@([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*\\.(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]){2,})$/i; //eslint-disable-line max-len\n                emails = value.split(/[\\s\\n\\,]+/g);\n\n                for (i = 0; i < emails.length; i++) {\n                    if (!validRegexp.test(emails[i].trim())) {\n                        return false;\n                    }\n                }\n\n                return true;\n            },\n            $.mage.__('Please enter valid email addresses, separated by commas. For example, [email protected], [email protected].') //eslint-disable-line max-len\n        ],\n\n        'validate-cc-type-select': [\n\n            /**\n             * Validate credit card type matches credit card number\n             * @param {*} value - select credit card type\n             * @param {*} element - element contains the select box for credit card types\n             * @param {*} params - selector for credit card number\n             * @return {Boolean}\n             */\n            function (value, element, params) {\n                if (value && params && creditCartTypes[value]) {\n                    return creditCartTypes[value][0].test($(params).val().replace(/\\s+/g, ''));\n                }\n\n                return false;\n            },\n            $.mage.__('Card type does not match credit card number.')\n        ],\n        'validate-cc-number': [\n\n            /**\n             * Validate credit card number based on mod 10.\n             *\n             * @param {*} value - credit card number\n             * @return {Boolean}\n             */\n            function (value) {\n                if (value) {\n                    return validateCreditCard(value);\n                }\n\n                return false;\n            },\n            $.mage.__('Please enter a valid credit card number.')\n        ],\n        'validate-cc-type': [\n\n            /**\n             * Validate credit card number is for the correct credit card type.\n             *\n             * @param {String} value - credit card number\n             * @param {*} element - element contains credit card number\n             * @param {*} params - selector for credit card type\n             * @return {Boolean}\n             */\n            function (value, element, params) {\n                var ccType;\n\n                if (value && params) {\n                    ccType = $(params).val();\n                    value = value.replace(/\\s/g, '').replace(/\\-/g, '');\n\n                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {\n                        return creditCartTypes[ccType][0].test(value);\n                    } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {\n                        return true;\n                    }\n                }\n\n                return false;\n            },\n            $.mage.__('Credit card number does not match credit card type.')\n        ],\n        'validate-cc-exp': [\n\n            /**\n             * Validate credit card expiration date, make sure it's within the year and not before current month.\n             *\n             * @param {*} value - month\n             * @param {*} element - element contains month\n             * @param {*} params - year selector\n             * @return {Boolean}\n             */\n            function (value, element, params) {\n                var isValid = false,\n                    month, year, currentTime, currentMonth, currentYear;\n\n                if (value && params) {\n                    month = value;\n                    year = $(params).val();\n                    currentTime = new Date();\n                    currentMonth = currentTime.getMonth() + 1;\n                    currentYear = currentTime.getFullYear();\n\n                    isValid = !year || year > currentYear || year == currentYear && month >= currentMonth; //eslint-disable-line\n                }\n\n                return isValid;\n            },\n            $.mage.__('Incorrect credit card expiration date.')\n        ],\n        'validate-cc-cvn': [\n\n            /**\n             * Validate credit card cvn based on credit card type.\n             *\n             * @param {*} value - credit card cvn\n             * @param {*} element - element contains credit card cvn\n             * @param {*} params - credit card type selector\n             * @return {*}\n             */\n            function (value, element, params) {\n                var ccType;\n\n                if (value && params) {\n                    ccType = $(params).val();\n\n                    if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {\n                        return creditCartTypes[ccType][1].test(value);\n                    }\n                }\n\n                return false;\n            },\n            $.mage.__('Please enter a valid credit card verification number.')\n        ],\n        'validate-cc-ukss': [\n\n            /**\n             * Validate Switch/Solo/Maestro issue number and start date is filled.\n             *\n             * @param {*} value - input field value\n             * @return {*}\n             */\n            function (value) {\n                return value;\n            },\n            $.mage.__('Please enter issue number or start date for switch/solo card type.')\n        ],\n        'validate-length': [\n            function (v, elm) {\n                var reMax = new RegExp(/^maximum-length-[0-9]+$/),\n                    reMin = new RegExp(/^minimum-length-[0-9]+$/),\n                    validator = this,\n                    result = true,\n                    length = 0;\n\n                $.each(elm.className.split(' '), function (index, name) {\n                    if (name.match(reMax) && result) {\n                        length = name.split('-')[2];\n                        result = v.length <= length;\n                        validator.validateMessage =\n                            $.mage.__('Please enter less or equal than %1 symbols.').replace('%1', length);\n                    }\n\n                    if (name.match(reMin) && result && !$.mage.isEmpty(v)) {\n                        length = name.split('-')[2];\n                        result = v.length >= length;\n                        validator.validateMessage =\n                            $.mage.__('Please enter more or equal than %1 symbols.').replace('%1', length);\n                    }\n                });\n\n                return result;\n            }, function () {\n                return this.validateMessage;\n            }\n        ],\n        'required-entry': [\n            function (value) {\n                return !$.mage.isEmpty(value);\n            }, $.mage.__('This is a required field.')\n        ],\n        'not-negative-amount': [\n            function (v) {\n                if (v.length) {\n                    return (/^\\s*\\d+([,.]\\d+)*\\s*%?\\s*$/).test(v);\n                }\n\n                return true;\n            },\n            $.mage.__('Please enter positive number in this field.')\n        ],\n        'validate-per-page-value-list': [\n            function (v) {\n                var isValid = true,\n                    values = v.split(','),\n                    i;\n\n                if ($.mage.isEmpty(v)) {\n                    return isValid;\n                }\n\n                for (i = 0; i < values.length; i++) {\n                    if (!/^[0-9]+$/.test(values[i])) {\n                        isValid = false;\n                    }\n                }\n\n                return isValid;\n            },\n            $.mage.__('Please enter a valid value, ex: 10,20,30')\n        ],\n        'validate-per-page-value': [\n            function (v, elm) {\n                var values;\n\n                if ($.mage.isEmpty(v)) {\n                    return false;\n                }\n                values = $('#' + elm.id + '_values').val().split(',');\n\n                return values.indexOf(v) !== -1;\n            },\n            $.mage.__('Please enter a valid value from list')\n        ],\n        'validate-new-password': [\n            function (v) {\n                if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {\n                    return false;\n                }\n\n                if ($.mage.isEmpty(v) && v !== '') {\n                    return false;\n                }\n\n                return true;\n            },\n            $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')\n        ],\n        'required-if-not-specified': [\n            function (value, element, params) {\n                var valid = false,\n                    alternate = $(params),\n                    alternateValue;\n\n                if (alternate.length > 0) {\n                    valid = this.check(alternate);\n                    // if valid, it may be blank, so check for that\n                    if (valid) {\n                        alternateValue = alternate.val();\n\n                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line\n                            valid = false;\n                        }\n                    }\n                }\n\n                if (!valid) {\n                    valid = !this.optional(element);\n                }\n\n                return valid;\n            },\n            $.mage.__('This is a required field.')\n        ],\n        'required-if-all-sku-empty-and-file-not-loaded': [\n            function (value, element, params) {\n                var valid = false,\n                    alternate = $(params.specifiedId),\n                    alternateValue;\n\n                if (alternate.length > 0) {\n                    valid = this.check(alternate);\n                    // if valid, it may be blank, so check for that\n                    if (valid) {\n                        alternateValue = alternate.val();\n\n                        if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line\n                            valid = false;\n                        }\n                    }\n                }\n\n                if (!valid) {\n                    valid = !this.optional(element);\n                }\n\n                $('input[' + params.dataSku + '=true]').each(function () {\n                    if ($(this).val() !== '') {\n                        valid = true;\n                    }\n                });\n\n                return valid;\n            },\n            $.mage.__('Please enter valid SKU key.')\n        ],\n        'required-if-specified': [\n            function (value, element, params) {\n                var valid = true,\n                    dependent = $(params),\n                    dependentValue;\n\n                if (dependent.length > 0) {\n                    valid = this.check(dependent);\n                    // if valid, it may be blank, so check for that\n                    if (valid) {\n                        dependentValue = dependent.val();\n                        valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;\n                    }\n                }\n\n                if (valid) {\n                    valid = !this.optional(element);\n                } else {\n                    valid = true; // dependent was not valid, so don't even check\n                }\n\n                return valid;\n            },\n            $.mage.__('This is a required field.')\n        ],\n        'required-number-if-specified': [\n            function (value, element, params) {\n                var valid = true,\n                    dependent = $(params),\n                    depeValue;\n\n                if (dependent.length) {\n                    valid = this.check(dependent);\n\n                    if (valid) {\n                        depeValue = dependent[0].value;\n                        valid = !!(depeValue && depeValue.length);\n                    }\n                }\n\n                return valid ? !!value.length : true;\n            },\n            $.mage.__('Please enter a valid number.')\n        ],\n        'datetime-validation': [\n            function (value, element) {\n                var isValid = true;\n\n                if ($(element).val().length === 0) {\n                    isValid = false;\n                    $(element).addClass('mage-error');\n                }\n\n                return isValid;\n            },\n            $.mage.__('This is required field')\n        ],\n        'required-text-swatch-entry': [\n            tableSingleValidation,\n            $.mage.__('Admin is a required field in each row.')\n        ],\n        'required-visual-swatch-entry': [\n            tableSingleValidation,\n            $.mage.__('Admin is a required field in each row.')\n        ],\n        'required-dropdown-attribute-entry': [\n            tableSingleValidation,\n            $.mage.__('Admin is a required field in each row.')\n        ],\n        'validate-item-quantity': [\n            function (value, element, params) {\n                var validator = this,\n                    result = false,\n                    // obtain values for validation\n                    qty = $.mage.parseNumber(value),\n                    isMinAllowedValid = typeof params.minAllowed === 'undefined' ||\n                        qty >= $.mage.parseNumber(params.minAllowed),\n                    isMaxAllowedValid = typeof params.maxAllowed === 'undefined' ||\n                        qty <= $.mage.parseNumber(params.maxAllowed),\n                    isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' ||\n                        resolveModulo(qty, $.mage.parseNumber(params.qtyIncrements)) === 0.0;\n\n                result = qty > 0;\n\n                if (result === false) {\n                    validator.itemQtyErrorMessage = $.mage.__('Please enter a quantity greater than 0.');//eslint-disable-line max-len\n\n                    return result;\n                }\n\n                result = isMinAllowedValid;\n\n                if (result === false) {\n                    validator.itemQtyErrorMessage = $.mage.__('The fewest you may purchase is %1.').replace('%1', params.minAllowed);//eslint-disable-line max-len\n\n                    return result;\n                }\n\n                result = isMaxAllowedValid;\n\n                if (result === false) {\n                    validator.itemQtyErrorMessage = $.mage.__('The maximum you may purchase is %1.').replace('%1', params.maxAllowed);//eslint-disable-line max-len\n\n                    return result;\n                }\n\n                result = isQtyIncrementsValid;\n\n                if (result === false) {\n                    validator.itemQtyErrorMessage = $.mage.__('You can buy this product only in quantities of %1 at a time.').replace('%1', params.qtyIncrements);//eslint-disable-line max-len\n\n                    return result;\n                }\n\n                return result;\n            }, function () {\n                return this.itemQtyErrorMessage;\n            }\n        ],\n        'password-not-equal-to-user-name': [\n            function (value, element, params) {\n                if (typeof params === 'string') {\n                    return value.toLowerCase() !== params.toLowerCase();\n                }\n\n                return true;\n            },\n            $.mage.__('The password can\\'t be the same as the email address. Create a new password and try again.')\n        ]\n    };\n\n    $.each(rules, function (i, rule) {\n        rule.unshift(i);\n        $.validator.addMethod.apply($.validator, rule);\n    });\n    $.validator.addClassRules({\n        'required-option': {\n            required: true\n        },\n        'required-options-count': {\n            required: true\n        },\n        'validate-both-passwords': {\n            'validate-cpassword': true\n        }\n    });\n    $.validator.messages = $.extend($.validator.messages, {\n        required: $.mage.__('This is a required field.'),\n        remote: $.mage.__('Please fix this field.'),\n        email: $.mage.__('Please enter a valid email address.'),\n        url: $.mage.__('Please enter a valid URL.'),\n        date: $.mage.__('Please enter a valid date.'),\n        dateISO: $.mage.__('Please enter a valid date (ISO).'),\n        number: $.mage.__('Please enter a valid number.'),\n        digits: $.mage.__('Please enter only digits.'),\n        creditcard: $.mage.__('Please enter a valid credit card number.'),\n        equalTo: $.mage.__('Please enter the same value again.'),\n        maxlength: $.validator.format($.mage.__('Please enter no more than {0} characters.')),\n        minlength: $.validator.format($.mage.__('Please enter at least {0} characters.')),\n        rangelength: $.validator.format($.mage.__('Please enter a value between {0} and {1} characters long.')),\n        range: $.validator.format($.mage.__('Please enter a value between {0} and {1}.')),\n        max: $.validator.format($.mage.__('Please enter a value less than or equal to {0}.')),\n        min: $.validator.format($.mage.__('Please enter a value greater than or equal to {0}.'))\n    });\n\n    if ($.metadata) {\n        // Setting the type as html5 to enable data-validate attribute\n        $.metadata.setType('html5');\n    }\n\n    showLabel = $.validator.prototype.showLabel;\n    $.extend(true, $.validator.prototype, {\n        /**\n         * @param {*} element\n         * @param {*} message\n         */\n        showLabel: function (element, message) {\n            var label, elem;\n\n            showLabel.call(this, element, message);\n\n            // ARIA (adding aria-invalid & aria-describedby)\n            label = this.errorsFor(element);\n            elem = $(element);\n\n            if (!label.attr('id')) {\n                label.attr('id', this.idOrName(element) + '-error');\n            }\n            elem.attr('aria-invalid', 'true')\n                .attr('aria-describedby', label.attr('id'));\n        }\n    });\n\n    /**\n     * Validate form field without instantiating validate plug-in.\n     *\n     * @param {Element|String} element - DOM element or selector\n     * @return {Boolean} validation result\n     */\n    $.validator.validateElement = function (element) {\n        var form, validator, valid, classes;\n\n        element = $(element);\n        form = element.get(0).form;\n        validator = form ? $(form).data('validator') : null;\n\n        if (validator) {\n            return validator.element(element.get(0));\n        }\n        valid = true;\n        classes = element.prop('class').split(' ');\n        $.each(classes, $.proxy(function (i, className) {\n            if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {\n                valid = false;\n\n                return valid;\n            }\n        }, this));\n\n        return valid;\n    };\n\n    originValidateDelegate = $.fn.validateDelegate;\n\n    /**\n     * @return {*}\n     */\n    $.fn.validateDelegate = function () {\n        if (!this[0].form) {\n            return this;\n        }\n\n        return originValidateDelegate.apply(this, arguments);\n    };\n\n    /**\n     * Validate single element.\n     *\n     * @param {Element} element\n     * @param {Object} config\n     * @returns {*}\n     */\n    $.validator.validateSingleElement = function (element, config) {\n        var errors = {},\n            valid = true,\n            validateConfig = {\n                errorElement: 'label',\n                ignore: '.ignore-validate',\n                hideError: false\n            },\n            form, validator, classes, elementValue;\n\n        $.extend(validateConfig, config);\n        element = $(element).not(validateConfig.ignore);\n\n        if (!element.length) {\n            return true;\n        }\n\n        form = element.get(0).form;\n        validator = form ? $(form).data('validator') : null;\n\n        if (validator) {\n            return validator.element(element.get(0));\n        }\n\n        classes = element.prop('class').split(' ');\n        validator = element.parent().data('validator') ||\n            $.mage.validation(validateConfig, element.parent()).validate;\n\n        element.removeClass(validator.settings.errorClass);\n        validator.toHide = validator.toShow;\n        validator.hideErrors();\n        validator.toShow = validator.toHide = $([]);\n\n        $.each(classes, $.proxy(function (i, className) {\n            elementValue = element.val();\n\n            if (element.is(':checkbox') || element.is(':radio')) {\n                elementValue = element.is(':checked') || null;\n            }\n\n            if (this.methods[className] && !this.methods[className](elementValue, element.get(0))) {\n                valid = false;\n                errors[element.get(0).name] = this.messages[className];\n                validator.invalid[element.get(0).name] = true;\n\n                if (!validateConfig.hideError) {\n                    validator.showErrors(errors);\n                }\n\n                return valid;\n            }\n        }, this));\n\n        return valid;\n    };\n\n    $.widget('mage.validation', {\n        options: {\n            meta: 'validate',\n            onfocusout: false,\n            onkeyup: false,\n            onclick: false,\n            ignoreTitle: true,\n            errorClass: 'mage-error',\n            errorElement: 'div',\n\n            /**\n             * @param {*} error\n             * @param {*} element\n             */\n            errorPlacement: function (error, element) {\n                var errorPlacement = element,\n                    fieldWrapper;\n\n                // logic for date-picker error placement\n                if (element.hasClass('_has-datepicker')) {\n                    errorPlacement = element.siblings('button');\n                }\n                // logic for field wrapper\n                fieldWrapper = element.closest('.addon');\n\n                if (fieldWrapper.length) {\n                    errorPlacement = fieldWrapper.after(error);\n                }\n                //logic for checkboxes/radio\n                if (element.is(':checkbox') || element.is(':radio')) {\n                    errorPlacement = element.parents('.control').children().last();\n\n                    //fallback if group does not have .control parent\n                    if (!errorPlacement.length) {\n                        errorPlacement = element.siblings('label').last();\n                    }\n                }\n                //logic for control with tooltip\n                if (element.siblings('.tooltip').length) {\n                    errorPlacement = element.siblings('.tooltip');\n                }\n                //logic for select with tooltip in after element\n                if (element.next().find('.tooltip').length) {\n                    errorPlacement = element.next();\n                }\n                errorPlacement.after(error);\n            }\n        },\n\n        /**\n         * Check if form pass validation rules without submit.\n         *\n         * @return boolean\n         */\n        isValid: function () {\n            return this.element.valid();\n        },\n\n        /**\n         * Remove validation error messages\n         */\n        clearError: function () {\n            if (arguments.length) {\n                $.each(arguments, $.proxy(function (index, item) {\n                    this.validate.prepareElement(item);\n                    this.validate.hideErrors();\n                }, this));\n            } else {\n                this.validate.resetForm();\n            }\n        },\n\n        /**\n         * Validation creation.\n         *\n         * @protected\n         */\n        _create: function () {\n            this.validate = this.element.validate(this.options);\n\n            // ARIA (adding aria-required attribute)\n            this.element\n                .find('.field.required')\n                .find('.control')\n                .find('input, select, textarea')\n                .attr('aria-required', 'true');\n\n            this._listenFormValidate();\n        },\n\n        /**\n         * Validation listening.\n         *\n         * @protected\n         */\n        _listenFormValidate: function () {\n            $('form').on('invalid-form.validate', this.listenFormValidateHandler);\n        },\n\n        /**\n         * Handle form validation. Focus on first invalid form field.\n         *\n         * @param {jQuery.Event} event\n         * @param {Object} validation\n         */\n        listenFormValidateHandler: function (event, validation) {\n            var firstActive = $(validation.errorList[0].element || []),\n                lastActive = $(validation.findLastActive() ||\n                    validation.errorList.length && validation.errorList[0].element || []),\n                windowHeight = $(window).height(),\n                parent, successList;\n\n            if (lastActive.is(':hidden')) {\n                parent = lastActive.parent();\n                $('html, body').animate({\n                    scrollTop: parent.offset().top - windowHeight / 2\n                });\n            }\n\n            // ARIA (removing aria attributes if success)\n            successList = validation.successList;\n\n            if (successList.length) {\n                $.each(successList, function () {\n                    $(this)\n                        .removeAttr('aria-describedby')\n                        .removeAttr('aria-invalid');\n                });\n            }\n\n            if (firstActive.length) {\n                $('html, body').stop().animate({\n                    scrollTop: firstActive.parent().offset().top - windowHeight / 2\n                });\n                firstActive.focus();\n            }\n        }\n    });\n\n    return $.mage.validation;\n});\n","mage/redirect-url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.redirectUrl', {\n        options: {\n            event: 'click',\n            url: undefined\n        },\n\n        /**\n         * This method binds elements found in this widget.\n         * @private\n         */\n        _bind: function () {\n            var handlers = {};\n\n            handlers[this.options.event] = '_onEvent';\n            this._on(handlers);\n        },\n\n        /**\n         * This method constructs a new widget.\n         * @private\n         */\n        _create: function () {\n            this._bind();\n        },\n\n        /**\n         * This method set the url for the redirect.\n         * @private\n         */\n        _onEvent: function () {\n            if (this.options.url) {\n                location.href = this.options.url;\n            } else {\n                location.href = this.element.val();\n            }\n        }\n    });\n\n    return $.mage.redirectUrl;\n});\n","mage/collapsible.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget',\n    'jquery-ui-modules/core',\n    'jquery/jquery-storageapi',\n    'mage/mage'\n], function ($) {\n    'use strict';\n\n    var hideProps = {},\n        showProps = {};\n\n    hideProps.height = 'hide';\n    showProps.height = 'show';\n\n    $.widget('mage.collapsible', {\n        options: {\n            active: false,\n            disabled: false,\n            collapsible: true,\n            header: '[data-role=title]',\n            content: '[data-role=content]',\n            trigger: '[data-role=trigger]',\n            closedState: null,\n            openedState: null,\n            disabledState: null,\n            ajaxUrlElement: '[data-ajax=true]',\n            ajaxContent: false,\n            loadingClass: null,\n            saveState: false,\n            animate: false,\n            icons: {\n                activeHeader: null,\n                header: null\n            },\n            collateral: {\n                element: null,\n                openedState: null\n            }\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            this.storage = $.localStorage;\n            this.icons = false;\n\n            if (typeof this.options.icons === 'string') {\n                this.options.icons = JSON.parse(this.options.icons);\n            }\n\n            this._processPanels();\n            this._processState();\n            this._refresh();\n\n            if (this.options.icons.header && this.options.icons.activeHeader) {\n                this._createIcons();\n                this.icons = true;\n            }\n\n            this.element.on('dimensionsChanged', function (e) {\n                if (e.target && e.target.classList.contains('active')) {\n                    this._scrollToTopIfNotVisible();\n                }\n            }.bind(this));\n\n            this._bind('click');\n            this._trigger('created');\n        },\n\n        /**\n         * @private\n         */\n        _refresh: function () {\n            this.trigger.attr('tabIndex', 0);\n\n            if (this.options.active && !this.options.disabled) {\n                if (this.options.openedState) {\n                    this.element.addClass(this.options.openedState);\n                }\n\n                if (this.options.collateral.element && this.options.collateral.openedState) {\n                    $(this.options.collateral.element).addClass(this.options.collateral.openedState);\n                }\n\n                if (this.options.ajaxContent) {\n                    this._loadContent();\n                }\n                // ARIA (updates aria attributes)\n                this.header.attr({\n                    'aria-selected': false\n                });\n            } else if (this.options.disabled) {\n                this.disable();\n            } else {\n                this.content.hide();\n\n                if (this.options.closedState) {\n                    this.element.addClass(this.options.closedState);\n                }\n            }\n        },\n\n        /**\n         * Processing the state:\n         *     If deep linking is used and the anchor is the id of the content or the content contains this id,\n         *     and the collapsible element is a nested one having collapsible parents, in order to see the content,\n         *     all the parents must be expanded.\n         * @private\n         */\n        _processState: function () {\n            var anchor = window.location.hash,\n                isValid = $.mage.isValidSelector(anchor),\n                urlPath = window.location.pathname.replace(/\\./g, ''),\n                state;\n\n            this.stateKey = encodeURIComponent(urlPath + this.element.attr('id'));\n\n            if (isValid &&\n                ($(this.content.find(anchor)).length > 0 || this.content.attr('id') === anchor.replace('#', ''))\n            ) {\n                this.element.parents('[data-collapsible=true]').collapsible('forceActivate');\n\n                if (!this.options.disabled) {\n                    this.options.active = true;\n\n                    if (this.options.saveState) { //eslint-disable-line max-depth\n                        this.storage.set(this.stateKey, true);\n                    }\n                }\n            } else if (this.options.saveState && !this.options.disabled) {\n                state = this.storage.get(this.stateKey);\n\n                if (typeof state === 'undefined' || state === null) {\n                    this.storage.set(this.stateKey, this.options.active);\n                } else if (state === true) {\n                    this.options.active = true;\n                } else if (state === false) {\n                    this.options.active = false;\n                }\n            }\n        },\n\n        /**\n         * @private\n         */\n        _createIcons: function () {\n            var icons = this.options.icons;\n\n            if (icons) {\n                $('<span>')\n                    .addClass(icons.header)\n                    .attr('data-role', 'icons')\n                    .prependTo(this.header);\n\n                if (this.options.active && !this.options.disabled) {\n                    this.header.children('[data-role=icons]')\n                        .removeClass(icons.header)\n                        .addClass(icons.activeHeader);\n                }\n            }\n        },\n\n        /**\n         * @private\n         */\n        _destroyIcons: function () {\n            this.header\n                .children('[data-role=icons]')\n                .remove();\n        },\n\n        /**\n         * @private\n         */\n        _destroy: function () {\n            var options = this.options;\n\n            this.element.removeAttr('data-collapsible');\n\n            this.trigger.removeAttr('tabIndex');\n\n            if (options.openedState) {\n                this.element.removeClass(options.openedState);\n            }\n\n            if (this.options.collateral.element && this.options.collateral.openedState) {\n                $(this.options.collateral.element).removeClass(this.options.collateral.openedState);\n            }\n\n            if (options.closedState) {\n                this.element.removeClass(options.closedState);\n            }\n\n            if (options.disabledState) {\n                this.element.removeClass(options.disabledState);\n            }\n\n            if (this.icons) {\n                this._destroyIcons();\n            }\n        },\n\n        /**\n         * @private\n         */\n        _processPanels: function () {\n            var headers, triggers;\n\n            this.element.attr('data-collapsible', 'true');\n\n            if (typeof this.options.header === 'object') {\n                this.header = this.options.header;\n            } else {\n                headers = this.element.find(this.options.header);\n\n                if (headers.length > 0) {\n                    this.header = headers.eq(0);\n                } else {\n                    this.header = this.element;\n                }\n            }\n\n            if (typeof this.options.content === 'object') {\n                this.content = this.options.content;\n            } else {\n                this.content = this.header.next(this.options.content).eq(0);\n            }\n\n            // ARIA (init aria attributes)\n            if (this.header.attr('id')) {\n                this.content.attr('aria-labelledby', this.header.attr('id'));\n            }\n\n            if (this.content.attr('id')) {\n                this.header.attr('aria-controls', this.content.attr('id'));\n            }\n\n            this.header\n                .attr({\n                    'role': 'tab',\n                    'aria-selected': this.options.active,\n                    'aria-expanded': this.options.active\n                });\n\n            // For collapsible widget only (not tabs or accordion)\n            if (this.header.parent().attr('role') !== 'presentation') {\n                this.header\n                    .parent()\n                    .attr('role', 'tablist');\n            }\n\n            this.content.attr({\n                'role': 'tabpanel',\n                'aria-hidden': !this.options.active\n            });\n\n            if (typeof this.options.trigger === 'object') {\n                this.trigger = this.options.trigger;\n            } else {\n                triggers = this.header.find(this.options.trigger);\n\n                if (triggers.length > 0) {\n                    this.trigger = triggers.eq(0);\n                } else {\n                    this.trigger = this.header;\n                }\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _keydown: function (event) {\n            var keyCode;\n\n            if (event.altKey || event.ctrlKey) {\n                return;\n            }\n\n            keyCode = $.ui.keyCode;\n\n            switch (event.keyCode) {\n                case keyCode.SPACE:\n                case keyCode.ENTER:\n                    this._eventHandler(event);\n                    break;\n            }\n\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _bind: function (event) {\n            var self = this;\n\n            this.events = {\n                keydown: '_keydown'\n            };\n\n            if (event) {\n                $.each(event.split(' '), function (index, eventName) {\n                    self.events[eventName] = '_eventHandler';\n                });\n            }\n            this._off(this.trigger);\n\n            if (!this.options.disabled) {\n                this._on(this.trigger, this.events);\n            }\n        },\n\n        /**\n         * Disable.\n         */\n        disable: function () {\n            this.options.disabled = true;\n            this._off(this.trigger);\n            this.forceDeactivate();\n\n            if (this.options.disabledState) {\n                this.element.addClass(this.options.disabledState);\n            }\n            this.trigger.attr('tabIndex', -1);\n        },\n\n        /**\n         * Enable.\n         */\n        enable: function () {\n            this.options.disabled = false;\n            this._on(this.trigger, this.events);\n            this.forceActivate();\n\n            if (this.options.disabledState) {\n                this.element.removeClass(this.options.disabledState);\n            }\n            this.trigger.attr('tabIndex', 0);\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _eventHandler: function (event) {\n\n            if (this.options.active && this.options.collapsible) {\n                this.deactivate();\n            } else {\n                this.activate();\n\n            }\n            event.preventDefault();\n\n        },\n\n        /**\n         * @param {*} prop\n         * @private\n         */\n        _animate: function (prop) {\n            var duration,\n                easing,\n                animate = this.options.animate;\n\n            if (typeof animate === 'number') {\n                duration = animate;\n            }\n\n            if (typeof animate === 'string') {\n                animate = JSON.parse(animate);\n            }\n            duration = duration || animate.duration;\n            easing = animate.easing;\n            this.content.animate(prop, duration, easing);\n        },\n\n        /**\n         * Deactivate.\n         */\n        deactivate: function () {\n            if (this.options.animate) {\n                this._animate(hideProps);\n            } else {\n                this.content.hide();\n            }\n            this._close();\n        },\n\n        /**\n         * Force deactivate.\n         */\n        forceDeactivate: function () {\n            this.content.hide();\n            this._close();\n\n        },\n\n        /**\n         * @private\n         */\n        _close: function () {\n            this.options.active = false;\n\n            if (this.options.saveState) {\n                this.storage.set(this.stateKey, false);\n            }\n\n            if (this.options.openedState) {\n                this.element.removeClass(this.options.openedState);\n            }\n\n            if (this.options.collateral.element && this.options.collateral.openedState) {\n                $(this.options.collateral.element).removeClass(this.options.collateral.openedState);\n            }\n\n            if (this.options.closedState) {\n                this.element.addClass(this.options.closedState);\n            }\n\n            if (this.icons) {\n                this.header.children('[data-role=icons]')\n                    .removeClass(this.options.icons.activeHeader)\n                    .addClass(this.options.icons.header);\n            }\n\n            // ARIA (updates aria attributes)\n            this.header.attr({\n                'aria-selected': 'false',\n                'aria-expanded': 'false'\n            });\n            this.content.attr({\n                'aria-hidden': 'true'\n            });\n\n            this.element.trigger('dimensionsChanged', {\n                opened: false\n            });\n        },\n\n        /**\n         * Activate.\n         *\n         * @return void;\n         */\n        activate: function () {\n            if (this.options.disabled) {\n                return;\n            }\n\n            if (this.options.animate) {\n                this._animate(showProps);\n            } else {\n                this.content.show();\n            }\n            this._open();\n        },\n\n        /**\n         * Force activate.\n         */\n        forceActivate: function () {\n            if (!this.options.disabled) {\n                this.content.show();\n                this._open();\n            }\n        },\n\n        /**\n         * @private\n         */\n        _open: function () {\n            this.element.trigger('beforeOpen');\n            this.options.active = true;\n\n            if (this.options.ajaxContent) {\n                this._loadContent();\n            }\n\n            if (this.options.saveState) {\n                this.storage.set(this.stateKey, true);\n            }\n\n            if (this.options.openedState) {\n                this.element.addClass(this.options.openedState);\n            }\n\n            if (this.options.collateral.element && this.options.collateral.openedState) {\n                $(this.options.collateral.element).addClass(this.options.collateral.openedState);\n            }\n\n            if (this.options.closedState) {\n                this.element.removeClass(this.options.closedState);\n            }\n\n            if (this.icons) {\n                this.header.children('[data-role=icons]')\n                    .removeClass(this.options.icons.header)\n                    .addClass(this.options.icons.activeHeader);\n            }\n\n            // ARIA (updates aria attributes)\n            this.header.attr({\n                'aria-selected': 'true',\n                'aria-expanded': 'true'\n            });\n            this.content.attr({\n                'aria-hidden': 'false'\n            });\n\n            this.element.trigger('dimensionsChanged', {\n                opened: true\n            });\n        },\n\n        /**\n         * @private\n         */\n        _loadContent: function () {\n            var url = this.element.find(this.options.ajaxUrlElement).attr('href'),\n                that = this;\n\n            if (url) {\n                that.xhr = $.get({\n                    url: url,\n                    dataType: 'html'\n                }, function () {\n                });\n            }\n\n            if (that.xhr && that.xhr.statusText !== 'canceled') {\n                if (that.options.loadingClass) {\n                    that.element.addClass(that.options.loadingClass);\n                }\n                that.content.attr('aria-busy', 'true');\n                that.xhr.done(function (response) {\n                    setTimeout(function () {\n                        that.content.html(response);\n                    }, 1);\n                });\n                that.xhr.always(function (jqXHR, status) {\n                    setTimeout(function () {\n                        if (status === 'abort') {\n                            that.content.stop(false, true);\n                        }\n\n                        if (that.options.loadingClass) {\n                            that.element.removeClass(that.options.loadingClass);\n                        }\n                        that.content.removeAttr('aria-busy');\n\n                        if (jqXHR === that.xhr) {\n                            delete that.xhr;\n                        }\n                    }, 1);\n                });\n            }\n        },\n\n        /**\n         * @private\n         */\n        _scrollToTopIfNotVisible: function () {\n            if (this._isElementOutOfViewport()) {\n                this.header[0].scrollIntoView();\n            }\n        },\n\n        /**\n         * @private\n         * @return {Boolean}\n         */\n        _isElementOutOfViewport: function () {\n            var headerRect = this.header[0].getBoundingClientRect(),\n                contentRect = this.content.get().length ? this.content[0].getBoundingClientRect() : false,\n                headerOut,\n                contentOut;\n\n            headerOut = headerRect.bottom - headerRect.height < 0 ||\n                headerRect.right - headerRect.width < 0 ||\n                headerRect.left + headerRect.width > window.innerWidth ||\n                headerRect.top + headerRect.height > window.innerHeight;\n\n            contentOut = contentRect ? contentRect.bottom - contentRect.height < 0 ||\n                contentRect.right - contentRect.width < 0 ||\n                contentRect.left + contentRect.width > window.innerWidth ||\n                contentRect.top + contentRect.height > window.innerHeight : false;\n\n            return headerOut ? headerOut : contentOut;\n        }\n    });\n\n    return $.mage.collapsible;\n});\n","mage/edit-trigger.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n    'jquery',\n    'mage/template',\n    'jquery-ui-modules/widget'\n], function ($, mageTemplate) {\n    'use strict';\n\n    var editTriggerPrototype;\n\n    $.widget('mage.editTrigger', {\n        options: {\n            img: '',\n            alt: '[TR]',\n            template: '#translate-inline-icon',\n            zIndex: 2000,\n            editSelector: '[data-translate]',\n            delay: 2000,\n            offsetTop: -3,\n            singleElement: true\n        },\n\n        /**\n         * editTriger creation\n         * @protected\n         */\n        _create: function () {\n            this.tmpl = mageTemplate(this.options.template);\n            this._initTrigger();\n            this._bind();\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _getCss: function () {\n            return {\n                position: 'absolute',\n                cursor: 'pointer',\n                display: 'none',\n                'z-index': this.options.zIndex\n            };\n        },\n\n        /**\n         * @param {*} appendTo\n         * @return {*|jQuery}\n         * @private\n         */\n        _createTrigger: function (appendTo) {\n            var tmpl = this.tmpl({\n                data: this.options\n            });\n\n            return $(tmpl)\n                .css(this._getCss())\n                .data('role', 'edit-trigger-element')\n                .appendTo(appendTo);\n        },\n\n        /**\n         * @private\n         */\n        _initTrigger: function () {\n            this.trigger = this._createTrigger($('body'));\n        },\n\n        /**\n         * Bind on mousemove event\n         * @protected\n         */\n        _bind: function () {\n            this.trigger.on('click.' + this.widgetName, $.proxy(this._onClick, this));\n            this.element.on('mousemove.' + this.widgetName, $.proxy(this._onMouseMove, this));\n        },\n\n        /**\n         * Show editTriger\n         */\n        show: function () {\n            if (this.trigger.is(':hidden')) {\n                this.trigger.show();\n            }\n        },\n\n        /**\n         * Hide editTriger\n         */\n        hide: function () {\n            this.currentTarget = null;\n\n            if (this.trigger && this.trigger.is(':visible')) {\n                this.trigger.hide();\n            }\n        },\n\n        /**\n         * Set editTriger position\n         * @protected\n         */\n        _setPosition: function (el) {\n            var offset = el.offset();\n\n            this.trigger.css({\n                top: offset.top + el.outerHeight() + this.options.offsetTop,\n                left: offset.left\n            });\n        },\n\n        /**\n         * Show/hide trigger on mouse move.\n         *\n         * @param {jQuery.Event} e\n         * @protected\n         */\n        _onMouseMove: function (e) {\n            var target = $(e.target),\n                inner = target.find(this.options.editSelector);\n\n            if ($(e.target).is('button') && inner.length) {\n                target = inner;\n            } else if (!target.is(this.trigger) && !target.is(this.options.editSelector)) {\n                target = target.parents(this.options.editSelector).first();\n            }\n\n            if (target.length) {\n                if (!target.is(this.trigger)) {\n                    this._setPosition(target);\n                    this.currentTarget = target;\n                }\n                this.show();\n            } else {\n                this.hide();\n            }\n        },\n\n        /**\n         * Trigger event \"edit\" on element for translate.\n         *\n         * @param {jQuery.Event} e\n         * @protected\n         */\n        _onClick: function (e) {\n            e.preventDefault();\n            e.stopImmediatePropagation();\n            $(this.currentTarget).trigger('edit.' + this.widgetName);\n            this.hide(true);\n        },\n\n        /**\n         * Destroy editTriger\n         */\n        destroy: function () {\n            this.trigger.remove();\n            this.element.off('.' + this.widgetName);\n\n            return $.Widget.prototype.destroy.call(this);\n        }\n    });\n\n    /**\n     * Extention for widget editTrigger - hide trigger with delay\n     */\n    editTriggerPrototype = $.mage.editTrigger.prototype;\n\n    $.widget('mage.editTrigger', $.extend({}, editTriggerPrototype, {\n        /**\n         * Added clear timeout on trigger show\n         */\n        show: function () {\n            editTriggerPrototype.show.apply(this, arguments);\n\n            if (this.options.delay) {\n                this._clearTimer();\n            }\n        },\n\n        /**\n         * Added setTimeout on trigger hide\n         */\n        hide: function (immediate) {\n            if (!immediate && this.options.delay) {\n                if (!this.timer) {\n                    this.timer = setTimeout($.proxy(function () {\n                        editTriggerPrototype.hide.apply(this, arguments);\n                        this._clearTimer();\n                    }, this), this.options.delay);\n                }\n            } else {\n                editTriggerPrototype.hide.apply(this, arguments);\n            }\n        },\n\n        /**\n         * Clear timer\n         * @protected\n         */\n        _clearTimer: function () {\n            if (this.timer) {\n                clearTimeout(this.timer);\n                this.timer = null;\n            }\n        }\n    }));\n\n    return $.mage.editTrigger;\n});\n","mage/url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable strict */\ndefine([], function () {\n    var baseUrl = '';\n\n    return {\n        /**\n         * @param {String} url\n         */\n        setBaseUrl: function (url) {\n            baseUrl = url;\n        },\n\n        /**\n         * @param {String} path\n         * @return {*}\n         */\n        build: function (path) {\n            if (path.indexOf(baseUrl) !== -1) {\n                return path;\n            }\n\n            return baseUrl + path;\n        }\n    };\n});\n","mage/cookies.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/mage',\n    'js-cookie/cookie-wrapper'\n], function ($) {\n    'use strict';\n\n    /**\n     * Helper for cookies manipulation\n     * @returns {CookieHelper}\n     * @constructor\n     */\n    var CookieHelper = function () {\n\n        /**\n         * Cookie default values.\n         * @type {Object}\n         */\n        this.defaults = {\n            expires: null,\n            path: '/',\n            domain: null,\n            secure: false,\n            lifetime: null,\n            samesite: 'lax'\n        };\n\n        /**\n         * Calculate cookie expiration date based on its lifetime.\n         * @param {Object} options - Cookie option values\n         * @return {Date|null} Calculated cookie expiration date or null if no lifetime provided.\n         * @private\n         */\n        function lifetimeToExpires(options, defaults) {\n            var expires,\n                lifetime;\n\n            lifetime = options.lifetime || defaults.lifetime;\n\n            if (lifetime && lifetime > 0) {\n                expires = options.expires || new Date();\n\n                return new Date(expires.getTime() + lifetime * 1000);\n            }\n\n            return null;\n        }\n\n        /**\n         * Set a cookie's value by cookie name based on optional cookie options.\n         * @param {String} name - The name of the cookie.\n         * @param {String} value - The cookie's value.\n         * @param {Object} options - Optional options (e.g. lifetime, expires, path, etc.)\n         */\n        this.set = function (name, value, options) {\n            var expires,\n                path,\n                domain,\n                secure,\n                samesite;\n\n            options = $.extend({}, this.defaults, options || {});\n            expires = lifetimeToExpires(options, this.defaults) || options.expires;\n            path = options.path;\n            domain = options.domain;\n            secure = options.secure;\n            samesite = options.samesite;\n\n            document.cookie = name + '=' + encodeURIComponent(value) +\n                (expires ? '; expires=' + expires.toUTCString() :  '') +\n                (path ? '; path=' + path : '') +\n                (domain ? '; domain=' + domain : '') +\n                (secure ? '; secure' : '') +\n                '; samesite=' + (samesite ? samesite : 'lax');\n        };\n\n        /**\n         * Get a cookie's value by cookie name.\n         * @param {String} name  - The name of the cookie.\n         * @return {(null|String)}\n         */\n        this.get = function (name) {\n            var arg = name + '=',\n                aLength = arg.length,\n                cookie = document.cookie,\n                cLength = cookie.length,\n                i = 0,\n                j = 0;\n\n            while (i < cLength) {\n                j = i + aLength;\n\n                if (cookie.substring(i, j) === arg) {\n                    return this.getCookieVal(j);\n                }\n                i = cookie.indexOf(' ', i) + 1;\n\n                if (i === 0) {\n                    break;\n                }\n            }\n\n            return null;\n        };\n\n        /**\n         * Clear a cookie's value by name.\n         * @param {String} name - The name of the cookie being cleared.\n         */\n        this.clear = function (name) {\n            if (this.get(name)) {\n                this.set(name, '', {\n                    expires: new Date('Jan 01 1970 00:00:01 GMT')\n                });\n            }\n        };\n\n        /**\n         * Return URI decoded cookie component value (e.g. expires, path, etc.) based on a\n         * numeric offset in the document's cookie value.\n         * @param {Number} offset - Offset into the document's cookie value.\n         * @return {String}\n         */\n        this.getCookieVal = function (offset) {\n            var cookie = document.cookie,\n                endstr = cookie.indexOf(';', offset);\n\n            if (endstr === -1) {\n                endstr = cookie.length;\n            }\n\n            return decodeURIComponent(cookie.substring(offset, endstr));\n        };\n\n        return this;\n    };\n\n    $.extend(true, $, {\n        mage: {\n            cookies: new CookieHelper()\n        }\n    });\n\n    return function (pageOptions) {\n        $.extend($.mage.cookies.defaults, pageOptions);\n        $.extend($.cookie.defaults, $.mage.cookies.defaults);\n    };\n});\n","mage/sticky.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.sticky', {\n        options: {\n            /**\n             * Element selector, who's height will be used to restrict the\n             * maximum offsetTop position of the stuck element.\n             * Default uses document body.\n             * @type {String}\n             */\n            container: '',\n\n            /**\n             * Spacing in pixels above the stuck element\n             * @type {Number|Function} Number or Function that will return a Number\n             */\n            spacingTop: 0,\n\n            /**\n             * Allows postponing sticking, until element will go out of the\n             * screen for the number of pixels.\n             * @type {Number|Function} Number or Function that will return a Number\n             */\n            stickAfter: 0,\n\n            /**\n             * CSS class for active sticky state\n             * @type {String}\n             */\n            stickyClass: '_sticky'\n        },\n\n        /**\n         * Retrieve option value\n         * @param  {String} option\n         * @return {*}\n         * @private\n         */\n        _getOptionValue: function (option) {\n            var value = this.options[option] || 0;\n\n            if (typeof value === 'function') {\n                value = this.options[option]();\n            }\n\n            return value;\n        },\n\n        /**\n         * Bind handlers to scroll event\n         * @private\n         */\n        _create: function () {\n            $(window).on({\n                'scroll': $.proxy(this._stick, this),\n                'resize': $.proxy(this.reset, this)\n            });\n\n            this.element.on('dimensionsChanged', $.proxy(this.reset, this));\n\n            this.reset();\n\n            // Application of the workaround for IE11 and Edge\n            this.normalizeIE11AndEdgeScroll();\n        },\n\n        /**\n         * float Block on windowScroll\n         * @private\n         */\n        _stick: function () {\n            var offset,\n                isStatic,\n                stuck,\n                stickAfter;\n\n            isStatic = this.element.css('position') === 'static';\n\n            if (!isStatic && this.element.is(':visible')) {\n                offset = $(document).scrollTop() -\n                    this.parentOffset +\n                    this._getOptionValue('spacingTop');\n\n                offset = Math.max(0, Math.min(offset, this.maxOffset));\n\n                stuck = this.element.hasClass(this.options.stickyClass);\n                stickAfter = this._getOptionValue('stickAfter');\n\n                if (offset && !stuck && offset < stickAfter) {\n                    offset = 0;\n                }\n\n                this.element\n                    .toggleClass(this.options.stickyClass, offset > 0)\n                    .css('top', offset);\n            }\n        },\n\n        /**\n         * Defines maximum offset value of the element.\n         * @private\n         */\n        _calculateDimens: function () {\n            var $parent         = this.element.parent(),\n                topMargin       = parseInt(this.element.css('margin-top'), 10),\n                parentHeight    = $parent.height() - topMargin,\n                height          = this.element.innerHeight(),\n                maxScroll       = document.body.offsetHeight - window.innerHeight;\n\n            if (this.options.container.length > 0) {\n                maxScroll = $(this.options.container).height();\n            }\n\n            this.parentOffset   = $parent.offset().top + topMargin;\n            this.maxOffset      = maxScroll - this.parentOffset;\n\n            if (this.maxOffset + height >= parentHeight) {\n                this.maxOffset = parentHeight - height;\n            }\n\n            return this;\n        },\n\n        /**\n         * Facade method that places sticky element where it should be.\n         */\n        reset: function () {\n            this._calculateDimens()\n                ._stick();\n        },\n\n        /**\n         * Workaround for IE11 and Edge that solves the IE known rendering issue\n         * that prevents sticky element from jumpy movement on scrolling the page.\n         *\n         * Alternatively, undesired jumpy movement can be eliminated by changing the setting in IE:\n         * Settings > Internet options > Advanced tab > inside 'Browsing' item > set 'Use smooth scrolling' to False\n         */\n        normalizeIE11AndEdgeScroll: function () {\n            if (navigator.userAgent.match(/Trident.*rv[ :]*11\\.|Edge\\//)) {\n                document.body.addEventListener('mousewheel', function () {\n                    event.preventDefault();\n                    window.scrollTo(0, window.pageYOffset - event.wheelDelta);\n                });\n            }\n        }\n    });\n\n    return $.mage.sticky;\n});\n","mage/dropdown.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/dialog',\n    'mage/translate'\n], function ($) {\n    'use strict';\n\n    var timer = null;\n\n    /**\n     * Dropdown Widget - this widget is a wrapper for the jQuery UI Dialog\n     */\n    $.widget('mage.dropdownDialog', $.ui.dialog, {\n        options: {\n            triggerEvent: 'click',\n            triggerClass: null,\n            parentClass: null,\n            triggerTarget: null,\n            defaultDialogClass: 'mage-dropdown-dialog',\n            dialogContentClass: null,\n            shadowHinter: null,\n            closeOnMouseLeave: true,\n            closeOnClickOutside: true,\n            minHeight: null,\n            minWidth: null,\n            width: null,\n            modal: false,\n            timeout: null,\n            autoOpen: false,\n            createTitleBar: false,\n            autoPosition: false,\n            autoSize: false,\n            draggable: false,\n            resizable: false,\n            bodyClass: '',\n            buttons: [\n                {\n                    'class': 'action close',\n                    'text': $.mage.__('Close'),\n\n                    /**\n                     * Click action.\n                     */\n                    'click': function () {\n                        $(this).dropdownDialog('close');\n                    }\n                }\n            ]\n        },\n\n        /**\n         * extend default functionality to bind the opener for dropdown\n         * @private\n         */\n        _create: function () {\n            var _self = this;\n\n            this._super();\n            this.uiDialog.addClass(this.options.defaultDialogClass);\n\n            if (_self.options.triggerTarget) {\n                $(_self.options.triggerTarget).on(_self.options.triggerEvent, function (event) {\n                    event.preventDefault();\n                    event.stopPropagation();\n\n                    if (!_self._isOpen) {\n                        $('.' + _self.options.defaultDialogClass + ' > .ui-dialog-content').dropdownDialog('close');\n                        _self.open();\n                    } else {\n                        _self.close(event);\n                    }\n                });\n            }\n\n            if (_self.options.shadowHinter) {\n                _self.hinter = $('<div class=\"' + _self.options.shadowHinter + '\"></div>');\n                _self.element.append(_self.hinter);\n            }\n        },\n\n        /**\n         * Extend default functionality to close the dropdown\n         * with custom delay on mouse out and also to close when clicking outside\n         */\n        open: function () {\n            var _self = this;\n\n            this._super();\n\n            if (_self.options.dialogContentClass) {\n                _self.element.addClass(_self.options.dialogContentClass);\n            }\n\n            if (_self.options.closeOnMouseLeave) {\n\n                this._mouseEnter(_self.uiDialog);\n                this._mouseLeave(_self.uiDialog);\n\n                if (_self.options.triggerTarget) {\n                    this._mouseLeave($(_self.options.triggerTarget));\n                }\n            }\n\n            if (_self.options.closeOnClickOutside) {\n                $('body').on('click.outsideDropdown', function (event) {\n                    if (_self._isOpen && !$(event.target).closest('.ui-dialog').length) {\n                        if (timer) {\n                            clearTimeout(timer);\n                        }\n                        _self.close(event);\n                    }\n                });\n            }\n            // adding the class on the opener and parent element for dropdown\n            if (_self.options.triggerClass) {\n                $(_self.options.triggerTarget).addClass(_self.options.triggerClass);\n            }\n\n            if (_self.options.parentClass) {\n                $(_self.options.appendTo).addClass(_self.options.parentClass);\n            }\n\n            if (_self.options.bodyClass) {\n                $('body').addClass(_self.options.bodyClass);\n            }\n\n            if (_self.options.shadowHinter) {\n                _self._setShadowHinterPosition();\n            }\n        },\n\n        /**\n         * extend default functionality to reset the timer and remove the active class for opener\n         */\n        close: function () {\n            this._super();\n\n            if (this.options.dialogContentClass) {\n                this.element.removeClass(this.options.dialogContentClass);\n            }\n\n            if (this.options.triggerClass) {\n                $(this.options.triggerTarget).removeClass(this.options.triggerClass);\n            }\n\n            if (this.options.parentClass) {\n                $(this.options.appendTo).removeClass(this.options.parentClass);\n            }\n\n            if (this.options.bodyClass) {\n                $('body').removeClass(this.options.bodyClass);\n            }\n\n            if (timer) {\n                clearTimeout(timer);\n            }\n\n            if (this.options.triggerTarget) {\n                $(this.options.triggerTarget).off('mouseleave');\n            }\n            this.uiDialog.off('mouseenter');\n            this.uiDialog.off('mouseleave');\n            $('body').off('click.outsideDropdown');\n        },\n\n        /**\n         * _setShadowHinterPosition\n         * @private\n         */\n        _setShadowHinterPosition: function () {\n            var _self = this,\n                offset;\n\n            offset = _self.options.position.of.offset().left -\n                _self.element.offset().left +\n                _self.options.position.of.outerWidth() / 2;\n            offset = isNaN(offset) ? 0 : Math.floor(offset);\n            _self.hinter.css('left', offset);\n        },\n\n        /**\n         * @private\n         */\n        _position: function () {\n            if (this.options.autoPosition) {\n                this._super();\n            }\n        },\n\n        /**\n         * @private\n         */\n        _createTitlebar: function () {\n            if (this.options.createTitleBar) {\n                this._super();\n            } else {\n                // the title bar close button is referenced\n                // in _focusTabbable function, so to prevent errors it must be declared\n                this.uiDialogTitlebarClose = $('<div></div>');\n            }\n        },\n\n        /**\n         * @private\n         */\n        _size: function () {\n            if (this.options.autoSize) {\n                this._super();\n            }\n        },\n\n        /**\n         * @param {Object} handler\n         * @private\n         */\n        _mouseLeave: function (handler) {\n            var _self = this;\n\n            handler.on('mouseleave', function (event) {\n                event.stopPropagation();\n\n                if (_self._isOpen) {\n                    if (timer) {\n                        clearTimeout(timer);\n                    }\n                    timer = setTimeout(function (e) {\n                        _self.close(e);\n                    }, _self.options.timeout);\n                }\n            });\n        },\n\n        /**\n         * @param {Object} handler\n         * @private\n         */\n        _mouseEnter: function (handler) {\n            handler.on('mouseenter', function (event) {\n                event.stopPropagation();\n\n                if (timer) {\n                    clearTimeout(timer);\n                }\n            });\n        },\n\n        /**\n         * @param {String} key\n         * @param {*} value\n         * @private\n         */\n        _setOption: function (key, value) {\n            this._super(key, value);\n\n            if (key === 'triggerTarget') {\n                this.options.triggerTarget = value;\n            }\n        }\n    });\n\n    return $.mage.dropdownDialog;\n});\n","mage/loader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/template',\n    'jquery-ui-modules/widget',\n    'mage/translate'\n], function ($, mageTemplate) {\n    'use strict';\n\n    $.widget('mage.loader', {\n        loaderStarted: 0,\n        options: {\n            icon: '',\n            texts: {\n                loaderText: $.mage.__('Please wait...'),\n                imgAlt: $.mage.__('Loading...')\n            },\n            template:\n                '<div class=\"loading-mask\" data-role=\"loader\">' +\n                    '<div class=\"loader\">' +\n                        '<img alt=\"<%- data.texts.imgAlt %>\" src=\"<%- data.icon %>\">' +\n                        '<p><%- data.texts.loaderText %></p>' +\n                    '</div>' +\n                '</div>'\n\n        },\n\n        /**\n         * Loader creation\n         * @protected\n         */\n        _create: function () {\n            this._bind();\n        },\n\n        /**\n         * Bind on ajax events\n         * @protected\n         */\n        _bind: function () {\n            this._on({\n                'processStop': 'hide',\n                'processStart': 'show',\n                'show.loader': 'show',\n                'hide.loader': 'hide',\n                'contentUpdated.loader': '_contentUpdated'\n            });\n        },\n\n        /**\n         * Verify loader present after content updated\n         *\n         * This will be cleaned up by the task MAGETWO-11070\n         *\n         * @param {EventObject} e\n         * @private\n         */\n        _contentUpdated: function (e) {\n            this.show(e);\n        },\n\n        /**\n         * Show loader\n         */\n        show: function (e, ctx) {\n            this._render();\n            this.loaderStarted++;\n            this.spinner.show();\n\n            if (ctx) {\n                this.spinner\n                    .css({\n                        width: ctx.outerWidth(),\n                        height: ctx.outerHeight(),\n                        position: 'absolute'\n                    })\n                    .position({\n                        my: 'top left',\n                        at: 'top left',\n                        of: ctx\n                    });\n            }\n\n            return false;\n        },\n\n        /**\n         * Hide loader\n         */\n        hide: function () {\n            if (this.loaderStarted > 0) {\n                this.loaderStarted--;\n\n                if (this.loaderStarted === 0) {\n                    this.spinner.hide();\n                }\n            }\n\n            return false;\n        },\n\n        /**\n         * Render loader\n         * @protected\n         */\n        _render: function () {\n            var html;\n\n            if (!this.spinnerTemplate) {\n                this.spinnerTemplate = mageTemplate(this.options.template);\n\n                html = $(this.spinnerTemplate({\n                    data: this.options\n                }));\n\n                html.prependTo(this.element);\n\n                this.spinner = html;\n            }\n        },\n\n        /**\n         * Destroy loader\n         */\n        _destroy: function () {\n            this.spinner.remove();\n        }\n    });\n\n    /**\n     * This widget takes care of registering the needed loader listeners on the body\n     */\n    $.widget('mage.loaderAjax', {\n        options: {\n            defaultContainer: '[data-container=body]',\n            loadingClass: 'ajax-loading'\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            this._bind();\n            // There should only be one instance of this widget, and it should be attached\n            // to the body only. Having it on the page twice will trigger multiple processStarts.\n            if (window.console && !this.element.is(this.options.defaultContainer) && $.mage.isDevMode(undefined)) {\n                console.warn('This widget is intended to be attached to the body, not below.');\n            }\n        },\n\n        /**\n         * @private\n         */\n        _bind: function () {\n            $(document).on({\n                'ajaxSend': this._onAjaxSend.bind(this),\n                'ajaxComplete': this._onAjaxComplete.bind(this)\n            });\n        },\n\n        /**\n         * @param {Object} loaderContext\n         * @return {*}\n         * @private\n         */\n        _getJqueryObj: function (loaderContext) {\n            var ctx;\n\n            // Check to see if context is jQuery object or not.\n            if (loaderContext) {\n                if (loaderContext.jquery) {\n                    ctx = loaderContext;\n                } else {\n                    ctx = $(loaderContext);\n                }\n            } else {\n                ctx = $('[data-container=\"body\"]');\n            }\n\n            return ctx;\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @param {Object} jqxhr\n         * @param {Object} settings\n         * @private\n         */\n        _onAjaxSend: function (e, jqxhr, settings) {\n            var ctx;\n\n            $(this.options.defaultContainer)\n                .addClass(this.options.loadingClass)\n                .attr({\n                    'aria-busy': true\n                });\n\n            if (settings && settings.showLoader) {\n                ctx = this._getJqueryObj(settings.loaderContext);\n                ctx.trigger('processStart');\n\n                // Check to make sure the loader is there on the page if not report it on the console.\n                // NOTE that this check should be removed before going live. It is just an aid to help\n                // in finding the uses of the loader that maybe broken.\n                if (window.console && !ctx.parents('[data-role=\"loader\"]').length) {\n                    console.warn('Expected to start loader but did not find one in the dom');\n                }\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @param {Object} jqxhr\n         * @param {Object} settings\n         * @private\n         */\n        _onAjaxComplete: function (e, jqxhr, settings) {\n            $(this.options.defaultContainer)\n                .removeClass(this.options.loadingClass)\n                .attr('aria-busy', false);\n\n            if (settings && settings.showLoader) {\n                this._getJqueryObj(settings.loaderContext).trigger('processStop');\n            }\n        }\n\n    });\n\n    return {\n        loader: $.mage.loader,\n        loaderAjax: $.mage.loaderAjax\n    };\n});\n","mage/translate-inline.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/template',\n    'mage/utils/misc',\n    'mage/translate',\n    'jquery-ui-modules/dialog'\n], function ($, mageTemplate, miscUtils) {\n    'use strict';\n\n    $.widget('mage.translateInline', $.ui.dialog, {\n        options: {\n            translateForm: {\n                template: '#translate-form-template',\n                data: {\n                    id: 'translate-inline-form',\n                    message: 'Please refresh the page to see your changes after submitting this form. ' +\n                        'Note: browser cache refresh may be required'\n                }\n            },\n            autoOpen: false,\n            translateArea: null,\n            modal: true,\n            dialogClass: 'popup-window window-translate-inline',\n            width: '75%',\n            title: $.mage.__('Translate'),\n            height: 470,\n            position: {\n                my: 'left top',\n                at: 'center top',\n                of: 'body'\n            },\n            buttons: [{\n                text: $.mage.__('Submit'),\n                'class': 'action-primary',\n\n                /**\n                 * Click\n                 */\n                click: function () {\n                    $(this).translateInline('submit');\n                }\n            },\n            {\n                text: $.mage.__('Close'),\n                'class': 'action-close',\n\n                /**\n                 * Click.\n                 */\n                click: function () {\n                    $(this).translateInline('close');\n                }\n            }],\n\n            /**\n             * Open.\n             */\n            open: function () {\n                var $uiDialog = $(this).closest('.ui-dialog'),\n                    topMargin = $uiDialog.children('.ui-dialog-titlebar').outerHeight() + 45;\n\n                $uiDialog\n                    .addClass('ui-dialog-active')\n                    .css('margin-top', topMargin);\n            },\n\n            /**\n             * Close.\n             */\n            close: function () {\n                $(this).closest('.ui-dialog').removeClass('ui-dialog-active');\n            }\n        },\n\n        /**\n         * Translate Inline creation\n         * @protected\n         */\n        _create: function () {\n            var $translateArea = $(this.options.translateArea);\n\n            if (!$translateArea.length) {\n                $translateArea = $('body');\n            }\n            $translateArea.on('edit.editTrigger', $.proxy(this._onEdit, this));\n\n            this.tmpl = mageTemplate(this.options.translateForm.template);\n\n            this._super();\n        },\n\n        /**\n         * @param {*} templateData\n         * @return {*|jQuery|HTMLElement}\n         * @private\n         */\n        _prepareContent: function (templateData) {\n            var data = $.extend({\n                items: templateData,\n                escape: miscUtils.escape\n            }, this.options.translateForm.data);\n\n            this.data = data;\n\n            return $(this.tmpl({\n                data: data\n            }));\n        },\n\n        /**\n         * Render translation form and open dialog\n         * @param {Object} e - object\n         * @protected\n         */\n        _onEdit: function (e) {\n            this.target = e.target;\n            this.element.html(this._prepareContent($(e.target).data('translate')));\n            this.open(e);\n        },\n\n        /**\n         * Submit.\n         */\n        submit: function () {\n            if (this.formIsSubmitted) {\n                return;\n            }\n            this._formSubmit();\n        },\n\n        /**\n         * Send ajax request on form submit\n         * @protected\n         */\n        _formSubmit: function () {\n            var parameters = $.param({\n                    area: this.options.area\n                }) + '&' + $('#' + this.options.translateForm.data.id).serialize();\n\n            this.formIsSubmitted = true;\n\n            $.ajax({\n                url: this.options.ajaxUrl,\n                type: 'POST',\n                data: parameters,\n                loaderContext: this.element,\n                showLoader: true\n            }).always($.proxy(this._formSubmitComplete, this));\n        },\n\n        /**\n         * @param {Object} response\n         * @private\n         */\n        _formSubmitComplete: function (response) {\n            var responseJSON = response.responseJSON || response;\n\n            this.close();\n            this.formIsSubmitted = false;\n            $.mage.translate.add(responseJSON);\n            this._updatePlaceholder(responseJSON[this.data.items[0].original]);\n        },\n\n        /**\n         * @param {*} newValue\n         * @private\n         */\n        _updatePlaceholder: function (newValue) {\n            var $target = $(this.target),\n                translateObject = $target.data('translate')[0];\n\n            translateObject.shown = newValue;\n            translateObject.translated = newValue;\n            $.mage.translate.add(this.data.items[0].original, newValue);\n\n            $target.html(newValue);\n        },\n\n        /**\n         * Destroy translateInline\n         */\n        destroy: function () {\n            this.element.off('.editTrigger');\n            this._super();\n        }\n    });\n\n    return $.mage.translateInline;\n});\n","mage/mage.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/apply/main'\n], function ($, mage) {\n    'use strict';\n\n    /**\n     * Main namespace for Magento extensions\n     * @type {Object}\n     */\n    $.mage = $.mage || {};\n\n    /**\n     * Plugin mage, initialize components on elements\n     * @param {String} name - Components' path.\n     * @param {Object} config - Components' config.\n     * @returns {JQuery} Chainable.\n     */\n    $.fn.mage = function (name, config) {\n        config = config || {};\n\n        this.each(function (index, el) {\n            mage.applyFor(el, config, name);\n        });\n\n        return this;\n    };\n\n    $.extend($.mage, {\n        /**\n         * Handle all components declared via data attribute\n         * @return {Object} $.mage\n         */\n        init: function () {\n            mage.apply();\n\n            return this;\n        },\n\n        /**\n         * Method handling redirects and page refresh\n         * @param {String} url - redirect URL\n         * @param {(undefined|String)} type - 'assign', 'reload', 'replace'\n         * @param {(undefined|Number)} timeout - timeout in milliseconds before processing the redirect or reload\n         * @param {(undefined|Boolean)} forced - true|false used for 'reload' only\n         */\n        redirect: function (url, type, timeout, forced) {\n            var _redirect;\n\n            forced  = !!forced;\n            timeout = timeout || 0;\n            type    = type || 'assign';\n\n            /**\n             * @private\n             */\n            _redirect = function () {\n                window.location[type](type === 'reload' ? forced : url);\n            };\n\n            timeout ? setTimeout(_redirect, timeout) : _redirect();\n        },\n\n        /**\n         * Checks if provided string is a valid selector.\n         * @param {String} selector - Selector to check.\n         * @returns {Boolean}\n         */\n        isValidSelector: function (selector) {\n            try {\n                document.querySelector(selector);\n\n                return true;\n            } catch (e) {\n                return false;\n            }\n        }\n    });\n\n    /**\n     * Init components inside of dynamically updated elements\n     */\n    $(document).on('contentUpdated', 'body', function () {\n        if (mage) {\n            mage.apply();\n        }\n    });\n\n    return $.mage;\n});\n","mage/common.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'domReady!'\n], function ($) {\n    'use strict';\n\n    /* Form with auto submit feature */\n    $('form[data-auto-submit=\"true\"]').trigger('submit');\n\n    //Add form keys.\n    $(document).on(\n        'submit',\n        'form',\n        function (e) {\n            var formKeyElement,\n                existingFormKeyElement,\n                isKeyPresentInForm,\n                isActionExternal,\n                baseUrl = window.BASE_URL,\n                form = $(e.target),\n                formKey = $('input[name=\"form_key\"]').val(),\n                formMethod = form.prop('method'),\n                formAction = form.prop('action');\n\n            isActionExternal = formAction.indexOf(baseUrl) !== 0;\n\n            existingFormKeyElement = form.find('input[name=\"form_key\"]');\n            isKeyPresentInForm = existingFormKeyElement.length;\n\n            /* Verifies that existing auto-added form key is a direct form child element,\n               protection from a case when one form contains another form. */\n            if (isKeyPresentInForm && existingFormKeyElement.attr('auto-added-form-key') === '1') {\n                isKeyPresentInForm = form.find('> input[name=\"form_key\"]').length;\n            }\n\n            if (formKey && !isKeyPresentInForm && !isActionExternal && formMethod !== 'get') {\n                formKeyElement = document.createElement('input');\n                formKeyElement.setAttribute('type', 'hidden');\n                formKeyElement.setAttribute('name', 'form_key');\n                formKeyElement.setAttribute('value', formKey);\n                formKeyElement.setAttribute('auto-added-form-key', '1');\n                form.get(0).appendChild(formKeyElement);\n            }\n        }\n    );\n});\n","mage/touch-slider.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'underscore',\n    'jquery-ui-modules/slider'\n], function ($, _) {\n    'use strict';\n\n    /**\n     * Adds support for touch events for regular jQuery UI slider.\n     */\n    $.widget('mage.touchSlider', $.ui.slider, {\n\n        /**\n         * Creates instance of widget.\n         *\n         * @override\n         */\n        _create: function () {\n            _.bindAll(\n                this,\n                '_mouseDown',\n                '_mouseMove',\n                '_onTouchEnd'\n            );\n\n            return this._superApply(arguments);\n        },\n\n        /**\n         * Initializes mouse events on element.\n         * @override\n         */\n        _mouseInit: function () {\n            var result = this._superApply(arguments);\n\n            this.element\n                .off('mousedown.' + this.widgetName)\n                .on('touchstart.' + this.widgetName, this._mouseDown);\n\n            return result;\n        },\n\n        /**\n         * Elements' 'mousedown' event handler polyfill.\n         * @override\n         */\n        _mouseDown: function (event) {\n            var prevDelegate = this._mouseMoveDelegate,\n                result;\n\n            event = this._touchToMouse(event);\n            result = this._super(event);\n\n            if (prevDelegate === this._mouseMoveDelegate) {\n                return result;\n            }\n\n            $(document)\n                .off('mousemove.' + this.widgetName)\n                .off('mouseup.' + this.widgetName);\n\n            $(document)\n                .on('touchmove.' + this.widgetName, this._mouseMove)\n                .on('touchend.' + this.widgetName, this._onTouchEnd)\n                .on('tochleave.' + this.widgetName, this._onTouchEnd);\n\n            return result;\n        },\n\n        /**\n         * Documents' 'mousemove' event handler polyfill.\n         *\n         * @override\n         * @param {Event} event - Touch event object.\n         */\n        _mouseMove: function (event) {\n            event = this._touchToMouse(event);\n\n            return this._super(event);\n        },\n\n        /**\n         * Documents' 'touchend' event handler.\n         */\n        _onTouchEnd: function (event) {\n            $(document).trigger('mouseup');\n\n            return this._mouseUp(event);\n        },\n\n        /**\n         * Removes previously assigned touch handlers.\n         *\n         * @override\n         */\n        _mouseUp: function () {\n            this._removeTouchHandlers();\n\n            return this._superApply(arguments);\n        },\n\n        /**\n         * Removes previously assigned touch handlers.\n         *\n         * @override\n         */\n        _mouseDestroy: function () {\n            this._removeTouchHandlers();\n\n            return this._superApply(arguments);\n        },\n\n        /**\n         * Removes touch events from document object.\n         */\n        _removeTouchHandlers: function () {\n            $(document)\n                .off('touchmove.' + this.widgetName)\n                .off('touchend.' + this.widgetName)\n                .off('touchleave.' + this.widgetName);\n        },\n\n        /**\n         * Adds properties to the touch event to mimic mouse event.\n         *\n         * @param {Event} event - Touch event object.\n         * @returns {Event}\n         */\n        _touchToMouse: function (event) {\n            var orig = event.originalEvent,\n                touch = orig.touches[0];\n\n            return _.extend(event, {\n                which:      1,\n                pageX:      touch.pageX,\n                pageY:      touch.pageY,\n                clientX:    touch.clientX,\n                clientY:    touch.clientY,\n                screenX:    touch.screenX,\n                screenY:    touch.screenY\n            });\n        }\n    });\n\n    return $.mage.touchSlider;\n});\n","mage/calendar.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/*eslint max-depth: 0*/\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget',\n    'jquery-ui-modules/datepicker',\n    'jquery-ui-modules/timepicker'\n], function ($) {\n    'use strict';\n\n    var calendarBasePrototype,\n        datepickerPrototype = $.datepicker.constructor.prototype;\n\n    $.datepicker.markerClassName = '_has-datepicker';\n\n    /**\n     * Extend JQuery date picker prototype with store local time methods\n     */\n    $.extend(datepickerPrototype, {\n        /**\n         * Get date/time according to store settings.\n         * We use serverTimezoneOffset (in seconds) instead of serverTimezoneSeconds\n         * in order to have ability to know actual store time even if page hadn't been reloaded\n         * @returns {Date}\n         */\n        _getTimezoneDate: function (options) {\n            // local time in ms\n            var ms = Date.now();\n\n            options = options || $.calendarConfig || {};\n\n            // Adjust milliseconds according to store timezone offset,\n            // mind the GMT zero offset\n            if (typeof options.serverTimezoneOffset !== 'undefined') {\n                // Make UTC time and add store timezone offset in seconds\n                ms += new Date().getTimezoneOffset() * 60 * 1000 + options.serverTimezoneOffset * 1000;\n            } else if (typeof options.serverTimezoneSeconds !== 'undefined') {\n                //Set milliseconds according to client local timezone offset\n                ms = (options.serverTimezoneSeconds + new Date().getTimezoneOffset() * 60) * 1000;\n            }\n\n            return new Date(ms);\n        },\n\n        /**\n         * Set date/time according to store settings.\n         * @param {String|Object} target - the target input field or division or span\n         */\n        _setTimezoneDateDatepicker: function (target) {\n            this._setDateDatepicker(target, this._getTimezoneDate());\n        }\n    });\n\n    /**\n     * Widget calendar\n     */\n    $.widget('mage.calendar', {\n        options: {\n            autoComplete: true\n        },\n\n        /**\n         * Merge global options with options passed to widget invoke\n         * @protected\n         */\n        _create: function () {\n            this._enableAMPM();\n            this.options = $.extend(\n                {},\n                $.calendarConfig ? $.calendarConfig : {},\n                this.options.showsTime ? {\n                    showTime: true,\n                    showHour: true,\n                    showMinute: true\n                } : {},\n                this.options\n            );\n            this._initPicker(this.element);\n            this._overwriteGenerateHtml();\n        },\n\n        /**\n         * Get picker name\n         * @protected\n         */\n        _picker: function () {\n            return this.options.showsTime ? 'datetimepicker' : 'datepicker';\n        },\n\n        /**\n         * Fix for Timepicker - Set ampm option for Timepicker if timeformat contains string 'tt'\n         * @protected\n         */\n        _enableAMPM: function () {\n            if (this.options.timeFormat && this.options.timeFormat.indexOf('tt') >= 0) {\n                this.options.ampm = true;\n            }\n        },\n\n        /**\n         * Wrapper for overwrite jQuery UI datepicker function.\n         */\n        _overwriteGenerateHtml: function () {\n            /**\n             * Overwrite jQuery UI datepicker function.\n             * Reason: magento date could be set before calendar show\n             * but local date will be styled as current in original _generateHTML\n             *\n             * @param {Object} inst - instance datepicker.\n             * @return {String} html template\n             */\n            $.datepicker.constructor.prototype._generateHTML = function (inst) {\n                var today = this._getTimezoneDate(),\n                    isRTL = this._get(inst, 'isRTL'),\n                    showButtonPanel = this._get(inst, 'showButtonPanel'),\n                    hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext'),\n                    navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat'),\n                    numMonths = this._getNumberOfMonths(inst),\n                    showCurrentAtPos = this._get(inst, 'showCurrentAtPos'),\n                    stepMonths = this._get(inst, 'stepMonths'),\n                    isMultiMonth = parseInt(numMonths[0], 10) !== 1 || parseInt(numMonths[1], 10) !== 1,\n                    currentDate = this._daylightSavingAdjust(!inst.currentDay ? new Date(9999, 9, 9) :\n                        new Date(inst.currentYear, inst.currentMonth, inst.currentDay)),\n                    minDate = this._getMinMaxDate(inst, 'min'),\n                    maxDate = this._getMinMaxDate(inst, 'max'),\n                    drawMonth = inst.drawMonth - showCurrentAtPos,\n                    drawYear = inst.drawYear,\n                    maxDraw,\n                    prevText = this._get(inst, 'prevText'),\n                    prev,\n                    nextText = this._get(inst, 'nextText'),\n                    next,\n                    currentText = this._get(inst, 'currentText'),\n                    gotoDate,\n                    controls,\n                    buttonPanel,\n                    firstDay,\n                    showWeek = this._get(inst, 'showWeek'),\n                    dayNames = this._get(inst, 'dayNames'),\n                    dayNamesMin = this._get(inst, 'dayNamesMin'),\n                    monthNames = this._get(inst, 'monthNames'),\n                    monthNamesShort =  this._get(inst, 'monthNamesShort'),\n                    beforeShowDay = this._get(inst, 'beforeShowDay'),\n                    showOtherMonths = this._get(inst, 'showOtherMonths'),\n                    selectOtherMonths = this._get(inst, 'selectOtherMonths'),\n                    defaultDate = this._getDefaultDate(inst),\n                    html = '',\n                    row = 0,\n                    col = 0,\n                    selectedDate,\n                    cornerClass = ' ui-corner-all',\n                    group = '',\n                    calender = '',\n                    dow = 0,\n                    thead,\n                    day,\n                    daysInMonth,\n                    leadDays,\n                    curRows,\n                    numRows,\n                    printDate,\n                    dRow = 0,\n                    tbody,\n                    daySettings,\n                    otherMonth,\n                    unselectable;\n\n                if (drawMonth < 0) {\n                    drawMonth += 12;\n                    drawYear--;\n                }\n\n                if (maxDate) {\n                    maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),\n                        maxDate.getMonth() - numMonths[0] * numMonths[1] + 1, maxDate.getDate()));\n                    maxDraw = minDate && maxDraw < minDate ? minDate : maxDraw;\n\n                    while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {\n                        drawMonth--;\n\n                        if (drawMonth < 0) {\n                            drawMonth = 11;\n                            drawYear--;\n\n                        }\n                    }\n                }\n                inst.drawMonth = drawMonth;\n                inst.drawYear = drawYear;\n                prevText = !navigationAsDateFormat ? prevText : this.formatDate(prevText,\n                    this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),\n                    this._getFormatConfig(inst));\n                prev = this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?\n                    '<a class=\"ui-datepicker-prev ui-corner-all\" data-handler=\"prev\" data-event=\"click\"' +\n                    ' title=\"' + prevText + '\">' +\n                    '<span class=\"ui-icon ui-icon-circle-triangle-' + (isRTL ? 'e' : 'w') + '\">' +\n                    '' + prevText + '</span></a>'\n                    : hideIfNoPrevNext ? ''\n                        :   '<a class=\"ui-datepicker-prev ui-corner-all ui-state-disabled\" title=\"' +\n                            '' + prevText + '\"><span class=\"ui-icon ui-icon-circle-triangle-' +\n                            '' + (isRTL ? 'e' : 'w') + '\">' + prevText + '</span></a>';\n                nextText = !navigationAsDateFormat ?\n                    nextText\n                    :   this.formatDate(nextText,\n                        this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),\n                        this._getFormatConfig(inst));\n                next = this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?\n                    '<a class=\"ui-datepicker-next ui-corner-all\" data-handler=\"next\" data-event=\"click\"' +\n                    'title=\"' + nextText + '\"><span class=\"ui-icon ui-icon-circle-triangle-' +\n                    '' + (isRTL ? 'w' : 'e') + '\">' + nextText + '</span></a>'\n                    : hideIfNoPrevNext ? ''\n                        :   '<a class=\"ui-datepicker-next ui-corner-all ui-state-disabled\" title=\"' + nextText + '\">' +\n                            '<span class=\"ui-icon ui-icon-circle-triangle-' + (isRTL ? 'w' : 'e') + '\">' + nextText +\n                            '</span></a>';\n                gotoDate = this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today;\n                currentText = !navigationAsDateFormat ? currentText :\n                    this.formatDate(currentText, gotoDate, this._getFormatConfig(inst));\n                controls = !inst.inline ?\n                    '<button type=\"button\" class=\"ui-datepicker-close ui-state-default ui-priority-primary ' +\n                    'ui-corner-all\" data-handler=\"hide\" data-event=\"click\">' +\n                    this._get(inst, 'closeText') + '</button>'\n                    : '';\n                buttonPanel = showButtonPanel ?\n                    '<div class=\"ui-datepicker-buttonpane ui-widget-content\">' + (isRTL ? controls : '') +\n                    (this._isInRange(inst, gotoDate) ? '<button type=\"button\" class=\"ui-datepicker-current ' +\n                    'ui-state-default ui-priority-secondary ui-corner-all\" data-handler=\"today\" data-event=\"click\"' +\n                    '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';\n                firstDay = parseInt(this._get(inst, 'firstDay'), 10);\n                firstDay = isNaN(firstDay) ? 0 : firstDay;\n\n                for (row = 0; row < numMonths[0]; row++) {\n                    this.maxRows = 4;\n\n                    for (col = 0; col < numMonths[1]; col++) {\n                        selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));\n\n                        calender = '';\n\n                        if (isMultiMonth) {\n                            calender += '<div class=\"ui-datepicker-group';\n\n                            if (numMonths[1] > 1) {\n                                switch (col) {\n                                    case 0: calender += ' ui-datepicker-group-first';\n                                        cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left');\n                                        break;\n\n                                    case numMonths[1] - 1: calender += ' ui-datepicker-group-last';\n                                        cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right');\n                                        break;\n\n                                    default: calender += ' ui-datepicker-group-middle'; cornerClass = '';\n                                }\n                            }\n                            calender += '\">';\n                        }\n                        calender += '<div class=\"ui-datepicker-header ' +\n                            'ui-widget-header ui-helper-clearfix' + cornerClass + '\">' +\n                            (/all|left/.test(cornerClass) && parseInt(row, 10) === 0 ? isRTL ? next : prev : '') +\n                            (/all|right/.test(cornerClass) && parseInt(row, 10) === 0 ? isRTL ? prev : next : '') +\n                            this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,\n                            row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers\n                            '</div><table class=\"ui-datepicker-calendar\"><thead>' +\n                            '<tr>';\n                        thead = showWeek ?\n                            '<th class=\"ui-datepicker-week-col\">' + this._get(inst, 'weekHeader') + '</th>' : '';\n\n                        for (dow = 0; dow < 7; dow++) { // days of the week\n                            day = (dow + firstDay) % 7;\n                            thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ?\n                                ' class=\"ui-datepicker-week-end\"' : '') + '>' +\n                                '<span title=\"' + dayNames[day] + '\">' + dayNamesMin[day] + '</span></th>';\n                        }\n                        calender += thead + '</tr></thead><tbody>';\n                        daysInMonth = this._getDaysInMonth(drawYear, drawMonth);\n\n                        if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {\n                            inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);\n                        }\n                        leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;\n                        curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate\n                        numRows = isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows;\n                        this.maxRows = numRows;\n                        printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));\n\n                        for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows\n                            calender += '<tr>';\n                            tbody = !showWeek ? '' : '<td class=\"ui-datepicker-week-col\">' +\n                            this._get(inst, 'calculateWeek')(printDate) + '</td>';\n\n                            for (dow = 0; dow < 7; dow++) { // create date picker days\n                                daySettings = beforeShowDay ?\n                                    beforeShowDay.apply(inst.input ? inst.input[0] : null, [printDate]) : [true, ''];\n                                otherMonth = printDate.getMonth() !== drawMonth;\n                                unselectable = otherMonth && !selectOtherMonths || !daySettings[0] ||\n                                minDate && printDate < minDate || maxDate && printDate > maxDate;\n                                tbody += '<td class=\"' +\n                                ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends\n                                (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months\n                                (printDate.getTime() === selectedDate.getTime() &&\n                                drawMonth === inst.selectedMonth && inst._keyEvent || // user pressed key\n                                defaultDate.getTime() === printDate.getTime() &&\n                                defaultDate.getTime() === selectedDate.getTime() ?\n                                    // or defaultDate is current printedDate and defaultDate is selectedDate\n                                ' ' + this._dayOverClass : '') + // highlight selected day\n                                (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled' : '') +\n                                (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates\n                                (printDate.getTime() === currentDate.getTime() ? ' ' + this._currentClass : '') +\n                                (printDate.getDate() === today.getDate() && printDate.getMonth() === today.getMonth() &&\n                                printDate.getYear() === today.getYear() ? ' ui-datepicker-today' : '')) + '\"' +\n                                ((!otherMonth || showOtherMonths) && daySettings[2] ?\n                                ' title=\"' + daySettings[2] + '\"' : '') + // cell title\n                                (unselectable ? '' : ' data-handler=\"selectDay\" data-event=\"click\" data-month=\"' +\n                                '' + printDate.getMonth() + '\" data-year=\"' + printDate.getFullYear() + '\"') + '>' +\n                                (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months\n                                    unselectable ? '<span class=\"ui-state-default\">' + printDate.getDate() + '</span>'\n                                        : '<a class=\"ui-state-default' +\n                                    (printDate.getTime() === today.getTime() ? ' ' : '') +\n                                    (printDate.getTime() === currentDate.getTime() ? ' ui-state-active' : '') +\n                                    (otherMonth ? ' ui-priority-secondary' : '') +\n                                    '\" data-date=\"' + printDate.getDate() + '\" href=\"#\">' +\n                                        printDate.getDate() + '</a>') + '</td>';\n                                printDate.setDate(printDate.getDate() + 1);\n                                printDate = this._daylightSavingAdjust(printDate);\n                            }\n                            calender += tbody + '</tr>';\n                        }\n                        drawMonth++;\n\n                        if (drawMonth > 11) {\n                            drawMonth = 0;\n                            drawYear++;\n                        }\n                        calender += '</tbody></table>' + (isMultiMonth ? '</div>' +\n                        (numMonths[0] > 0 && col === numMonths[1] - 1 ? '<div class=\"ui-datepicker-row-break\"></div>'\n                            : '') : '');\n                        group += calender;\n                    }\n                    html += group;\n                }\n                html += buttonPanel + ($.ui.ie6 && !inst.inline ?\n                    '<iframe src=\"javascript:false;\" class=\"ui-datepicker-cover\" frameborder=\"0\"></iframe>' : '');\n                inst._keyEvent = false;\n\n                return html;\n            };\n        },\n\n        /**\n         * Set current date if the date is not set\n         * @protected\n         * @param {Object} element\n         */\n        _setCurrentDate: function (element) {\n            if (!element.val()) {\n                element[this._picker()]('setTimezoneDate').val('');\n            }\n        },\n\n        /**\n         * Init Datetimepicker\n         * @protected\n         * @param {Object} element\n         */\n        _initPicker: function (element) {\n            var picker = element[this._picker()](this.options),\n                pickerButtonText = picker.next('.ui-datepicker-trigger')\n                    .find('img')\n                    .attr('title');\n\n            picker.next('.ui-datepicker-trigger')\n                .addClass('v-middle')\n                .text('') // Remove jQuery UI datepicker generated image\n                .append('<span>' + pickerButtonText + '</span>');\n\n            $(element).attr('autocomplete', this.options.autoComplete ? 'on' : 'off');\n\n            this._setCurrentDate(element);\n        },\n\n        /**\n         * destroy instance of datetimepicker\n         */\n        _destroy: function () {\n            this.element[this._picker()]('destroy');\n            this._super();\n        },\n\n        /**\n         * Method is kept for backward compatibility and unit-tests acceptance\n         * see \\mage\\calendar\\calendar-test.js\n         * @return {Object} date\n         */\n        getTimezoneDate: function () {\n            return datepickerPrototype._getTimezoneDate.call(this, this.options);\n        }\n    });\n\n    calendarBasePrototype = $.mage.calendar.prototype;\n\n    /**\n     * Extension for Calendar - date and time format convert functionality\n     * @var {Object}\n     */\n    $.widget('mage.calendar', $.extend({}, calendarBasePrototype,\n            /** @lends {$.mage.calendar.prototype} */ {\n                /**\n                 * key - backend format, value - jquery format\n                 * @type {Object}\n                 * @private\n                 */\n                dateTimeFormat: {\n                    date: {\n                        'EEEE': 'DD',\n                        'EEE': 'D',\n                        'EE': 'D',\n                        'E': 'D',\n                        'D': 'o',\n                        'MMMM': 'MM',\n                        'MMM': 'M',\n                        'MM': 'mm',\n                        'M': 'mm',\n                        'yyyy': 'yy',\n                        'y': 'yy',\n                        'Y': 'yy',\n                        'yy': 'yy' // Always long year format on frontend\n                    },\n                    time: {\n                        'a': 'TT'\n                    }\n                },\n\n                /**\n                 * Add Date and Time converting to _create method\n                 * @protected\n                 */\n                _create: function () {\n                    if (this.options.dateFormat) {\n                        this.options.dateFormat = this._convertFormat(this.options.dateFormat, 'date');\n                    }\n\n                    if (this.options.timeFormat) {\n                        this.options.timeFormat = this._convertFormat(this.options.timeFormat, 'time');\n                    }\n                    calendarBasePrototype._create.apply(this, arguments);\n                },\n\n                /**\n                 * Converting date or time format\n                 * @protected\n                 * @param {String} format\n                 * @param {String} type\n                 * @return {String}\n                 */\n                _convertFormat: function (format, type) {\n                    var symbols = format.match(/([a-z]+)/ig),\n                        separators = format.match(/([^a-z]+)/ig),\n                        self = this,\n                        convertedFormat = '';\n\n                    if (symbols) {\n                        $.each(symbols, function (key, val) {\n                            convertedFormat +=\n                                (self.dateTimeFormat[type][val] || val) +\n                                (separators[key] || '');\n                        });\n                    }\n\n                    return convertedFormat;\n                }\n            })\n    );\n\n    /**\n     * Widget dateRange\n     * @extends $.mage.calendar\n     */\n    $.widget('mage.dateRange', $.mage.calendar, {\n\n        /**\n         * creates two instances of datetimepicker for date range selection\n         * @protected\n         */\n        _initPicker: function () {\n            var from,\n                to;\n\n            if (this.options.from && this.options.to) {\n                from = this.element.find('#' + this.options.from.id);\n                to = this.element.find('#' + this.options.to.id);\n                this.options.onSelect = $.proxy(function (selectedDate) {\n                    to[this._picker()]('option', 'minDate', selectedDate);\n                }, this);\n                $.mage.calendar.prototype._initPicker.call(this, from);\n                from.on('change', $.proxy(function () {\n                    to[this._picker()]('option', 'minDate', from[this._picker()]('getDate'));\n                }, this));\n                this.options.onSelect = $.proxy(function (selectedDate) {\n                    from[this._picker()]('option', 'maxDate', selectedDate);\n                }, this);\n                $.mage.calendar.prototype._initPicker.call(this, to);\n                to.on('change', $.proxy(function () {\n                    from[this._picker()]('option', 'maxDate', to[this._picker()]('getDate'));\n                }, this));\n            }\n        },\n\n        /**\n         * destroy two instances of datetimepicker\n         */\n        _destroy: function () {\n            if (this.options.from) {\n                this.element.find('#' + this.options.from.id)[this._picker()]('destroy');\n            }\n\n            if (this.options.to) {\n                this.element.find('#' + this.options.to.id)[this._picker()]('destroy');\n            }\n            this._super();\n        }\n    });\n\n    // Overrides the \"today\" button functionality to select today's date when clicked.\n    $.datepicker._gotoTodayOriginal = $.datepicker._gotoToday;\n\n    /**\n     * overwrite jQuery UI _showDatepicker function for proper HTML generation conditions.\n     *\n     */\n    $.datepicker._showDatepickerOriginal = $.datepicker._showDatepicker;\n\n    /**\n     * Triggers original method showDataPicker for rendering calendar\n     * @param {HTMLObject} input\n     * @private\n     */\n    $.datepicker._showDatepicker = function (input) {\n        if (!input.disabled) {\n            $.datepicker._showDatepickerOriginal.call(this, input);\n        }\n    };\n\n    /**\n     * _gotoToday\n     * @param {Object} el\n     */\n    $.datepicker._gotoToday = function (el) {\n        //Set date/time according to timezone offset\n        $(el).datepicker('setTimezoneDate')\n            // To ensure that user can re-select date field without clicking outside it first.\n            .trigger('blur').trigger('change');\n    };\n\n    return {\n        dateRange:  $.mage.dateRange,\n        calendar:   $.mage.calendar\n    };\n});\n","mage/multiselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery',\n    'text!mage/multiselect.html',\n    'Magento_Ui/js/modal/alert',\n    'jquery-ui-modules/widget',\n    'jquery/editableMultiselect/js/jquery.multiselect'\n], function (_, $, searchTemplate, alert) {\n    'use strict';\n\n    $.widget('mage.multiselect2', {\n        options: {\n            mselectContainer: 'section.mselect-list',\n            mselectItemsWrapperClass: 'mselect-items-wrapper',\n            mselectCheckedClass: 'mselect-checked',\n            containerClass: 'paginated',\n            searchInputClass: 'admin__action-multiselect-search',\n            selectedItemsCountClass: 'admin__action-multiselect-items-selected',\n            currentPage: 1,\n            lastAppendValue: 0,\n            updateDelay: 1000,\n            optionsLoaded: false\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            $.fn.multiselect.call(this.element, this.options);\n        },\n\n        /** @inheritdoc */\n        _init: function () {\n            this.domElement = this.element.get(0);\n\n            this.$container = $(this.options.mselectContainer);\n            this.$wrapper = this.$container.find('.' + this.options.mselectItemsWrapperClass);\n            this.$item = this.$wrapper.find('div').first();\n            this.selectedValues = [];\n            this.values = {};\n\n            this.$container.addClass(this.options.containerClass).prepend(searchTemplate);\n            this.$input = this.$container.find('.' + this.options.searchInputClass);\n            this.$selectedCounter = this.$container.find('.' + this.options.selectedItemsCountClass);\n            this.filter = '';\n\n            if (this.domElement.options.length) {\n                this._setLastAppendOption(this.domElement.options[this.domElement.options.length - 1].value);\n            }\n\n            this._initElement();\n            this._events();\n        },\n\n        /**\n         * Leave only saved/selected options in select element.\n         *\n         * @private\n         */\n        _initElement: function () {\n            this.element.empty();\n            _.each(this.options.selectedValues, function (value) {\n                this._createSelectedOption({\n                    value: value,\n                    label: value\n                });\n            }, this);\n        },\n\n        /**\n         * Attach required events.\n         *\n         * @private\n         */\n        _events: function () {\n            var onKeyUp = _.debounce(this.onKeyUp, this.options.updateDelay);\n\n            _.bindAll(this, 'onScroll', 'onCheck', 'onOptionsChange');\n\n            this.$wrapper.on('scroll', this.onScroll);\n            this.$wrapper.on('change.mselectCheck', '[type=checkbox]', this.onCheck);\n            this.$input.on('keyup', _.bind(onKeyUp, this));\n            this.element.on('change.hiddenSelect', this.onOptionsChange);\n        },\n\n        /**\n         * Behaves multiselect scroll.\n         */\n        onScroll: function () {\n            var height = this.$wrapper.height(),\n                scrollHeight = this.$wrapper.prop('scrollHeight'),\n                scrollTop = Math.ceil(this.$wrapper.prop('scrollTop'));\n\n            if (!this.options.optionsLoaded && scrollHeight - height <= scrollTop) {\n                this.loadOptions();\n            }\n        },\n\n        /**\n         * Behaves keyup event on input search\n         */\n        onKeyUp: function () {\n            if (this.getSearchCriteria() === this.filter) {\n                return false;\n            }\n\n            this.setFilter();\n            this.clearMultiselectOptions();\n            this.setCurrentPage(0);\n            this.loadOptions();\n        },\n\n        /**\n         * Callback for select change event\n         */\n        onOptionsChange: function () {\n            this.selectedValues = _.map(this.domElement.options, function (option) {\n                this.values[option.value] = true;\n\n                return option.value;\n            }, this);\n\n            this._updateSelectedCounter();\n        },\n\n        /**\n         * Overrides native check behaviour.\n         *\n         * @param {Event} event\n         */\n        onCheck: function (event) {\n            var checkbox = event.target,\n                option = {\n                    value: checkbox.value,\n                    label: $(checkbox).parent('label').text()\n                };\n\n            checkbox.checked ? this._createSelectedOption(option) : this._removeSelectedOption(option);\n            event.stopPropagation();\n        },\n\n        /**\n         * Show error message.\n         *\n         * @param {String} message\n         */\n        onError: function (message) {\n            alert({\n                content: message\n            });\n        },\n\n        /**\n         * Updates current filter state.\n         */\n        setFilter: function () {\n            this.filter = this.getSearchCriteria() || '';\n        },\n\n        /**\n         * Reads search input value.\n         *\n         * @return {String}\n         */\n        getSearchCriteria: function () {\n            return this.$input.val().trim();\n        },\n\n        /**\n         * Load options data.\n         */\n        loadOptions: function () {\n            var nextPage = this.getCurrentPage() + 1;\n\n            this.$wrapper.trigger('processStart');\n            this.$input.prop('disabled', true);\n\n            $.get(this.options.nextPageUrl, {\n                p: nextPage,\n                s: this.filter\n            })\n            .done(function (response) {\n                if (response.success) {\n                    this.appendOptions(response.result);\n                    this.setCurrentPage(nextPage);\n                } else {\n                    this.onError(response.errorMessage);\n                }\n            }.bind(this))\n            .always(function () {\n                this.$wrapper.trigger('processStop');\n                this.$input.prop('disabled', false);\n\n                if (this.filter) {\n                    this.$input.focus();\n                }\n            }.bind(this));\n        },\n\n        /**\n         * Append loaded options\n         *\n         * @param {Array} options\n         */\n        appendOptions: function (options) {\n            var divOptions = [];\n\n            if (!options.length) {\n                return false;\n            }\n\n            if (this.isOptionsLoaded(options)) {\n                return;\n            }\n\n            options.forEach(function (option) {\n                if (!this.values[option.value]) {\n                    this.values[option.value] = true;\n                    option.selected = this._isOptionSelected(option);\n                    divOptions.push(this._createMultiSelectOption(option));\n                    this._setLastAppendOption(option.value);\n                }\n            }, this);\n\n            this.$wrapper.append(divOptions);\n        },\n\n        /**\n         * Clear multiselect options\n         */\n        clearMultiselectOptions: function () {\n            this._setLastAppendOption(0);\n            this.values = {};\n            this.$wrapper.empty();\n        },\n\n        /**\n         * Checks if all options are already loaded\n         *\n         * @return {Boolean}\n         */\n        isOptionsLoaded: function (options) {\n            this.options.optionsLoaded = this.options.lastAppendValue === options[options.length - 1].value;\n\n            return this.options.optionsLoaded;\n        },\n\n        /**\n         * Setter for current page.\n         *\n         * @param {Number} page\n         */\n        setCurrentPage: function (page) {\n            this.options.currentPage = page;\n        },\n\n        /**\n         * Getter for current page.\n         *\n         * @return {Number}\n         */\n        getCurrentPage: function () {\n            return this.options.currentPage;\n        },\n\n        /**\n         * Creates new selected option for select element\n         *\n         * @param {Object} option - option object\n         * @param {String} option.value - option value\n         * @param {String} option.label - option label\n         * @private\n         */\n        _createSelectedOption: function (option) {\n            var selectOption = new Option(option.label, option.value, false, true);\n\n            this.element.append(selectOption);\n            this.selectedValues.push(option.value);\n            this._updateSelectedCounter();\n\n            return selectOption;\n        },\n\n        /**\n         * Remove passed option from select element\n         *\n         * @param {Object} option - option object\n         * @param {String} option.value - option value\n         * @param {String} option.label - option label\n         * @return {Object} option\n         * @private\n         */\n        _removeSelectedOption: function (option) {\n            var unselectedOption = _.findWhere(this.domElement.options, {\n                value: option.value\n            });\n\n            if (!_.isUndefined(unselectedOption)) {\n                this.domElement.remove(unselectedOption.index);\n                this.selectedValues.splice(_.indexOf(this.selectedValues, option.value), 1);\n                this._updateSelectedCounter();\n            }\n\n            return unselectedOption;\n        },\n\n        /**\n         * Creates new DIV option for multiselect widget\n         *\n         * @param {Object} option - option object\n         * @param {String} option.value - option value\n         * @param {String} option.label - option label\n         * @param {Boolean} option.selected - is option selected\n         * @private\n         */\n        _createMultiSelectOption: function (option) {\n            var item = this.$item.clone(),\n                checkbox = item.find('input'),\n                isSelected = !!option.selected;\n\n            checkbox.val(option.value)\n                .prop('checked', isSelected)\n                .toggleClass(this.options.mselectCheckedClass, isSelected);\n\n            item.find('label > span').text(option.label);\n\n            return item;\n        },\n\n        /**\n         * Checks if passed option should be selected\n         *\n         * @param {Object} option - option object\n         * @param {String} option.value - option value\n         * @param {String} option.label - option label\n         * @param {Boolean} option.selected - is option selected\n         * @return {Boolean}\n         * @private\n         */\n        _isOptionSelected: function (option) {\n            return !!~this.selectedValues.indexOf(option.value);\n        },\n\n        /**\n         * Saves last added option value.\n         *\n         * @param {Number} value\n         * @private\n         */\n        _setLastAppendOption: function (value) {\n            this.options.lastAppendValue = value;\n        },\n\n        /**\n         * Updates counter of selected items.\n         *\n         * @private\n         */\n        _updateSelectedCounter: function () {\n            this.$selectedCounter.text(this.selectedValues.length);\n        }\n    });\n\n    return $.mage.multiselect2;\n});\n","mage/dataPost.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/template',\n    'Magento_Ui/js/modal/confirm',\n    'jquery-ui-modules/widget'\n], function ($, mageTemplate, uiConfirm) {\n    'use strict';\n\n    $.widget('mage.dataPost', {\n        options: {\n            formTemplate: '<form action=\"<%- data.action %>\" method=\"post\">' +\n            '<% _.each(data.data, function(value, index) { %>' +\n            '<input name=\"<%- index %>\" value=\"<%- value %>\">' +\n            '<% }) %></form>',\n            postTrigger: ['a[data-post]', 'button[data-post]', 'span[data-post]'],\n            formKeyInputSelector: 'input[name=\"form_key\"]'\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            this._bind();\n        },\n\n        /** @inheritdoc */\n        _bind: function () {\n            var events = {};\n\n            $.each(this.options.postTrigger, function (index, value) {\n                events['click ' + value] = '_postDataAction';\n            });\n\n            this._on(events);\n        },\n\n        /**\n         * Handler for click.\n         *\n         * @param {Object} e\n         * @private\n         */\n        _postDataAction: function (e) {\n            var params = $(e.currentTarget).data('post');\n\n            e.preventDefault();\n            this.postData(params);\n        },\n\n        /**\n         * Data post action.\n         *\n         * @param {Object} params\n         */\n        postData: function (params) {\n            var formKey = $(this.options.formKeyInputSelector).val(),\n                $form, input;\n\n            if (formKey) {\n                params.data['form_key'] = formKey;\n            }\n\n            $form = $(mageTemplate(this.options.formTemplate, {\n                data: params\n            }));\n\n            if (params.files) {\n                $form[0].enctype = 'multipart/form-data';\n                $.each(params.files, function (key, files) {\n                    if (files instanceof FileList) {\n                        input = document.createElement('input');\n                        input.type = 'file';\n                        input.name = key;\n                        input.files = files;\n                        $form[0].appendChild(input);\n                    }\n                });\n            }\n\n            if (params.data.confirmation) {\n                uiConfirm({\n                    content: params.data.confirmationMessage,\n                    actions: {\n                        /** @inheritdoc */\n                        confirm: function () {\n                            $form.appendTo('body').hide().trigger('submit');\n                        }\n                    }\n                });\n            } else {\n                $form.appendTo('body').hide().trigger('submit');\n            }\n        }\n    });\n\n    $(document).dataPost();\n\n    return $.mage.dataPost;\n});\n","mage/polyfill.js":"(function (root, doc) {\n    'use strict';\n\n    var Storage;\n\n    try {\n        if (!root.localStorage || !root.sessionStorage) {\n            throw new Error();\n        }\n\n        localStorage.setItem('storage_test', 1);\n        localStorage.removeItem('storage_test');\n    } catch (e) {\n        /**\n         * Returns a storage object to shim local or sessionStorage\n         * @param {String} type - either 'local' or 'session'\n         */\n        Storage = function (type) {\n            var data;\n\n            /**\n             * Creates a cookie\n             * @param {String} name\n             * @param {String} value\n             * @param {Integer} days\n             */\n            function createCookie(name, value, days) {\n                var date, expires;\n\n                if (days) {\n                    date = new Date();\n                    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n                    expires = '; expires=' + date.toGMTString();\n                } else {\n                    expires = '';\n                }\n                doc.cookie = name + '=' + value + expires + '; path=/';\n            }\n\n            /**\n             * Reads value of a cookie\n             * @param {String} name\n             */\n            function readCookie(name) {\n                var nameEQ = name + '=',\n                    ca = doc.cookie.split(';'),\n                    i = 0,\n                    c;\n\n                for (i = 0; i < ca.length; i++) {\n                    c = ca[i];\n\n                    while (c.charAt(0) === ' ') {\n                        c = c.substring(1, c.length);\n                    }\n\n                    if (c.indexOf(nameEQ) === 0) {\n                        return c.substring(nameEQ.length, c.length);\n                    }\n                }\n\n                return null;\n            }\n\n            /**\n             * Returns cookie name based upon the storage type.\n             * If this is session storage, the function returns a unique cookie per tab\n             */\n            function getCookieName() {\n\n                if (type !== 'session') {\n                    return 'localstorage';\n                }\n\n                if (!root.name) {\n                    root.name = new Date().getTime();\n                }\n\n                return 'sessionStorage' + root.name;\n            }\n\n            /**\n             * Sets storage cookie to a data object\n             * @param {Object} dataObject\n             */\n            function setData(dataObject) {\n                data = encodeURIComponent(JSON.stringify(dataObject));\n                createCookie(getCookieName(), data, 365);\n            }\n\n            /**\n             * Clears value of cookie data\n             */\n            function clearData() {\n                createCookie(getCookieName(), '', 365);\n            }\n\n            /**\n             * @returns value of cookie data\n             */\n            function getData() {\n                var dataResponse = readCookie(getCookieName());\n\n                return dataResponse ? JSON.parse(decodeURIComponent(dataResponse)) : {};\n            }\n\n            data = getData();\n\n            return {\n                length: 0,\n\n                /**\n                 * Clears data from storage\n                 */\n                clear: function () {\n                    data = {};\n                    this.length = 0;\n                    clearData();\n                },\n\n                /**\n                 * Gets an item from storage\n                 * @param {String} key\n                 */\n                getItem: function (key) {\n                    return data[key] === undefined ? null : data[key];\n                },\n\n                /**\n                 * Gets an item by index from storage\n                 * @param {Integer} i\n                 */\n                key: function (i) {\n                    var ctr = 0,\n                        k;\n\n                    for (k in data) {\n\n                        if (data.hasOwnProperty(k)) {\n\n                            // eslint-disable-next-line max-depth\n                            if (ctr.toString() === i.toString()) {\n                                return k;\n                            }\n                            ctr++;\n                        }\n                    }\n\n                    return null;\n                },\n\n                /**\n                 * Removes an item from storage\n                 * @param {String} key\n                 */\n                removeItem: function (key) {\n                    delete data[key];\n                    this.length--;\n                    setData(data);\n                },\n\n                /**\n                 * Sets an item from storage\n                 * @param {String} key\n                 * @param {String} value\n                 */\n                setItem: function (key, value) {\n                    data[key] = value.toString();\n                    this.length++;\n                    setData(data);\n                }\n            };\n        };\n\n        root.localStorage.prototype = root.localStorage = new Storage('local');\n        root.sessionStorage.prototype = root.sessionStorage = new Storage('session');\n    }\n})(window, document);\n","mage/terms.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    /**\n     * @param {*} args\n     */\n    $.fn.terms = function (args) {\n\n        // default\n        var defaults = {\n                start: 0,\n                wrapper: '',\n                showAnchor: '',\n                effects: 'slide'\n            },\n            options = $.extend(defaults, args);\n\n        this.each(function () {\n            var obj = $(this),\n                wrapper = options.wrapper !== '' ? '> ' + options.wrapper : '',\n                switches = $(wrapper + '> [data-section=\"title\"] > [data-toggle=\"switch\"]', obj),\n                terms = $(wrapper + '> [data-section=\"content\"]', obj),\n                t = switches.length,\n                marginTop = $(switches[0]).closest('[data-section=\"title\"]').css('position') == 'absolute' ? 0 : null, //eslint-disable-line\n                title,\n                current,\n\n                /**\n                 * @param {*} item\n                 */\n                showItem = function (item) {\n                    if (item != current && !$(switches[item]).closest('[data-section=\"title\"]').hasClass('disabled')) { //eslint-disable-line\n                        $(switches).closest('[data-section=\"title\"]').removeClass('active');\n\n                        if (options.wrapper !== '') {\n                            $(switches).parent().parent().removeClass('active');\n                        }\n                        $(terms).removeClass('active');\n                        $(switches[item]).closest('[data-section=\"title\"]').addClass('active');\n\n                        if (options.wrapper !== '') {\n                            $(switches[current]).parent().parent().addClass('active');\n                        }\n                        $(terms[item]).addClass('active');\n                        current = item;\n                    } else if (\n                        // Check if this is accordion width as criteria for now\n                        (obj.attr('data-sections') == 'accordion' || $(switches[item]).closest('[data-section=\"title\"]').css('width') == obj.css('width')) && //eslint-disable-line\n                        item == current && !$(switches[item]).closest('[data-section=\"title\"]').hasClass('disabled') //eslint-disable-line\n                    ) {\n                        $(switches).closest('[data-section=\"title\"]').removeClass('active');\n\n                        if (options.wrapper !== '') {\n                            $(switches).parent().parent().removeClass('active');\n                        }\n                        $(terms).removeClass('active');\n                        current = -1;\n                    }\n                },\n\n                /**\n                 * Init.\n                 */\n                init = function () {\n                    var linksList, i, classes, dataSection, itemHref, itemClass, fromUrl;\n\n                    if (t > 0) {\n                        if ($(switches[0]).closest('[data-section=\"title\"]').css('display') == 'table-cell') { //eslint-disable-line\n                            obj.addClass('adjusted');\n\n                            if (obj[0].tagName == 'DL') { //eslint-disable-line eqeqeq, max-depth\n                                linksList = $('<dd>');\n                            } else {\n                                linksList = $('<div>');\n                            }\n                            linksList.addClass('sections-nav');\n                            obj.prepend(linksList);\n\n                            for (i = 0; i < t; i++) { //eslint-disable-line max-depth\n                                title = $(switches[i]).html();\n                                classes = $(switches[i]).closest('[data-section=\"title\"]').attr('class');\n                                dataSection = $(switches[i]).closest('[data-section=\"title\"]').attr('data-section');\n                                itemHref = $(switches[i]).attr('href');\n                                itemClass = $(switches[i]).attr('class');\n                                $(switches[i]).parent('[data-section=\"title\"]').hide();\n                                switches[i] = $('<a/>', {\n                                    href: itemHref,\n                                    'class': itemClass,\n                                    html: title\n                                }).appendTo(linksList);\n                                $(switches[i]).wrap(\n                                    '<strong class=\"' + classes + '\" data-section=\"' + dataSection + '\" />'\n                                );\n                            }\n                        }\n                        $(switches).each(function (ind, el) {\n                            $(el).on('click', function (event) {\n                                event.preventDefault();\n                                showItem(ind);\n                            });\n\n                            if (marginTop !== null) {\n                                $(el).closest('[data-section=\"title\"]').css({\n                                    'top': marginTop + 'px'\n                                });\n                                marginTop += $(el).closest('[data-section=\"title\"]').outerHeight(true);\n                                obj.css({\n                                    'min-height': marginTop + 'px'\n                                });\n                            }\n                        });\n\n                        fromUrl = false;\n\n                        if (window.location.hash.length > 0) {\n                            $(terms).each(function (ind, el) {\n                                if ('#info-' + $(el).attr('id') == window.location.hash) { //eslint-disable-line eqeqeq\n                                    showItem(ind);\n                                    $('html, body').animate({\n                                        scrollTop: $(switches[ind]).offset().top\n                                    }, 700);\n                                    fromUrl = true;\n                                }\n                            });\n                        }\n\n                        if (fromUrl === false) {\n                            if (options.start % 1 === 0) { //eslint-disable-line max-depth\n                                current = options.start + 1;\n                                showItem(options.start);\n                            } else {\n                                $(terms).each(function (ind, el) {\n                                    if ($(el).attr('id') == options.start) { //eslint-disable-line eqeqeq\n                                        current = ind + 1;\n                                        showItem(ind);\n                                        $('html, body').animate({\n                                            scrollTop: $(switches[ind]).offset().top\n                                        }, 700);\n                                    }\n                                });\n                            }\n                        }\n                    }\n                };\n\n            init();\n        });\n    };\n\n    return function (data, el) {\n        $(el).terms(data);\n    };\n});\n","mage/accordion.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/tabs'\n], function ($, tabs) {\n    'use strict';\n\n    $.widget('mage.accordion', tabs, {\n        options: {\n            active: [0],\n            multipleCollapsible: false,\n            openOnFocus: false\n        },\n\n        /**\n         * @private\n         */\n        _callCollapsible: function () {\n            var self = this,\n                disabled = false,\n                active = false;\n\n            if (typeof this.options.active === 'string') {\n                this.options.active = this.options.active.split(' ').map(function (item) {\n                    return parseInt(item, 10);\n                });\n            }\n\n            $.each(this.collapsibles, function (i) {\n                disabled = active = false;\n\n                if ($.inArray(i, self.options.disabled) !== -1) {\n                    disabled = true;\n                }\n\n                if ($.inArray(i, self.options.active) !== -1) {\n                    active = true;\n                }\n                self._instantiateCollapsible(this, i, active, disabled);\n            });\n        },\n\n        /**\n         * Overwrites default functionality to provide the option to activate/deactivate multiple sections simultaneous\n         * @param {*} action\n         * @param {*} index\n         * @private\n         */\n        _toggleActivate: function (action, index) {\n            var self = this;\n\n            if (Array.isArray(index && this.options.multipleCollapsible)) {\n                $.each(index, function () {\n                    self.collapsibles.eq(this).collapsible(action);\n                });\n            } else if (index === undefined && this.options.multipleCollapsible) {\n                this.collapsibles.collapsible(action);\n            } else {\n                this._super(action, index);\n            }\n        },\n\n        /**\n         * If the Accordion allows multiple section to be active at the same time, if deep linking is used\n         * sections that don't contain the id from anchor shouldn't be closed, otherwise the accordion uses the\n         * tabs behavior\n         * @private\n         */\n        _handleDeepLinking: function () {\n            if (!this.options.multipleCollapsible) {\n                this._super();\n            }\n        },\n\n        /**\n         * Prevent default behavior that closes the other sections when one gets activated if the Accordion allows\n         * multiple sections simultaneous\n         * @private\n         */\n        _closeOthers: function () {\n            var self = this;\n\n            if (!this.options.multipleCollapsible) {\n                $.each(this.collapsibles, function () {\n                    $(this).on('beforeOpen', function () {\n                        self.collapsibles.not(this).collapsible('deactivate');\n                    });\n                });\n            }\n            $.each(this.collapsibles, function () {\n                $(this).on('beforeOpen', function () {\n                    var section = $(this);\n\n                    section.addClass('allow').prevAll().addClass('allow');\n                    section.nextAll().removeClass('allow');\n                });\n            });\n        }\n    });\n\n    return $.mage.accordion;\n});\n","mage/fieldset-controls.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget'\n], function ($) {\n    'use strict';\n\n    /**\n     * This widget will allow a control with the fieldsetResetControl widget attached to reset a set of input fields.\n     * The input fields to reset are defined by the inputSelector selector. The widget will store a clone of the fields\n     * on create, and on trigger of fieldsetReset event it resets the defined fields. The event is triggered by the\n     * reset control widget.\n     *\n     * For inputs of type file, the whole dom element is replaced as changing the value is a security violation\n     * For inputs of type checkbox or radio, the checked attribute is added or removed as appropriate\n     * For all others the jquery .val method is used to update to value to the original.\n     */\n    $.widget('mage.fieldsetControls', {\n        original: undefined,\n        options: {\n            inputSelector: '[data-reset=\"true\"]'\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            this.original = this.element.find(this.options.inputSelector).clone(true);\n            this._bind();\n        },\n\n        /**\n         * @private\n         */\n        _bind: function () {\n            this._on({\n                'fieldsetReset': '_onReset'\n            });\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @private\n         */\n        _onReset: function (e) {\n            var items;\n\n            e.stopPropagation();\n            // find all the ones we have to remove\n            items = this.element.find(this.options.inputSelector);\n            // loop over replacing each one.\n            items.each($.proxy(function (index, item) {\n                if ($(item).attr('type') == 'file') { //eslint-disable-line eqeqeq\n                    // Replace the current one we found with a clone of the original saved earlier\n                    $(item).replaceWith($(this.original[index]).clone(true));\n                } else if ($(item).attr('type') == 'checkbox' || $(item).attr('type') == 'radio') { //eslint-disable-line\n                    // Return to original state.\n                    if ($(this.original[index]).attr('checked') === undefined) {\n                        $(item).removeAttr('checked');\n                    } else {\n                        $(item).attr('checked', $(this.original[index]).attr('checked'));\n                    }\n                } else {\n                    // Replace the value with the original\n                    $(item).val($(this.original[index]).val());\n                }\n            }, this));\n        }\n    });\n\n    $.widget('mage.fieldsetResetControl', {\n        /**\n         * @private\n         */\n        _create: function () {\n            this._bind();\n        },\n\n        /**\n         * @private\n         */\n        _bind: function () {\n            this._on({\n                click: '_onClick'\n            });\n        },\n\n        /**\n         * @param {jQuery.Event} e\n         * @private\n         */\n        _onClick: function (e) {\n            e.stopPropagation();\n            $(this.element).trigger('fieldsetReset');\n        }\n    });\n\n    return {\n        fieldsetControls: $.mage.fieldsetControls,\n        fieldsetResetControl: $.mage.fieldsetResetControl\n    };\n});\n","mage/toggle.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.toggleAdvanced', {\n        options: {\n            baseToggleClass: 'active' // Class used to be toggled on clicked element\n        },\n\n        /**\n         * Toggle creation\n         * @private\n         */\n        _create: function () {\n            this.beforeCreate();\n            this._bindCore();\n            this.afterCreate();\n        },\n\n        /**\n         *  Core bound events & setup\n         * @protected\n         */\n        _bindCore: function () {\n            var widget = this;\n\n            this.element.on('click', $.proxy(function (e) {\n                widget._onClick();\n                e.preventDefault();\n            }, this));\n        },\n\n        /**\n         * Binding Click event\n         *\n         * @protected\n         */\n        _onClick: function () {\n            this._prepareOptions();\n            this._toggleSelectors();\n        },\n\n        /**\n         * Method used to look for data attributes to override default options\n         *\n         * @protected\n         */\n        _prepareOptions: function () {\n            this.options.baseToggleClass = this.element.data('base-toggle-class') ?\n                this.element.data('base-toggle-class') : this.options.baseToggleClass;\n        },\n\n        /**\n         * Method responsible for hiding and revealing specified DOM elements\n         * Toggle the class on clicked element\n         *\n         * @protected\n         */\n        _toggleSelectors: function () {\n            this.element.toggleClass(this.options.baseToggleClass);\n        },\n\n        /**\n         * Method used to inject 3rd party functionality before create\n         * @public\n         */\n        beforeCreate: function () {},\n\n        /**\n         * Method used to inject 3rd party functionality after create\n         * @public\n         */\n        afterCreate: function () {}\n    });\n\n    // Extension for mage.toggle - Adding selectors support for other DOM elements we wish to toggle\n    $.widget('mage.toggleAdvanced', $.mage.toggleAdvanced, {\n\n        options: {\n            selectorsToggleClass: 'hidden',    // Class used to be toggled on selectors DOM elements\n            toggleContainers: null\n        },\n\n        /**\n         * Method responsible for hiding and revealing specified DOM elements\n         * If data-toggle-selectors attribute is present - toggle will be done on these selectors\n         * Otherwise we toggle the class on clicked element\n         *\n         * @protected\n         * @override\n         */\n        _toggleSelectors: function () {\n            this._super();\n\n            if (this.options.toggleContainers) {\n                $(this.options.toggleContainers).toggleClass(this.options.selectorsToggleClass);\n            } else {\n                this.element.toggleClass(this.options.baseToggleClass);\n            }\n        },\n\n        /**\n         * Method used to look for data attributes to override default options\n         *\n         * @protected\n         * @override\n         */\n        _prepareOptions: function () {\n            this.options.selectorsToggleClass = this.element.data('selectors-toggle-class') ?\n                this.element.data('selectors-toggle-class') : this.options.selectorsToggleClass;\n            this.options.toggleContainers = this.element.data('toggle-selectors') ?\n                this.element.data('toggle-selectors') : this.options.toggleContainers;\n            this._super();\n        }\n    });\n\n    // Extension for mage.toggle - Adding label toggle\n    $.widget('mage.toggleAdvanced', $.mage.toggleAdvanced, {\n\n        options: {\n            newLabel: null,             // Text of the new label to be used on toggle\n            curLabel: null,             // Text of the old label to be used on toggle\n            currentLabelElement: null   // Current label container\n        },\n\n        /**\n         * Binding Click event\n         *\n         * @protected\n         * @override\n         */\n        _onClick: function () {\n            this._super();\n            this._toggleLabel();\n        },\n\n        /**\n         * Method responsible for replacing clicked element labels\n         * @protected\n         */\n        _toggleLabel: function () {\n            var cachedLabel, currentLabelSelector;\n\n            if (this.options.newLabel) {\n                cachedLabel = this.options.newLabel;\n                currentLabelSelector = this.options.currentLabelElement ?\n                        $(this.options.currentLabelElement) : this.element;\n\n                this.element.data('toggle-label', this.options.curLabel);\n                currentLabelSelector.html(this.options.newLabel);\n\n                this.options.curLabel = this.options.newLabel;\n                this.options.newLabel = cachedLabel;\n            }\n        },\n\n        /**\n         * Method used to look for data attributes to override default options\n         *\n         * @protected\n         * @override\n         */\n        _prepareOptions: function () {\n            this.options.newLabel = this.element.data('toggle-label') ?\n                this.element.data('toggle-label') : this.options.newLabel;\n\n            this.options.currentLabelElement = this.element.data('current-label-el') ?\n                this.element.data('current-label-el') : this.options.currentLabelElement;\n\n            if (!this.options.currentLabelElement) {\n                this.options.currentLabelElement = this.element;\n            }\n\n            this.options.curLabel = $(this.options.currentLabelElement).html();\n\n            this._super();\n        }\n    });\n\n    return $.mage.toggleAdvanced;\n});\n","mage/bootstrap.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/apply/main',\n    'Magento_Ui/js/lib/knockout/bootstrap'\n], function ($, mage) {\n    'use strict';\n\n    $.ajaxSetup({\n        cache: false\n    });\n\n    /**\n     * Init all components defined via data-mage-init attribute.\n     * Execute in a separate task to prevent main thread blocking.\n     */\n    setTimeout(mage.apply);\n});\n","mage/dropdowns.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    /**\n     * @param {Object} options\n     */\n    $.fn.dropdown = function (options) {\n        var defaults = {\n                parent: null,\n                autoclose: true,\n                btnArrow: '.arrow',\n                menu: '[data-target=\"dropdown\"]',\n                activeClass: 'active'\n            },\n            actionElem = $(this),\n            self = this;\n\n        options = $.extend(defaults, options);\n        actionElem = $(this);\n        self = this;\n\n        /**\n         * @param {HTMLElement} elem\n         */\n        this.openDropdown = function (elem) {\n            elem\n                .addClass(options.activeClass)\n                .attr('aria-expanded', true)\n                .parent()\n                    .addClass(options.activeClass);\n\n            elem.parent()\n                .find(options.menu)\n                .attr('aria-hidden', false);\n\n            $(options.btnArrow, elem).text('-');\n        };\n\n        /**\n         * @param {HTMLElement} elem\n         */\n        this.closeDropdown = function (elem) {\n            elem.removeClass(options.activeClass)\n                .attr('aria-expanded', false)\n                .parent()\n                    .removeClass(options.activeClass);\n\n            elem.parent()\n                .find(options.menu)\n                .attr('aria-hidden', true);\n\n            $(options.btnArrow, elem).text('+');\n        };\n\n        /**\n         * Reset all dropdowns.\n         *\n         * @param {Object} param\n         */\n        this.reset = function (param) {\n            var params = param || {},\n                dropdowns = params.elems || actionElem;\n\n            dropdowns.each(function (index, elem) {\n                self.closeDropdown($(elem));\n            });\n        };\n\n        /* document Event bindings */\n        if (options.autoclose === true) {\n            $(document).on('click.hideDropdown', this.reset);\n            $(document).on('keyup.hideDropdown', function (e) {\n                var ESC_CODE = '27';\n\n                if (e.keyCode == ESC_CODE) { //eslint-disable-line eqeqeq\n                    self.reset();\n                }\n            });\n        }\n\n        if (options.events) {\n            $.each(options.events, function (index, event) {\n                $(document).on(event.name, event.selector, event.action);\n            });\n        }\n\n        return this.each(function () {\n            var elem = $(this),\n                parent = $(options.parent).length > 0 ? $(options.parent) : elem.parent(),\n                menu = $(options.menu, parent) || $('.dropdown-menu', parent);\n\n            // ARIA (adding aria attributes)\n            if (menu.length) {\n                elem.attr('aria-haspopup', true);\n            }\n\n            if (!elem.hasClass(options.activeClass)) {\n                elem.attr('aria-expanded', false);\n                menu.attr('aria-hidden', true);\n            } else {\n                elem.attr('aria-expanded', true);\n                menu.attr('aria-hidden', false);\n            }\n\n            if (!elem.is('a, button')) {\n                elem.attr('role', 'button');\n                elem.attr('tabindex', 0);\n            }\n\n            if (elem.attr('data-trigger-keypress-button')) {\n                elem.on('keypress', function (e) {\n                    var keyCode = e.keyCode || e.which,\n                        ENTER_CODE = 13;\n\n                    if (keyCode === ENTER_CODE) {\n                        e.preventDefault();\n                        elem.trigger('click.toggleDropdown');\n                    }\n                });\n            }\n\n            elem.on('click.toggleDropdown', function () {\n                var el = actionElem;\n\n                if (options.autoclose === true) {\n                    actionElem = $();\n                    $(document).trigger('click.hideDropdown');\n                    actionElem = el;\n                }\n\n                self[el.hasClass(options.activeClass) ? 'closeDropdown' : 'openDropdown'](elem);\n\n                return false;\n            });\n        });\n    };\n\n    return function (data, el) {\n        $(el).dropdown(data);\n    };\n});\n","mage/template.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    /**\n     * Checks if provided string is a valid DOM selector.\n     *\n     * @param {String} selector - Selector to be checked.\n     * @returns {Boolean}\n     */\n    function isSelector(selector) {\n        try {\n            document.querySelector(selector);\n\n            return true;\n        } catch (e) {\n            return false;\n        }\n    }\n\n    /**\n     * Unescapes characters used in underscore templates.\n     *\n     * @param {String} str - String to be processed.\n     * @returns {String}\n     */\n    function unescape(str) {\n        return str.replace(/&lt;%|%3C%/g, '<%').replace(/%&gt;|%%3E/g, '%>');\n    }\n\n    /**\n     * If 'tmpl' is a valid selector, returns target node's innerHTML if found.\n     * Else, returns empty string and emits console warning.\n     * If 'tmpl' is not a selector, returns 'tmpl' as is.\n     *\n     * @param {String} tmpl\n     * @returns {String}\n     */\n    function getTmplString(tmpl) {\n        if (isSelector(tmpl)) {\n            tmpl = document.querySelector(tmpl);\n\n            if (tmpl) {\n                tmpl = tmpl.innerHTML.trim();\n            } else {\n                console.warn('No template was found by selector: ' + tmpl);\n\n                tmpl = '';\n            }\n        }\n\n        return unescape(tmpl);\n    }\n\n    /**\n     * Compiles or renders template provided either\n     * by selector or by the template string.\n     *\n     * @param {String} tmpl - Template string or selector.\n     * @param {(Object|Array|Function)} [data] - Data object with which to render template.\n     * @returns {String|Function}\n     */\n    return function (tmpl, data) {\n        var render;\n\n        tmpl   = getTmplString(tmpl);\n        render = _.template(tmpl);\n\n        return !_.isUndefined(data) ?\n            render(data) :\n            render;\n    };\n});\n","mage/translate.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/mage',\n    'mageTranslationDictionary',\n    'underscore'\n], function ($, mage, dictionary, _) {\n    'use strict';\n\n    $.extend(true, $, {\n        mage: {\n            translate: (function () {\n                /**\n                 * Key-value translations storage\n                 * @type {Object}\n                 * @private\n                 */\n                var _data = dictionary;\n\n                return {\n                    /**\n                     * Add new translation (two string parameters) or several translations (object)\n                     */\n                    add: function () {\n                        if (arguments.length > 1) {\n                            _data[arguments[0]] = arguments[1];\n                        } else if (typeof arguments[0] === 'object') {\n                            $.extend(_data, arguments[0]);\n                        }\n                    },\n\n                    /**\n                     * Make a translation with parsing (to handle case when _data represents tuple)\n                     * @param {String} text\n                     * @return {String}\n                     */\n                    translate: function (text) {\n                        return typeof _data[text] !== 'undefined' ? _data[text] : text;\n                    }\n                };\n            }())\n        }\n    });\n    $.mage.__ = $.proxy($.mage.translate.translate, $.mage.translate);\n\n    // Provide i18n wrapper to be used in underscore templates for translation\n    _.extend(_, {\n        /**\n         * Make a translation using $.mage.__\n         *\n         * @param {String} text\n         * @return {String}\n         */\n        i18n: function (text) {\n            return $.mage.__(text);\n        }\n    });\n\n    return $.mage.__;\n});\n","mage/item-table.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n    'jquery',\n    'mage/template',\n    'jquery-ui-modules/widget'\n], function ($, mageTemplate) {\n    'use strict';\n\n    $.widget('mage.itemTable', {\n        options: {\n            addBlock: '[data-template=\"add-block\"]',\n            addBlockData: {},\n            addEvent: 'click',\n            addSelector: '[data-role=\"add\"]',\n            itemsSelector: '[data-container=\"items\"]',\n            keepLastRow: true\n        },\n\n        /**\n         * This method adds a new instance of the block to the items.\n         * @private\n         */\n        _add: function () {\n            var hideShowDelete,\n                deletableItems,\n                addedBlock;\n\n            // adding a new row, so increment the count to give each row a unique index\n            this.rowIndex++;\n\n            // make sure the block data has the rowIndex\n            this.options.addBlockData.rowIndex = this.rowIndex;\n\n            // render the form\n            addedBlock = $(this.addBlockTmpl({\n                data: this.options.addBlockData\n            }));\n\n            // add the row to the item block\n            this.element.find(this.options.itemsSelector).append(addedBlock);\n\n            // initialize all mage content\n            addedBlock.trigger('contentUpdated');\n\n            // determine all existing items in the collection\n            deletableItems = this._getDeletableItems();\n\n            // for the most part, show the delete mechanism, except in the case where there is only one it should not\n            // be deleted\n            hideShowDelete = 'showDelete';\n\n            if (this.options.keepLastRow && deletableItems.length === 1) {\n                hideShowDelete = 'hideDelete';\n            }\n\n            // loop through each control and perform that action on the deletable item\n            $.each(deletableItems, function (index) {\n                $(deletableItems[index]).trigger(hideShowDelete);\n            });\n        },\n\n        /**\n         * This method binds elements found in this widget.\n         * @private\n         */\n        _bind: function () {\n            var handlers = {};\n\n            // since the first handler is dynamic, generate the object using array notation\n            handlers[this.options.addEvent + ' ' + this.options.addSelector] = '_add';\n            handlers.deleteItem = '_onDeleteItem';\n\n            this._on(handlers);\n        },\n\n        /**\n         * This method constructs a new widget.\n         * @private\n         */\n        _create: function () {\n            this._bind();\n\n            this.addBlockTmpl = mageTemplate(this.options.addBlock);\n\n            // nothing in the table, so indicate that\n            this.rowIndex = -1;\n\n            // make sure the block data is an object\n            if (this.options.addBlockData == null || typeof this.options.addBlockData !== 'object') {\n                // reset the block data to an empty object\n                this.options.addBlockData = {};\n            }\n\n            // add the first row to the table\n            this._add();\n        },\n\n        /**\n         * This method returns the list of widgets associated with deletable items from the container (direct children\n         * only).\n         * @private\n         */\n        _getDeletableItems: function () {\n            return this.element.find(this.options.itemsSelector + '> .deletableItem');\n        },\n\n        /**\n         * This method removes the item associated with the message.\n         * @private\n         */\n        _onDeleteItem: function (e) {\n            var deletableItems;\n\n            // parent elements don't need to see this event\n            e.stopPropagation();\n\n            // remove the deletable item\n            $(e.target).remove();\n\n            if (this.options.keepLastRow) {\n                // determine if there is only one element remaining, in which case, disable the delete mechanism on it\n                deletableItems = this._getDeletableItems();\n\n                if (deletableItems.length === 1) {\n                    $(deletableItems[0]).trigger('hideDelete');\n                }\n            }\n        }\n    });\n\n    return $.mage.itemTable;\n});\n","mage/tabs.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget',\n    'jquery/ui-modules/widgets/tabs',\n    'mage/mage',\n    'mage/collapsible'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.tabs', {\n        options: {\n            active: 0,\n            disabled: [],\n            openOnFocus: true,\n            collapsible: false,\n            collapsibleElement: '[data-role=collapsible]',\n            header: '[data-role=title]',\n            content: '[data-role=content]',\n            trigger: '[data-role=trigger]',\n            closedState: null,\n            openedState: null,\n            disabledState: null,\n            ajaxUrlElement: '[data-ajax=true]',\n            ajaxContent: false,\n            loadingClass: null,\n            saveState: false,\n            animate: false,\n            icons: {\n                activeHeader: null,\n                header: null\n            }\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            if (typeof this.options.disabled === 'string') {\n                this.options.disabled = this.options.disabled.split(' ').map(function (item) {\n                    return parseInt(item, 10);\n                });\n            }\n            this._processPanels();\n            this._handleDeepLinking();\n            this._processTabIndex();\n            this._closeOthers();\n            this._bind();\n        },\n\n        /**\n         * @private\n         */\n        _destroy: function () {\n            $.each(this.collapsibles, function () {\n                $(this).collapsible('destroy');\n            });\n        },\n\n        /**\n         * If deep linking is used, all sections must be closed but the one that contains the anchor.\n         * @private\n         */\n        _handleDeepLinking: function () {\n            var self = this,\n                anchor = window.location.hash,\n                isValid = $.mage.isValidSelector(anchor),\n                anchorId = anchor.replace('#', '');\n\n            if (anchor && isValid) {\n                $.each(self.contents, function (i) {\n                    if ($(this).attr('id') === anchorId || $(this).find('#' + anchorId).length) {\n                        self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate');\n\n                        return false;\n                    }\n                });\n            }\n        },\n\n        /**\n         * When the widget gets instantiated, the first tab that is not disabled receive focusable property\n         * All tabs receive tabIndex 0\n         * @private\n         */\n        _processTabIndex: function () {\n            var self = this;\n\n            self.triggers.attr('tabIndex', 0);\n            $.each(this.collapsibles, function (i) {\n                self.triggers.attr('tabIndex', 0);\n                self.triggers.eq(i).attr('tabIndex', 0);\n            });\n        },\n\n        /**\n         * Prepare the elements for instantiating the collapsible widget\n         * @private\n         */\n        _processPanels: function () {\n            var isNotNested = this._isNotNested.bind(this);\n\n            this.contents = this.element\n                .find(this.options.content)\n                .filter(isNotNested);\n\n            this.collapsibles =  this.element\n                .find(this.options.collapsibleElement)\n                .filter(isNotNested);\n\n            this.collapsibles\n                .attr('role', 'presentation')\n                .parent()\n                .attr('role', 'tablist');\n\n            this.headers = this.element\n                .find(this.options.header)\n                .filter(isNotNested);\n\n            if (this.headers.length === 0) {\n                this.headers = this.collapsibles;\n            }\n            this.triggers = this.element\n                .find(this.options.trigger)\n                .filter(isNotNested);\n\n            if (this.triggers.length === 0) {\n                this.triggers = this.headers;\n            }\n            this._callCollapsible();\n        },\n\n        /**\n         * Checks if element is not in nested container to keep the correct scope of collapsible\n         * @param {Number} index\n         * @param {HTMLElement} element\n         * @private\n         * @return {Boolean}\n         */\n        _isNotNested: function (index, element) {\n            var parentContent = $(element).parents(this.options.content);\n\n            return !parentContent.length || !this.element.find(parentContent).length;\n        },\n\n        /**\n         * Setting the disabled and active tabs and calling instantiation of collapsible\n         * @private\n         */\n        _callCollapsible: function () {\n            var self = this,\n                disabled = false,\n                active = false;\n\n            $.each(this.collapsibles, function (i) {\n                disabled = active = false;\n\n                if ($.inArray(i, self.options.disabled) !== -1) {\n                    disabled = true;\n                }\n\n                if (i === self.options.active) {\n                    active = true;\n                }\n                self._instantiateCollapsible(this, i, active, disabled);\n            });\n        },\n\n        /**\n         * Instantiate collapsible.\n         *\n         * @param {HTMLElement} element\n         * @param {Number} index\n         * @param {*} active\n         * @param {*} disabled\n         * @private\n         */\n        _instantiateCollapsible: function (element, index, active, disabled) {\n            $(element).collapsible(\n                $.extend({}, this.options, {\n                    active: active,\n                    disabled: disabled,\n                    header: this.headers.eq(index),\n                    content: this.contents.eq(index),\n                    trigger: this.triggers.eq(index)\n                })\n            );\n        },\n\n        /**\n         * Adding callback to close others tabs when one gets opened\n         * @private\n         */\n        _closeOthers: function () {\n            var self = this;\n\n            $.each(this.collapsibles, function () {\n                $(this).on('beforeOpen', function () {\n                    self.collapsibles.not(this).collapsible('forceDeactivate');\n                });\n            });\n        },\n\n        /**\n         * @param {*} index\n         */\n        activate: function (index) {\n            this._toggleActivate('activate', index);\n        },\n\n        /**\n         * @param {*} index\n         */\n        deactivate: function (index) {\n            this._toggleActivate('deactivate', index);\n        },\n\n        /**\n         * @param {*} action\n         * @param {*} index\n         * @private\n         */\n        _toggleActivate: function (action, index) {\n            this.collapsibles.eq(index).collapsible(action);\n        },\n\n        /**\n         * @param {*} index\n         */\n        disable: function (index) {\n            this._toggleEnable('disable', index);\n        },\n\n        /**\n         * @param {*} index\n         */\n        enable: function (index) {\n            this._toggleEnable('enable', index);\n        },\n\n        /**\n         * @param {*} action\n         * @param {*} index\n         * @private\n         */\n        _toggleEnable: function (action, index) {\n            var self = this;\n\n            if (Array.isArray(index)) {\n                $.each(index, function () {\n                    self.collapsibles.eq(this).collapsible(action);\n                });\n            } else if (index === undefined) {\n                this.collapsibles.collapsible(action);\n            } else {\n                this.collapsibles.eq(index).collapsible(action);\n            }\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _keydown: function (event) {\n            var self = this,\n                keyCode, toFocus, toFocusIndex, enabledTriggers, length, currentIndex, nextToFocus;\n\n            if (event.altKey || event.ctrlKey) {\n                return;\n            }\n            keyCode = $.ui.keyCode;\n            toFocus = false;\n            enabledTriggers = [];\n\n            $.each(this.triggers, function () {\n                if (!self.collapsibles.eq(self.triggers.index($(this))).collapsible('option', 'disabled')) {\n                    enabledTriggers.push(this);\n                }\n            });\n            length = $(enabledTriggers).length;\n            currentIndex = $(enabledTriggers).index(event.target);\n\n            /**\n             * @param {String} direction\n             * @return {*}\n             */\n            nextToFocus = function (direction) {\n                if (length > 0) {\n                    if (direction === 'right') {\n                        toFocusIndex = (currentIndex + 1) % length;\n                    } else {\n                        toFocusIndex = (currentIndex + length - 1) % length;\n                    }\n\n                    return enabledTriggers[toFocusIndex];\n                }\n\n                return event.target;\n            };\n\n            switch (event.keyCode) {\n                case keyCode.RIGHT:\n                case keyCode.DOWN:\n                    toFocus = nextToFocus('right');\n                    break;\n\n                case keyCode.LEFT:\n                case keyCode.UP:\n                    toFocus = nextToFocus('left');\n                    break;\n\n                case keyCode.HOME:\n                    toFocus = enabledTriggers[0];\n                    break;\n\n                case keyCode.END:\n                    toFocus = enabledTriggers[length - 1];\n                    break;\n            }\n\n            if (toFocus) {\n                toFocusIndex = this.triggers.index(toFocus);\n                $(event.target).attr('tabIndex', -1);\n                $(toFocus).attr('tabIndex', 0);\n                toFocus.focus();\n\n                if (this.options.openOnFocus) {\n                    this.activate(toFocusIndex);\n                }\n                event.preventDefault();\n            }\n        },\n\n        /**\n         * @private\n         */\n        _bind: function () {\n            var events = {\n                keydown: '_keydown'\n            };\n\n            this._off(this.triggers);\n            this._on(this.triggers, events);\n        }\n    });\n\n    return $.mage.tabs;\n});\n","mage/popup-window.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery-ui-modules/widget'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.popupWindow', {\n        options: {\n            centerBrowser: 0, // center window over browser window? {1 (YES) or 0 (NO)}. overrides top and left\n            centerScreen: 0, // center window over entire screen? {1 (YES) or 0 (NO)}. overrides top and left\n            height: 500, // sets the height in pixels of the window.\n            left: 0, // left position when the window appears.\n            location: 0, // determines whether the address bar is displayed {1 (YES) or 0 (NO)}.\n            menubar: 0, // determines whether the menu bar is displayed {1 (YES) or 0 (NO)}.\n            resizable: 0, // whether the window can be resized {1 (YES) or 0 (NO)}.\n            scrollbars: 0, // determines whether scrollbars appear on the window {1 (YES) or 0 (NO)}.\n            status: 0, // whether a status line appears at the bottom of the window {1 (YES) or 0 (NO)}.\n            width: 500, // sets the width in pixels of the window.\n            windowName: null, // name of window set from the name attribute of the element that invokes the click\n            windowURL: null, // url used for the popup\n            top: 0, // top position when the window appears.\n            toolbar: 0 // determines whether a toolbar is displayed {1 (YES) or 0 (NO)}.\n        },\n\n        /**\n         * @private\n         */\n        _create: function () {\n            this.element.on('click', $.proxy(this._openPopupWindow, this));\n        },\n\n        /**\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _openPopupWindow: function (event) {\n            var element = $(event.target),\n                settings = this.options,\n                windowFeatures =\n                    'height=' + settings.height +\n                        ',width=' + settings.width +\n                        ',toolbar=' + settings.toolbar +\n                        ',scrollbars=' + settings.scrollbars +\n                        ',status=' + settings.status +\n                        ',resizable=' + settings.resizable +\n                        ',location=' + settings.location +\n                        ',menuBar=' + settings.menubar,\n                centeredX,\n                centeredY;\n\n            settings.windowName = settings.windowName || element.attr('name');\n            settings.windowURL = settings.windowURL || element.attr('href');\n\n            if (settings.centerBrowser) {\n                centeredY = window.screenY + (window.outerHeight / 2 - settings.height / 2);\n                centeredX = window.screenX + (window.outerWidth / 2 - settings.width / 2);\n                windowFeatures += ',left=' + centeredX + ',top=' + centeredY;\n            } else if (settings.centerScreen) {\n                centeredY = (screen.height - settings.height) / 2;\n                centeredX = (screen.width - settings.width) / 2;\n                windowFeatures += ',left=' + centeredX + ',top=' + centeredY;\n            } else {\n                windowFeatures += ',left=' + settings.left + ',top=' + settings.top;\n            }\n\n            window.open(settings.windowURL, settings.windowName, windowFeatures).focus();\n            event.preventDefault();\n        }\n    });\n\n    return $.mage.popupWindow;\n});\n","mage/validation/validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'mage/validation',\n    'mage/translate'\n], function ($) {\n    'use strict';\n\n    $.each({\n        'validate-grouped-qty': [\n            function (value, element, params) {\n                var result = false,\n                    total = 0;\n\n                $(params).find('input[data-validate*=\"validate-grouped-qty\"]').each(function (i, e) {\n                    var val = $(e).val(),\n                        valInt;\n\n                    if (val && val.length > 0) {\n                        result = true;\n                        valInt = parseFloat(val) || 0;\n\n                        if (valInt >= 0) {\n                            total += valInt;\n                        } else {\n                            result = false;\n\n                            return result;\n                        }\n                    }\n                });\n\n                return result && total > 0;\n            },\n            $.mage.__('Please specify the quantity of product(s).')\n        ],\n        'validate-one-checkbox-required-by-name': [\n            function (value, element, params) {\n                var checkedCount = 0,\n                    container;\n\n                if (element.type === 'checkbox') {\n                    $('[name=\"' + element.name + '\"]').each(\n                        function () {\n                            if ($(this).is(':checked')) {\n                                checkedCount += 1;\n\n                                return false;\n                            }\n                        }\n                    );\n                }\n                container = '#' + params;\n\n                if (checkedCount > 0) {\n                    $(container).removeClass('validation-failed');\n                    $(container).addClass('validation-passed');\n\n                    return true;\n                }\n                $(container).addClass('validation-failed');\n                $(container).removeClass('validation-passed');\n\n                return false;\n            },\n            $.mage.__('Please select one of the options.')\n        ],\n        'validate-date-between': [\n            function (value, element, params) {\n                var minDate = new Date(params[0]),\n                    maxDate = new Date(params[1]),\n                    inputDate = new Date(element.value),\n                    message;\n\n                minDate.setHours(0);\n                maxDate.setHours(0);\n\n                if (inputDate >= minDate && inputDate <= maxDate) {\n                    return true;\n                }\n                message = $.mage.__('Please enter a date between %min and %max.');\n                this.dateBetweenErrorMessage = message.replace('%min', minDate).replace('%max', maxDate);\n\n                return false;\n            },\n            function () {\n                return this.dateBetweenErrorMessage;\n            }\n        ],\n        'validate-dob': [\n            function (val, element, params) {\n                var dob = $(element).parents('.customer-dob'),\n                    dayVal, monthVal, yearVal, dobLength, day, month, year, curYear,\n                    validYearMessage, validateDayInMonth, validDateMessage, today, dateEntered;\n\n                $(dob).find('.' + this.settings.errorClass).removeClass(this.settings.errorClass);\n                dayVal = $(dob).find(params[0]).find('input:text').val();\n                monthVal = $(dob).find(params[1]).find('input:text').val();\n                yearVal = $(dob).find(params[2]).find('input:text').val();\n                dobLength = dayVal.length + monthVal.length + yearVal.length;\n\n                if (params[3] && dobLength === 0) {\n                    this.dobErrorMessage = $.mage.__('This is a required field.');\n\n                    return false;\n                }\n\n                if (!params[3] && dobLength === 0) {\n                    return true;\n                }\n                day = parseInt(dayVal, 10) || 0;\n                month = parseInt(monthVal, 10) || 0;\n                year = parseInt(yearVal, 10) || 0;\n                curYear = new Date().getFullYear();\n\n                if (!day || !month || !year) {\n                    this.dobErrorMessage = $.mage.__('Please enter a valid full date.');\n\n                    return false;\n                }\n\n                if (month < 1 || month > 12) {\n                    this.dobErrorMessage = $.mage.__('Please enter a valid month (1-12).');\n\n                    return false;\n                }\n\n                if (year < 1900 || year > curYear) {\n                    validYearMessage = $.mage.__('Please enter a valid year (1900-%1).');\n                    this.dobErrorMessage = validYearMessage.replace('%1', curYear.toString());\n\n                    return false;\n                }\n                validateDayInMonth = new Date(year, month, 0).getDate();\n\n                if (day < 1 || day > validateDayInMonth) {\n                    validDateMessage = $.mage.__('Please enter a valid day (1-%1).');\n                    this.dobErrorMessage = validDateMessage.replace('%1', validateDayInMonth.toString());\n\n                    return false;\n                }\n                today = new Date();\n                dateEntered = new Date();\n                dateEntered.setFullYear(year, month - 1, day);\n\n                if (dateEntered > today) {\n                    this.dobErrorMessage = $.mage.__('Please enter a date from the past.');\n\n                    return false;\n                }\n\n                day = day % 10 === day ? '0' + day : day;\n                month = month % 10 === month ? '0' + month : month;\n                $(element).val(month + '/' + day + '/' + year);\n\n                return true;\n            },\n            function () {\n                return this.dobErrorMessage;\n            }\n        ]\n    }, function (i, rule) {\n        rule.unshift(i);\n        $.validator.addMethod.apply($.validator, rule);\n    });\n});\n","mage/validation/url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([], function () {\n    'use strict';\n\n    return {\n\n        /**\n         * Redirects to the url if it is considered safe\n         *\n         * @param {String} path - url to be redirected to\n         */\n        redirect: function (path) {\n            path = this.sanitize(path);\n\n            if (this.validate(path)) {\n                window.location.href = path;\n            }\n        },\n\n        /**\n         * Validates url\n         *\n         * @param {Object} path - url to be validated\n         * @returns {Boolean}\n         */\n        validate: function (path) {\n            var hostname = window.location.hostname;\n\n            if (path.indexOf(hostname) === -1 ||\n                path.indexOf('javascript:') !== -1 ||\n                path.indexOf('vbscript:') !== -1) {\n                return false;\n            }\n\n            return true;\n        },\n\n        /**\n         * Sanitize url, replacing disallowed chars\n         *\n         * @param {String} path - url to be normalized\n         * @returns {String}\n         */\n        sanitize: function (path) {\n            return path.replace('[^-A-Za-z0-9+&@#/%?=~_|!:,.;\\(\\)]', '');\n        }\n    };\n});\n","mage/utils/strings.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    var jsonRe = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/;\n\n    return {\n\n        /**\n         * Attempts to convert string to one of the primitive values,\n         * or to parse it as a valid json object.\n         *\n         * @param {String} str - String to be processed.\n         * @returns {*}\n         */\n        castString: function (str) {\n            try {\n                str = str === 'true' ? true :\n                    str === 'false' ? false :\n                        str === 'null' ? null :\n                            +str + '' === str ? +str :\n                                jsonRe.test(str) ? JSON.parse(str) :\n                                    str;\n            } catch (e) {\n            }\n\n            return str;\n        },\n\n        /**\n         * Splits string by separator if it's possible,\n         * otherwise returns the incoming value.\n         *\n         * @param {(String|Array|*)} str - String to split.\n         * @param {String} [separator=' '] - Seperator based on which to split the string.\n         * @returns {Array|*} Splitted string or the incoming value.\n         */\n        stringToArray: function (str, separator) {\n            separator = separator || ' ';\n\n            return typeof str === 'string' ?\n                str.split(separator) :\n                str;\n        },\n\n        /**\n         * Converts the incoming string which consists\n         * of a specified delimiters into a format commonly used in form elements.\n         *\n         * @param {String} name - The incoming string.\n         * @param {String} [separator='.']\n         * @returns {String} Serialized string.\n         *\n         * @example\n         *      utils.serializeName('one.two.three');\n         *      => 'one[two][three]';\n         */\n        serializeName: function (name, separator) {\n            var result;\n\n            separator = separator || '.';\n            name = name.split(separator);\n\n            result = name.shift();\n\n            name.forEach(function (part) {\n                result += '[' + part + ']';\n            });\n\n            return result;\n        },\n\n        /**\n         * Checks wether the incoming value is not empty,\n         * e.g. not 'null' or 'undefined'\n         *\n         * @param {*} value - Value to check.\n         * @returns {Boolean}\n         */\n        isEmpty: function (value) {\n            return value === '' || _.isUndefined(value) || _.isNull(value);\n        },\n\n        /**\n         * Adds 'prefix' to the 'part' value if it was provided.\n         *\n         * @param {String} prefix\n         * @param {String} part\n         * @returns {String}\n         */\n        fullPath: function (prefix, part) {\n            return prefix ? prefix + '.' + part : part;\n        },\n\n        /**\n         * Splits incoming string and returns its' part specified by offset.\n         *\n         * @param {String} parts\n         * @param {Number} [offset]\n         * @param {String} [delimiter=.]\n         * @returns {String}\n         */\n        getPart: function (parts, offset, delimiter) {\n            delimiter = delimiter || '.';\n            parts = parts.split(delimiter);\n            offset = this.formatOffset(parts, offset);\n\n            parts.splice(offset, 1);\n\n            return parts.join(delimiter) || '';\n        },\n\n        /**\n         * Converts nameThroughCamelCase to name-through-minus\n         *\n         * @param {String} string\n         * @returns {String}\n         */\n        camelCaseToMinus: function camelCaseToMinus(string) {\n            return ('' + string)\n                .split('')\n                .map(function (symbol, index) {\n                    return index ?\n                        symbol.toUpperCase() === symbol ?\n                        '-' + symbol.toLowerCase() :\n                            symbol :\n                        symbol.toLowerCase();\n                })\n                .join('');\n        },\n\n        /**\n         * Converts name-through-minus to nameThroughCamelCase\n         *\n         * @param {String} string\n         * @returns {String}\n         */\n        minusToCamelCase: function minusToCamelCase(string) {\n            return ('' + string)\n                .split('-')\n                .map(function (part, index) {\n                    return index ? part.charAt(0).toUpperCase() + part.slice(1) : part;\n                })\n                .join('');\n        }\n    };\n});\n","mage/utils/arrays.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    './strings'\n], function (_, utils) {\n    'use strict';\n\n    /**\n     * Defines index of an item in a specified container.\n     *\n     * @param {*} item - Item whose index should be defined.\n     * @param {Array} container - Container upon which to perform search.\n     * @returns {Number}\n     */\n    function getIndex(item, container) {\n        var index = container.indexOf(item);\n\n        if (~index) {\n            return index;\n        }\n\n        return _.findIndex(container, function (value) {\n            return value && value.name === item;\n        });\n    }\n\n    return {\n        /**\n         * Facade method to remove/add value from/to array\n         * without creating a new instance.\n         *\n         * @param {Array} arr - Array to be modified.\n         * @param {*} value - Value to add/remove.\n         * @param {Boolean} add - Flag that specfies operation.\n         * @returns {Utils} Chainable.\n         */\n        toggle: function (arr, value, add) {\n            return add ?\n                this.add(arr, value) :\n                this.remove(arr, value);\n        },\n\n        /**\n         * Removes the incoming value from array in case\n         * without creating a new instance of it.\n         *\n         * @param {Array} arr - Array to be modified.\n         * @param {*} value - Value to be removed.\n         * @returns {Utils} Chainable.\n         */\n        remove: function (arr, value) {\n            var index = arr.indexOf(value);\n\n            if (~index) {\n                arr.splice(index, 1);\n            }\n\n            return this;\n        },\n\n        /**\n         * Adds the incoming value to array if\n         * it's not alredy present in there.\n         *\n         * @param {Array} arr - Array to be modifed.\n         * @param {...*} arguments - Values to be added.\n         * @returns {Utils} Chainable.\n         */\n        add: function (arr) {\n            var values = _.toArray(arguments).slice(1);\n\n            values.forEach(function (value) {\n                if (!~arr.indexOf(value)) {\n                    arr.push(value);\n                }\n            });\n\n            return this;\n        },\n\n        /**\n         * Inserts specified item into container at a specified position.\n         *\n         * @param {*} item - Item to be inserted into container.\n         * @param {Array} container - Container of items.\n         * @param {*} [position=-1] - Position at which item should be inserted.\n         *      Position can represent:\n         *          - specific index in container\n         *          - item which might already be present in container\n         *          - structure with one of these properties: after, before\n         * @returns {Boolean|*}\n         *      - true if element has changed its' position\n         *      - false if nothing has changed\n         *      - inserted value if it wasn't present in container\n         */\n        insert: function (item, container, position) {\n            var currentIndex = getIndex(item, container),\n                newIndex,\n                target;\n\n            if (typeof position === 'undefined') {\n                position = -1;\n            } else if (typeof position === 'string') {\n                position = isNaN(+position) ? position : +position;\n            }\n\n            newIndex = position;\n\n            if (~currentIndex) {\n                target = container.splice(currentIndex, 1)[0];\n\n                if (typeof item === 'string') {\n                    item = target;\n                }\n            }\n\n            if (typeof position !== 'number') {\n                target = position.after || position.before || position;\n\n                newIndex = getIndex(target, container);\n\n                if (~newIndex && (position.after || newIndex >= currentIndex)) {\n                    newIndex++;\n                }\n            }\n\n            if (newIndex < 0) {\n                newIndex += container.length + 1;\n            }\n\n            container[newIndex] ?\n                container.splice(newIndex, 0, item) :\n                container[newIndex] = item;\n\n            return !~currentIndex ? item : currentIndex !== newIndex;\n        },\n\n        /**\n         * @param {Array} elems\n         * @param {Number} offset\n         * @return {Number|*}\n         */\n        formatOffset: function (elems, offset) {\n            if (utils.isEmpty(offset)) {\n                offset = -1;\n            }\n\n            offset = +offset;\n\n            if (offset < 0) {\n                offset += elems.length + 1;\n            }\n\n            return offset;\n        }\n    };\n});\n","mage/utils/compare.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore',\n    'mage/utils/objects'\n], function (_, utils) {\n    'use strict';\n\n    var result = [];\n\n    /**\n     * Checks if all of the provided arrays contains equal values.\n     *\n     * @param {(Boolean|Array)} [keepOrder=false]\n     * @param {Array} target\n     * @returns {Boolean}\n     */\n    function equalArrays(keepOrder, target) {\n        var args = _.toArray(arguments),\n            arrays;\n\n        if (!Array.isArray(keepOrder)) {\n            arrays      = args.slice(2);\n        } else {\n            target      = keepOrder;\n            keepOrder   = false;\n            arrays      = args.slice(1);\n        }\n\n        if (!arrays.length) {\n            return true;\n        }\n\n        return arrays.every(function (array) {\n            if (array === target) {\n                return true;\n            } else if (array.length !== target.length) {\n                return false;\n            } else if (!keepOrder) {\n                return !_.difference(target, array).length;\n            }\n\n            return array.every(function (value, index) {\n                return target[index] === value;\n            });\n        });\n    }\n\n    /**\n     * Checks if two values are different.\n     *\n     * @param {*} a - First value.\n     * @param {*} b - Second value.\n     * @returns {Boolean}\n     */\n    function isDifferent(a, b) {\n        var oldIsPrimitive = utils.isPrimitive(a);\n\n        if (Array.isArray(a) && Array.isArray(b)) {\n            return !equalArrays(true, a, b);\n        }\n\n        return oldIsPrimitive ? a !== b : true;\n    }\n\n    /**\n     * @param {String} prefix\n     * @param {String} part\n     */\n    function getPath(prefix, part) {\n        return prefix ? prefix + '.' + part : part;\n    }\n\n    /**\n     * Checks if object has own specified property.\n     *\n     * @param {*} obj - Value to be checked.\n     * @param {String} key - Key of the property.\n     * @returns {Boolean}\n     */\n    function hasOwn(obj, key) {\n        return Object.prototype.hasOwnProperty.call(obj, key);\n    }\n\n    /**\n     * @param {Array} changes\n     */\n    function getContainers(changes) {\n        var containers  = {},\n            indexed     = _.indexBy(changes, 'path');\n\n        _.each(indexed, function (change, name) {\n            var path;\n\n            name.split('.').forEach(function (part) {\n                path = getPath(path, part);\n\n                if (path in indexed) {\n                    return;\n                }\n\n                (containers[path] = containers[path] || []).push(change);\n            });\n        });\n\n        return containers;\n    }\n\n    /**\n     * @param {String} path\n     * @param {String} name\n     * @param {String} type\n     * @param {String} newValue\n     * @param {String} oldValue\n     */\n    function addChange(path, name, type, newValue, oldValue) {\n        var data;\n\n        data = {\n            path: path,\n            name: name,\n            type: type\n        };\n\n        if (type !== 'remove') {\n            data.value = newValue;\n            data.oldValue = oldValue;\n        } else {\n            data.oldValue = newValue;\n        }\n\n        result.push(data);\n    }\n\n    /**\n     * @param {String} ns\n     * @param {String} name\n     * @param {String} type\n     * @param {String} iterator\n     * @param {String} placeholder\n     */\n    function setAll(ns, name, type, iterator, placeholder) {\n        var key;\n\n        if (arguments.length > 4) {\n            type === 'add' ?\n                addChange(ns, name, 'update', iterator, placeholder) :\n                addChange(ns, name, 'update', placeholder, iterator);\n        } else {\n            addChange(ns, name, type, iterator);\n        }\n\n        if (!utils.isObject(iterator)) {\n            return;\n        }\n\n        for (key in iterator) {\n            if (hasOwn(iterator, key)) {\n                setAll(getPath(ns, key), key, type, iterator[key]);\n            }\n        }\n    }\n\n    /*eslint-disable max-depth*/\n    /**\n     * @param {Object} old\n     * @param {Object} current\n     * @param {String} ns\n     * @param {String} name\n     */\n    function compare(old, current, ns, name) {\n        var key,\n            oldIsObj = utils.isObject(old),\n            newIsObj = utils.isObject(current);\n\n        if (oldIsObj && newIsObj) {\n            for (key in old) {\n                if (hasOwn(old, key) && !hasOwn(current, key)) {\n                    setAll(getPath(ns, key), key, 'remove', old[key]);\n                }\n            }\n\n            for (key in current) {\n                if (hasOwn(current, key)) {\n                    hasOwn(old, key) ?\n                        compare(old[key], current[key], getPath(ns, key), key) :\n                        setAll(getPath(ns, key), key, 'add', current[key]);\n                }\n            }\n        } else if (oldIsObj) {\n            setAll(ns, name, 'remove', old, current);\n        } else if (newIsObj) {\n            setAll(ns, name, 'add', current, old);\n        } else if (isDifferent(old, current)) {\n            addChange(ns, name, 'update', current, old);\n        }\n    }\n\n    /*eslint-enable max-depth*/\n\n    return {\n\n        /**\n         *\n         * @returns {Object}\n         */\n        compare: function () {\n            var changes;\n\n            compare.apply(null, arguments);\n\n            changes = result.splice(0);\n\n            return {\n                containers: getContainers(changes),\n                changes: changes,\n                equal: !changes.length\n            };\n        },\n\n        equalArrays: equalArrays\n    };\n});\n","mage/utils/misc.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery',\n    'mage/utils/objects'\n], function (_, $, utils) {\n    'use strict';\n\n    var defaultAttributes,\n        ajaxSettings,\n        map;\n\n    defaultAttributes = {\n        method: 'post',\n        enctype: 'multipart/form-data'\n    };\n\n    ajaxSettings = {\n        default: {\n            method: 'POST',\n            cache: false,\n            processData: false,\n            contentType: false\n        },\n        simple: {\n            method: 'POST',\n            dataType: 'json'\n        }\n    };\n\n    map = {\n        'D': 'DDD',\n        'dd': 'DD',\n        'd': 'D',\n        'EEEE': 'dddd',\n        'EEE': 'ddd',\n        'e': 'd',\n        'yyyy': 'YYYY',\n        'yy': 'YY',\n        'y': 'YYYY',\n        'a': 'A'\n    };\n\n    return {\n\n        /**\n         * Generates a unique identifier.\n         *\n         * @param {Number} [size=7] - Length of a resulting identifier.\n         * @returns {String}\n         */\n        uniqueid: function (size) {\n            var code = Math.random() * 25 + 65 | 0,\n                idstr = String.fromCharCode(code);\n\n            size = size || 7;\n\n            while (idstr.length < size) {\n                code = Math.floor(Math.random() * 42 + 48);\n\n                if (code < 58 || code > 64) {\n                    idstr += String.fromCharCode(code);\n                }\n            }\n\n            return idstr;\n        },\n\n        /**\n         * Limits function call.\n         *\n         * @param {Object} owner\n         * @param {String} target\n         * @param {Number} limit\n         */\n        limit: function (owner, target, limit) {\n            var fn = owner[target];\n\n            owner[target] = _.debounce(fn.bind(owner), limit);\n        },\n\n        /**\n         * Converts mage date format to a moment.js format.\n         *\n         * @param {String} mageFormat\n         * @returns {String}\n         */\n        normalizeDate: function (mageFormat) {\n            var result = mageFormat;\n\n            _.each(map, function (moment, mage) {\n                result = result.replace(\n                    new RegExp(mage + '(?=([^\\u0027]*\\u0027[^\\u0027]*\\u0027)*[^\\u0027]*$)'),\n                    moment\n                );\n            });\n            result = result.replace(/'(.*?)'/g, '[$1]');\n            return result;\n        },\n\n        /**\n         * Puts provided value in range of min and max parameters.\n         *\n         * @param {Number} value - Value to be located.\n         * @param {Number} min - Min value.\n         * @param {Number} max - Max value.\n         * @returns {Number}\n         */\n        inRange: function (value, min, max) {\n            return Math.min(Math.max(min, value), max);\n        },\n\n        /**\n         * Serializes and sends data via POST request.\n         *\n         * @param {Object} options - Options object that consists of\n         *      a 'url' and 'data' properties.\n         * @param {Object} attrs - Attributes that will be added to virtual form.\n         */\n        submit: function (options, attrs) {\n            var form        = document.createElement('form'),\n                data        = utils.serialize(options.data),\n                attributes  = _.extend({}, defaultAttributes, attrs || {});\n\n            if (!attributes.action) {\n                attributes.action = options.url;\n            }\n\n            data['form_key'] = window.FORM_KEY;\n\n            _.each(attributes, function (value, name) {\n                form.setAttribute(name, value);\n            });\n\n            data = _.map(\n                data,\n                function (value, name) {\n                    return '<input type=\"hidden\" ' +\n                        'name=\"' + _.escape(name) + '\" ' +\n                        'value=\"' + _.escape(value) + '\"' +\n                        ' />';\n                }\n            ).join('');\n\n            form.insertAdjacentHTML('afterbegin', data);\n            document.body.appendChild(form);\n\n            form.submit();\n        },\n\n        /**\n         * Serializes and sends data via AJAX POST request.\n         *\n         * @param {Object} options - Options object that consists of\n         *      a 'url' and 'data' properties.\n         * @param {Object} config\n         */\n        ajaxSubmit: function (options, config) {\n            var t = new Date().getTime(),\n                settings;\n\n            options.data['form_key'] = window.FORM_KEY;\n            options.data = this.prepareFormData(options.data, config.ajaxSaveType);\n            settings = _.extend({}, ajaxSettings[config.ajaxSaveType], options || {});\n\n            if (!config.ignoreProcessEvents) {\n                $('body').trigger('processStart');\n            }\n\n            return $.ajax(settings)\n                .done(function (data) {\n                    if (config.response) {\n                        data.t = t;\n                        config.response.data(data);\n                        config.response.status(undefined);\n                        config.response.status(!data.error);\n                    }\n                })\n                .fail(function () {\n                    if (config.response) {\n                        config.response.status(undefined);\n                        config.response.status(false);\n                        config.response.data({\n                            error: true,\n                            messages: 'Something went wrong.',\n                            t: t\n                        });\n                    }\n                })\n                .always(function () {\n                    if (!config.ignoreProcessEvents) {\n                        $('body').trigger('processStop');\n                    }\n                });\n        },\n\n        /**\n         * Creates FormData object and append this data.\n         *\n         * @param {Object} data\n         * @param {String} type\n         * @returns {FormData}\n         */\n        prepareFormData: function (data, type) {\n            var formData;\n\n            if (type === 'default') {\n                formData = new FormData();\n                _.each(utils.serialize(data), function (val, name) {\n                    formData.append(name, val);\n                });\n            } else if (type === 'simple') {\n                formData = utils.serialize(data);\n            }\n\n            return formData;\n        },\n\n        /**\n         * Filters data object. Finds properties with suffix\n         * and sets their values to properties with the same name without suffix.\n         *\n         * @param {Object} data - The data object that should be filtered\n         * @param {String} suffix - The string by which data object should be filtered\n         * @param {String} separator - The string that is separator between property and suffix\n         *\n         * @returns {Object} Filtered data object\n         */\n        filterFormData: function (data, suffix, separator) {\n            data = data || {};\n            suffix = suffix || 'prepared-for-send';\n            separator = separator || '-';\n\n            _.each(data, function (value, key) {\n                if (_.isObject(value) && !Array.isArray(value)) {\n                    this.filterFormData(value, suffix, separator);\n                } else if (_.isString(key) && ~key.indexOf(suffix)) {\n                    data[key.split(separator)[0]] = value;\n                    delete data[key];\n                }\n            }, this);\n\n            return data;\n        },\n\n        /**\n         * Replaces special characters with their corresponding HTML entities.\n         *\n         * @param {String} string - Text to escape.\n         * @returns {String} Escaped text.\n         */\n        escape: function (string) {\n            return string ? $('<p></p>').text(string).html().replace(/\"/g, '&quot;') : string;\n        },\n\n        /**\n         * Replaces symbol codes with their unescaped counterparts.\n         *\n         * @param {String} data\n         *\n         * @returns {String}\n         */\n        unescape: function (data) {\n            var unescaped = _.unescape(data),\n                mapCharacters = {\n                    '&#039;': '\\''\n                };\n\n            _.each(mapCharacters, function (value, key) {\n                unescaped = unescaped.replace(key, value);\n            });\n\n            return unescaped;\n        },\n\n        /**\n         * Converts PHP IntlFormatter format to moment format.\n         *\n         * @param {String} format - PHP format\n         * @returns {String} - moment compatible formatting\n         */\n        convertToMomentFormat: function (format) {\n            var newFormat;\n\n            newFormat = format.replace(/yyyy|yy|y/, 'YYYY'); // replace the year\n            newFormat = newFormat.replace(/dd|d/g, 'DD'); // replace the date\n\n            return newFormat;\n        },\n\n        /**\n         * Get Url Parameters.\n         *\n         * @param {String} url - Url string\n         * @returns {Object}\n         */\n        getUrlParameters: function (url) {\n            var params = {},\n                queries = url.split('?'),\n                temp,\n                i,\n                l;\n\n            if (!queries[1]) {\n                return params;\n            }\n\n            queries = queries[1].split('&');\n\n            for (i = 0, l = queries.length; i < l; i++) {\n                temp = queries[i].split('=');\n\n                if (temp[1]) {\n                    params[temp[0]] = decodeURIComponent(temp[1].replace(/\\+/g, '%20'));\n                } else {\n                    params[temp[0]] = '';\n                }\n            }\n\n            return params;\n        }\n    };\n});\n","mage/utils/objects.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'ko',\n    'jquery',\n    'underscore',\n    'mage/utils/strings'\n], function (ko, $, _, stringUtils) {\n    'use strict';\n\n    var primitives = [\n        'undefined',\n        'boolean',\n        'number',\n        'string'\n    ];\n\n    /**\n     * Sets nested property of a specified object.\n     * @private\n     *\n     * @param {Object} parent - Object to look inside for the properties.\n     * @param {Array} path - Splitted path the property.\n     * @param {*} value - Value of the last property in 'path' array.\n     * returns {*} New value for the property.\n     */\n    function setNested(parent, path, value) {\n        var last = path.pop(),\n            len = path.length,\n            pi = 0,\n            part = path[pi];\n\n        for (; pi < len; part = path[++pi]) {\n            if (!_.isObject(parent[part])) {\n                parent[part] = {};\n            }\n\n            parent = parent[part];\n        }\n\n        if (typeof parent[last] === 'function') {\n            parent[last](value);\n        } else {\n            parent[last] = value;\n        }\n\n        return value;\n    }\n\n    /**\n     * Retrieves value of a nested property.\n     * @private\n     *\n     * @param {Object} parent - Object to look inside for the properties.\n     * @param {Array} path - Splitted path the property.\n     * @returns {*} Value of the property.\n     */\n    function getNested(parent, path) {\n        var exists = true,\n            len = path.length,\n            pi = 0;\n\n        for (; pi < len && exists; pi++) {\n            parent = parent[path[pi]];\n\n            if (typeof parent === 'undefined') {\n                exists = false;\n            }\n        }\n\n        if (exists) {\n            if (ko.isObservable(parent)) {\n                parent = parent();\n            }\n\n            return parent;\n        }\n    }\n\n    /**\n     * Removes property from a specified object.\n     * @private\n     *\n     * @param {Object} parent - Object from which to remove property.\n     * @param {Array} path - Splitted path to the property.\n     */\n    function removeNested(parent, path) {\n        var field = path.pop();\n\n        parent = getNested(parent, path);\n\n        if (_.isObject(parent)) {\n            delete parent[field];\n        }\n    }\n\n    return {\n\n        /**\n         * Retrieves or defines objects' property by a composite path.\n         *\n         * @param {Object} data - Container for the properties specified in path.\n         * @param {String} path - Objects' properties divided by dots.\n         * @param {*} [value] - New value for the last property.\n         * @returns {*} Returns value of the last property in chain.\n         *\n         * @example\n         *      utils.nested({}, 'one.two', 3);\n         *      => { one: {two: 3} }\n         */\n        nested: function (data, path, value) {\n            var action = arguments.length > 2 ? setNested : getNested;\n\n            path = path ? path.split('.') : [];\n\n            return action(data, path, value);\n        },\n\n        /**\n         * Removes nested property from an object.\n         *\n         * @param {Object} data - Data source.\n         * @param {String} path - Path to the property e.g. 'one.two.three'\n         */\n        nestedRemove: function (data, path) {\n            path = path.split('.');\n\n            removeNested(data, path);\n        },\n\n        /**\n         * Flattens objects' nested properties.\n         *\n         * @param {Object} data - Object to flatten.\n         * @param {String} [separator='.'] - Objects' keys separator.\n         * @returns {Object} Flattened object.\n         *\n         * @example Example with a default separator.\n         *      utils.flatten({one: { two: { three: 'value'} }});\n         *      => { 'one.two.three': 'value' };\n         *\n         * @example Example with a custom separator.\n         *      utils.flatten({one: { two: { three: 'value'} }}, '=>');\n         *      => {'one=>two=>three': 'value'};\n         */\n        flatten: function (data, separator, parent, result) {\n            separator = separator || '.';\n            result = result || {};\n\n            if (!data) {\n                return result;\n            }\n\n            // UnderscoreJS each breaks when an object has a length property so we use Object.keys\n            _.each(Object.keys(data), function (name) {\n                var node = data[name];\n\n                if ({}.toString.call(node) === '[object Function]') {\n                    return;\n                }\n\n                if (parent) {\n                    name = parent + separator + name;\n                }\n\n                typeof node === 'object' ?\n                    this.flatten(node, separator, name, result) :\n                    result[name] = node;\n\n            }, this);\n\n            return result;\n        },\n\n        /**\n         * Opposite operation of the 'flatten' method.\n         *\n         * @param {Object} data - Previously flattened object.\n         * @param {String} [separator='.'] - Keys separator.\n         * @returns {Object} Object with nested properties.\n         *\n         * @example Example using custom separator.\n         *      utils.unflatten({'one=>two': 'value'}, '=>');\n         *      => {\n         *          one: { two: 'value' }\n         *      };\n         */\n        unflatten: function (data, separator) {\n            var result = {};\n\n            separator = separator || '.';\n\n            _.each(data, function (value, nodes) {\n                nodes = nodes.split(separator);\n\n                setNested(result, nodes, value);\n            });\n\n            return result;\n        },\n\n        /**\n         * Same operation as 'flatten' method,\n         * but returns objects' keys wrapped in '[]'.\n         *\n         * @param {Object} data - Object that should be serialized.\n         * @returns {Object} Serialized data.\n         *\n         * @example\n         *      utils.serialize({one: { two: { three: 'value'} }});\n         *      => { 'one[two][three]': 'value' }\n         */\n        serialize: function (data) {\n            var result = {};\n\n            data = this.flatten(data);\n\n            _.each(data, function (value, keys) {\n                keys = stringUtils.serializeName(keys);\n                value = _.isUndefined(value) ? '' : value;\n\n                result[keys] = value;\n            }, this);\n\n            return result;\n        },\n\n        /**\n         * Performs deep extend of specified objects.\n         *\n         * @returns {Object|Array} Extended object.\n         */\n        extend: function () {\n            var args = _.toArray(arguments);\n\n            args.unshift(true);\n\n            return $.extend.apply($, args);\n        },\n\n        /**\n         * Performs a deep clone of a specified object.\n         *\n         * @param {(Object|Array)} data - Data that should be copied.\n         * @returns {Object|Array} Cloned object.\n         */\n        copy: function (data) {\n            var result = data,\n                isArray = Array.isArray(data),\n                placeholder;\n\n            if (this.isObject(data) || isArray) {\n                placeholder = isArray ? [] : {};\n                result = this.extend(placeholder, data);\n            }\n\n            return result;\n        },\n\n        /**\n         * Performs a deep clone of a specified object.\n         * Doesn't save links to original object.\n         *\n         * @param {*} original - Object to clone\n         * @returns {*}\n         */\n        hardCopy: function (original) {\n            if (original === null || typeof original !== 'object') {\n                return original;\n            }\n\n            return JSON.parse(JSON.stringify(original));\n        },\n\n        /**\n         * Removes specified nested properties from the target object.\n         *\n         * @param {Object} target - Object whose properties should be removed.\n         * @param {(...String|Array|Object)} list - List that specifies properties to be removed.\n         * @returns {Object} Modified object.\n         *\n         * @example Basic usage\n         *      var obj = {a: {b: 2}, c: 'a'};\n         *\n         *      omit(obj, 'a.b');\n         *      => {'a.b': 2};\n         *      obj => {a: {}, c: 'a'};\n         *\n         * @example Various syntaxes that would return same result\n         *      omit(obj, ['a.b', 'c']);\n         *      omit(obj, 'a.b', 'c');\n         *      omit(obj, {'a.b': true, 'c': true});\n         */\n        omit: function (target, list) {\n            var removed = {},\n                ignored = list;\n\n            if (this.isObject(list)) {\n                ignored = [];\n\n                _.each(list, function (value, key) {\n                    if (value) {\n                        ignored.push(key);\n                    }\n                });\n            } else if (_.isString(list)) {\n                ignored = _.toArray(arguments).slice(1);\n            }\n\n            _.each(ignored, function (path) {\n                var value = this.nested(target, path);\n\n                if (!_.isUndefined(value)) {\n                    removed[path] = value;\n\n                    this.nestedRemove(target, path);\n                }\n            }, this);\n\n            return removed;\n        },\n\n        /**\n         * Checks if provided value is a plain object.\n         *\n         * @param {*} value - Value to be checked.\n         * @returns {Boolean}\n         */\n        isObject: function (value) {\n            var objProto = Object.prototype;\n\n            return typeof value == 'object' ?\n            objProto.toString.call(value) === '[object Object]' :\n                false;\n        },\n\n        /**\n         *\n         * @param {*} value\n         * @returns {Boolean}\n         */\n        isPrimitive: function (value) {\n            return value === null || ~primitives.indexOf(typeof value);\n        },\n\n        /**\n         * Iterates over obj props/array elems recursively, applying action to each one\n         *\n         * @param {Object|Array} data - Data to be iterated.\n         * @param {Function} action - Callback to be called with each item as an argument.\n         * @param {Number} [maxDepth=7] - Max recursion depth.\n         */\n        forEachRecursive: function (data, action, maxDepth) {\n            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n                return;\n            }\n\n            if (!_.isObject(data)) {\n                action(data);\n\n                return;\n            }\n\n            _.each(data, function (value) {\n                this.forEachRecursive(value, action, maxDepth);\n            }, this);\n\n            action(data);\n        },\n\n        /**\n         * Maps obj props/array elems recursively\n         *\n         * @param {Object|Array} data - Data to be iterated.\n         * @param {Function} action - Callback to transform each item.\n         * @param {Number} [maxDepth=7] - Max recursion depth.\n         *\n         * @returns {Object|Array}\n         */\n        mapRecursive: function (data, action, maxDepth) {\n            var newData;\n\n            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n                return data;\n            }\n\n            if (!_.isObject(data)) {\n                return action(data);\n            }\n\n            if (_.isArray(data)) {\n                newData = _.map(data, function (item) {\n                    return this.mapRecursive(item, action, maxDepth);\n                }, this);\n\n                return action(newData);\n            }\n\n            newData = _.mapObject(data, function (val, key) {\n                if (data.hasOwnProperty(key)) {\n                    return this.mapRecursive(val, action, maxDepth);\n                }\n\n                return val;\n            }, this);\n\n            return action(newData);\n        },\n\n        /**\n         * Removes empty(in common sence) obj props/array elems\n         *\n         * @param {*} data - Data to be cleaned.\n         * @returns {*}\n         */\n        removeEmptyValues: function (data) {\n            if (!_.isObject(data)) {\n                return data;\n            }\n\n            if (_.isArray(data)) {\n                return data.filter(function (item) {\n                    return !this.isEmptyObj(item);\n                }, this);\n            }\n\n            return _.omit(data, this.isEmptyObj.bind(this));\n        },\n\n        /**\n         * Checks that argument of any type is empty in common sence:\n         * empty string, string with spaces only, object without own props, empty array, null or undefined\n         *\n         * @param {*} val - Value to be checked.\n         * @returns {Boolean}\n         */\n        isEmptyObj: function (val) {\n\n            return _.isObject(val) && _.isEmpty(val) ||\n            this.isEmpty(val) ||\n            val && val.trim && this.isEmpty(val.trim());\n        }\n    };\n});\n\n","mage/utils/template.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable no-shadow */\n\ndefine([\n    'jquery',\n    'underscore',\n    'mage/utils/objects',\n    'mage/utils/strings'\n], function ($, _, utils, stringUtils) {\n    'use strict';\n\n    var tmplSettings = _.templateSettings,\n        interpolate = /\\$\\{([\\s\\S]+?)\\}/g,\n        opener = '${',\n        template,\n        hasStringTmpls;\n\n    /**\n     * Identifies whether ES6 templates are supported.\n     */\n    hasStringTmpls = (function () {\n        var testString = 'var foo = \"bar\"; return `${ foo }` === foo';\n\n        try {\n            return Function(testString)();\n        } catch (e) {\n            return false;\n        }\n    })();\n\n    /**\n     * Objects can specify how to use templating for their properties - getting that configuration.\n     *\n     * To disable rendering for all properties of your object add __disableTmpl: true.\n     * To disable for specific property add __disableTmpl: {propertyName: true}.\n     * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}.\n     *\n     * @param {String} tmpl\n     * @param {Object | undefined} target\n     * @returns {Boolean|Object}\n     */\n    function isTmplIgnored(tmpl, target) {\n        var parsedTmpl;\n\n        try {\n            parsedTmpl = JSON.parse(tmpl);\n\n            if (typeof parsedTmpl === 'object') {\n                return tmpl.includes('__disableTmpl');\n            }\n        } catch (e) {\n        }\n\n        if (typeof target !== 'undefined') {\n            if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) {\n                return target.__disableTmpl;\n            }\n        }\n\n        return false;\n\n    }\n\n    if (hasStringTmpls) {\n\n        /*eslint-disable no-unused-vars, no-eval*/\n        /**\n         * Evaluates template string using ES6 templates.\n         *\n         * @param {String} tmpl - Template string.\n         * @param {Object} $ - Data object used in a template.\n         * @returns {String} Compiled template.\n         */\n        template = function (tmpl, $) {\n            return eval('`' + tmpl + '`');\n        };\n\n        /*eslint-enable no-unused-vars, no-eval*/\n    } else {\n\n        /**\n         * Fallback function used when ES6 templates are not supported.\n         * Uses underscore templates renderer.\n         *\n         * @param {String} tmpl - Template string.\n         * @param {Object} data - Data object used in a template.\n         * @returns {String} Compiled template.\n         */\n        template = function (tmpl, data) {\n            var cached = tmplSettings.interpolate;\n\n            tmplSettings.interpolate = interpolate;\n\n            tmpl = _.template(tmpl, {\n                variable: '$'\n            })(data);\n\n            tmplSettings.interpolate = cached;\n\n            return tmpl;\n        };\n    }\n\n    /**\n     * Checks if provided value contains template syntax.\n     *\n     * @param {*} value - Value to be checked.\n     * @returns {Boolean}\n     */\n    function isTemplate(value) {\n        return typeof value === 'string' &&\n            value.indexOf(opener) !== -1 &&\n            // the below pattern almost always indicates an accident which should not cause template evaluation\n            // refuse to evaluate\n            value.indexOf('${{') === -1;\n    }\n\n    /**\n     * Iteratively processes provided string\n     * until no templates syntax will be found.\n     *\n     * @param {String} tmpl - Template string.\n     * @param {Object} data - Data object used in a template.\n     * @param {Boolean} [castString=false] - Flag that indicates whether template\n     *      should be casted after evaluation to a value of another type or\n     *      that it should be leaved as a string.\n     * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0.\n     * @returns {*} Compiled template.\n     */\n    function render(tmpl, data, castString, maxCycles) {\n        var last = tmpl,\n            cycles = 0;\n\n        while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) {\n            if (!isTmplIgnored(tmpl)) {\n                tmpl = template(tmpl, data);\n            }\n\n            if (tmpl === last) {\n                break;\n            }\n\n            last = tmpl;\n            cycles++;\n        }\n\n        return castString ?\n            stringUtils.castString(tmpl) :\n            tmpl;\n    }\n\n    return {\n\n        /**\n         * Applies provided data to the template.\n         *\n         * @param {Object|String} tmpl\n         * @param {Object} [data] - Data object to match with template.\n         * @param {Boolean} [castString=false] - Flag that indicates whether template\n         *      should be casted after evaluation to a value of another type or\n         *      that it should be leaved as a string.\n         * @returns {*}\n         *\n         * @example Template defined as a string.\n         *      var source = { foo: 'Random Stuff', bar: 'Some' };\n         *\n         *      utils.template('${ $.bar } ${ $.foo }', source);\n         *      => 'Some Random Stuff';\n         *\n         * @example Template defined as an object.\n         *      var tmpl = {\n         *              key: {'${ $.$data.bar }': '${ $.$data.foo }'},\n         *              foo: 'bar',\n         *              x1: 2, x2: 5,\n         *              delta: '${ $.x2 - $.x1 }',\n         *              baz: 'Upper ${ $.foo.toUpperCase() }'\n         *      };\n         *\n         *      utils.template(tmpl, source);\n         *      => {\n         *          key: {'Some': 'Random Stuff'},\n         *          foo: 'bar',\n         *          x1: 2, x2: 5,\n         *          delta: 3,\n         *          baz: 'Upper BAR'\n         *      };\n         */\n        template: function (tmpl, data, castString, dontClone) {\n            if (typeof tmpl === 'string') {\n                return render(tmpl, data, castString);\n            }\n\n            if (!dontClone) {\n                tmpl = utils.copy(tmpl);\n            }\n\n            tmpl.$data = data || {};\n\n            /**\n             * Template iterator function.\n             */\n            _.each(tmpl, function iterate(value, key, list) {\n                var disabled,\n                    maxCycles;\n\n                if (key === '$data') {\n                    return;\n                }\n\n                if (isTemplate(key)) {\n                    delete list[key];\n\n                    key = render(key, tmpl);\n                    list[key] = value;\n                }\n\n                if (isTemplate(value)) {\n                    //Getting template disabling settings, can be true for all disabled and separate settings\n                    //for each property.\n                    disabled = isTmplIgnored(value, list);\n\n                    if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) {\n                        //Checking if specific settings for a property provided.\n                        maxCycles = disabled[key];\n                    }\n\n                    if (disabled === true || maxCycles === true) {\n                        //Rendering for all properties is disabled.\n                        maxCycles = 0;\n                    }\n\n                    list[key] = render(value, tmpl, castString, maxCycles);\n                } else if ($.isPlainObject(value) || Array.isArray(value)) {\n                    _.each(value, iterate);\n                }\n            });\n\n            delete tmpl.$data;\n\n            return tmpl;\n        }\n    };\n});\n","mage/utils/wrapper.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Utility methods used to wrap and extend functions.\n *\n * @example Usage of a 'wrap' method with arguments delegation.\n *      var multiply = function (a, b) {\n *          return a * b;\n *      };\n *\n *      multiply = module.wrap(multiply, function (orig) {\n *          return 'Result is: ' + orig();\n *      });\n *\n *      multiply(2, 2);\n *      => 'Result is: 4'\n *\n * @example Usage of 'wrapSuper' method.\n *      var multiply = function (a, b) {\n *         return a * b;\n *      };\n *\n *      var obj = {\n *          multiply: module.wrapSuper(multiply, function () {\n *              return 'Result is: ' + this._super();\n *          });\n *      };\n *\n *      obj.multiply(2, 2);\n *      => 'Result is: 4'\n */\ndefine([\n    'underscore'\n], function (_) {\n    'use strict';\n\n    /**\n     * Checks if string has a '_super' substring.\n     */\n    var superReg = /\\b_super\\b/;\n\n    return {\n\n        /**\n         * Wraps target function with a specified wrapper, which will receive\n         * reference to the original function as a first argument.\n         *\n         * @param {Function} target - Function to be wrapped.\n         * @param {Function} wrapper - Wrapper function.\n         * @returns {Function} Wrapper function.\n         */\n        wrap: function (target, wrapper) {\n            if (!_.isFunction(target) || !_.isFunction(wrapper)) {\n                return wrapper;\n            }\n\n            return function () {\n                var args    = _.toArray(arguments),\n                    ctx     = this,\n                    _super;\n\n                /**\n                 * Function that will be passed to the wrapper.\n                 * If no arguments will be passed to it, then the original\n                 * function will be called with an arguments of a wrapper function.\n                 */\n                _super = function () {\n                    var superArgs = arguments.length ? arguments : args.slice(1);\n\n                    return target.apply(ctx, superArgs);\n                };\n\n                args.unshift(_super);\n\n                return wrapper.apply(ctx, args);\n            };\n        },\n\n        /**\n         * Wraps the incoming function to implement support of the '_super' method.\n         *\n         * @param {Function} target - Function to be wrapped.\n         * @param {Function} wrapper - Wrapper function.\n         * @returns {Function} Wrapped function.\n         */\n        wrapSuper: function (target, wrapper) {\n            if (!this.hasSuper(wrapper) || !_.isFunction(target)) {\n                return wrapper;\n            }\n\n            return function () {\n                var _super  = this._super,\n                    args    = arguments,\n                    result;\n\n                /**\n                 * Temporary define '_super' method which\n                 * contains call to the original function.\n                 */\n                this._super = function () {\n                    var superArgs = arguments.length ? arguments : args;\n\n                    return target.apply(this, superArgs);\n                };\n\n                result = wrapper.apply(this, args);\n\n                this._super = _super;\n\n                return result;\n            };\n        },\n\n        /**\n         * Checks wether the incoming method contains calls of the '_super' method.\n         *\n         * @param {Function} fn - Function to be checked.\n         * @returns {Boolean}\n         */\n        hasSuper: function (fn) {\n            return _.isFunction(fn) && superReg.test(fn);\n        },\n\n        /**\n         * Extends target object with provided extenders.\n         * If property in target and extender objects is a function,\n         * then it will be wrapped using 'wrap' method.\n         *\n         * @param {Object} target - Object to be extended.\n         * @param {...Object} extenders - Multiple extenders objects.\n         * @returns {Object} Modified target object.\n         */\n        extend: function (target) {\n            var extenders = _.toArray(arguments).slice(1),\n                iterator = this._extend.bind(this, target);\n\n            extenders.forEach(iterator);\n\n            return target;\n        },\n\n        /**\n         * Same as the 'extend' method, but operates only on one extender object.\n         *\n         * @private\n         * @param {Object} target\n         * @param {Object} extender\n         */\n        _extend: function (target, extender) {\n            _.each(extender, function (value, key) {\n                target[key] = this.wrap(target[key], extender[key]);\n            }, this);\n        }\n    };\n});\n","mage/utils/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(function (require) {\n    'use strict';\n\n    var utils = {},\n        _ = require('underscore'),\n        root = typeof self == 'object' && self.self === self && self ||\n            typeof global == 'object' && global.global === global && global ||\n            Function('return this')() || {};\n\n    root._ = _;\n\n    return _.extend(\n        utils,\n        require('./arrays'),\n        require('./compare'),\n        require('./misc'),\n        require('./objects'),\n        require('./strings'),\n        require('./template')\n    );\n});\n","mage/apply/scripts.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery'\n], function (_, $) {\n    'use strict';\n\n    var scriptSelector = 'script[type=\"text/x-magento-init\"]',\n        dataAttr = 'data-mage-init',\n        virtuals = [];\n\n    /**\n     * Adds components to the virtual list.\n     *\n     * @param {Object} components\n     */\n    function addVirtual(components) {\n        virtuals.push({\n            el: false,\n            data: components\n        });\n    }\n\n    /**\n     * Merges provided data with a current data\n     * of a elements' \"data-mage-init\" attribute.\n     *\n     * @param {Object} components - Object with components and theirs configuration.\n     * @param {HTMLElement} elem - Element whose data should be modified.\n     */\n    function setData(components, elem) {\n        var data = elem.getAttribute(dataAttr);\n\n        data = data ? JSON.parse(data) : {};\n        _.each(components, function (obj, key) {\n            if (_.has(obj, 'mixins')) {\n                data[key] = data[key] || {};\n                data[key].mixins = data[key].mixins || [];\n                data[key].mixins = data[key].mixins.concat(obj.mixins);\n                delete obj.mixins;\n            }\n        });\n\n        data = $.extend(true, data, components);\n        data = JSON.stringify(data);\n        elem.setAttribute(dataAttr, data);\n    }\n\n    /**\n     * Search for the elements by privded selector and extends theirs data.\n     *\n     * @param {Object} components - Object with components and theirs configuration.\n     * @param {String} selector - Selector for the elements.\n     */\n    function processElems(components, selector) {\n        var elems,\n            iterator;\n\n        if (selector === '*') {\n            addVirtual(components);\n\n            return;\n        }\n\n        elems = document.querySelectorAll(selector);\n        iterator = setData.bind(null, components);\n\n        _.toArray(elems).forEach(iterator);\n    }\n\n    /**\n     * Parses content of a provided script node.\n     * Note: node will be removed from DOM.\n     *\n     * @param {HTMLScriptElement} node - Node to be processed.\n     * @returns {Object}\n     */\n    function getNodeData(node) {\n        var data = node.textContent;\n\n        node.parentNode.removeChild(node);\n\n        return JSON.parse(data);\n    }\n\n    /**\n     * Parses 'script' tags with a custom type attribute and moves it's data\n     * to a 'data-mage-init' attribute of an element found by provided selector.\n     * Note: All found script nodes will be removed from DOM.\n     *\n     * @returns {Array} An array of components not assigned to the specific element.\n     *\n     * @example Sample declaration.\n     *      <script type=\"text/x-magento-init\">\n     *          {\n     *              \"body\": {\n     *                  \"path/to/component\": {\"foo\": \"bar\"}\n     *              }\n     *          }\n     *      </script>\n     *\n     * @example Providing data without selector.\n     *      {\n     *          \"*\": {\n     *              \"path/to/component\": {\"bar\": \"baz\"}\n     *          }\n     *      }\n     */\n    return function () {\n        var nodes = document.querySelectorAll(scriptSelector);\n\n        _.toArray(nodes)\n            .map(getNodeData)\n            .forEach(function (item) {\n                _.each(item, processElems);\n            });\n\n        return virtuals.splice(0, virtuals.length);\n    };\n});\n","mage/apply/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'jquery',\n    './scripts'\n], function (_, $, processScripts) {\n    'use strict';\n\n    var dataAttr = 'data-mage-init',\n        nodeSelector = '[' + dataAttr + ']';\n\n    /**\n     * Initializes components assigned to a specified element via data-* attribute.\n     *\n     * @param {HTMLElement} el - Element to initialize components with.\n     * @param {Object|String} config - Initial components' config.\n     * @param {String} component - Components' path.\n     */\n    function init(el, config, component) {\n        require([component], function (fn) {\n            var $el;\n\n            if (typeof fn === 'object') {\n                fn = fn[component].bind(fn);\n            }\n\n            if (_.isFunction(fn)) {\n                fn = fn.bind(null, config, el);\n            } else {\n                $el = $(el);\n\n                if ($el[component]) {\n                    // eslint-disable-next-line jquery-no-bind-unbind\n                    fn = $el[component].bind($el, config);\n                }\n            }\n            // Init module in separate task to prevent blocking main thread.\n            setTimeout(fn);\n        }, function (error) {\n            if ('console' in window && typeof window.console.error === 'function') {\n                console.error(error);\n            }\n\n            return true;\n        });\n    }\n\n    /**\n     * Parses elements 'data-mage-init' attribute as a valid JSON data.\n     * Note: data-mage-init attribute will be removed.\n     *\n     * @param {HTMLElement} el - Element whose attribute should be parsed.\n     * @returns {Object}\n     */\n    function getData(el) {\n        var data = el.getAttribute(dataAttr);\n\n        el.removeAttribute(dataAttr);\n\n        return {\n            el: el,\n            data: JSON.parse(data)\n        };\n    }\n\n    return {\n        /**\n         * Initializes components assigned to HTML elements via [data-mage-init].\n         *\n         * @example Sample 'data-mage-init' declaration.\n         *      data-mage-init='{\"path/to/component\": {\"foo\": \"bar\"}}'\n         */\n        apply: function (context) {\n            var virtuals = processScripts(!context ? document : context),\n                nodes = document.querySelectorAll(nodeSelector);\n\n            _.toArray(nodes)\n                .map(getData)\n                .concat(virtuals)\n                .forEach(function (itemContainer) {\n                    var element = itemContainer.el;\n\n                    _.each(itemContainer.data, function (obj, key) {\n                            if (obj.mixins) {\n                                require(obj.mixins, function () { //eslint-disable-line max-nested-callbacks\n                                    var i, len;\n\n                                    for (i = 0, len = arguments.length; i < len; i++) {\n                                        $.extend(\n                                            true,\n                                            itemContainer.data[key],\n                                            arguments[i](itemContainer.data[key], element)\n                                        );\n                                    }\n\n                                    delete obj.mixins;\n                                    init.call(null, element, obj, key);\n                                });\n                            } else {\n                                init.call(null, element, obj, key);\n                            }\n\n                        }\n                    );\n\n                });\n        },\n        applyFor: init\n    };\n});\n","mage/requirejs/text.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/* inspired by http://github.com/requirejs/text */\n/*global XDomainRequest */\n\ndefine(['module'], function (module) {\n    'use strict';\n\n    var xmlRegExp = /^\\s*<\\?xml(\\s)+version=[\\'\\\"](\\d)*.(\\d)*[\\'\\\"](\\s)*\\?>/im,\n        bodyRegExp = /<body[^>]*>\\s*([\\s\\S]+)\\s*<\\/body>/im,\n        stripReg = /!strip$/i,\n        defaultConfig = module.config && module.config() || {};\n\n    /**\n     * Strips <?xml ...?> declarations so that external SVG and XML documents can be\n     * added to a document without worry.\n     * Also, if the string is an HTML document, only the part inside the body tag is returned.\n     *\n     * @param {String} external\n     * @returns {String}\n     */\n    function stripContent(external) {\n        var matches;\n\n        if (!external) {\n            return '';\n        }\n\n        matches = external.match(bodyRegExp);\n        external = matches ?\n            matches[1] :\n            external.replace(xmlRegExp, '');\n\n        return external;\n    }\n\n    /**\n     * Checks that url match current location\n     *\n     * @param {String} url\n     * @returns {Boolean}\n     */\n    function sameDomain(url) {\n        var uProtocol, uHostName, uPort,\n            xdRegExp = /^([\\w:]+)?\\/\\/([^\\/\\\\]+)/i,\n            location = window.location,\n            match = xdRegExp.exec(url);\n\n        if (!match) {\n            return true;\n        }\n        uProtocol = match[1];\n        uHostName = match[2];\n\n        uHostName = uHostName.split(':');\n        uPort = uHostName[1] || '';\n        uHostName = uHostName[0];\n\n        return (!uProtocol || uProtocol === location.protocol) &&\n            (!uHostName || uHostName.toLowerCase() === location.hostname.toLowerCase()) &&\n            (!uPort && !uHostName || uPort === location.port);\n    }\n\n    /**\n     * @returns {XMLHttpRequest|XDomainRequest|null}\n     */\n    function createRequest(url) {\n        var xhr = new XMLHttpRequest();\n\n        if (!sameDomain(url) && typeof XDomainRequest !== 'undefined') {\n            xhr = new XDomainRequest();\n        }\n\n        return xhr;\n    }\n\n    /**\n     * XHR requester. Returns value to callback.\n     *\n     * @param {String} url\n     * @param {Function} callback\n     * @param {Function} fail\n     * @param {Object} headers\n     */\n    function getContent(url, callback, fail, headers) {\n        var xhr = createRequest(url),\n            header;\n\n        xhr.open('GET', url);\n\n        /*eslint-disable max-depth */\n        if ('setRequestHeader' in xhr && headers) {\n            for (header in headers) {\n                if (headers.hasOwnProperty(header)) {\n                    xhr.setRequestHeader(header.toLowerCase(), headers[header]);\n                }\n            }\n        }\n\n        /**\n         * @inheritdoc\n         */\n        xhr.onreadystatechange = function () {\n            var status, err;\n\n            //Do not explicitly handle errors, those should be\n            //visible via console output in the browser.\n            if (xhr.readyState === 4) {\n                status = xhr.status || 0;\n\n                if (status > 399 && status < 600) {\n                    //An http 4xx or 5xx error. Signal an error.\n                    err = new Error(url + ' HTTP status: ' + status);\n                    err.xhr = xhr;\n\n                    if (fail) {\n                        fail(err);\n                    }\n                } else {\n                    callback(xhr.responseText);\n\n                    if (defaultConfig.onXhrComplete) {\n                        defaultConfig.onXhrComplete(xhr, url);\n                    }\n                }\n            }\n        };\n\n        /*eslint-enable max-depth */\n\n        if (defaultConfig.onXhr) {\n            defaultConfig.onXhr(xhr, url);\n        }\n\n        xhr.send();\n    }\n\n    /**\n     * Main method used by RequireJs.\n     *\n     * @param {String} name - has format: some.module.filext!strip\n     * @param {Function} req\n     * @param {Function|undefined} onLoad\n     */\n    function loadContent(name, req, onLoad) {\n\n        var toStrip = stripReg.test(name),\n            url = req.toUrl(name.replace(stripReg, '')),\n            headers = defaultConfig.headers;\n\n        getContent(url, function (content) {\n                content = toStrip ? stripContent(content) : content;\n                onLoad(content);\n            }, onLoad.error, headers);\n    }\n\n    return {\n        load: loadContent,\n        get: getContent\n    };\n});\n","mage/requirejs/baseUrlResolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Sample configuration:\n *\n require.config({\n        \"config\": {\n            \"baseUrlInterceptor\": {\n                \"Magento_Ui/js/lib/knockout/bindings/collapsible.js\": \"../../../../frontend/Magento/luma/en_US/\"\n            }\n        }\n    });\n */\n\n/* global jsSuffixRegExp */\n/* eslint-disable max-depth */\ndefine('baseUrlInterceptor', [\n    'module'\n], function (module) {\n    'use strict';\n\n    /**\n     * RequireJS Context object\n     */\n    var ctx = require.s.contexts._,\n\n        /**\n         * Original function\n         *\n         * @type {Function}\n         */\n        origNameToUrl = ctx.nameToUrl,\n\n        /**\n         * Original function\n         *\n         * @type {Function}\n         */\n        newContextConstr = require.s.newContext;\n\n    /**\n     * Remove dots from URL\n     *\n     * @param {Array} ary\n     */\n    function trimDots(ary) {\n        var i, part, length = ary.length;\n\n        for (i = 0; i < length; i++) {\n            part = ary[i];\n\n            if (part === '.') {\n                ary.splice(i, 1);\n                i -= 1;\n            } else if (part === '..') {\n                if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {\n                    //End of the line. Keep at least one non-dot\n                    //path segment at the front so it can be mapped\n                    //correctly to disk. Otherwise, there is likely\n                    //no path mapping for a path starting with '..'.\n                    //This can still fail, but catches the most reasonable\n                    //uses of ..\n                    break;\n                } else if (i > 0) {\n                    ary.splice(i - 1, 2);\n                    i -= 2;\n                }\n            }\n        }\n    }\n\n    /**\n     * Normalize URL string (remove '/../')\n     *\n     * @param {String} name\n     * @param {String} baseName\n     * @param {Object} applyMap\n     * @param {Object} localContext\n     * @returns {*}\n     */\n    function normalize(name, baseName, applyMap, localContext) {\n        var lastIndex,\n            baseParts = baseName && baseName.split('/'),\n            normalizedBaseParts = baseParts;\n\n        //Adjust any relative paths.\n        if (name && name.charAt(0) === '.') {\n            //If have a base name, try to normalize against it,\n            //otherwise, assume it is a top-level require that will\n            //be relative to baseUrl in the end.\n            if (baseName) {\n                //Convert baseName to array, and lop off the last part,\n                //so that . matches that 'directory' and not name of the baseName's\n                //module. For instance, baseName of 'one/two/three', maps to\n                //'one/two/three.js', but we want the directory, 'one/two' for\n                //this normalization.\n                normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);\n                name = name.split('/');\n                lastIndex = name.length - 1;\n\n                // If wanting node ID compatibility, strip .js from end\n                // of IDs. Have to do this here, and not in nameToUrl\n                // because node allows either .js or non .js to map\n                // to same file.\n                if (localContext.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n                    name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n                }\n\n                name = normalizedBaseParts.concat(name);\n                trimDots(name);\n                name = name.join('/');\n            } else if (name.indexOf('./') === 0) {\n                // No baseName, so this is ID is resolved relative\n                // to baseUrl, pull off the leading dot.\n                name = name.substring(2);\n            }\n        }\n\n        return name;\n    }\n\n    /**\n     * Get full url.\n     *\n     * @param {Object} context\n     * @param {String} url\n     * @return {String}\n     */\n    function getUrl(context, url) {\n        var baseUrl = context.config.baseUrl,\n            newConfig = context.config,\n            modulePath = url.replace(baseUrl, ''),\n            newBaseUrl,\n            rewrite = module.config()[modulePath];\n\n        if (!rewrite) {\n            return url;\n        }\n\n        newBaseUrl = normalize(rewrite, baseUrl, undefined, newConfig);\n\n        return newBaseUrl + modulePath;\n    }\n\n    /**\n     * Replace original function.\n     *\n     * @returns {*}\n     */\n    ctx.nameToUrl = function () {\n        return getUrl(ctx, origNameToUrl.apply(ctx, arguments));\n    };\n\n    /**\n     * Replace original function.\n     *\n     * @return {*}\n     */\n    require.s.newContext = function () {\n        var newCtx = newContextConstr.apply(require.s, arguments),\n            newOrigNameToUrl = newCtx.nameToUrl;\n\n        /**\n         * New implementation of native function.\n         *\n         * @returns {String}\n         */\n        newCtx.nameToUrl = function () {\n            return getUrl(newCtx, newOrigNameToUrl.apply(newCtx, arguments));\n        };\n\n        return newCtx;\n    };\n});\n\nrequire(['baseUrlInterceptor'], function () {\n    'use strict';\n\n});\n","mage/requirejs/resolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'underscore',\n    'domReady!'\n], function (_) {\n    'use strict';\n\n    var context = require.s.contexts._,\n        execCb = context.execCb,\n        registry = context.registry,\n        callbacks = [],\n        retries = 10,\n        updateDelay = 1,\n        ready,\n        update;\n\n    /**\n     * Checks if provided callback already exists in the callbacks list.\n     *\n     * @param {Object} callback - Callback object to be checked.\n     * @returns {Boolean}\n     */\n    function isSubscribed(callback) {\n        return !!_.findWhere(callbacks, callback);\n    }\n\n    /**\n     * Checks if provided module is rejected during load.\n     *\n     * @param {Object} module - Module to be checked.\n     * @return {Boolean}\n     */\n    function isRejected(module) {\n        return registry[module.id] && (registry[module.id].inited || registry[module.id].error);\n    }\n\n    /**\n     * Checks if provided module had path fallback triggered.\n     *\n     * @param {Object} module - Module to be checked.\n     * @return {Boolean}\n     */\n    function isPathFallback(module) {\n        return registry[module.id] && registry[module.id].events.error;\n    }\n\n    /**\n     * Checks if provided module has unresolved dependencies.\n     *\n     * @param {Object} module - Module to be checked.\n     * @returns {Boolean}\n     */\n    function isPending(module) {\n        if (!module.depCount) {\n            return false;\n        }\n\n        return module.depCount >\n            _.filter(module.depMaps, isRejected).length + _.filter(module.depMaps, isPathFallback).length;\n    }\n\n    /**\n     * Checks if requirejs's registry object contains pending modules.\n     *\n     * @returns {Boolean}\n     */\n    function hasPending() {\n        return _.some(registry, isPending);\n    }\n\n    /**\n     * Checks if 'resolver' module is in ready\n     * state and that there are no pending modules.\n     *\n     * @returns {Boolean}\n     */\n    function isReady() {\n        return ready && !hasPending();\n    }\n\n    /**\n     * Invokes provided callback handler.\n     *\n     * @param {Object} callback\n     */\n    function invoke(callback) {\n        callback.handler.call(callback.ctx);\n    }\n\n    /**\n     * Sets 'resolver' module to a ready state\n     * and invokes pending callbacks.\n     */\n    function resolve() {\n        ready = true;\n\n        callbacks.splice(0).forEach(invoke);\n    }\n\n    /**\n     * Drops 'ready' flag and runs the update process.\n     */\n    function tick() {\n        ready = false;\n\n        update(retries);\n    }\n\n    /**\n     * Adds callback which will be invoked\n     * when all of the pending modules are initiated.\n     *\n     * @param {Function} handler - 'Ready' event handler function.\n     * @param {Object} [ctx] - Optional context with which handler\n     *      will be invoked.\n     */\n    function subscribe(handler, ctx) {\n        var callback = {\n            handler: handler,\n            ctx: ctx\n        };\n\n        if (!isSubscribed(callback)) {\n            callbacks.push(callback);\n\n            if (isReady()) {\n                _.defer(tick);\n            }\n        }\n    }\n\n    /**\n     * Checks for all modules to be initiated\n     * and invokes pending callbacks if it's so.\n     *\n     * @param {Number} [retry] - Number of retries\n     *      that will be used to repeat the 'update' function\n     *      invokation in case if there are no pending requests.\n     */\n    update = _.debounce(function (retry) {\n        if (!hasPending()) {\n            retry ? update(--retry) : resolve();\n        }\n    }, updateDelay);\n\n    /**\n     * Overrides requirejs's original 'execCb' method\n     * in order to track pending modules.\n     *\n     * @returns {*} Result of original method call.\n     */\n    context.execCb = function () {\n        var exported = execCb.apply(context, arguments);\n\n        tick();\n\n        return exported;\n    };\n\n    return subscribe;\n});\n","mage/gallery/gallery.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'fotorama/fotorama',\n    'underscore',\n    'matchMedia',\n    'mage/template',\n    'text!mage/gallery/gallery.html',\n    'uiClass',\n    'mage/translate'\n], function ($, fotorama, _, mediaCheck, template, galleryTpl, Class, $t) {\n    'use strict';\n\n    /**\n     * Retrieves index if the main item.\n     * @param {Array.<Object>} data - Set of gallery items.\n     */\n    var getMainImageIndex = function (data) {\n            var mainIndex;\n\n            if (_.every(data, function (item) {\n                    return _.isObject(item);\n                })\n            ) {\n                mainIndex = _.findIndex(data, function (item) {\n                    return item.isMain;\n                });\n            }\n\n            return mainIndex > 0 ? mainIndex : 0;\n        },\n\n        /**\n         * Helper for parse translate property\n         *\n         * @param {Element} el - el that to parse\n         * @returns {Array} - array of properties.\n         */\n        getTranslate = function (el) {\n            var slideTransform = $(el).attr('style').split(';');\n\n            slideTransform = $.map(slideTransform, function (style) {\n                style = style.trim();\n\n                if (style.startsWith('transform: translate3d')) {\n                    return style.match(/transform: translate3d\\((.+)px,(.+)px,(.+)px\\)/);\n                }\n\n                return false;\n            });\n\n            return slideTransform.filter(Boolean);\n        },\n\n        /**\n         * @param {*} str\n         * @return {*}\n         * @private\n         */\n        _toNumber = function (str) {\n            var type = typeof str;\n\n            if (type === 'string') {\n                return parseInt(str); //eslint-disable-line radix\n            }\n\n            return str;\n        };\n\n    return Class.extend({\n\n        defaults: {\n            settings: {},\n            config: {},\n            startConfig: {}\n        },\n\n        /**\n         * Checks if device has touch interface.\n         * @return {Boolean} The result of searching touch events on device.\n         */\n        isTouchEnabled: (function () {\n            return 'ontouchstart' in document.documentElement;\n        })(),\n\n        /**\n         * Initializes gallery.\n         * @param {Object} config - Gallery configuration.\n         * @param {String} element - String selector of gallery DOM element.\n         */\n        initialize: function (config, element) {\n            var self = this;\n\n            this._super();\n\n            _.bindAll(this,\n                '_focusSwitcher'\n            );\n\n            /*turn off arrows for touch devices*/\n            if (this.isTouchEnabled) {\n                config.options.arrows = false;\n\n                if (config.fullscreen) {\n                    config.fullscreen.arrows = false;\n                }\n            }\n\n            config.options.width = _toNumber(config.options.width);\n            config.options.height = _toNumber(config.options.height);\n            config.options.thumbwidth = _toNumber(config.options.thumbwidth);\n            config.options.thumbheight = _toNumber(config.options.thumbheight);\n\n            config.options.swipe = true;\n            this.config = config;\n\n            this.settings = {\n                $element: $(element),\n                $pageWrapper: $('body>.page-wrapper'),\n                currentConfig: config,\n                defaultConfig: _.clone(config),\n                fullscreenConfig: _.clone(config.fullscreen),\n                breakpoints: config.breakpoints,\n                activeBreakpoint: {},\n                fotoramaApi: null,\n                isFullscreen: false,\n                api: null,\n                data: _.clone(config.data)\n            };\n            config.options.ratio = config.options.width / config.options.height;\n            config.options.height = null;\n\n            $.extend(true, this.startConfig, config);\n\n            this.initGallery();\n            this.initApi();\n            this.setupBreakpoints();\n            this.initFullscreenSettings();\n\n            this.settings.$element.on('click', '.fotorama__stage__frame', function () {\n                if (\n                    !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length &&\n                    !$(this).hasClass('fotorama-video-container')\n                ) {\n                    self.openFullScreen();\n                }\n            });\n\n            if (this.isTouchEnabled && this.settings.isFullscreen) {\n                this.settings.$element.on('tap', '.fotorama__stage__frame', function () {\n                    var translate = getTranslate($(this).parents('.fotorama__stage__shaft'));\n\n                    if (translate[1] === '0' && !$(this).hasClass('fotorama-video-container')) {\n                        self.openFullScreen();\n                        self.settings.$pageWrapper.hide();\n                    }\n                });\n            }\n        },\n\n        /**\n         * Open gallery fullscreen\n         */\n        openFullScreen: function () {\n            this.settings.api.fotorama.requestFullScreen();\n            this.settings.$fullscreenIcon.css({\n                opacity: 1,\n                visibility: 'visible',\n                display: 'block'\n            });\n        },\n\n        /**\n         * Gallery fullscreen settings.\n         */\n        initFullscreenSettings: function () {\n            var settings = this.settings,\n                self = this;\n\n            settings.$gallery = this.settings.$element.find('[data-gallery-role=\"gallery\"]');\n            settings.$fullscreenIcon = this.settings.$element.find('[data-gallery-role=\"fotorama__fullscreen-icon\"]');\n            settings.focusableStart = this.settings.$element.find('[data-gallery-role=\"fotorama__focusable-start\"]');\n            settings.focusableEnd = this.settings.$element.find('[data-gallery-role=\"fotorama__focusable-end\"]');\n            settings.closeIcon = this.settings.$element.find('[data-gallery-role=\"fotorama__fullscreen-icon\"]');\n            settings.fullscreenConfig.swipe = true;\n\n            settings.$gallery.on('fotorama:fullscreenenter', function () {\n                settings.closeIcon.show();\n                settings.focusableStart.attr('tabindex', '0');\n                settings.focusableEnd.attr('tabindex', '0');\n                settings.focusableStart.on('focusin', self._focusSwitcher);\n                settings.focusableEnd.on('focusin', self._focusSwitcher);\n                settings.api.updateOptions(settings.defaultConfig.options, true);\n                settings.api.updateOptions(settings.fullscreenConfig, true);\n\n                if (!_.isEqual(settings.activeBreakpoint, {}) && settings.breakpoints) {\n                    settings.api.updateOptions(settings.activeBreakpoint.options, true);\n                }\n                settings.isFullscreen = true;\n            });\n\n            settings.$gallery.on('fotorama:fullscreenexit', function () {\n                settings.closeIcon.hide();\n                settings.focusableStart.attr('tabindex', '-1');\n                settings.focusableEnd.attr('tabindex', '-1');\n                settings.api.updateOptions(settings.defaultConfig.options, true);\n                settings.focusableStart.off('focusin', this._focusSwitcher);\n                settings.focusableEnd.off('focusin', this._focusSwitcher);\n                settings.closeIcon.hide();\n\n                if (!_.isEqual(settings.activeBreakpoint, {}) && settings.breakpoints) {\n                    settings.api.updateOptions(settings.activeBreakpoint.options, true);\n                }\n                settings.isFullscreen = false;\n                settings.$element.data('gallery').updateOptions({\n                    swipe: true\n                });\n            });\n        },\n\n        /**\n         * Switcher focus.\n         */\n        _focusSwitcher: function (e) {\n            var target = $(e.target),\n                settings = this.settings;\n\n            if (target.is(settings.focusableStart)) {\n                this._setFocus('start');\n            } else if (target.is(settings.focusableEnd)) {\n                this._setFocus('end');\n            }\n        },\n\n        /**\n         * Set focus to element.\n         * @param {String} position - can be \"start\" and \"end\"\n         *      positions.\n         *      If position is \"end\" - sets focus to first\n         *      focusable element in modal window scope.\n         *      If position is \"start\" - sets focus to last\n         *      focusable element in modal window scope\n         */\n        _setFocus: function (position) {\n            var settings = this.settings,\n                focusableElements,\n                infelicity;\n\n            if (position === 'end') {\n                settings.$gallery.find(settings.closeIcon).trigger('focus');\n            } else if (position === 'start') {\n                infelicity = 3; //Constant for find last focusable element\n                focusableElements = settings.$gallery.find(':focusable');\n                focusableElements.eq(focusableElements.length - infelicity).trigger('focus');\n            }\n        },\n\n        /**\n         * Initializes gallery with configuration options.\n         */\n        initGallery: function () {\n            var breakpoints = {},\n                settings = this.settings,\n                config = this.config,\n                tpl = template(galleryTpl, {\n                    next: $t('Next'),\n                    previous: $t('Previous')\n                }),\n                mainImageIndex,\n                $element = settings.$element,\n                $fotoramaElement,\n                $fotoramaStage;\n\n            if (settings.breakpoints) {\n                _.each(_.values(settings.breakpoints), function (breakpoint) {\n                    var conditions;\n\n                    _.each(_.pairs(breakpoint.conditions), function (pair) {\n                        conditions = conditions ? conditions + ' and (' + pair[0] + ': ' + pair[1] + ')' :\n                        '(' + pair[0] + ': ' + pair[1] + ')';\n                    });\n                    breakpoints[conditions] = breakpoint.options;\n                });\n                settings.breakpoints = breakpoints;\n            }\n\n            _.extend(config, config.options,\n                {\n                    options: undefined,\n                    click: false,\n                    breakpoints: null\n                }\n            );\n            settings.currentConfig = config;\n\n            $element\n                .css('min-height', settings.$element.height())\n                .append(tpl);\n\n            $fotoramaElement = $element.find('[data-gallery-role=\"gallery\"]');\n\n            $fotoramaStage = $fotoramaElement.find('.fotorama__stage');\n            $fotoramaStage.css('position', 'absolute');\n\n            $fotoramaElement.fotorama(config);\n            $fotoramaElement.find('.fotorama__stage__frame.fotorama__active')\n                    .one('f:load', function () {\n                        // Remove placeholder when main gallery image loads.\n                        $element.find('.gallery-placeholder__image').remove();\n                        $element\n                            .removeClass('_block-content-loading')\n                            .css('min-height', '');\n\n                        $fotoramaStage.css('position', '');\n                    });\n            settings.$elementF = $fotoramaElement;\n            settings.fotoramaApi = $fotoramaElement.data('fotorama');\n\n            $.extend(true, config, this.startConfig);\n\n            mainImageIndex = getMainImageIndex(config.data);\n\n            if (mainImageIndex) {\n                this.settings.fotoramaApi.show({\n                    index: mainImageIndex,\n                    time: 0\n                });\n            }\n        },\n\n        /**\n         * Creates breakpoints for gallery.\n         */\n        setupBreakpoints: function () {\n            var pairs,\n                settings = this.settings,\n                config = this.config,\n                startConfig = this.startConfig,\n                isInitialized = {},\n                isTouchEnabled = this.isTouchEnabled;\n\n            if (_.isObject(settings.breakpoints)) {\n                pairs = _.pairs(settings.breakpoints);\n                _.each(pairs, function (pair) {\n                    var mediaQuery = pair[0];\n\n                    isInitialized[mediaQuery] = false;\n                    mediaCheck({\n                        media: mediaQuery,\n\n                        /**\n                         * Is triggered when breakpoint enties.\n                         */\n                        entry: function () {\n                            $.extend(true, config, _.clone(startConfig));\n\n                            settings.api.updateOptions(settings.defaultConfig.options, true);\n\n                            if (settings.isFullscreen) {\n                                settings.api.updateOptions(settings.fullscreenConfig, true);\n                            }\n\n                            if (isTouchEnabled) {\n                                settings.breakpoints[mediaQuery].options.arrows = false;\n\n                                if (settings.breakpoints[mediaQuery].options.fullscreen) {\n                                    settings.breakpoints[mediaQuery].options.fullscreen.arrows = false;\n                                }\n                            }\n\n                            settings.api.updateOptions(settings.breakpoints[mediaQuery].options, true);\n                            $.extend(true, config, settings.breakpoints[mediaQuery]);\n                            settings.activeBreakpoint = settings.breakpoints[mediaQuery];\n\n                            isInitialized[mediaQuery] = true;\n                        },\n\n                        /**\n                         * Is triggered when breakpoint exits.\n                         */\n                        exit: function () {\n                            if (isInitialized[mediaQuery]) {\n                                $.extend(true, config, _.clone(startConfig));\n                                settings.api.updateOptions(settings.defaultConfig.options, true);\n\n                                if (settings.isFullscreen) {\n                                    settings.api.updateOptions(settings.fullscreenConfig, true);\n                                }\n                                settings.activeBreakpoint = {};\n                            } else {\n                                isInitialized[mediaQuery] = true;\n                            }\n                        }\n                    });\n                });\n            }\n        },\n\n        /**\n         * Creates gallery's API.\n         */\n        initApi: function () {\n            var settings = this.settings,\n                config = this.config,\n                api = {\n\n                    /**\n                     * Contains fotorama's API methods.\n                     */\n                    fotorama: settings.fotoramaApi,\n\n                    /**\n                     * Displays the last image on preview.\n                     */\n                    last: function () {\n                        settings.fotoramaApi.show('>>');\n                    },\n\n                    /**\n                     * Displays the first image on preview.\n                     */\n                    first: function () {\n                        settings.fotoramaApi.show('<<');\n                    },\n\n                    /**\n                     * Displays previous element on preview.\n                     */\n                    prev: function () {\n                        settings.fotoramaApi.show('<');\n                    },\n\n                    /**\n                     * Displays next element on preview.\n                     */\n                    next: function () {\n                        settings.fotoramaApi.show('>');\n                    },\n\n                    /**\n                     * Displays image with appropriate count number on preview.\n                     * @param {Number} index - Number of image that should be displayed.\n                     */\n                    seek: function (index) {\n                        if (_.isNumber(index) && index !== 0) {\n\n                            if (index > 0) {\n                                index -= 1;\n                            }\n                            settings.fotoramaApi.show(index);\n                        }\n                    },\n\n                    /**\n                     * Updates gallery with new set of options.\n                     * @param {Object} configuration - Standart gallery configuration object.\n                     * @param {Boolean} isInternal - Is this function called via breakpoints.\n                     */\n                    updateOptions: function (configuration, isInternal) {\n\n                        var $selectable = $('a[href], area[href], input, select, ' +\n                                'textarea, button, iframe, object, embed, *[tabindex], *[contenteditable]')\n                                .not('[tabindex=-1], [disabled], :hidden'),\n                            $focus = $(':focus'),\n                            index;\n\n                        if (_.isObject(configuration)) {\n\n                            //Saves index of focus\n                            $selectable.each(function (number) {\n                                if ($(this).is($focus)) {\n                                    index = number;\n                                }\n                            });\n\n                            if (this.isTouchEnabled) {\n                                configuration.arrows = false;\n                            }\n                            configuration.click = false;\n                            configuration.breakpoints = null;\n\n                            if (!isInternal) {\n                                !_.isEqual(settings.activeBreakpoint, {} && settings.breakpoints) ?\n                                    $.extend(true, settings.activeBreakpoint.options, configuration) :\n\n                                    settings.isFullscreen ?\n                                        $.extend(true, settings.fullscreenConfig, configuration) :\n                                        $.extend(true, settings.defaultConfig.options, configuration);\n\n                            }\n                            $.extend(true, settings.currentConfig.options, configuration);\n                            settings.fotoramaApi.setOptions(settings.currentConfig.options);\n\n                            if (_.isNumber(index)) {\n                                $selectable.eq(index).trigger('focus');\n                            }\n                        }\n                    },\n\n                    /**\n                     * Updates gallery with specific set of items.\n                     * @param {Array.<Object>} data - Set of gallery items to update.\n                     */\n                    updateData: function (data) {\n                        var mainImageIndex;\n\n                        if (_.isArray(data)) {\n                            settings.fotoramaApi.load(data);\n                            mainImageIndex = getMainImageIndex(data);\n\n                            if (settings.fotoramaApi.activeIndex !== mainImageIndex) {\n                                settings.fotoramaApi.show({\n                                    index: mainImageIndex,\n                                    time: 0\n                                });\n                            }\n\n                            $.extend(false, settings, {\n                                data: data,\n                                defaultConfig: data\n                            });\n                            $.extend(false, config, {\n                                data: data\n                            });\n                        }\n                    },\n\n                    /**\n                     * Returns current images list\n                     *\n                     * @returns {Array}\n                     */\n                    returnCurrentImages: function () {\n                        var images = [];\n\n                        _.each(this.fotorama.data, function (item) {\n                            images.push(_.omit(item, '$navThumbFrame', '$navDotFrame', '$stageFrame', 'labelledby'));\n                        });\n\n                        return images;\n                    },\n\n                    /**\n                     * Updates gallery data partially by index\n                     * @param {Number} index - Index of image in data array to be updated.\n                     * @param {Object} item - Standart gallery image object.\n                     *\n                     */\n                    updateDataByIndex: function (index, item) {\n                        settings.fotoramaApi.spliceByIndex(index, item);\n                    }\n                };\n\n            settings.$element.data('gallery', api);\n            settings.api = settings.$element.data('gallery');\n            settings.$element.trigger('gallery:loaded');\n        }\n    });\n});\n","mage/msie/file-reader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    /**\n     * Init \"readAsBinaryString\" function for FileReader class.\n     * It need for IE11\n     * @param {Blob} fileData\n     */\n    var readAsBinaryStringIEFunc = function (fileData) {\n        var binary = '',\n            self = this,\n            reader = new FileReader();\n\n        /**\n         * Read file as binary string\n         */\n        reader.onload = function () {\n            var bytes, length, index;\n\n            /* eslint-disable no-undef */\n            bytes = new Uint8Array(reader.result);\n            /* eslint-enable */\n            length = bytes.length;\n\n            for (index = 0; index < length; index++) {\n                binary += String.fromCharCode(bytes[index]);\n            }\n            //self.result  - readonly so assign binary\n            self.content = binary;\n            $(self).trigger('onload');\n        };\n        reader.readAsArrayBuffer(fileData);\n    };\n\n    if (typeof FileReader.prototype.readAsBinaryString === 'undefined') {\n        FileReader.prototype.readAsBinaryString = readAsBinaryStringIEFunc;\n    }\n});\n","fotorama/fotorama.js":"/*!\n * Fotorama 4.6.4 | http://fotorama.io/license/\n */\nfotoramaVersion = '4.6.4';\n(function (window, document, location, $, undefined) {\n    \"use strict\";\n    var _fotoramaClass = 'fotorama',\n        _fullscreenClass = 'fotorama__fullscreen',\n\n        wrapClass = _fotoramaClass + '__wrap',\n        wrapCss2Class = wrapClass + '--css2',\n        wrapCss3Class = wrapClass + '--css3',\n        wrapVideoClass = wrapClass + '--video',\n        wrapFadeClass = wrapClass + '--fade',\n        wrapSlideClass = wrapClass + '--slide',\n        wrapNoControlsClass = wrapClass + '--no-controls',\n        wrapNoShadowsClass = wrapClass + '--no-shadows',\n        wrapPanYClass = wrapClass + '--pan-y',\n        wrapRtlClass = wrapClass + '--rtl',\n        wrapOnlyActiveClass = wrapClass + '--only-active',\n        wrapNoCaptionsClass = wrapClass + '--no-captions',\n        wrapToggleArrowsClass = wrapClass + '--toggle-arrows',\n\n        stageClass = _fotoramaClass + '__stage',\n        stageFrameClass = stageClass + '__frame',\n        stageFrameVideoClass = stageFrameClass + '--video',\n        stageShaftClass = stageClass + '__shaft',\n\n        grabClass = _fotoramaClass + '__grab',\n        pointerClass = _fotoramaClass + '__pointer',\n\n        arrClass = _fotoramaClass + '__arr',\n        arrDisabledClass = arrClass + '--disabled',\n        arrPrevClass = arrClass + '--prev',\n        arrNextClass = arrClass + '--next',\n\n        navClass = _fotoramaClass + '__nav',\n        navWrapClass = navClass + '-wrap',\n        navShaftClass = navClass + '__shaft',\n        navShaftVerticalClass = navWrapClass + '--vertical',\n        navShaftListClass = navWrapClass + '--list',\n        navShafthorizontalClass = navWrapClass + '--horizontal',\n        navDotsClass = navClass + '--dots',\n        navThumbsClass = navClass + '--thumbs',\n        navFrameClass = navClass + '__frame',\n\n        fadeClass = _fotoramaClass + '__fade',\n        fadeFrontClass = fadeClass + '-front',\n        fadeRearClass = fadeClass + '-rear',\n\n        shadowClass = _fotoramaClass + '__shadow',\n        shadowsClass = shadowClass + 's',\n        shadowsLeftClass = shadowsClass + '--left',\n        shadowsRightClass = shadowsClass + '--right',\n        shadowsTopClass = shadowsClass + '--top',\n        shadowsBottomClass = shadowsClass + '--bottom',\n\n        activeClass = _fotoramaClass + '__active',\n        selectClass = _fotoramaClass + '__select',\n\n        hiddenClass = _fotoramaClass + '--hidden',\n\n        fullscreenClass = _fotoramaClass + '--fullscreen',\n        fullscreenIconClass = _fotoramaClass + '__fullscreen-icon',\n\n        errorClass = _fotoramaClass + '__error',\n        loadingClass = _fotoramaClass + '__loading',\n        loadedClass = _fotoramaClass + '__loaded',\n        loadedFullClass = loadedClass + '--full',\n        loadedImgClass = loadedClass + '--img',\n\n        grabbingClass = _fotoramaClass + '__grabbing',\n\n        imgClass = _fotoramaClass + '__img',\n        imgFullClass = imgClass + '--full',\n\n        thumbClass = _fotoramaClass + '__thumb',\n        thumbArrLeft = thumbClass + '__arr--left',\n        thumbArrRight = thumbClass + '__arr--right',\n        thumbBorderClass = thumbClass + '-border',\n\n        htmlClass = _fotoramaClass + '__html',\n\n        videoContainerClass = _fotoramaClass + '-video-container',\n        videoClass = _fotoramaClass + '__video',\n        videoPlayClass = videoClass + '-play',\n        videoCloseClass = videoClass + '-close',\n\n\n        horizontalImageClass = _fotoramaClass + '_horizontal_ratio',\n        verticalImageClass = _fotoramaClass + '_vertical_ratio',\n        fotoramaSpinnerClass = _fotoramaClass + '__spinner',\n        spinnerShowClass = fotoramaSpinnerClass + '--show';\n    var JQUERY_VERSION = $ && $.fn.jquery.split('.');\n\n    if (!JQUERY_VERSION\n        || JQUERY_VERSION[0] < 1\n        || (JQUERY_VERSION[0] == 1 && JQUERY_VERSION[1] < 8)) {\n        throw 'Fotorama requires jQuery 1.8 or later and will not run without it.';\n    }\n\n    var _ = {};\n    /* Modernizr 2.8.3 (Custom Build) | MIT & BSD\n     * Build: http://modernizr.com/download/#-csstransforms3d-csstransitions-touch-prefixed\n     */\n\n    var Modernizr = (function (window, document, undefined) {\n        var version = '2.8.3',\n            Modernizr = {},\n\n\n            docElement = document.documentElement,\n\n            mod = 'modernizr',\n            modElem = document.createElement(mod),\n            mStyle = modElem.style,\n            inputElem,\n\n\n            toString = {}.toString,\n\n            prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),\n\n\n            omPrefixes = 'Webkit Moz O ms',\n\n            cssomPrefixes = omPrefixes.split(' '),\n\n            domPrefixes = omPrefixes.toLowerCase().split(' '),\n\n\n            tests = {},\n            inputs = {},\n            attrs = {},\n\n            classes = [],\n\n            slice = classes.slice,\n\n            featureName,\n\n\n            injectElementWithStyles = function (rule, callback, nodes, testnames) {\n\n                var style, ret, node, docOverflow,\n                    div = document.createElement('div'),\n                    body = document.body,\n                    fakeBody = body || document.createElement('body');\n\n                if (parseInt(nodes, 10)) {\n                    while (nodes--) {\n                        node = document.createElement('div');\n                        node.id = testnames ? testnames[nodes] : mod + (nodes + 1);\n                        div.appendChild(node);\n                    }\n                }\n\n                style = ['&#173;', '<style id=\"s', mod, '\">', rule, '</style>'].join('');\n                div.id = mod;\n                (body ? div : fakeBody).innerHTML += style;\n                fakeBody.appendChild(div);\n                if (!body) {\n                    fakeBody.style.background = '';\n                    fakeBody.style.overflow = 'hidden';\n                    docOverflow = docElement.style.overflow;\n                    docElement.style.overflow = 'hidden';\n                    docElement.appendChild(fakeBody);\n                }\n\n                ret = callback(div, rule);\n                if (!body) {\n                    fakeBody.parentNode.removeChild(fakeBody);\n                    docElement.style.overflow = docOverflow;\n                } else {\n                    div.parentNode.removeChild(div);\n                }\n\n                return !!ret;\n\n            },\n            _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;\n\n        if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) {\n            hasOwnProp = function (object, property) {\n                return _hasOwnProperty.call(object, property);\n            };\n        }\n        else {\n            hasOwnProp = function (object, property) {\n                return ((property in object) && is(object.constructor.prototype[property], 'undefined'));\n            };\n        }\n\n\n        if (!Function.prototype.bind) {\n            Function.prototype.bind = function bind(that) {\n\n                var target = this;\n\n                if (typeof target != \"function\") {\n                    throw new TypeError();\n                }\n\n                var args = slice.call(arguments, 1),\n                    bound = function () {\n\n                        if (this instanceof bound) {\n\n                            var F = function () {\n                            };\n                            F.prototype = target.prototype;\n                            var self = new F();\n\n                            var result = target.apply(\n                                self,\n                                args.concat(slice.call(arguments))\n                            );\n                            if (Object(result) === result) {\n                                return result;\n                            }\n                            return self;\n\n                        } else {\n\n                            return target.apply(\n                                that,\n                                args.concat(slice.call(arguments))\n                            );\n\n                        }\n\n                    };\n\n                return bound;\n            };\n        }\n\n        function setCss(str) {\n            mStyle.cssText = str;\n        }\n\n        function setCssAll(str1, str2) {\n            return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));\n        }\n\n        function is(obj, type) {\n            return typeof obj === type;\n        }\n\n        function contains(str, substr) {\n            return !!~('' + str).indexOf(substr);\n        }\n\n        function testProps(props, prefixed) {\n            for (var i in props) {\n                var prop = props[i];\n                if (!contains(prop, \"-\") && mStyle[prop] !== undefined) {\n                    return prefixed == 'pfx' ? prop : true;\n                }\n            }\n            return false;\n        }\n\n        function testDOMProps(props, obj, elem) {\n            for (var i in props) {\n                var item = obj[props[i]];\n                if (item !== undefined) {\n\n                    if (elem === false) return props[i];\n\n                    if (is(item, 'function')) {\n                        return item.bind(elem || obj);\n                    }\n\n                    return item;\n                }\n            }\n            return false;\n        }\n\n        function testPropsAll(prop, prefixed, elem) {\n\n            var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),\n                props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');\n\n            if (is(prefixed, \"string\") || is(prefixed, \"undefined\")) {\n                return testProps(props, prefixed);\n\n            } else {\n                props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');\n                return testDOMProps(props, prefixed, elem);\n            }\n        }\n\n        tests['touch'] = function () {\n            var bool;\n\n            if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {\n                bool = true;\n            } else {\n                injectElementWithStyles(['@media (', prefixes.join('touch-enabled),('), mod, ')', '{#modernizr{top:9px;position:absolute}}'].join(''), function (node) {\n                    bool = node.offsetTop === 9;\n                });\n            }\n\n            return bool;\n        };\n        tests['csstransforms3d'] = function () {\n\n            var ret = !!testPropsAll('perspective');\n\n            if (ret && 'webkitPerspective' in docElement.style) {\n\n                injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function (node, rule) {\n                    ret = node.offsetLeft === 9 && node.offsetHeight === 3;\n                });\n            }\n            return ret;\n        };\n\n\n        tests['csstransitions'] = function () {\n            return testPropsAll('transition');\n        };\n\n\n        for (var feature in tests) {\n            if (hasOwnProp(tests, feature)) {\n                featureName = feature.toLowerCase();\n                Modernizr[featureName] = tests[feature]();\n\n                classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);\n            }\n        }\n\n\n        Modernizr.addTest = function (feature, test) {\n            if (typeof feature == 'object') {\n                for (var key in feature) {\n                    if (hasOwnProp(feature, key)) {\n                        Modernizr.addTest(key, feature[key]);\n                    }\n                }\n            } else {\n\n                feature = feature.toLowerCase();\n\n                if (Modernizr[feature] !== undefined) {\n                    return Modernizr;\n                }\n\n                test = typeof test == 'function' ? test() : test;\n\n                if (typeof enableClasses !== \"undefined\" && enableClasses) {\n                    docElement.className += ' ' + (test ? '' : 'no-') + feature;\n                }\n                Modernizr[feature] = test;\n\n            }\n\n            return Modernizr;\n        };\n\n\n        setCss('');\n        modElem = inputElem = null;\n\n\n        Modernizr._version = version;\n\n        Modernizr._prefixes = prefixes;\n        Modernizr._domPrefixes = domPrefixes;\n        Modernizr._cssomPrefixes = cssomPrefixes;\n\n\n        Modernizr.testProp = function (prop) {\n            return testProps([prop]);\n        };\n\n        Modernizr.testAllProps = testPropsAll;\n        Modernizr.testStyles = injectElementWithStyles;\n        Modernizr.prefixed = function (prop, obj, elem) {\n            if (!obj) {\n                return testPropsAll(prop, 'pfx');\n            } else {\n                return testPropsAll(prop, obj, elem);\n            }\n        };\n        return Modernizr;\n    })(window, document);\n\n    var fullScreenApi = {\n            ok: false,\n            is: function () {\n                return false;\n            },\n            request: function () {\n            },\n            cancel: function () {\n            },\n            event: '',\n            prefix: ''\n        },\n        browserPrefixes = 'webkit moz o ms khtml'.split(' ');\n\n// check for native support\n    if (typeof document.cancelFullScreen != 'undefined') {\n        fullScreenApi.ok = true;\n    } else {\n        // check for fullscreen support by vendor prefix\n        for (var i = 0, il = browserPrefixes.length; i < il; i++) {\n            fullScreenApi.prefix = browserPrefixes[i];\n            if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {\n                fullScreenApi.ok = true;\n                break;\n            }\n        }\n    }\n\n// update methods to do something useful\n    if (fullScreenApi.ok) {\n        fullScreenApi.event = fullScreenApi.prefix + 'fullscreenchange';\n        fullScreenApi.is = function () {\n            switch (this.prefix) {\n                case '':\n                    return document.fullScreen;\n                case 'webkit':\n                    return document.webkitIsFullScreen;\n                default:\n                    return document[this.prefix + 'FullScreen'];\n            }\n        };\n        fullScreenApi.request = function (el) {\n            return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();\n        };\n        fullScreenApi.cancel = function (el) {\n            if (!this.is()) {\n                return false;\n            }\n            return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();\n        };\n    }\n    /* Bez v1.0.10-g5ae0136\n     * http://github.com/rdallasgray/bez\n     *\n     * A plugin to convert CSS3 cubic-bezier co-ordinates to jQuery-compatible easing functions\n     *\n     * With thanks to Nikolay Nemshilov for clarification on the cubic-bezier maths\n     * See http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html\n     *\n     * Copyright 2011 Robert Dallas Gray. All rights reserved.\n     * Provided under the FreeBSD license: https://github.com/rdallasgray/bez/blob/master/LICENSE.txt\n     */\n    function bez(coOrdArray) {\n        var encodedFuncName = \"bez_\" + $.makeArray(arguments).join(\"_\").replace(\".\", \"p\");\n        if (typeof $['easing'][encodedFuncName] !== \"function\") {\n            var polyBez = function (p1, p2) {\n                var A = [null, null],\n                    B = [null, null],\n                    C = [null, null],\n                    bezCoOrd = function (t, ax) {\n                        C[ax] = 3 * p1[ax];\n                        B[ax] = 3 * (p2[ax] - p1[ax]) - C[ax];\n                        A[ax] = 1 - C[ax] - B[ax];\n                        return t * (C[ax] + t * (B[ax] + t * A[ax]));\n                    },\n                    xDeriv = function (t) {\n                        return C[0] + t * (2 * B[0] + 3 * A[0] * t);\n                    },\n                    xForT = function (t) {\n                        var x = t, i = 0, z;\n                        while (++i < 14) {\n                            z = bezCoOrd(x, 0) - t;\n                            if (Math.abs(z) < 1e-3) break;\n                            x -= z / xDeriv(x);\n                        }\n                        return x;\n                    };\n                return function (t) {\n                    return bezCoOrd(xForT(t), 1);\n                }\n            };\n            $['easing'][encodedFuncName] = function (x, t, b, c, d) {\n                return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t / d) + b;\n            }\n        }\n        return encodedFuncName;\n    }\n\n    var $WINDOW = $(window),\n        $DOCUMENT = $(document),\n        $HTML,\n        $BODY,\n\n        QUIRKS_FORCE = location.hash.replace('#', '') === 'quirks',\n        TRANSFORMS3D = Modernizr.csstransforms3d,\n        CSS3 = TRANSFORMS3D && !QUIRKS_FORCE,\n        COMPAT = TRANSFORMS3D || document.compatMode === 'CSS1Compat',\n        FULLSCREEN = fullScreenApi.ok,\n\n        MOBILE = navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),\n        SLOW = !CSS3 || MOBILE,\n\n        MS_POINTER = navigator.msPointerEnabled,\n\n        WHEEL = \"onwheel\" in document.createElement(\"div\") ? \"wheel\" : document.onmousewheel !== undefined ? \"mousewheel\" : \"DOMMouseScroll\",\n\n        TOUCH_TIMEOUT = 250,\n        TRANSITION_DURATION = 300,\n\n        SCROLL_LOCK_TIMEOUT = 1400,\n\n        AUTOPLAY_INTERVAL = 5000,\n        MARGIN = 2,\n        THUMB_SIZE = 64,\n\n        WIDTH = 500,\n        HEIGHT = 333,\n\n        STAGE_FRAME_KEY = '$stageFrame',\n        NAV_DOT_FRAME_KEY = '$navDotFrame',\n        NAV_THUMB_FRAME_KEY = '$navThumbFrame',\n\n        AUTO = 'auto',\n\n        BEZIER = bez([.1, 0, .25, 1]),\n\n        MAX_WIDTH = 1200,\n\n        /**\n         * Number of thumbnails in slide. Calculated only on setOptions and resize.\n         * @type {number}\n         */\n        thumbsPerSlide = 1,\n\n        OPTIONS = {\n\n            /**\n             * Set width for gallery.\n             * Default value - width of first image\n             * Number - set value in px\n             * String - set value in quotes\n             *\n             */\n            width: null,\n\n            /**\n             * Set min-width for gallery\n             *\n             */\n            minwidth: null,\n\n            /**\n             * Set max-width for gallery\n             *\n             */\n            maxwidth: '100%',\n\n            /**\n             * Set height for gallery\n             * Default value - height of first image\n             * Number - set value in px\n             * String - set value in quotes\n             *\n             */\n            height: null,\n\n            /**\n             * Set min-height for gallery\n             *\n             */\n            minheight: null,\n\n            /**\n             * Set max-height for gallery\n             *\n             */\n            maxheight: null,\n\n            /**\n             * Set proportion ratio for gallery depends of image\n             *\n             */\n            ratio: null, // '16/9' || 500/333 || 1.5\n\n            margin: MARGIN,\n\n            nav: 'dots', // 'thumbs' || false\n            navposition: 'bottom', // 'top'\n            navwidth: null,\n            thumbwidth: THUMB_SIZE,\n            thumbheight: THUMB_SIZE,\n            thumbmargin: MARGIN,\n            thumbborderwidth: MARGIN,\n\n            allowfullscreen: false, // true || 'native'\n\n            transition: 'slide', // 'crossfade' || 'dissolve'\n            clicktransition: null,\n            transitionduration: TRANSITION_DURATION,\n\n            captions: true,\n\n            startindex: 0,\n\n            loop: false,\n\n            autoplay: false,\n            stopautoplayontouch: true,\n\n            keyboard: false,\n\n            arrows: true,\n            click: true,\n            swipe: false,\n            trackpad: false,\n\n            shuffle: false,\n\n            direction: 'ltr', // 'rtl'\n\n            shadows: true,\n\n            showcaption: true,\n\n            /**\n             * Set type of thumbnail navigation\n             */\n            navdir: 'horizontal',\n\n            /**\n             * Set configuration to show or hide arrows in thumb navigation\n             */\n            navarrows: true,\n\n            /**\n             * Set type of navigation. Can be thumbs or slides\n             */\n            navtype: 'thumbs'\n\n        },\n\n        KEYBOARD_OPTIONS = {\n            left: true,\n            right: true,\n            down: true,\n            up: true,\n            space: false,\n            home: false,\n            end: false\n        };\n\n    function noop() {\n    }\n\n    function minMaxLimit(value, min, max) {\n        return Math.max(isNaN(min) ? -Infinity : min, Math.min(isNaN(max) ? Infinity : max, value));\n    }\n\n    function readTransform(css, dir) {\n        return css.match(/ma/) && css.match(/-?\\d+(?!d)/g)[css.match(/3d/) ?\n                (dir === 'vertical' ? 13 : 12) : (dir === 'vertical' ? 5 : 4)\n                ]\n    }\n\n    function readPosition($el, dir) {\n        if (CSS3) {\n            return +readTransform($el.css('transform'), dir);\n        } else {\n            return +$el.css(dir === 'vertical' ? 'top' : 'left').replace('px', '');\n        }\n    }\n\n    function getTranslate(pos, direction) {\n        var obj = {};\n\n        if (CSS3) {\n\n            switch (direction) {\n                case 'vertical':\n                    obj.transform = 'translate3d(0, ' + (pos) + 'px,0)';\n                    break;\n                case 'list':\n                    break;\n                default :\n                    obj.transform = 'translate3d(' + (pos) + 'px,0,0)';\n                    break;\n            }\n        } else {\n            direction === 'vertical' ?\n                obj.top = pos :\n                obj.left = pos;\n        }\n        return obj;\n    }\n\n    function getDuration(time) {\n        return {'transition-duration': time + 'ms'};\n    }\n\n    function unlessNaN(value, alternative) {\n        return isNaN(value) ? alternative : value;\n    }\n\n    function numberFromMeasure(value, measure) {\n        return unlessNaN(+String(value).replace(measure || 'px', ''));\n    }\n\n    function numberFromPercent(value) {\n        return /%$/.test(value) ? numberFromMeasure(value, '%') : undefined;\n    }\n\n    function numberFromWhatever(value, whole) {\n        return unlessNaN(numberFromPercent(value) / 100 * whole, numberFromMeasure(value));\n    }\n\n    function measureIsValid(value) {\n        return (!isNaN(numberFromMeasure(value)) || !isNaN(numberFromMeasure(value, '%'))) && value;\n    }\n\n    function getPosByIndex(index, side, margin, baseIndex) {\n\n        return (index - (baseIndex || 0)) * (side + (margin || 0));\n    }\n\n    function getIndexByPos(pos, side, margin, baseIndex) {\n        return -Math.round(pos / (side + (margin || 0)) - (baseIndex || 0));\n    }\n\n    function bindTransitionEnd($el) {\n        var elData = $el.data();\n\n        if (elData.tEnd) return;\n\n        var el = $el[0],\n            transitionEndEvent = {\n                WebkitTransition: 'webkitTransitionEnd',\n                MozTransition: 'transitionend',\n                OTransition: 'oTransitionEnd otransitionend',\n                msTransition: 'MSTransitionEnd',\n                transition: 'transitionend'\n            };\n        addEvent(el, transitionEndEvent[Modernizr.prefixed('transition')], function (e) {\n            elData.tProp && e.propertyName.match(elData.tProp) && elData.onEndFn();\n        });\n        elData.tEnd = true;\n    }\n\n    function afterTransition($el, property, fn, time) {\n        var ok,\n            elData = $el.data();\n\n        if (elData) {\n            elData.onEndFn = function () {\n                if (ok) return;\n                ok = true;\n                clearTimeout(elData.tT);\n                fn();\n            };\n            elData.tProp = property;\n\n            // Passive call, just in case of fail of native transition-end event\n            clearTimeout(elData.tT);\n            elData.tT = setTimeout(function () {\n                elData.onEndFn();\n            }, time * 1.5);\n\n            bindTransitionEnd($el);\n        }\n    }\n\n\n    function stop($el, pos/*, _001*/) {\n        var dir = $el.navdir || 'horizontal';\n        if ($el.length) {\n            var elData = $el.data();\n            if (CSS3) {\n                $el.css(getDuration(0));\n                elData.onEndFn = noop;\n                clearTimeout(elData.tT);\n            } else {\n                $el.stop();\n            }\n            var lockedPos = getNumber(pos, function () {\n                return readPosition($el, dir);\n            });\n\n            $el.css(getTranslate(lockedPos, dir/*, _001*/));//.width(); // `.width()` for reflow\n            return lockedPos;\n        }\n    }\n\n    function getNumber() {\n        var number;\n        for (var _i = 0, _l = arguments.length; _i < _l; _i++) {\n            number = _i ? arguments[_i]() : arguments[_i];\n            if (typeof number === 'number') {\n                break;\n            }\n        }\n\n        return number;\n    }\n\n    function edgeResistance(pos, edge) {\n        return Math.round(pos + ((edge - pos) / 1.5));\n    }\n\n    function getProtocol() {\n        getProtocol.p = getProtocol.p || (location.protocol === 'https:' ? 'https://' : 'http://');\n        return getProtocol.p;\n    }\n\n    function parseHref(href) {\n        var a = document.createElement('a');\n        a.href = href;\n        return a;\n    }\n\n    function findVideoId(href, forceVideo) {\n        if (typeof href !== 'string') return href;\n        href = parseHref(href);\n\n        var id,\n            type;\n\n        if (href.host.match(/youtube\\.com/) && href.search) {\n            //.log();\n            id = href.search.split('v=')[1];\n            if (id) {\n                var ampersandPosition = id.indexOf('&');\n                if (ampersandPosition !== -1) {\n                    id = id.substring(0, ampersandPosition);\n                }\n                type = 'youtube';\n            }\n        } else if (href.host.match(/youtube\\.com|youtu\\.be|youtube-nocookie.com/)) {\n            id = href.pathname.replace(/^\\/(embed\\/|v\\/)?/, '').replace(/\\/.*/, '');\n            type = 'youtube';\n        } else if (href.host.match(/vimeo\\.com/)) {\n            type = 'vimeo';\n            id = href.pathname.replace(/^\\/(video\\/)?/, '').replace(/\\/.*/, '');\n        }\n\n        if ((!id || !type) && forceVideo) {\n            id = href.href;\n            type = 'custom';\n        }\n\n        return id ? {id: id, type: type, s: href.search.replace(/^\\?/, ''), p: getProtocol()} : false;\n    }\n\n    function getVideoThumbs(dataFrame, data, fotorama) {\n        var img, thumb, video = dataFrame.video;\n        if (video.type === 'youtube') {\n            thumb = getProtocol() + 'img.youtube.com/vi/' + video.id + '/default.jpg';\n            img = thumb.replace(/\\/default.jpg$/, '/hqdefault.jpg');\n            dataFrame.thumbsReady = true;\n        } else if (video.type === 'vimeo') {\n            $.ajax({\n                url: getProtocol() + 'vimeo.com/api/oembed.json',\n                data: {\n                    url: 'https://vimeo.com/' + video.id\n                },\n                dataType: 'jsonp',\n                success: function (json) {\n                    dataFrame.thumbsReady = true;\n                    updateData(data, {\n                        img: json[0].thumbnail_url,\n                        thumb: json[0].thumbnail_url\n                    }, dataFrame.i, fotorama);\n                }\n            });\n        } else {\n            dataFrame.thumbsReady = true;\n        }\n\n        return {\n            img: img,\n            thumb: thumb\n        }\n    }\n\n    function updateData(data, _dataFrame, i, fotorama) {\n        for (var _i = 0, _l = data.length; _i < _l; _i++) {\n            var dataFrame = data[_i];\n\n            if (dataFrame.i === i && dataFrame.thumbsReady) {\n                var clear = {videoReady: true};\n                clear[STAGE_FRAME_KEY] = clear[NAV_THUMB_FRAME_KEY] = clear[NAV_DOT_FRAME_KEY] = false;\n\n                fotorama.splice(_i, 1, $.extend(\n                    {},\n                    dataFrame,\n                    clear,\n                    _dataFrame\n                ));\n\n                break;\n            }\n        }\n    }\n\n    function getDataFromHtml($el) {\n        var data = [];\n\n        function getDataFromImg($img, imgData, checkVideo) {\n            var $child = $img.children('img').eq(0),\n                _imgHref = $img.attr('href'),\n                _imgSrc = $img.attr('src'),\n                _thumbSrc = $child.attr('src'),\n                _video = imgData.video,\n                video = checkVideo ? findVideoId(_imgHref, _video === true) : false;\n\n            if (video) {\n                _imgHref = false;\n            } else {\n                video = _video;\n            }\n\n            getDimensions($img, $child, $.extend(imgData, {\n                video: video,\n                img: imgData.img || _imgHref || _imgSrc || _thumbSrc,\n                thumb: imgData.thumb || _thumbSrc || _imgSrc || _imgHref\n            }));\n        }\n\n        function getDimensions($img, $child, imgData) {\n            var separateThumbFLAG = imgData.thumb && imgData.img !== imgData.thumb,\n                width = numberFromMeasure(imgData.width || $img.attr('width')),\n                height = numberFromMeasure(imgData.height || $img.attr('height'));\n\n            $.extend(imgData, {\n                width: width,\n                height: height,\n                thumbratio: getRatio(imgData.thumbratio || (numberFromMeasure(imgData.thumbwidth || ($child && $child.attr('width')) || separateThumbFLAG || width) / numberFromMeasure(imgData.thumbheight || ($child && $child.attr('height')) || separateThumbFLAG || height)))\n            });\n        }\n\n        $el.children().each(function () {\n            var $this = $(this),\n                dataFrame = optionsToLowerCase($.extend($this.data(), {id: $this.attr('id')}));\n            if ($this.is('a, img')) {\n                getDataFromImg($this, dataFrame, true);\n            } else if (!$this.is(':empty')) {\n                getDimensions($this, null, $.extend(dataFrame, {\n                    html: this,\n                    _html: $this.html() // Because of IE\n                }));\n            } else return;\n\n            data.push(dataFrame);\n        });\n\n        return data;\n    }\n\n    function isHidden(el) {\n        return el.offsetWidth === 0 && el.offsetHeight === 0;\n    }\n\n    function isDetached(el) {\n        return !$.contains(document.documentElement, el);\n    }\n\n    function waitFor(test, fn, timeout, i) {\n        if (!waitFor.i) {\n            waitFor.i = 1;\n            waitFor.ii = [true];\n        }\n\n        i = i || waitFor.i;\n\n        if (typeof waitFor.ii[i] === 'undefined') {\n            waitFor.ii[i] = true;\n        }\n\n        if (test()) {\n            fn();\n        } else {\n            waitFor.ii[i] && setTimeout(function () {\n                waitFor.ii[i] && waitFor(test, fn, timeout, i);\n            }, timeout || 100);\n        }\n\n        return waitFor.i++;\n    }\n\n    waitFor.stop = function (i) {\n        waitFor.ii[i] = false;\n    };\n\n    function fit($el, measuresToFit) {\n        var elData = $el.data(),\n            measures = elData.measures;\n\n        if (measures && (!elData.l ||\n            elData.l.W !== measures.width ||\n            elData.l.H !== measures.height ||\n            elData.l.r !== measures.ratio ||\n            elData.l.w !== measuresToFit.w ||\n            elData.l.h !== measuresToFit.h)) {\n\n            var height = minMaxLimit(measuresToFit.h, 0, measures.height),\n                width = height * measures.ratio;\n\n            UTIL.setRatio($el, width, height);\n\n            elData.l = {\n                W: measures.width,\n                H: measures.height,\n                r: measures.ratio,\n                w: measuresToFit.w,\n                h: measuresToFit.h\n            };\n        }\n\n        return true;\n    }\n\n    function setStyle($el, style) {\n        var el = $el[0];\n        if (el.styleSheet) {\n            el.styleSheet.cssText = style;\n        } else {\n            $el.html(style);\n        }\n    }\n\n    function findShadowEdge(pos, min, max, dir) {\n        return min === max ? false :\n            dir === 'vertical' ?\n                (pos <= min ? 'top' : pos >= max ? 'bottom' : 'top bottom') :\n                (pos <= min ? 'left' : pos >= max ? 'right' : 'left right');\n    }\n\n    function smartClick($el, fn, _options) {\n        _options = _options || {};\n\n        $el.each(function () {\n            var $this = $(this),\n                thisData = $this.data(),\n                startEvent;\n\n            if (thisData.clickOn) return;\n\n            thisData.clickOn = true;\n\n            $.extend(touch($this, {\n                onStart: function (e) {\n                    startEvent = e;\n                    (_options.onStart || noop).call(this, e);\n                },\n                onMove: _options.onMove || noop,\n                onTouchEnd: _options.onTouchEnd || noop,\n                onEnd: function (result) {\n                    if (result.moved) return;\n                    fn.call(this, startEvent);\n                }\n            }), {noMove: true});\n        });\n    }\n\n    function div(classes, child) {\n        return '<div class=\"' + classes + '\">' + (child || '') + '</div>';\n    }\n\n\n    /**\n     * Function transforming into valid classname\n     * @param className - name of the class\n     * @returns {string} - dom format of class name\n     */\n    function cls(className) {\n        return \".\" + className;\n    }\n\n    /**\n     *\n     * @param {json-object} videoItem Parsed object from data.video item or href from link a in input dates\n     * @returns {string} DOM view of video iframe\n     */\n    function createVideoFrame(videoItem) {\n        var frame = '<iframe src=\"' + videoItem.p + videoItem.type + '.com/embed/' + videoItem.id + '\" frameborder=\"0\" allowfullscreen></iframe>';\n        return frame;\n    }\n\n// Fisher\u2013Yates Shuffle\n// http://bost.ocks.org/mike/shuffle/\n    function shuffle(array) {\n        // While there remain elements to shuffle\n        var l = array.length;\n        while (l) {\n            // Pick a remaining element\n            var i = Math.floor(Math.random() * l--);\n\n            // And swap it with the current element\n            var t = array[l];\n            array[l] = array[i];\n            array[i] = t;\n        }\n\n        return array;\n    }\n\n    function clone(array) {\n        return Object.prototype.toString.call(array) == '[object Array]'\n            && $.map(array, function (frame) {\n                return $.extend({}, frame);\n            });\n    }\n\n    function lockScroll($el, left, top) {\n        $el\n            .scrollLeft(left || 0)\n            .scrollTop(top || 0);\n    }\n\n    function optionsToLowerCase(options) {\n        if (options) {\n            var opts = {};\n            $.each(options, function (key, value) {\n                opts[key.toLowerCase()] = value;\n            });\n\n            return opts;\n        }\n    }\n\n    function getRatio(_ratio) {\n        if (!_ratio) return;\n        var ratio = +_ratio;\n        if (!isNaN(ratio)) {\n            return ratio;\n        } else {\n            ratio = _ratio.split('/');\n            return +ratio[0] / +ratio[1] || undefined;\n        }\n    }\n\n    function addEvent(el, e, fn, bool) {\n        if (!e) return;\n        el.addEventListener ? el.addEventListener(e, fn, !!bool) : el.attachEvent('on' + e, fn);\n    }\n\n    /**\n     *\n     * @param position guess position for navShaft\n     * @param restriction object contains min and max values for position\n     * @returns {*} filtered value of position\n     */\n    function validateRestrictions(position, restriction) {\n        if (position > restriction.max) {\n            position = restriction.max;\n        } else {\n            if (position < restriction.min) {\n                position = restriction.min;\n            }\n        }\n        return position;\n    }\n\n    function validateSlidePos(opt, navShaftTouchTail, guessIndex, offsetNav, $guessNavFrame, $navWrap, dir) {\n        var position,\n            size,\n            wrapSize;\n        if (dir === 'horizontal') {\n            size = opt.thumbwidth;\n            wrapSize = $navWrap.width();\n        } else {\n            size = opt.thumbheight;\n            wrapSize = $navWrap.height();\n        }\n        if ( (size + opt.margin) * (guessIndex + 1) >= (wrapSize - offsetNav) ) {\n            if (dir === 'horizontal') {\n                position = -$guessNavFrame.position().left;\n            } else {\n                position = -$guessNavFrame.position().top;\n            }\n        } else {\n            if ((size + opt.margin) * (guessIndex) <= Math.abs(offsetNav)) {\n                if (dir === 'horizontal') {\n                    position = -$guessNavFrame.position().left + wrapSize - (size + opt.margin);\n                } else {\n                    position = -$guessNavFrame.position().top + wrapSize - (size + opt.margin);\n                }\n            } else {\n                position = offsetNav;\n            }\n        }\n        position = validateRestrictions(position, navShaftTouchTail);\n\n        return position || 0;\n    }\n\n    function elIsDisabled(el) {\n        return !!el.getAttribute('disabled');\n    }\n\n    function disableAttr(FLAG, disable) {\n        if (disable) {\n            return {disabled: FLAG};\n        } else {\n            return {tabindex: FLAG * -1 + '', disabled: FLAG};\n\n        }\n    }\n\n    function addEnterUp(el, fn) {\n        addEvent(el, 'keyup', function (e) {\n            elIsDisabled(el) || e.keyCode == 13 && fn.call(el, e);\n        });\n    }\n\n    function addFocus(el, fn) {\n        addEvent(el, 'focus', el.onfocusin = function (e) {\n            fn.call(el, e);\n        }, true);\n    }\n\n    function stopEvent(e, stopPropagation) {\n        e.preventDefault ? e.preventDefault() : (e.returnValue = false);\n        stopPropagation && e.stopPropagation && e.stopPropagation();\n    }\n\n    function getDirectionSign(forward) {\n        return forward ? '>' : '<';\n    }\n\n    var UTIL = (function () {\n\n        function setRatioClass($el, wh, ht) {\n            var rateImg = wh / ht;\n\n            if (rateImg <= 1) {\n                $el.parent().removeClass(horizontalImageClass);\n                $el.parent().addClass(verticalImageClass);\n            } else {\n                $el.parent().removeClass(verticalImageClass);\n                $el.parent().addClass(horizontalImageClass);\n            }\n        }\n\n        /**\n         * Set specific attribute in thumbnail template\n         * @param $frame DOM item of specific thumbnail\n         * @param value Value which must be setted into specific attribute\n         * @param searchAttr Name of attribute where value must be included\n         */\n        function setThumbAttr($frame, value, searchAttr) {\n            var attr = searchAttr;\n\n            if (!$frame.attr(attr) && $frame.attr(attr) !== undefined) {\n                $frame.attr(attr, value);\n            }\n\n            if ($frame.find(\"[\" + attr + \"]\").length) {\n                $frame.find(\"[\" + attr + \"]\")\n                    .each(function () {\n                        $(this).attr(attr, value);\n                    });\n            }\n        }\n\n        /**\n         * Method describe behavior need to render caption on preview or not\n         * @param frameItem specific item from data\n         * @param isExpected {bool} if items with caption need render them or not\n         * @returns {boolean} if true then caption should be rendered\n         */\n        function isExpectedCaption(frameItem, isExpected, undefined) {\n            var expected = false,\n                frameExpected;\n\n            frameItem.showCaption === undefined || frameItem.showCaption === true ? frameExpected = true : frameExpected = false;\n\n            if (!isExpected) {\n                return false;\n            }\n\n            if (frameItem.caption && frameExpected) {\n                expected = true;\n            }\n\n            return expected;\n        }\n\n        return {\n            setRatio: setRatioClass,\n            setThumbAttr: setThumbAttr,\n            isExpectedCaption: isExpectedCaption\n        };\n\n    }(UTIL || {}, jQuery));\n\n    function slide($el, options) {\n        var elData = $el.data(),\n            elPos = Math.round(options.pos),\n            onEndFn = function () {\n                if (elData && elData.sliding) {\n                    elData.sliding = false;\n                }\n                (options.onEnd || noop)();\n            };\n\n        if (typeof options.overPos !== 'undefined' && options.overPos !== options.pos) {\n            elPos = options.overPos;\n        }\n\n        var translate = $.extend(getTranslate(elPos, options.direction), options.width && {width: options.width}, options.height && {height: options.height});\n        if (elData && elData.sliding) {\n            elData.sliding = true;\n        }\n\n        if (CSS3) {\n            $el.css($.extend(getDuration(options.time), translate));\n\n            if (options.time > 10) {\n                afterTransition($el, 'transform', onEndFn, options.time);\n            } else {\n                onEndFn();\n            }\n        } else {\n            $el.stop().animate(translate, options.time, BEZIER, onEndFn);\n        }\n    }\n\n    function fade($el1, $el2, $frames, options, fadeStack, chain) {\n        var chainedFLAG = typeof chain !== 'undefined';\n        if (!chainedFLAG) {\n            fadeStack.push(arguments);\n            Array.prototype.push.call(arguments, fadeStack.length);\n            if (fadeStack.length > 1) return;\n        }\n\n        $el1 = $el1 || $($el1);\n        $el2 = $el2 || $($el2);\n\n        var _$el1 = $el1[0],\n            _$el2 = $el2[0],\n            crossfadeFLAG = options.method === 'crossfade',\n            onEndFn = function () {\n                if (!onEndFn.done) {\n                    onEndFn.done = true;\n                    var args = (chainedFLAG || fadeStack.shift()) && fadeStack.shift();\n                    args && fade.apply(this, args);\n                    (options.onEnd || noop)(!!args);\n                }\n            },\n            time = options.time / (chain || 1);\n\n        $frames.removeClass(fadeRearClass + ' ' + fadeFrontClass);\n\n        $el1\n            .stop()\n            .addClass(fadeRearClass);\n        $el2\n            .stop()\n            .addClass(fadeFrontClass);\n\n        crossfadeFLAG && _$el2 && $el1.fadeTo(0, 0);\n\n        $el1.fadeTo(crossfadeFLAG ? time : 0, 1, crossfadeFLAG && onEndFn);\n        $el2.fadeTo(time, 0, onEndFn);\n\n        (_$el1 && crossfadeFLAG) || _$el2 || onEndFn();\n    }\n\n    var lastEvent,\n        moveEventType,\n        preventEvent,\n        preventEventTimeout,\n        dragDomEl;\n\n    function extendEvent(e) {\n        var touch = (e.touches || [])[0] || e;\n        e._x = touch.pageX || touch.originalEvent.pageX;\n        e._y = touch.clientY || touch.originalEvent.clientY;\n        e._now = $.now();\n    }\n\n    function touch($el, options) {\n        var el = $el[0],\n            tail = {},\n            touchEnabledFLAG,\n            startEvent,\n            $target,\n            controlTouch,\n            touchFLAG,\n            targetIsSelectFLAG,\n            targetIsLinkFlag,\n            isDisabledSwipe,\n            tolerance,\n            moved;\n\n        function onStart(e) {\n            $target = $(e.target);\n            tail.checked = targetIsSelectFLAG = targetIsLinkFlag = isDisabledSwipe = moved = false;\n\n            if (touchEnabledFLAG\n                || tail.flow\n                || (e.touches && e.touches.length > 1)\n                || e.which > 1\n                || (lastEvent && lastEvent.type !== e.type && preventEvent)\n                || (targetIsSelectFLAG = options.select && $target.is(options.select, el))) return targetIsSelectFLAG;\n\n            touchFLAG = e.type === 'touchstart';\n            targetIsLinkFlag = $target.is('a, a *', el);\n            isDisabledSwipe = $target.hasClass('disableSwipe');\n            controlTouch = tail.control;\n\n            tolerance = (tail.noMove || tail.noSwipe || controlTouch) ? 16 : !tail.snap ? 4 : 0;\n\n            extendEvent(e);\n\n            startEvent = lastEvent = e;\n            moveEventType = e.type.replace(/down|start/, 'move').replace(/Down/, 'Move');\n\n            (options.onStart || noop).call(el, e, {control: controlTouch, $target: $target});\n\n            touchEnabledFLAG = tail.flow = true;\n\n            if (!isDisabledSwipe && (!touchFLAG || tail.go)) stopEvent(e);\n        }\n\n        function onMove(e) {\n            if ((e.touches && e.touches.length > 1)\n                || (MS_POINTER && !e.isPrimary)\n                || moveEventType !== e.type\n                || !touchEnabledFLAG) {\n                touchEnabledFLAG && onEnd();\n                (options.onTouchEnd || noop)();\n                return;\n            }\n\n            isDisabledSwipe = $(e.target).hasClass('disableSwipe');\n\n            if (isDisabledSwipe) {\n                return;\n            }\n\n            extendEvent(e);\n\n            var xDiff = Math.abs(e._x - startEvent._x), // opt _x \u2192 _pageX\n                yDiff = Math.abs(e._y - startEvent._y),\n                xyDiff = xDiff - yDiff,\n                xWin = (tail.go || tail.x || xyDiff >= 0) && !tail.noSwipe,\n                yWin = xyDiff < 0;\n\n            if (touchFLAG && !tail.checked) {\n                if (touchEnabledFLAG = xWin) {\n                    stopEvent(e);\n                }\n            } else {\n                stopEvent(e);\n                if (movedEnough(xDiff,yDiff)) {\n                    (options.onMove || noop).call(el, e, {touch: touchFLAG});\n                }\n            }\n\n            if (!moved && movedEnough(xDiff, yDiff) && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) {\n                moved = true;\n            }\n\n            tail.checked = tail.checked || xWin || yWin;\n        }\n\n        function movedEnough(xDiff, yDiff) {\n            return xDiff > yDiff && xDiff > 1.5;\n        }\n\n        function onEnd(e) {\n            (options.onTouchEnd || noop)();\n\n            var _touchEnabledFLAG = touchEnabledFLAG;\n            tail.control = touchEnabledFLAG = false;\n\n            if (_touchEnabledFLAG) {\n                tail.flow = false;\n            }\n\n            if (!_touchEnabledFLAG || (targetIsLinkFlag && !tail.checked)) return;\n\n            e && stopEvent(e);\n\n            preventEvent = true;\n            clearTimeout(preventEventTimeout);\n            preventEventTimeout = setTimeout(function () {\n                preventEvent = false;\n            }, 1000);\n\n            (options.onEnd || noop).call(el, {\n                moved: moved,\n                $target: $target,\n                control: controlTouch,\n                touch: touchFLAG,\n                startEvent: startEvent,\n                aborted: !e || e.type === 'MSPointerCancel'\n            });\n        }\n\n        function onOtherStart() {\n            if (tail.flow) return;\n            tail.flow = true;\n        }\n\n        function onOtherEnd() {\n            if (!tail.flow) return;\n            tail.flow = false;\n        }\n\n        if (MS_POINTER) {\n            addEvent(el, 'MSPointerDown', onStart);\n            addEvent(document, 'MSPointerMove', onMove);\n            addEvent(document, 'MSPointerCancel', onEnd);\n            addEvent(document, 'MSPointerUp', onEnd);\n        } else {\n            addEvent(el, 'touchstart', onStart);\n            addEvent(el, 'touchmove', onMove);\n            addEvent(el, 'touchend', onEnd);\n\n            addEvent(document, 'touchstart', onOtherStart, true);\n            addEvent(document, 'touchend', onOtherEnd);\n            addEvent(document, 'touchcancel', onOtherEnd);\n\n            $WINDOW.on('scroll', onOtherEnd);\n\n            $el.on('mousedown', onStart);\n            $DOCUMENT\n                .on('mousemove', onMove)\n                .on('mouseup', onEnd);\n        }\n        if (Modernizr.touch) {\n            dragDomEl = 'a';\n        } else {\n            dragDomEl = 'div';\n        }\n        $el.on('click', dragDomEl, function (e) {\n            tail.checked && stopEvent(e);\n        });\n\n        return tail;\n    }\n\n    function moveOnTouch($el, options) {\n        var el = $el[0],\n            elData = $el.data(),\n            tail = {},\n            startCoo,\n            coo,\n            startElPos,\n            moveElPos,\n            edge,\n            moveTrack,\n            startTime,\n            endTime,\n            min,\n            max,\n            snap,\n            dir,\n            slowFLAG,\n            controlFLAG,\n            moved,\n            tracked;\n\n        function startTracking(e, noStop) {\n            tracked = true;\n            startCoo = coo = (dir === 'vertical') ? e._y : e._x;\n            startTime = e._now;\n\n            moveTrack = [\n                [startTime, startCoo]\n            ];\n\n            startElPos = moveElPos = tail.noMove || noStop ? 0 : stop($el, (options.getPos || noop)()/*, options._001*/);\n\n            (options.onStart || noop).call(el, e);\n        }\n\n        function onStart(e, result) {\n            min = tail.min;\n            max = tail.max;\n            snap = tail.snap,\n                dir = tail.direction || 'horizontal',\n                $el.navdir = dir;\n\n            slowFLAG = e.altKey;\n            tracked = moved = false;\n\n            controlFLAG = result.control;\n\n            if (!controlFLAG && !elData.sliding) {\n                startTracking(e);\n            }\n        }\n\n        function onMove(e, result) {\n            if (!tail.noSwipe) {\n                if (!tracked) {\n                    startTracking(e);\n                }\n                coo = (dir === 'vertical') ? e._y : e._x;\n\n                moveTrack.push([e._now, coo]);\n\n                moveElPos = startElPos - (startCoo - coo);\n\n                edge = findShadowEdge(moveElPos, min, max, dir);\n\n                if (moveElPos <= min) {\n                    moveElPos = edgeResistance(moveElPos, min);\n                } else if (moveElPos >= max) {\n                    moveElPos = edgeResistance(moveElPos, max);\n                }\n\n                if (!tail.noMove) {\n                    $el.css(getTranslate(moveElPos, dir));\n                    if (!moved) {\n                        moved = true;\n                        // only for mouse\n                        result.touch || MS_POINTER || $el.addClass(grabbingClass);\n                    }\n\n                    (options.onMove || noop).call(el, e, {pos: moveElPos, edge: edge});\n                }\n            }\n        }\n\n        function onEnd(result) {\n            if (tail.noSwipe && result.moved) return;\n\n            if (!tracked) {\n                startTracking(result.startEvent, true);\n            }\n\n            result.touch || MS_POINTER || $el.removeClass(grabbingClass);\n\n            endTime = $.now();\n\n            var _backTimeIdeal = endTime - TOUCH_TIMEOUT,\n                _backTime,\n                _timeDiff,\n                _timeDiffLast,\n                backTime = null,\n                backCoo,\n                virtualPos,\n                limitPos,\n                newPos,\n                overPos,\n                time = TRANSITION_DURATION,\n                speed,\n                friction = options.friction;\n\n            for (var _i = moveTrack.length - 1; _i >= 0; _i--) {\n                _backTime = moveTrack[_i][0];\n                _timeDiff = Math.abs(_backTime - _backTimeIdeal);\n                if (backTime === null || _timeDiff < _timeDiffLast) {\n                    backTime = _backTime;\n                    backCoo = moveTrack[_i][1];\n                } else if (backTime === _backTimeIdeal || _timeDiff > _timeDiffLast) {\n                    break;\n                }\n                _timeDiffLast = _timeDiff;\n            }\n\n            newPos = minMaxLimit(moveElPos, min, max);\n\n            var cooDiff = backCoo - coo,\n                forwardFLAG = cooDiff >= 0,\n                timeDiff = endTime - backTime,\n                longTouchFLAG = timeDiff > TOUCH_TIMEOUT,\n                swipeFLAG = !longTouchFLAG && moveElPos !== startElPos && newPos === moveElPos;\n\n            if (snap) {\n                newPos = minMaxLimit(Math[swipeFLAG ? (forwardFLAG ? 'floor' : 'ceil') : 'round'](moveElPos / snap) * snap, min, max);\n                min = max = newPos;\n            }\n\n            if (swipeFLAG && (snap || newPos === moveElPos)) {\n                speed = -(cooDiff / timeDiff);\n                time *= minMaxLimit(Math.abs(speed), options.timeLow, options.timeHigh);\n                virtualPos = Math.round(moveElPos + speed * time / friction);\n\n                if (!snap) {\n                    newPos = virtualPos;\n                }\n\n                if (!forwardFLAG && virtualPos > max || forwardFLAG && virtualPos < min) {\n                    limitPos = forwardFLAG ? min : max;\n                    overPos = virtualPos - limitPos;\n                    if (!snap) {\n                        newPos = limitPos;\n                    }\n                    overPos = minMaxLimit(newPos + overPos * .03, limitPos - 50, limitPos + 50);\n                    time = Math.abs((moveElPos - overPos) / (speed / friction));\n                }\n            }\n\n            time *= slowFLAG ? 10 : 1;\n\n            (options.onEnd || noop).call(el, $.extend(result, {\n                moved: result.moved || longTouchFLAG && snap,\n                pos: moveElPos,\n                newPos: newPos,\n                overPos: overPos,\n                time: time,\n                dir: dir\n            }));\n        }\n\n        tail = $.extend(touch(options.$wrap, $.extend({}, options, {\n            onStart: onStart,\n            onMove: onMove,\n            onEnd: onEnd\n        })), tail);\n\n        return tail;\n    }\n\n    function wheel($el, options) {\n        var el = $el[0],\n            lockFLAG,\n            lastDirection,\n            lastNow,\n            tail = {\n                prevent: {}\n            };\n\n        addEvent(el, WHEEL, function (e) {\n            var yDelta = e.wheelDeltaY || -1 * e.deltaY || 0,\n                xDelta = e.wheelDeltaX || -1 * e.deltaX || 0,\n                xWin = Math.abs(xDelta) && !Math.abs(yDelta),\n                direction = getDirectionSign(xDelta < 0),\n                sameDirection = lastDirection === direction,\n                now = $.now(),\n                tooFast = now - lastNow < TOUCH_TIMEOUT;\n\n            lastDirection = direction;\n            lastNow = now;\n\n            if (!xWin || !tail.ok || tail.prevent[direction] && !lockFLAG) {\n                return;\n            } else {\n                stopEvent(e, true);\n                if (lockFLAG && sameDirection && tooFast) {\n                    return;\n                }\n            }\n\n            if (options.shift) {\n                lockFLAG = true;\n                clearTimeout(tail.t);\n                tail.t = setTimeout(function () {\n                    lockFLAG = false;\n                }, SCROLL_LOCK_TIMEOUT);\n            }\n\n            (options.onEnd || noop)(e, options.shift ? direction : xDelta);\n\n        });\n\n        return tail;\n    }\n\n    jQuery.Fotorama = function ($fotorama, opts) {\n        $HTML = $('html');\n        $BODY = $('body');\n\n        var that = this,\n            stamp = $.now(),\n            stampClass = _fotoramaClass + stamp,\n            fotorama = $fotorama[0],\n            data,\n            dataFrameCount = 1,\n            fotoramaData = $fotorama.data(),\n            size,\n\n            $style = $('<style></style>'),\n\n            $anchor = $(div(hiddenClass)),\n            $wrap = $fotorama.find(cls(wrapClass)),\n            $stage = $wrap.find(cls(stageClass)),\n            stage = $stage[0],\n\n            $stageShaft = $fotorama.find(cls(stageShaftClass)),\n            $stageFrame = $(),\n            $arrPrev = $fotorama.find(cls(arrPrevClass)),\n            $arrNext = $fotorama.find(cls(arrNextClass)),\n            $arrs = $fotorama.find(cls(arrClass)),\n            $navWrap = $fotorama.find(cls(navWrapClass)),\n            $nav = $navWrap.find(cls(navClass)),\n            $navShaft = $nav.find(cls(navShaftClass)),\n            $navFrame,\n            $navDotFrame = $(),\n            $navThumbFrame = $(),\n\n            stageShaftData = $stageShaft.data(),\n            navShaftData = $navShaft.data(),\n\n            $thumbBorder = $fotorama.find(cls(thumbBorderClass)),\n            $thumbArrLeft = $fotorama.find(cls(thumbArrLeft)),\n            $thumbArrRight = $fotorama.find(cls(thumbArrRight)),\n\n            $fullscreenIcon = $fotorama.find(cls(fullscreenIconClass)),\n            fullscreenIcon = $fullscreenIcon[0],\n            $videoPlay = $(div(videoPlayClass)),\n            $videoClose = $fotorama.find(cls(videoCloseClass)),\n            videoClose = $videoClose[0],\n\n            $spinner = $fotorama.find(cls(fotoramaSpinnerClass)),\n\n            $videoPlaying,\n\n            activeIndex = false,\n            activeFrame,\n            activeIndexes,\n            repositionIndex,\n            dirtyIndex,\n            lastActiveIndex,\n            prevIndex,\n            nextIndex,\n            nextAutoplayIndex,\n            startIndex,\n\n            o_loop,\n            o_nav,\n            o_navThumbs,\n            o_navTop,\n            o_allowFullScreen,\n            o_nativeFullScreen,\n            o_fade,\n            o_thumbSide,\n            o_thumbSide2,\n            o_transitionDuration,\n            o_transition,\n            o_shadows,\n            o_rtl,\n            o_keyboard,\n            lastOptions = {},\n\n            measures = {},\n            measuresSetFLAG,\n\n            stageShaftTouchTail = {},\n            stageWheelTail = {},\n            navShaftTouchTail = {},\n            navWheelTail = {},\n\n            scrollTop,\n            scrollLeft,\n\n            showedFLAG,\n            pausedAutoplayFLAG,\n            stoppedAutoplayFLAG,\n\n            toDeactivate = {},\n            toDetach = {},\n\n            measuresStash,\n\n            touchedFLAG,\n\n            hoverFLAG,\n\n            navFrameKey,\n            stageLeft = 0,\n\n            fadeStack = [];\n\n        $wrap[STAGE_FRAME_KEY] = $('<div class=\"' + stageFrameClass + '\"></div>');\n        $wrap[NAV_THUMB_FRAME_KEY] = $($.Fotorama.jst.thumb());\n        $wrap[NAV_DOT_FRAME_KEY] = $($.Fotorama.jst.dots());\n\n        toDeactivate[STAGE_FRAME_KEY] = [];\n        toDeactivate[NAV_THUMB_FRAME_KEY] = [];\n        toDeactivate[NAV_DOT_FRAME_KEY] = [];\n        toDetach[STAGE_FRAME_KEY] = {};\n\n        $wrap.addClass(CSS3 ? wrapCss3Class : wrapCss2Class);\n\n        fotoramaData.fotorama = this;\n\n        /**\n         * Search video items in incoming data and transform object for video layout.\n         *\n         */\n        function checkForVideo() {\n            $.each(data, function (i, dataFrame) {\n                if (!dataFrame.i) {\n                    dataFrame.i = dataFrameCount++;\n                    var video = findVideoId(dataFrame.video, true);\n                    if (video) {\n                        var thumbs = {};\n                        dataFrame.video = video;\n                        if (!dataFrame.img && !dataFrame.thumb) {\n                            thumbs = getVideoThumbs(dataFrame, data, that);\n                        } else {\n                            dataFrame.thumbsReady = true;\n                        }\n                        updateData(data, {img: thumbs.img, thumb: thumbs.thumb}, dataFrame.i, that);\n                    }\n                }\n            });\n        }\n\n        /**\n         * Checks if current media object is YouTube or Vimeo video stream\n         * @returns {boolean}\n         */\n        function isVideo() {\n            return $((that.activeFrame || {}).$stageFrame || {}).hasClass('fotorama-video-container');\n        }\n\n        function allowKey(key) {\n            return o_keyboard[key];\n        }\n\n        function setStagePosition() {\n            if ($stage !== undefined) {\n\n                if (opts.navdir == 'vertical') {\n                    var padding = opts.thumbwidth + opts.thumbmargin;\n\n                    $stage.css('left', padding);\n                    $arrNext.css('right', padding);\n                    $fullscreenIcon.css('right', padding);\n                    $wrap.css('width', $wrap.css('width') + padding);\n                    $stageShaft.css('max-width', $wrap.width() - padding);\n                } else {\n                    $stage.css('left', '');\n                    $arrNext.css('right', '');\n                    $fullscreenIcon.css('right', '');\n                    $wrap.css('width', $wrap.css('width') + padding);\n                    $stageShaft.css('max-width', '');\n                }\n            }\n        }\n\n        function bindGlobalEvents(FLAG) {\n            var keydownCommon = 'keydown.' + _fotoramaClass,\n                localStamp = _fotoramaClass + stamp,\n                keydownLocal = 'keydown.' + localStamp,\n                keyupLocal = 'keyup.' + localStamp,\n                resizeLocal = 'resize.' + localStamp + ' ' + 'orientationchange.' + localStamp,\n                showParams;\n\n            if (FLAG) {\n                $DOCUMENT\n                    .on(keydownLocal, function (e) {\n                        var catched,\n                            index;\n\n                        if ($videoPlaying && e.keyCode === 27) {\n                            catched = true;\n                            unloadVideo($videoPlaying, true, true);\n                        } else if (that.fullScreen || (opts.keyboard && !that.index)) {\n                            if (e.keyCode === 27) {\n                                catched = true;\n                                that.cancelFullScreen();\n                            } else if ((e.shiftKey && e.keyCode === 32 && allowKey('space')) || (!e.altKey && !e.metaKey && e.keyCode === 37 && allowKey('left')) || (e.keyCode === 38 && allowKey('up') && $(':focus').attr('data-gallery-role'))) {\n                                that.longPress.progress();\n                                index = '<';\n                            } else if ((e.keyCode === 32 && allowKey('space')) || (!e.altKey && !e.metaKey && e.keyCode === 39 && allowKey('right')) || (e.keyCode === 40 && allowKey('down') && $(':focus').attr('data-gallery-role'))) {\n                                that.longPress.progress();\n                                index = '>';\n                            } else if (e.keyCode === 36 && allowKey('home')) {\n                                that.longPress.progress();\n                                index = '<<';\n                            } else if (e.keyCode === 35 && allowKey('end')) {\n                                that.longPress.progress();\n                                index = '>>';\n                            }\n                        }\n\n                        (catched || index) && stopEvent(e);\n                        showParams = {index: index, slow: e.altKey, user: true};\n                        index && (that.longPress.inProgress ?\n                            that.showWhileLongPress(showParams) :\n                            that.show(showParams));\n                    });\n\n                if (FLAG) {\n                    $DOCUMENT\n                        .on(keyupLocal, function (e) {\n                            if (that.longPress.inProgress) {\n                                that.showEndLongPress({user: true});\n                            }\n                            that.longPress.reset();\n                        });\n                }\n\n                if (!that.index) {\n                    $DOCUMENT\n                        .off(keydownCommon)\n                        .on(keydownCommon, 'textarea, input, select', function (e) {\n                            !$BODY.hasClass(_fullscreenClass) && e.stopPropagation();\n                        });\n                }\n\n                $WINDOW.on(resizeLocal, that.resize);\n            } else {\n                $DOCUMENT.off(keydownLocal);\n                $WINDOW.off(resizeLocal);\n            }\n        }\n\n        function appendElements(FLAG) {\n            if (FLAG === appendElements.f) return;\n\n            if (FLAG) {\n                $fotorama\n                    .addClass(_fotoramaClass + ' ' + stampClass)\n                    .before($anchor)\n                    .before($style);\n                addInstance(that);\n            } else {\n                $anchor.detach();\n                $style.detach();\n                $fotorama\n                    .html(fotoramaData.urtext)\n                    .removeClass(stampClass);\n\n                hideInstance(that);\n            }\n\n            bindGlobalEvents(FLAG);\n            appendElements.f = FLAG;\n        }\n\n        /**\n         * Set and install data from incoming @param {JSON} options or takes data attr from data-\"name\"=... values.\n         */\n        function setData() {\n            data = that.data = data || clone(opts.data) || getDataFromHtml($fotorama);\n            size = that.size = data.length;\n\n            ready.ok && opts.shuffle && shuffle(data);\n\n            checkForVideo();\n\n            activeIndex = limitIndex(activeIndex);\n\n            size && appendElements(true);\n        }\n\n        function stageNoMove() {\n            var _noMove = size < 2 || $videoPlaying;\n            stageShaftTouchTail.noMove = _noMove || o_fade;\n            stageShaftTouchTail.noSwipe = _noMove || !opts.swipe;\n\n            !o_transition && $stageShaft.toggleClass(grabClass, !opts.click && !stageShaftTouchTail.noMove && !stageShaftTouchTail.noSwipe);\n            MS_POINTER && $wrap.toggleClass(wrapPanYClass, !stageShaftTouchTail.noSwipe);\n        }\n\n        function setAutoplayInterval(interval) {\n            if (interval === true) interval = '';\n            opts.autoplay = Math.max(+interval || AUTOPLAY_INTERVAL, o_transitionDuration * 1.5);\n        }\n\n        function updateThumbArrow(opt) {\n            if (opt.navarrows && opt.nav === 'thumbs') {\n                $thumbArrLeft.show();\n                $thumbArrRight.show();\n            } else {\n                $thumbArrLeft.hide();\n                $thumbArrRight.hide();\n            }\n\n        }\n\n        function getThumbsInSlide($el, opts) {\n            return Math.floor($wrap.width() / (opts.thumbwidth + opts.thumbmargin));\n        }\n\n        /**\n         * Options on the fly\n         * */\n        function setOptions() {\n            if (!opts.nav || opts.nav === 'dots') {\n                opts.navdir = 'horizontal'\n            }\n\n            that.options = opts = optionsToLowerCase(opts);\n            thumbsPerSlide = getThumbsInSlide($wrap, opts);\n\n            o_fade = (opts.transition === 'crossfade' || opts.transition === 'dissolve');\n\n            o_loop = opts.loop && (size > 2 || (o_fade && (!o_transition || o_transition !== 'slide')));\n\n            o_transitionDuration = +opts.transitionduration || TRANSITION_DURATION;\n\n            o_rtl = opts.direction === 'rtl';\n\n            o_keyboard = $.extend({}, opts.keyboard && KEYBOARD_OPTIONS, opts.keyboard);\n            updateThumbArrow(opts);\n            var classes = {add: [], remove: []};\n\n            function addOrRemoveClass(FLAG, value) {\n                classes[FLAG ? 'add' : 'remove'].push(value);\n            }\n\n            if (size > 1) {\n                o_nav = opts.nav;\n                o_navTop = opts.navposition === 'top';\n                classes.remove.push(selectClass);\n\n                $arrs.toggle(!!opts.arrows);\n            } else {\n                o_nav = false;\n                $arrs.hide();\n            }\n\n            arrsUpdate();\n            stageWheelUpdate();\n            thumbArrUpdate();\n            if (opts.autoplay) setAutoplayInterval(opts.autoplay);\n\n            o_thumbSide = numberFromMeasure(opts.thumbwidth) || THUMB_SIZE;\n            o_thumbSide2 = numberFromMeasure(opts.thumbheight) || THUMB_SIZE;\n\n            stageWheelTail.ok = navWheelTail.ok = opts.trackpad && !SLOW;\n\n            stageNoMove();\n\n            extendMeasures(opts, [measures]);\n\n            o_navThumbs = o_nav === 'thumbs';\n\n            if ($navWrap.filter(':hidden') && !!o_nav) {\n                $navWrap.show();\n            }\n            if (o_navThumbs) {\n                frameDraw(size, 'navThumb');\n\n                $navFrame = $navThumbFrame;\n                navFrameKey = NAV_THUMB_FRAME_KEY;\n\n                setStyle($style, $.Fotorama.jst.style({\n                    w: o_thumbSide,\n                    h: o_thumbSide2,\n                    b: opts.thumbborderwidth,\n                    m: opts.thumbmargin,\n                    s: stamp,\n                    q: !COMPAT\n                }));\n\n                $nav\n                    .addClass(navThumbsClass)\n                    .removeClass(navDotsClass);\n            } else if (o_nav === 'dots') {\n                frameDraw(size, 'navDot');\n\n                $navFrame = $navDotFrame;\n                navFrameKey = NAV_DOT_FRAME_KEY;\n\n                $nav\n                    .addClass(navDotsClass)\n                    .removeClass(navThumbsClass);\n            } else {\n                $navWrap.hide();\n                o_nav = false;\n                $nav.removeClass(navThumbsClass + ' ' + navDotsClass);\n            }\n\n            if (o_nav) {\n                if (o_navTop) {\n                    $navWrap.insertBefore($stage);\n                } else {\n                    $navWrap.insertAfter($stage);\n                }\n                frameAppend.nav = false;\n\n                frameAppend($navFrame, $navShaft, 'nav');\n            }\n\n            o_allowFullScreen = opts.allowfullscreen;\n\n            if (o_allowFullScreen) {\n                $fullscreenIcon.prependTo($stage);\n                o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native';\n            } else {\n                $fullscreenIcon.detach();\n                o_nativeFullScreen = false;\n            }\n\n            addOrRemoveClass(o_fade, wrapFadeClass);\n            addOrRemoveClass(!o_fade, wrapSlideClass);\n            addOrRemoveClass(!opts.captions, wrapNoCaptionsClass);\n            addOrRemoveClass(o_rtl, wrapRtlClass);\n            addOrRemoveClass(opts.arrows, wrapToggleArrowsClass);\n\n            o_shadows = opts.shadows && !SLOW;\n            addOrRemoveClass(!o_shadows, wrapNoShadowsClass);\n\n            $wrap\n                .addClass(classes.add.join(' '))\n                .removeClass(classes.remove.join(' '));\n\n            lastOptions = $.extend({}, opts);\n            setStagePosition();\n        }\n\n        function normalizeIndex(index) {\n            return index < 0 ? (size + (index % size)) % size : index >= size ? index % size : index;\n        }\n\n        function limitIndex(index) {\n            return minMaxLimit(index, 0, size - 1);\n        }\n\n        function edgeIndex(index) {\n            return o_loop ? normalizeIndex(index) : limitIndex(index);\n        }\n\n        function getPrevIndex(index) {\n            return index > 0 || o_loop ? index - 1 : false;\n        }\n\n        function getNextIndex(index) {\n            return index < size - 1 || o_loop ? index + 1 : false;\n        }\n\n        function setStageShaftMinmaxAndSnap() {\n            stageShaftTouchTail.min = o_loop ? -Infinity : -getPosByIndex(size - 1, measures.w, opts.margin, repositionIndex);\n            stageShaftTouchTail.max = o_loop ? Infinity : -getPosByIndex(0, measures.w, opts.margin, repositionIndex);\n            stageShaftTouchTail.snap = measures.w + opts.margin;\n        }\n\n        function setNavShaftMinMax() {\n\n            var isVerticalDir = (opts.navdir === 'vertical');\n            var param = isVerticalDir ? $navShaft.height() : $navShaft.width();\n            var mainParam = isVerticalDir ? measures.h : measures.nw;\n            navShaftTouchTail.min = Math.min(0, mainParam - param);\n            navShaftTouchTail.max = 0;\n            navShaftTouchTail.direction = opts.navdir;\n            $navShaft.toggleClass(grabClass, !(navShaftTouchTail.noMove = navShaftTouchTail.min === navShaftTouchTail.max));\n        }\n\n        function eachIndex(indexes, type, fn) {\n            if (typeof indexes === 'number') {\n                indexes = new Array(indexes);\n                var rangeFLAG = true;\n            }\n            return $.each(indexes, function (i, index) {\n                if (rangeFLAG) index = i;\n                if (typeof index === 'number') {\n                    var dataFrame = data[normalizeIndex(index)];\n\n                    if (dataFrame) {\n                        var key = '$' + type + 'Frame',\n                            $frame = dataFrame[key];\n\n                        fn.call(this, i, index, dataFrame, $frame, key, $frame && $frame.data());\n                    }\n                }\n            });\n        }\n\n        function setMeasures(width, height, ratio, index) {\n            if (!measuresSetFLAG || (measuresSetFLAG === '*' && index === startIndex)) {\n\n                width = measureIsValid(opts.width) || measureIsValid(width) || WIDTH;\n                height = measureIsValid(opts.height) || measureIsValid(height) || HEIGHT;\n                that.resize({\n                    width: width,\n                    ratio: opts.ratio || ratio || width / height\n                }, 0, index !== startIndex && '*');\n            }\n        }\n\n        function loadImg(indexes, type, specialMeasures, again) {\n\n            eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {\n\n                if (!$frame) return;\n\n                var fullFLAG = that.fullScreen && !frameData.$full && type === 'stage';\n\n                if (frameData.$img && !again && !fullFLAG) return;\n\n                var img = new Image(),\n                    $img = $(img),\n                    imgData = $img.data();\n\n                frameData[fullFLAG ? '$full' : '$img'] = $img;\n\n                var srcKey = type === 'stage' ? (fullFLAG ? 'full' : 'img') : 'thumb',\n                    src = dataFrame[srcKey],\n                    dummy = fullFLAG ? dataFrame['img'] : dataFrame[type === 'stage' ? 'thumb' : 'img'];\n\n                if (type === 'navThumb') $frame = frameData.$wrap;\n\n                function triggerTriggerEvent(event) {\n                    var _index = normalizeIndex(index);\n                    triggerEvent(event, {\n                        index: _index,\n                        src: src,\n                        frame: data[_index]\n                    });\n                }\n\n                function error() {\n                    $img.remove();\n\n                    $.Fotorama.cache[src] = 'error';\n\n                    if ((!dataFrame.html || type !== 'stage') && dummy && dummy !== src) {\n                        dataFrame[srcKey] = src = dummy;\n                        frameData.$full = null;\n                        loadImg([index], type, specialMeasures, true);\n                    } else {\n                        if (src && !dataFrame.html && !fullFLAG) {\n                            $frame\n                                .trigger('f:error')\n                                .removeClass(loadingClass)\n                                .addClass(errorClass);\n\n                            triggerTriggerEvent('error');\n                        } else if (type === 'stage') {\n                            $frame\n                                .trigger('f:load')\n                                .removeClass(loadingClass + ' ' + errorClass)\n                                .addClass(loadedClass);\n\n                            triggerTriggerEvent('load');\n                            setMeasures();\n                        }\n\n                        frameData.state = 'error';\n\n                        if (size > 1 && data[index] === dataFrame && !dataFrame.html && !dataFrame.deleted && !dataFrame.video && !fullFLAG) {\n                            dataFrame.deleted = true;\n                            that.splice(index, 1);\n                        }\n                    }\n                }\n\n                function loaded() {\n                    $.Fotorama.measures[src] = imgData.measures = $.Fotorama.measures[src] || {\n                            width: img.width,\n                            height: img.height,\n                            ratio: img.width / img.height\n                        };\n\n                    setMeasures(imgData.measures.width, imgData.measures.height, imgData.measures.ratio, index);\n\n                    $img\n                        .off('load error')\n                        .addClass('' + (fullFLAG ? imgFullClass: imgClass))\n                        .attr('aria-hidden', 'false')\n                        .prependTo($frame);\n\n                    if ($frame.hasClass(stageFrameClass) && !$frame.hasClass(videoContainerClass)) {\n                        $frame.attr(\"href\", $img.attr(\"src\"));\n                    }\n\n                    fit($img, (\n                            $.isFunction(specialMeasures) ? specialMeasures() : specialMeasures) || measures);\n\n                    $.Fotorama.cache[src] = frameData.state = 'loaded';\n\n                    setTimeout(function () {\n                        $frame\n                            .trigger('f:load')\n                            .removeClass(loadingClass + ' ' + errorClass)\n                            .addClass(loadedClass + ' ' + (fullFLAG ? loadedFullClass : loadedImgClass));\n\n                        if (type === 'stage') {\n                            triggerTriggerEvent('load');\n                        } else if (dataFrame.thumbratio === AUTO || !dataFrame.thumbratio && opts.thumbratio === AUTO) {\n                            // danger! reflow for all thumbnails\n                            dataFrame.thumbratio = imgData.measures.ratio;\n                            reset();\n                        }\n                    }, 0);\n                }\n\n                if (!src) {\n                    error();\n                    return;\n                }\n\n                function waitAndLoad() {\n                    var _i = 10;\n                    waitFor(function () {\n                        return !touchedFLAG || !_i-- && !SLOW;\n                    }, function () {\n                        loaded();\n                    });\n                }\n\n                if (!$.Fotorama.cache[src]) {\n                    $.Fotorama.cache[src] = '*';\n\n                    $img\n                        .on('load', waitAndLoad)\n                        .on('error', error);\n                } else {\n                    (function justWait() {\n                        if ($.Fotorama.cache[src] === 'error') {\n                            error();\n                        } else if ($.Fotorama.cache[src] === 'loaded') {\n                            setTimeout(waitAndLoad, 0);\n                        } else {\n                            setTimeout(justWait, 100);\n                        }\n                    })();\n                }\n\n                frameData.state = '';\n                img.src = src;\n\n                if (frameData.data.caption) {\n                    img.alt = frameData.data.caption || \"\";\n                }\n\n                if (frameData.data.full) {\n                    $(img).data('original', frameData.data.full);\n                }\n\n                if (UTIL.isExpectedCaption(dataFrame, opts.showcaption)) {\n                    $(img).attr('aria-labelledby', dataFrame.labelledby);\n                }\n            });\n        }\n\n        function updateFotoramaState() {\n            var $frame = activeFrame[STAGE_FRAME_KEY];\n\n            if ($frame && !$frame.data().state) {\n                $spinner.addClass(spinnerShowClass);\n                $frame.on('f:load f:error', function () {\n                    $frame.off('f:load f:error');\n                    $spinner.removeClass(spinnerShowClass);\n                });\n            }\n        }\n\n        function addNavFrameEvents(frame) {\n            addEnterUp(frame, onNavFrameClick);\n            addFocus(frame, function () {\n\n                setTimeout(function () {\n                    lockScroll($nav);\n                }, 0);\n                slideNavShaft({time: o_transitionDuration, guessIndex: $(this).data().eq, minMax: navShaftTouchTail});\n            });\n        }\n\n        function frameDraw(indexes, type) {\n            eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {\n                if ($frame) return;\n\n                $frame = dataFrame[key] = $wrap[key].clone();\n                frameData = $frame.data();\n                frameData.data = dataFrame;\n                var frame = $frame[0],\n                    labelledbyValue = \"labelledby\" + $.now();\n\n                if (type === 'stage') {\n\n                    if (dataFrame.html) {\n                        $('<div class=\"' + htmlClass + '\"></div>')\n                            .append(\n                            dataFrame._html ? $(dataFrame.html)\n                                .removeAttr('id')\n                                .html(dataFrame._html) // Because of IE\n                                : dataFrame.html\n                        )\n                            .appendTo($frame);\n                    }\n\n                    if (dataFrame.id) {\n                        labelledbyValue = dataFrame.id || labelledbyValue;\n                    }\n                    dataFrame.labelledby = labelledbyValue;\n\n                    if (UTIL.isExpectedCaption(dataFrame, opts.showcaption)) {\n                        $($.Fotorama.jst.frameCaption({\n                            caption: dataFrame.caption,\n                            labelledby: labelledbyValue\n                        })).appendTo($frame);\n                    }\n\n                    dataFrame.video && $frame\n                        .addClass(stageFrameVideoClass)\n                        .append($videoPlay.clone());\n\n                    // This solves tabbing problems\n                    addFocus(frame, function (e) {\n                        setTimeout(function () {\n                            lockScroll($stage);\n                        }, 0);\n                        clickToShow({index: frameData.eq, user: true}, e);\n                    });\n\n                    $stageFrame = $stageFrame.add($frame);\n                } else if (type === 'navDot') {\n                    addNavFrameEvents(frame);\n                    $navDotFrame = $navDotFrame.add($frame);\n                } else if (type === 'navThumb') {\n                    addNavFrameEvents(frame);\n                    frameData.$wrap = $frame.children(':first');\n\n                    $navThumbFrame = $navThumbFrame.add($frame);\n                    if (dataFrame.video) {\n                        frameData.$wrap.append($videoPlay.clone());\n                    }\n                }\n            });\n        }\n\n        function callFit($img, measuresToFit) {\n            return $img && $img.length && fit($img, measuresToFit);\n        }\n\n        function stageFramePosition(indexes) {\n            eachIndex(indexes, 'stage', function (i, index, dataFrame, $frame, key, frameData) {\n                if (!$frame) return;\n\n                var normalizedIndex = normalizeIndex(index);\n                frameData.eq = normalizedIndex;\n\n                toDetach[STAGE_FRAME_KEY][normalizedIndex] = $frame.css($.extend({left: o_fade ? 0 : getPosByIndex(index, measures.w, opts.margin, repositionIndex)}, o_fade && getDuration(0)));\n\n                if (isDetached($frame[0])) {\n                    $frame.appendTo($stageShaft);\n                    unloadVideo(dataFrame.$video);\n                }\n\n                callFit(frameData.$img, measures);\n                callFit(frameData.$full, measures);\n\n                if ($frame.hasClass(stageFrameClass) && !($frame.attr('aria-hidden') === \"false\" && $frame.hasClass(activeClass))) {\n                    $frame.attr('aria-hidden', 'true');\n                }\n            });\n        }\n\n        function thumbsDraw(pos, loadFLAG) {\n            var leftLimit,\n                rightLimit,\n                exceedLimit;\n\n\n            if (o_nav !== 'thumbs' || isNaN(pos)) return;\n\n            leftLimit = -pos;\n            rightLimit = -pos + measures.nw;\n\n            if (opts.navdir === 'vertical') {\n                pos = pos - opts.thumbheight;\n                rightLimit = -pos + measures.h;\n            }\n\n            $navThumbFrame.each(function () {\n                var $this = $(this),\n                    thisData = $this.data(),\n                    eq = thisData.eq,\n                    getSpecialMeasures = function () {\n                        return {\n                            h: o_thumbSide2,\n                            w: thisData.w\n                        }\n                    },\n                    specialMeasures = getSpecialMeasures(),\n                    exceedLimit = opts.navdir === 'vertical' ?\n                    thisData.t > rightLimit : thisData.l > rightLimit;\n                specialMeasures.w = thisData.w;\n\n                if ((opts.navdir !== 'vertical' && thisData.l + thisData.w < leftLimit)\n                    || exceedLimit\n                    || callFit(thisData.$img, specialMeasures)) return;\n\n                loadFLAG && loadImg([eq], 'navThumb', getSpecialMeasures);\n            });\n        }\n\n        function frameAppend($frames, $shaft, type) {\n            if (!frameAppend[type]) {\n\n                var thumbsFLAG = type === 'nav' && o_navThumbs,\n                    left = 0,\n                    top = 0;\n\n                $shaft.append(\n                    $frames\n                        .filter(function () {\n                            var actual,\n                                $this = $(this),\n                                frameData = $this.data();\n                            for (var _i = 0, _l = data.length; _i < _l; _i++) {\n                                if (frameData.data === data[_i]) {\n                                    actual = true;\n                                    frameData.eq = _i;\n                                    break;\n                                }\n                            }\n                            return actual || $this.remove() && false;\n                        })\n                        .sort(function (a, b) {\n                            return $(a).data().eq - $(b).data().eq;\n                        })\n                        .each(function () {\n                            var $this = $(this),\n                                frameData = $this.data();\n                            UTIL.setThumbAttr($this, frameData.data.caption, \"aria-label\");\n                        })\n                        .each(function () {\n\n                            if (!thumbsFLAG) return;\n\n                            var $this = $(this),\n                                frameData = $this.data(),\n                                thumbwidth = Math.round(o_thumbSide2 * frameData.data.thumbratio) || o_thumbSide,\n                                thumbheight = Math.round(o_thumbSide / frameData.data.thumbratio) || o_thumbSide2;\n                            frameData.t = top;\n                            frameData.h = thumbheight;\n                            frameData.l = left;\n                            frameData.w = thumbwidth;\n\n                            $this.css({width: thumbwidth});\n\n                            top += thumbheight + opts.thumbmargin;\n                            left += thumbwidth + opts.thumbmargin;\n                        })\n                );\n\n                frameAppend[type] = true;\n            }\n        }\n\n        function getDirection(x) {\n            return x - stageLeft > measures.w / 3;\n        }\n\n        function disableDirrection(i) {\n            return !o_loop && (!(activeIndex + i) || !(activeIndex - size + i)) && !$videoPlaying;\n        }\n\n        function arrsUpdate() {\n            var disablePrev = disableDirrection(0),\n                disableNext = disableDirrection(1);\n            $arrPrev\n                .toggleClass(arrDisabledClass, disablePrev)\n                .attr(disableAttr(disablePrev, false));\n            $arrNext\n                .toggleClass(arrDisabledClass, disableNext)\n                .attr(disableAttr(disableNext, false));\n        }\n\n        function thumbArrUpdate() {\n            var isLeftDisable = false,\n                isRightDisable = false;\n            if (opts.navtype === 'thumbs' && !opts.loop) {\n                (activeIndex == 0) ? isLeftDisable = true : isLeftDisable = false;\n                (activeIndex == opts.data.length - 1) ? isRightDisable = true : isRightDisable = false;\n            }\n            if (opts.navtype === 'slides') {\n                var pos = readPosition($navShaft, opts.navdir);\n                pos >= navShaftTouchTail.max ? isLeftDisable = true : isLeftDisable = false;\n                pos <= Math.round(navShaftTouchTail.min) ? isRightDisable = true : isRightDisable = false;\n            }\n            $thumbArrLeft\n                .toggleClass(arrDisabledClass, isLeftDisable)\n                .attr(disableAttr(isLeftDisable, true));\n            $thumbArrRight\n                .toggleClass(arrDisabledClass, isRightDisable)\n                .attr(disableAttr(isRightDisable, true));\n        }\n\n        function stageWheelUpdate() {\n            if (stageWheelTail.ok) {\n                stageWheelTail.prevent = {'<': disableDirrection(0), '>': disableDirrection(1)};\n            }\n        }\n\n        function getNavFrameBounds($navFrame) {\n            var navFrameData = $navFrame.data(),\n                left,\n                top,\n                width,\n                height;\n\n            if (o_navThumbs) {\n                left = navFrameData.l;\n                top = navFrameData.t;\n                width = navFrameData.w;\n                height = navFrameData.h;\n            } else {\n                left = $navFrame.position().left;\n                width = $navFrame.width();\n            }\n\n            var horizontalBounds = {\n                c: left + width / 2,\n                min: -left + opts.thumbmargin * 10,\n                max: -left + measures.w - width - opts.thumbmargin * 10\n            };\n\n            var verticalBounds = {\n                c: top + height / 2,\n                min: -top + opts.thumbmargin * 10,\n                max: -top + measures.h - height - opts.thumbmargin * 10\n            };\n\n            return opts.navdir === 'vertical' ? verticalBounds : horizontalBounds;\n        }\n\n        function slideThumbBorder(time) {\n            var navFrameData = activeFrame[navFrameKey].data();\n            slide($thumbBorder, {\n                time: time * 1.2,\n                pos: (opts.navdir === 'vertical' ? navFrameData.t : navFrameData.l),\n                width: navFrameData.w,\n                height: navFrameData.h,\n                direction: opts.navdir\n            });\n        }\n\n        function slideNavShaft(options) {\n            var $guessNavFrame = data[options.guessIndex][navFrameKey],\n                typeOfAnimation = opts.navtype;\n\n            var overflowFLAG,\n                time,\n                minMax,\n                boundTop,\n                boundLeft,\n                l,\n                pos,\n                x;\n\n            if ($guessNavFrame) {\n                if (typeOfAnimation === 'thumbs') {\n                    overflowFLAG = navShaftTouchTail.min !== navShaftTouchTail.max;\n                    minMax = options.minMax || overflowFLAG && getNavFrameBounds(activeFrame[navFrameKey]);\n                    boundTop = overflowFLAG && (options.keep && slideNavShaft.t ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max));\n                    boundLeft = overflowFLAG && (options.keep && slideNavShaft.l ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max));\n                    l = (opts.navdir === 'vertical' ? boundTop : boundLeft);\n                    pos = overflowFLAG && minMaxLimit(l, navShaftTouchTail.min, navShaftTouchTail.max) || 0;\n                    time = options.time * 1.1;\n                    slide($navShaft, {\n                        time: time,\n                        pos: pos,\n                        direction: opts.navdir,\n                        onEnd: function () {\n                            thumbsDraw(pos, true);\n                            thumbArrUpdate();\n                        }\n                    });\n\n                    setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));\n                    slideNavShaft.l = l;\n                } else {\n                    x = readPosition($navShaft, opts.navdir);\n                    time = options.time * 1.11;\n\n                    pos = validateSlidePos(opts, navShaftTouchTail, options.guessIndex, x, $guessNavFrame, $navWrap, opts.navdir);\n\n                    slide($navShaft, {\n                        time: time,\n                        pos: pos,\n                        direction: opts.navdir,\n                        onEnd: function () {\n                            thumbsDraw(pos, true);\n                            thumbArrUpdate();\n                        }\n                    });\n                    setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));\n                }\n            }\n        }\n\n        function navUpdate() {\n            deactivateFrames(navFrameKey);\n            toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass).attr('data-active', true));\n        }\n\n        function deactivateFrames(key) {\n            var _toDeactivate = toDeactivate[key];\n\n            while (_toDeactivate.length) {\n                _toDeactivate.shift().removeClass(activeClass).attr('data-active', false);\n            }\n        }\n\n        function detachFrames(key) {\n            var _toDetach = toDetach[key];\n\n            $.each(activeIndexes, function (i, index) {\n                delete _toDetach[normalizeIndex(index)];\n            });\n\n            $.each(_toDetach, function (index, $frame) {\n                delete _toDetach[index];\n                $frame.detach();\n            });\n        }\n\n        function stageShaftReposition(skipOnEnd) {\n\n            repositionIndex = dirtyIndex = activeIndex;\n\n            var $frame = activeFrame[STAGE_FRAME_KEY];\n\n            if ($frame) {\n                deactivateFrames(STAGE_FRAME_KEY);\n                toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass).attr('data-active', true));\n\n                if ($frame.hasClass(stageFrameClass)) {\n                    $frame.attr('aria-hidden', 'false');\n                }\n\n                skipOnEnd || that.showStage.onEnd(true);\n                stop($stageShaft, 0, true);\n\n                detachFrames(STAGE_FRAME_KEY);\n                stageFramePosition(activeIndexes);\n                setStageShaftMinmaxAndSnap();\n                setNavShaftMinMax();\n                addEnterUp($stageShaft[0], function () {\n                    if (!$fotorama.hasClass(fullscreenClass)) {\n                        that.requestFullScreen();\n                        $fullscreenIcon.focus();\n                    }\n                });\n            }\n        }\n\n        function extendMeasures(options, measuresArray) {\n            if (!options) return;\n\n            $.each(measuresArray, function (i, measures) {\n                if (!measures) return;\n\n                $.extend(measures, {\n                    width: options.width || measures.width,\n                    height: options.height,\n                    minwidth: options.minwidth,\n                    maxwidth: options.maxwidth,\n                    minheight: options.minheight,\n                    maxheight: options.maxheight,\n                    ratio: getRatio(options.ratio)\n                })\n            });\n        }\n\n        function triggerEvent(event, extra) {\n            $fotorama.trigger(_fotoramaClass + ':' + event, [that, extra]);\n        }\n\n        function onTouchStart() {\n            clearTimeout(onTouchEnd.t);\n            touchedFLAG = 1;\n\n            if (opts.stopautoplayontouch) {\n                that.stopAutoplay();\n            } else {\n                pausedAutoplayFLAG = true;\n            }\n        }\n\n        function onTouchEnd() {\n            if (!touchedFLAG) return;\n            if (!opts.stopautoplayontouch) {\n                releaseAutoplay();\n                changeAutoplay();\n            }\n\n            onTouchEnd.t = setTimeout(function () {\n                touchedFLAG = 0;\n            }, TRANSITION_DURATION + TOUCH_TIMEOUT);\n        }\n\n        function releaseAutoplay() {\n            pausedAutoplayFLAG = !!($videoPlaying || stoppedAutoplayFLAG);\n        }\n\n        function changeAutoplay() {\n\n            clearTimeout(changeAutoplay.t);\n            waitFor.stop(changeAutoplay.w);\n\n            if (!opts.autoplay || pausedAutoplayFLAG) {\n                if (that.autoplay) {\n                    that.autoplay = false;\n                    triggerEvent('stopautoplay');\n                }\n\n                return;\n            }\n\n            if (!that.autoplay) {\n                that.autoplay = true;\n                triggerEvent('startautoplay');\n            }\n\n            var _activeIndex = activeIndex;\n\n\n            var frameData = activeFrame[STAGE_FRAME_KEY].data();\n            changeAutoplay.w = waitFor(function () {\n                return frameData.state || _activeIndex !== activeIndex;\n            }, function () {\n                changeAutoplay.t = setTimeout(function () {\n\n                    if (pausedAutoplayFLAG || _activeIndex !== activeIndex) return;\n\n                    var _nextAutoplayIndex = nextAutoplayIndex,\n                        nextFrameData = data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();\n\n                    changeAutoplay.w = waitFor(function () {\n\n                        return nextFrameData.state || _nextAutoplayIndex !== nextAutoplayIndex;\n                    }, function () {\n                        if (pausedAutoplayFLAG || _nextAutoplayIndex !== nextAutoplayIndex) return;\n                        that.show(o_loop ? getDirectionSign(!o_rtl) : nextAutoplayIndex);\n                    });\n                }, opts.autoplay);\n            });\n\n        }\n\n        that.startAutoplay = function (interval) {\n            if (that.autoplay) return this;\n            pausedAutoplayFLAG = stoppedAutoplayFLAG = false;\n            setAutoplayInterval(interval || opts.autoplay);\n            changeAutoplay();\n\n            return this;\n        };\n\n        that.stopAutoplay = function () {\n            if (that.autoplay) {\n                pausedAutoplayFLAG = stoppedAutoplayFLAG = true;\n                changeAutoplay();\n            }\n            return this;\n        };\n\n        that.showSlide = function (slideDir) {\n            var currentPosition = readPosition($navShaft, opts.navdir),\n                pos,\n                time = 500 * 1.1,\n                size = opts.navdir === 'horizontal' ? opts.thumbwidth : opts.thumbheight,\n                onEnd = function () {\n                    thumbArrUpdate();\n                };\n            if (slideDir === 'next') {\n                pos = currentPosition - (size + opts.margin) * thumbsPerSlide;\n            }\n            if (slideDir === 'prev') {\n                pos = currentPosition + (size + opts.margin) * thumbsPerSlide;\n            }\n            pos = validateRestrictions(pos, navShaftTouchTail);\n            thumbsDraw(pos, true);\n            slide($navShaft, {\n                time: time,\n                pos: pos,\n                direction: opts.navdir,\n                onEnd: onEnd\n            });\n        };\n\n        that.showWhileLongPress = function (options) {\n            if (that.longPress.singlePressInProgress) {\n                return;\n            }\n\n            var index = calcActiveIndex(options);\n            calcGlobalIndexes(index);\n            var time = calcTime(options) / 50;\n            var _activeFrame = activeFrame;\n            that.activeFrame = activeFrame = data[activeIndex];\n            var silent = _activeFrame === activeFrame && !options.user;\n\n            that.showNav(silent, options, time);\n\n            return this;\n        };\n\n        that.showEndLongPress = function (options) {\n            if (that.longPress.singlePressInProgress) {\n                return;\n            }\n\n            var index = calcActiveIndex(options);\n            calcGlobalIndexes(index);\n            var time = calcTime(options) / 50;\n            var _activeFrame = activeFrame;\n            that.activeFrame = activeFrame = data[activeIndex];\n\n            var silent = _activeFrame === activeFrame && !options.user;\n\n            that.showStage(silent, options, time);\n\n            showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;\n            lastActiveIndex = activeIndex;\n            return this;\n        };\n\n        function calcActiveIndex (options) {\n            var index;\n\n            if (typeof options !== 'object') {\n                index = options;\n                options = {};\n            } else {\n                index = options.index;\n            }\n\n            index = index === '>' ? dirtyIndex + 1 : index === '<' ? dirtyIndex - 1 : index === '<<' ? 0 : index === '>>' ? size - 1 : index;\n            index = isNaN(index) ? undefined : index;\n            index = typeof index === 'undefined' ? activeIndex || 0 : index;\n\n            return index;\n        }\n\n        function calcGlobalIndexes (index) {\n            that.activeIndex = activeIndex = edgeIndex(index);\n            prevIndex = getPrevIndex(activeIndex);\n            nextIndex = getNextIndex(activeIndex);\n            nextAutoplayIndex = normalizeIndex(activeIndex + (o_rtl ? -1 : 1));\n            activeIndexes = [activeIndex, prevIndex, nextIndex];\n\n            dirtyIndex = o_loop ? index : activeIndex;\n        }\n\n        function calcTime (options) {\n            var diffIndex = Math.abs(lastActiveIndex - dirtyIndex),\n                time = getNumber(options.time, function () {\n                    return Math.min(o_transitionDuration * (1 + (diffIndex - 1) / 12), o_transitionDuration * 2);\n                });\n\n            if (options.slow) {\n                time *= 10;\n            }\n\n            return time;\n        }\n\n        that.showStage = function (silent, options, time, e) {\n            if (e !== undefined && e.target.tagName == 'IFRAME') {\n                return;\n            }\n            unloadVideo($videoPlaying, activeFrame.i !== data[normalizeIndex(repositionIndex)].i);\n            frameDraw(activeIndexes, 'stage');\n            stageFramePosition(SLOW ? [dirtyIndex] : [dirtyIndex, getPrevIndex(dirtyIndex), getNextIndex(dirtyIndex)]);\n            updateTouchTails('go', true);\n\n            silent || triggerEvent('show', {\n                user: options.user,\n                time: time\n            });\n\n            pausedAutoplayFLAG = true;\n\n            var overPos = options.overPos;\n            var onEnd = that.showStage.onEnd = function (skipReposition) {\n                if (onEnd.ok) return;\n                onEnd.ok = true;\n\n                skipReposition || stageShaftReposition(true);\n\n                if (!silent) {\n                    triggerEvent('showend', {\n                        user: options.user\n                    });\n                }\n\n                if (!skipReposition && o_transition && o_transition !== opts.transition) {\n                    that.setOptions({transition: o_transition});\n                    o_transition = false;\n                    return;\n                }\n\n                updateFotoramaState();\n                loadImg(activeIndexes, 'stage');\n\n                updateTouchTails('go', false);\n                stageWheelUpdate();\n\n                stageCursor();\n                releaseAutoplay();\n                changeAutoplay();\n\n                if (that.fullScreen) {\n                    activeFrame[STAGE_FRAME_KEY].find('.' + imgFullClass).attr('aria-hidden', false);\n                    activeFrame[STAGE_FRAME_KEY].find('.' + imgClass).attr('aria-hidden', true)\n                } else {\n                    activeFrame[STAGE_FRAME_KEY].find('.' + imgFullClass).attr('aria-hidden', true);\n                    activeFrame[STAGE_FRAME_KEY].find('.' + imgClass).attr('aria-hidden', false)\n                }\n            };\n\n            if (!o_fade) {\n                slide($stageShaft, {\n                    pos: -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex),\n                    overPos: overPos,\n                    time: time,\n                    onEnd: onEnd\n                });\n            } else {\n                var $activeFrame = activeFrame[STAGE_FRAME_KEY],\n                    $prevActiveFrame = data[lastActiveIndex] && activeIndex !== lastActiveIndex ? data[lastActiveIndex][STAGE_FRAME_KEY] : null;\n\n                fade($activeFrame, $prevActiveFrame, $stageFrame, {\n                    time: time,\n                    method: opts.transition,\n                    onEnd: onEnd\n                }, fadeStack);\n            }\n\n            arrsUpdate();\n        };\n\n        that.showNav = function(silent, options, time){\n            thumbArrUpdate();\n            if (o_nav) {\n                navUpdate();\n\n                var guessIndex = limitIndex(activeIndex + minMaxLimit(dirtyIndex - lastActiveIndex, -1, 1));\n                slideNavShaft({\n                    time: time,\n                    coo: guessIndex !== activeIndex && options.coo,\n                    guessIndex: typeof options.coo !== 'undefined' ? guessIndex : activeIndex,\n                    keep: silent\n                });\n                if (o_navThumbs) slideThumbBorder(time);\n            }\n        };\n\n        that.show = function (options, e) {\n            that.longPress.singlePressInProgress = true;\n\n            var index = calcActiveIndex(options);\n            calcGlobalIndexes(index);\n            var time = calcTime(options);\n            var _activeFrame = activeFrame;\n            that.activeFrame = activeFrame = data[activeIndex];\n\n            var silent = _activeFrame === activeFrame && !options.user;\n\n            that.showStage(silent, options, time, e);\n            that.showNav(silent, options, time);\n\n            showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;\n            lastActiveIndex = activeIndex;\n            that.longPress.singlePressInProgress = false;\n\n            return this;\n        };\n\n        that.requestFullScreen = function () {\n            if (o_allowFullScreen && !that.fullScreen) {\n\n                //check that this is not video\n                if(isVideo()) {\n                    return;\n                }\n\n                scrollTop = $WINDOW.scrollTop();\n                scrollLeft = $WINDOW.scrollLeft();\n\n                lockScroll($WINDOW);\n\n                updateTouchTails('x', true);\n\n                measuresStash = $.extend({}, measures);\n\n                $fotorama\n                    .addClass(fullscreenClass)\n                    .appendTo($BODY.addClass(_fullscreenClass));\n\n                $HTML.addClass(_fullscreenClass);\n\n                unloadVideo($videoPlaying, true, true);\n\n                that.fullScreen = true;\n\n                if (o_nativeFullScreen) {\n                    fullScreenApi.request(fotorama);\n                }\n\n                loadImg(activeIndexes, 'stage');\n                updateFotoramaState();\n                triggerEvent('fullscreenenter');\n                that.resize();\n\n                if (!('ontouchstart' in window)) {\n                    $fullscreenIcon.focus();\n                }\n            }\n\n            return this;\n        };\n\n        function cancelFullScreen() {\n            if (that.fullScreen) {\n                that.fullScreen = false;\n\n                if (FULLSCREEN) {\n                    fullScreenApi.cancel(fotorama);\n                }\n\n                $BODY.removeClass(_fullscreenClass);\n                $HTML.removeClass(_fullscreenClass);\n\n                $fotorama\n                    .removeClass(fullscreenClass)\n                    .insertAfter($anchor);\n\n                measures = $.extend({}, measuresStash);\n\n                unloadVideo($videoPlaying, true, true);\n\n                updateTouchTails('x', false);\n\n                that.resize();\n                loadImg(activeIndexes, 'stage');\n\n                lockScroll($WINDOW, scrollLeft, scrollTop);\n\n                triggerEvent('fullscreenexit');\n            }\n        }\n\n        that.cancelFullScreen = function () {\n            if (o_nativeFullScreen && fullScreenApi.is()) {\n                fullScreenApi.cancel(document);\n            } else {\n                cancelFullScreen();\n            }\n\n            return this;\n        };\n\n        that.toggleFullScreen = function () {\n            return that[(that.fullScreen ? 'cancel' : 'request') + 'FullScreen']();\n        };\n\n        that.resize = function (options) {\n            if (!data) return this;\n\n            var time = arguments[1] || 0,\n                setFLAG = arguments[2];\n\n            thumbsPerSlide = getThumbsInSlide($wrap, opts);\n            extendMeasures(!that.fullScreen ? optionsToLowerCase(options) : {\n                width: $(window).width(),\n                maxwidth: null,\n                minwidth: null,\n                height: $(window).height(),\n                maxheight: null,\n                minheight: null\n            }, [measures, setFLAG || that.fullScreen || opts]);\n\n            var width = measures.width,\n                height = measures.height,\n                ratio = measures.ratio,\n                windowHeight = $WINDOW.height() - (o_nav ? $nav.height() : 0);\n\n            if (measureIsValid(width)) {\n                $wrap.css({width: ''});\n                $stage.css({width: ''});\n                $stageShaft.css({width: ''});\n                $nav.css({width: ''});\n                $wrap.css({minWidth: measures.minwidth || 0, maxWidth: measures.maxwidth || MAX_WIDTH});\n\n                if (o_nav === 'dots') {\n                    $navWrap.hide();\n                }\n                width = measures.W = measures.w = $wrap.width();\n                measures.nw = o_nav && numberFromWhatever(opts.navwidth, width) || width;\n\n                $stageShaft.css({width: measures.w, marginLeft: (measures.W - measures.w) / 2});\n\n                height = numberFromWhatever(height, windowHeight);\n\n                height = height || (ratio && width / ratio);\n\n                if (height) {\n                    width = Math.round(width);\n                    height = measures.h = Math.round(minMaxLimit(height, numberFromWhatever(measures.minheight, windowHeight), numberFromWhatever(measures.maxheight, windowHeight)));\n                    $stage.css({'width': width, 'height': height});\n\n                    if (opts.navdir === 'vertical' && !that.fullscreen) {\n                        $nav.width(opts.thumbwidth + opts.thumbmargin * 2);\n                    }\n\n                    if (opts.navdir === 'horizontal' && !that.fullscreen) {\n                        $nav.height(opts.thumbheight + opts.thumbmargin * 2);\n                    }\n\n                    if (o_nav === 'dots') {\n                        $nav.width(width)\n                            .height('auto');\n                        $navWrap.show();\n                    }\n\n                    if (opts.navdir === 'vertical' && that.fullScreen) {\n                        $stage.css('height', $WINDOW.height());\n                    }\n\n                    if (opts.navdir === 'horizontal' && that.fullScreen) {\n                        $stage.css('height', $WINDOW.height() - $nav.height());\n                    }\n\n                    if (o_nav) {\n                        switch (opts.navdir) {\n                            case 'vertical':\n                                $navWrap.removeClass(navShafthorizontalClass);\n                                $navWrap.removeClass(navShaftListClass);\n                                $navWrap.addClass(navShaftVerticalClass);\n                                $nav\n                                    .stop()\n                                    .animate({height: measures.h, width: opts.thumbwidth}, time);\n                                break;\n                            case 'list':\n                                $navWrap.removeClass(navShaftVerticalClass);\n                                $navWrap.removeClass(navShafthorizontalClass);\n                                $navWrap.addClass(navShaftListClass);\n                                break;\n                            default:\n                                $navWrap.removeClass(navShaftVerticalClass);\n                                $navWrap.removeClass(navShaftListClass);\n                                $navWrap.addClass(navShafthorizontalClass);\n                                $nav\n                                    .stop()\n                                    .animate({width: measures.nw}, time);\n                                break;\n                        }\n\n                        stageShaftReposition();\n                        slideNavShaft({guessIndex: activeIndex, time: time, keep: true});\n                        if (o_navThumbs && frameAppend.nav) slideThumbBorder(time);\n                    }\n\n                    measuresSetFLAG = setFLAG || true;\n\n                    ready.ok = true;\n                    ready();\n                }\n            }\n\n            stageLeft = $stage.offset().left;\n            setStagePosition();\n\n            return this;\n        };\n\n        that.setOptions = function (options) {\n            $.extend(opts, options);\n            reset();\n            return this;\n        };\n\n        that.shuffle = function () {\n            data && shuffle(data) && reset();\n            return this;\n        };\n\n        function setShadow($el, edge) {\n            if (o_shadows) {\n                $el.removeClass(shadowsLeftClass + ' ' + shadowsRightClass);\n                $el.removeClass(shadowsTopClass + ' ' + shadowsBottomClass);\n                edge && !$videoPlaying && $el.addClass(edge.replace(/^|\\s/g, ' ' + shadowsClass + '--'));\n            }\n        }\n\n        that.longPress = {\n            threshold: 1,\n            count: 0,\n            thumbSlideTime: 20,\n            progress: function(){\n                if (!this.inProgress) {\n                    this.count++;\n                    this.inProgress = this.count > this.threshold;\n                }\n            },\n            end: function(){\n                if(this.inProgress) {\n                    this.isEnded = true\n                }\n            },\n            reset: function(){\n                this.count = 0;\n                this.inProgress = false;\n                this.isEnded = false;\n            }\n        };\n\n        that.destroy = function () {\n            that.cancelFullScreen();\n            that.stopAutoplay();\n\n            data = that.data = null;\n\n            appendElements();\n\n            activeIndexes = [];\n            detachFrames(STAGE_FRAME_KEY);\n\n            reset.ok = false;\n\n            return this;\n        };\n\n        /**\n         *\n         * @returns {jQuery.Fotorama}\n         */\n        that.playVideo = function () {\n            var dataFrame = activeFrame,\n                video = dataFrame.video,\n                _activeIndex = activeIndex;\n\n            if (typeof video === 'object' && dataFrame.videoReady) {\n                o_nativeFullScreen && that.fullScreen && that.cancelFullScreen();\n\n                waitFor(function () {\n                    return !fullScreenApi.is() || _activeIndex !== activeIndex;\n                }, function () {\n                    if (_activeIndex === activeIndex) {\n                        dataFrame.$video = dataFrame.$video || $(div(videoClass)).append(createVideoFrame(video));\n                        dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);\n\n                        $wrap.addClass(wrapVideoClass);\n                        $videoPlaying = dataFrame.$video;\n\n                        stageNoMove();\n\n                        $arrs.blur();\n                        $fullscreenIcon.blur();\n\n                        triggerEvent('loadvideo');\n                    }\n                });\n            }\n\n            return this;\n        };\n\n        that.stopVideo = function () {\n            unloadVideo($videoPlaying, true, true);\n            return this;\n        };\n\n        that.spliceByIndex = function (index, newImgObj) {\n            newImgObj.i = index + 1;\n            newImgObj.img && $.ajax({\n                url: newImgObj.img,\n                type: 'HEAD',\n                success: function () {\n                    data.splice(index, 1, newImgObj);\n                    reset();\n                }\n            });\n        };\n\n        function unloadVideo($video, unloadActiveFLAG, releaseAutoplayFLAG) {\n            if (unloadActiveFLAG) {\n                $wrap.removeClass(wrapVideoClass);\n                $videoPlaying = false;\n\n                stageNoMove();\n            }\n\n            if ($video && $video !== $videoPlaying) {\n                $video.remove();\n                triggerEvent('unloadvideo');\n            }\n\n            if (releaseAutoplayFLAG) {\n                releaseAutoplay();\n                changeAutoplay();\n            }\n        }\n\n        function toggleControlsClass(FLAG) {\n            $wrap.toggleClass(wrapNoControlsClass, FLAG);\n        }\n\n        function stageCursor(e) {\n            if (stageShaftTouchTail.flow) return;\n\n            var x = e ? e.pageX : stageCursor.x,\n                pointerFLAG = x && !disableDirrection(getDirection(x)) && opts.click;\n\n            if (stageCursor.p !== pointerFLAG\n                && $stage.toggleClass(pointerClass, pointerFLAG)) {\n                stageCursor.p = pointerFLAG;\n                stageCursor.x = x;\n            }\n        }\n\n        $stage.on('mousemove', stageCursor);\n\n        function clickToShow(showOptions, e) {\n            clearTimeout(clickToShow.t);\n\n            if (opts.clicktransition && opts.clicktransition !== opts.transition) {\n                setTimeout(function () {\n                    var _o_transition = opts.transition;\n\n                    that.setOptions({transition: opts.clicktransition});\n\n                    // now safe to pass base transition to o_transition, so that.show will restor it\n                    o_transition = _o_transition;\n                    // this timeout is here to prevent jerking in some browsers\n                    clickToShow.t = setTimeout(function () {\n                        that.show(showOptions);\n                    }, 10);\n                }, 0);\n            } else {\n                that.show(showOptions, e);\n            }\n        }\n\n        function onStageTap(e, toggleControlsFLAG) {\n            var target = e.target,\n                $target = $(target);\n            if ($target.hasClass(videoPlayClass)) {\n                that.playVideo();\n            } else if (target === fullscreenIcon) {\n                that.toggleFullScreen();\n            } else if ($videoPlaying) {\n                target === videoClose && unloadVideo($videoPlaying, true, true);\n            } else if (!$fotorama.hasClass(fullscreenClass)) {\n                that.requestFullScreen();\n            }\n        }\n\n        function updateTouchTails(key, value) {\n            stageShaftTouchTail[key] = navShaftTouchTail[key] = value;\n        }\n\n        stageShaftTouchTail = moveOnTouch($stageShaft, {\n            onStart: onTouchStart,\n            onMove: function (e, result) {\n                setShadow($stage, result.edge);\n            },\n            onTouchEnd: onTouchEnd,\n            onEnd: function (result) {\n                var toggleControlsFLAG;\n\n                setShadow($stage);\n                toggleControlsFLAG = (MS_POINTER && !hoverFLAG || result.touch) &&\n                    opts.arrows;\n\n                if ((result.moved || (toggleControlsFLAG && result.pos !== result.newPos && !result.control)) && result.$target[0] !== $fullscreenIcon[0]) {\n                    var index = getIndexByPos(result.newPos, measures.w, opts.margin, repositionIndex);\n\n                    that.show({\n                        index: index,\n                        time: o_fade ? o_transitionDuration : result.time,\n                        overPos: result.overPos,\n                        user: true\n                    });\n                } else if (!result.aborted && !result.control) {\n                    onStageTap(result.startEvent, toggleControlsFLAG);\n                }\n            },\n            timeLow: 1,\n            timeHigh: 1,\n            friction: 2,\n            select: '.' + selectClass + ', .' + selectClass + ' *',\n            $wrap: $stage,\n            direction: 'horizontal'\n\n        });\n\n        navShaftTouchTail = moveOnTouch($navShaft, {\n            onStart: onTouchStart,\n            onMove: function (e, result) {\n                setShadow($nav, result.edge);\n            },\n            onTouchEnd: onTouchEnd,\n            onEnd: function (result) {\n\n                function onEnd() {\n                    slideNavShaft.l = result.newPos;\n                    releaseAutoplay();\n                    changeAutoplay();\n                    thumbsDraw(result.newPos, true);\n                    thumbArrUpdate();\n                }\n\n                if (!result.moved) {\n                    var target = result.$target.closest('.' + navFrameClass, $navShaft)[0];\n                    target && onNavFrameClick.call(target, result.startEvent);\n                } else if (result.pos !== result.newPos) {\n                    pausedAutoplayFLAG = true;\n                    slide($navShaft, {\n                        time: result.time,\n                        pos: result.newPos,\n                        overPos: result.overPos,\n                        direction: opts.navdir,\n                        onEnd: onEnd\n                    });\n                    thumbsDraw(result.newPos);\n                    o_shadows && setShadow($nav, findShadowEdge(result.newPos, navShaftTouchTail.min, navShaftTouchTail.max, result.dir));\n                } else {\n                    onEnd();\n                }\n            },\n            timeLow: .5,\n            timeHigh: 2,\n            friction: 5,\n            $wrap: $nav,\n            direction: opts.navdir\n        });\n\n        stageWheelTail = wheel($stage, {\n            shift: true,\n            onEnd: function (e, direction) {\n                onTouchStart();\n                onTouchEnd();\n                that.show({index: direction, slow: e.altKey})\n            }\n        });\n\n        navWheelTail = wheel($nav, {\n            onEnd: function (e, direction) {\n                onTouchStart();\n                onTouchEnd();\n                var newPos = stop($navShaft) + direction * .25;\n                $navShaft.css(getTranslate(minMaxLimit(newPos, navShaftTouchTail.min, navShaftTouchTail.max), opts.navdir));\n                o_shadows && setShadow($nav, findShadowEdge(newPos, navShaftTouchTail.min, navShaftTouchTail.max, opts.navdir));\n                navWheelTail.prevent = {'<': newPos >= navShaftTouchTail.max, '>': newPos <= navShaftTouchTail.min};\n                clearTimeout(navWheelTail.t);\n                navWheelTail.t = setTimeout(function () {\n                    slideNavShaft.l = newPos;\n                    thumbsDraw(newPos, true)\n                }, TOUCH_TIMEOUT);\n                thumbsDraw(newPos);\n            }\n        });\n\n        $wrap.hover(\n            function () {\n                setTimeout(function () {\n                    if (touchedFLAG) return;\n                    toggleControlsClass(!(hoverFLAG = true));\n                }, 0);\n            },\n            function () {\n                if (!hoverFLAG) return;\n                toggleControlsClass(!(hoverFLAG = false));\n            }\n        );\n\n        function onNavFrameClick(e) {\n            var index = $(this).data().eq;\n\n            if (opts.navtype === 'thumbs') {\n                clickToShow({index: index, slow: e.altKey, user: true, coo: e._x - $nav.offset().left});\n            } else {\n                clickToShow({index: index, slow: e.altKey, user: true});\n            }\n        }\n\n        function onArrClick(e) {\n            clickToShow({index: $arrs.index(this) ? '>' : '<', slow: e.altKey, user: true});\n        }\n\n        smartClick($arrs, function (e) {\n            stopEvent(e);\n            onArrClick.call(this, e);\n        }, {\n            onStart: function () {\n                onTouchStart();\n                stageShaftTouchTail.control = true;\n            },\n            onTouchEnd: onTouchEnd\n        });\n\n        smartClick($thumbArrLeft, function (e) {\n            stopEvent(e);\n            if (opts.navtype === 'thumbs') {\n\n                that.show('<');\n            } else {\n                that.showSlide('prev')\n            }\n        });\n\n        smartClick($thumbArrRight, function (e) {\n            stopEvent(e);\n            if (opts.navtype === 'thumbs') {\n                that.show('>');\n            } else {\n                that.showSlide('next')\n            }\n\n        });\n\n\n        function addFocusOnControls(el) {\n            addFocus(el, function () {\n                setTimeout(function () {\n                    lockScroll($stage);\n                }, 0);\n                toggleControlsClass(false);\n            });\n        }\n\n        $arrs.each(function () {\n            addEnterUp(this, function (e) {\n                onArrClick.call(this, e);\n            });\n            addFocusOnControls(this);\n        });\n\n        addEnterUp(fullscreenIcon, function () {\n            if ($fotorama.hasClass(fullscreenClass)) {\n                that.cancelFullScreen();\n                $stageShaft.focus();\n            } else {\n                that.requestFullScreen();\n                $fullscreenIcon.focus();\n            }\n\n        });\n        addFocusOnControls(fullscreenIcon);\n\n        function reset() {\n            setData();\n            setOptions();\n\n            if (!reset.i) {\n                reset.i = true;\n                // Only once\n                var _startindex = opts.startindex;\n                activeIndex = repositionIndex = dirtyIndex = lastActiveIndex = startIndex = edgeIndex(_startindex) || 0;\n                /*(o_rtl ? size - 1 : 0)*///;\n            }\n\n            if (size) {\n                if (changeToRtl()) return;\n\n                if ($videoPlaying) {\n                    unloadVideo($videoPlaying, true);\n                }\n\n                activeIndexes = [];\n\n                if (!isVideo()) {\n                    detachFrames(STAGE_FRAME_KEY);\n                }\n\n                reset.ok = true;\n\n                that.show({index: activeIndex, time: 0});\n                that.resize();\n            } else {\n                that.destroy();\n            }\n        }\n\n        function changeToRtl() {\n\n            if (!changeToRtl.f === o_rtl) {\n                changeToRtl.f = o_rtl;\n                activeIndex = size - 1 - activeIndex;\n                that.reverse();\n\n                return true;\n            }\n        }\n\n        $.each('load push pop shift unshift reverse sort splice'.split(' '), function (i, method) {\n            that[method] = function () {\n                data = data || [];\n                if (method !== 'load') {\n                    Array.prototype[method].apply(data, arguments);\n                } else if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].length) {\n                    data = clone(arguments[0]);\n                }\n                reset();\n                return that;\n            }\n        });\n\n        function ready() {\n            if (ready.ok) {\n                ready.ok = false;\n                triggerEvent('ready');\n            }\n        }\n\n        reset();\n    };\n    $.fn.fotorama = function (opts) {\n        return this.each(function () {\n            var that = this,\n                $fotorama = $(this),\n                fotoramaData = $fotorama.data(),\n                fotorama = fotoramaData.fotorama;\n\n            if (!fotorama) {\n                waitFor(function () {\n                    return !isHidden(that);\n                }, function () {\n                    fotoramaData.urtext = $fotorama.html();\n                    new $.Fotorama($fotorama,\n                        $.extend(\n                            {},\n                            OPTIONS,\n                            window.fotoramaDefaults,\n                            opts,\n                            fotoramaData\n                        )\n                    );\n                });\n            } else {\n                fotorama.setOptions(opts, true);\n            }\n        });\n    };\n    $.Fotorama.instances = [];\n\n    function calculateIndexes() {\n        $.each($.Fotorama.instances, function (index, instance) {\n            instance.index = index;\n        });\n    }\n\n    function addInstance(instance) {\n        $.Fotorama.instances.push(instance);\n        calculateIndexes();\n    }\n\n    function hideInstance(instance) {\n        $.Fotorama.instances.splice(instance.index, 1);\n        calculateIndexes();\n    }\n\n    $.Fotorama.cache = {};\n    $.Fotorama.measures = {};\n    $ = $ || {};\n    $.Fotorama = $.Fotorama || {};\n    $.Fotorama.jst = $.Fotorama.jst || {};\n\n    $.Fotorama.jst.dots = function (v) {\n        var __t, __p = '', __e = _.escape;\n        __p += '<div class=\"fotorama__nav__frame fotorama__nav__frame--dot\" tabindex=\"0\" role=\"button\" data-gallery-role=\"nav-frame\" data-nav-type=\"thumb\" aria-label>\\r\\n    <div class=\"fotorama__dot\"></div>\\r\\n</div>';\n        return __p\n    };\n\n    $.Fotorama.jst.frameCaption = function (v) {\n        var __t, __p = '', __e = _.escape;\n        __p += '<div class=\"fotorama__caption\" aria-hidden=\"true\">\\r\\n    <div class=\"fotorama__caption__wrap\" id=\"' +\n            ((__t = ( v.labelledby )) == null ? '' : __t) +\n            '\">' +\n            ((__t = ( v.caption )) == null ? '' : __t) +\n            '</div>\\r\\n</div>\\r\\n';\n        return __p\n    };\n\n    $.Fotorama.jst.style = function (v) {\n        var __t, __p = '', __e = _.escape;\n        __p += '.fotorama' +\n            ((__t = ( v.s )) == null ? '' : __t) +\n            ' .fotorama__nav--thumbs .fotorama__nav__frame{\\r\\npadding:' +\n            ((__t = ( v.m )) == null ? '' : __t) +\n            'px;\\r\\nheight:' +\n            ((__t = ( v.h )) == null ? '' : __t) +\n            'px}\\r\\n.fotorama' +\n            ((__t = ( v.s )) == null ? '' : __t) +\n            ' .fotorama__thumb-border{\\r\\nheight:' +\n            ((__t = ( v.h )) == null ? '' : __t) +\n            'px;\\r\\nborder-width:' +\n            ((__t = ( v.b )) == null ? '' : __t) +\n            'px;\\r\\nmargin-top:' +\n            ((__t = ( v.m )) == null ? '' : __t) +\n            'px}';\n        return __p\n    };\n\n    $.Fotorama.jst.thumb = function (v) {\n        var __t, __p = '', __e = _.escape;\n        __p += '<div class=\"fotorama__nav__frame fotorama__nav__frame--thumb\" tabindex=\"0\" role=\"button\" data-gallery-role=\"nav-frame\" data-nav-type=\"thumb\" aria-label>\\r\\n    <div class=\"fotorama__thumb\">\\r\\n    </div>\\r\\n</div>';\n        return __p\n    };\n})(window, document, location, typeof jQuery !== 'undefined' && jQuery);\n","js-storage/storage-wrapper.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'js-storage/js.storage'\n], function ($, storage) {\n    'use strict';\n\n    if (window.cookieStorage) {\n        var cookiesConfig = window.cookiesConfig || {};\n\n        $.extend(window.cookieStorage, {\n            _secure: !!cookiesConfig.secure,\n            _samesite: cookiesConfig.samesite ? cookiesConfig.samesite : 'lax',\n\n            /**\n             * Set value under name\n             * @param {String} name\n             * @param {String} value\n             * @param {Object} [options]\n             */\n            setItem: function (name, value, options) {\n                var _default = {\n                    expires: this._expires,\n                    path: this._path,\n                    domain: this._domain,\n                    secure: this._secure,\n                    samesite: this._samesite\n                };\n\n                $.cookie(this._prefix + name, value, $.extend(_default, options || {}));\n            },\n\n            /**\n             * Set default options\n             * @param {Object} c\n             * @returns {storage}\n             */\n            setConf: function (c) {\n                if (c.path) {\n                    this._path = c.path;\n                }\n\n                if (c.domain) {\n                    this._domain = c.domain;\n                }\n\n                if (c.expires) {\n                    this._expires = c.expires;\n                }\n\n                if (typeof c.secure !== 'undefined') {\n                    this._secure = c.secure;\n                }\n\n                if (typeof c.samesite !== 'undefined') {\n                    this._samesite = c.samesite;\n                }\n\n                return this;\n            }\n        });\n    }\n\n    $.alwaysUseJsonInStorage = $.alwaysUseJsonInStorage || storage.alwaysUseJsonInStorage;\n    $.cookieStorage = $.cookieStorage || storage.cookieStorage;\n    $.initNamespaceStorage = $.initNamespaceStorage || storage.initNamespaceStorage;\n    $.localStorage = $.localStorage || storage.localStorage;\n    $.namespaceStorages = $.namespaceStorages || storage.namespaceStorages;\n    $.removeAllStorages = $.removeAllStorages || storage.removeAllStorages;\n    $.sessionStorage = $.sessionStorage || storage.sessionStorage;\n});\n","js-storage/js.storage.js":"/*\n * JS Storage Plugin\n *\n * Copyright (c) 2019 Julien Maurel\n *\n * Licensed under the MIT license:\n * http://www.opensource.org/licenses/mit-license.php\n *\n * Project home:\n * https://github.com/julien-maurel/js-storage\n *\n * Version: 1.1.0\n */\n(function (factory) {\n    var registeredInModuleLoader = false;\n    if (typeof define === 'function' && define.amd) {\n        define(['jquery', 'jquery/jquery.cookie'], factory);\n        registeredInModuleLoader = true;\n    }\n    if (typeof exports === 'object') {\n        module.exports = factory();\n        registeredInModuleLoader = true;\n    }\n    if (!registeredInModuleLoader) {\n        var OldStorages = window.Storages;\n        var api = window.Storages = factory();\n        api.noConflict = function () {\n            window.Storages = OldStorages;\n            return api;\n        };\n    }\n}(function () {\n    // Variables used by utilities functions (like isPlainObject...)\n    var class2type = {};\n    var toString = class2type.toString;\n    var hasOwn = class2type.hasOwnProperty;\n    var fnToString = hasOwn.toString;\n    var ObjectFunctionString = fnToString.call(Object);\n    var getProto = Object.getPrototypeOf;\n    var apis = {};\n\n    // Prefix to use with cookie fallback\n    var cookie_local_prefix = \"ls_\";\n    var cookie_session_prefix = \"ss_\";\n\n    // Get items from a storage\n    function _get() {\n        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], vi, ret, tmp, i, j;\n        if (l < 1) {\n            throw new Error('Minimum 1 argument must be given');\n        } else if (Array.isArray(a0)) {\n            // If second argument is an array, return an object with value of storage for each item in this array\n            ret = {};\n            for (i in a0) {\n                if (a0.hasOwnProperty(i)) {\n                    vi = a0[i];\n                    try {\n                        ret[vi] = JSON.parse(s.getItem(vi));\n                    } catch (e) {\n                        ret[vi] = s.getItem(vi);\n                    }\n                }\n            }\n            return ret;\n        } else if (l == 1) {\n            // If only 1 argument, return value directly\n            try {\n                return JSON.parse(s.getItem(a0));\n            } catch (e) {\n                return s.getItem(a0);\n            }\n        } else {\n            // If more than 1 argument, parse storage to retrieve final value to return it\n            // Get first level\n            try {\n                ret = JSON.parse(s.getItem(a0));\n                if (!ret) {\n                    throw new ReferenceError(a0 + ' is not defined in this storage');\n                }\n            } catch (e) {\n                throw new ReferenceError(a0 + ' is not defined in this storage');\n            }\n            // Parse next levels\n            for (i = 1; i < l - 1; i++) {\n                ret = ret[a[i]];\n                if (ret === undefined) {\n                    throw new ReferenceError([].slice.call(a, 0, i + 1).join('.') + ' is not defined in this storage');\n                }\n            }\n            // If last argument is an array, return an object with value for each item in this array\n            // Else return value normally\n            if (Array.isArray(a[i])) {\n                tmp = ret;\n                ret = {};\n                for (j in a[i]) {\n                    if (a[i].hasOwnProperty(j)) {\n                        ret[a[i][j]] = tmp[a[i][j]];\n                    }\n                }\n                return ret;\n            } else {\n                return ret[a[i]];\n            }\n        }\n    }\n\n    // Set items of a storage\n    function _set() {\n        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], a1 = a[1], vi, to_store = isNaN(a1) ? {} : [], type, tmp, i;\n        if (l < 1 || !_isPlainObject(a0) && l < 2) {\n            throw new Error('Minimum 2 arguments must be given or first parameter must be an object');\n        } else if (_isPlainObject(a0)) {\n            // If first argument is an object, set values of storage for each property of this object\n            for (i in a0) {\n                if (a0.hasOwnProperty(i)) {\n                    vi = a0[i];\n                    if (!_isPlainObject(vi) && !this.alwaysUseJson) {\n                        s.setItem(i, vi);\n                    } else {\n                        s.setItem(i, JSON.stringify(vi));\n                    }\n                }\n            }\n            return a0;\n        } else if (l == 2) {\n            // If only 2 arguments, set value of storage directly\n            if (typeof a1 === 'object' || this.alwaysUseJson) {\n                s.setItem(a0, JSON.stringify(a1));\n            } else {\n                s.setItem(a0, a1);\n            }\n            return a1;\n        } else {\n            // If more than 3 arguments, parse storage to retrieve final node and set value\n            // Get first level\n            try {\n                tmp = s.getItem(a0);\n                if (tmp != null) {\n                    to_store = JSON.parse(tmp);\n                }\n            } catch (e) {\n            }\n            tmp = to_store;\n            // Parse next levels and set value\n            for (i = 1; i < l - 2; i++) {\n                vi = a[i];\n                type = isNaN(a[i + 1]) ? \"object\" : \"array\";\n                if (!tmp[vi] || type == \"object\" && !_isPlainObject(tmp[vi]) || type == \"array\" && !Array.isArray(tmp[vi])) {\n                    if (type == \"array\") tmp[vi] = [];\n                    else tmp[vi] = {};\n                }\n                tmp = tmp[vi];\n            }\n            tmp[a[i]] = a[i + 1];\n            s.setItem(a0, JSON.stringify(to_store));\n            return to_store;\n        }\n    }\n\n    // Remove items from a storage\n    function _remove() {\n        var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], to_store, tmp, i, j;\n        if (l < 1) {\n            throw new Error('Minimum 1 argument must be given');\n        } else if (Array.isArray(a0)) {\n            // If first argument is an array, remove values from storage for each item of this array\n            for (i in a0) {\n                if (a0.hasOwnProperty(i)) {\n                    s.removeItem(a0[i]);\n                }\n            }\n            return true;\n        } else if (l == 1) {\n            // If only 2 arguments, remove value from storage directly\n            s.removeItem(a0);\n            return true;\n        } else {\n            // If more than 2 arguments, parse storage to retrieve final node and remove value\n            // Get first level\n            try {\n                to_store = tmp = JSON.parse(s.getItem(a0));\n            } catch (e) {\n                throw new ReferenceError(a0 + ' is not defined in this storage');\n            }\n            // Parse next levels and remove value\n            for (i = 1; i < l - 1; i++) {\n                tmp = tmp[a[i]];\n                if (tmp === undefined) {\n                    throw new ReferenceError([].slice.call(a, 1, i).join('.') + ' is not defined in this storage');\n                }\n            }\n            // If last argument is an array,remove value for each item in this array\n            // Else remove value normally\n            if (Array.isArray(a[i])) {\n                for (j in a[i]) {\n                    if (a[i].hasOwnProperty(j)) {\n                        delete tmp[a[i][j]];\n                    }\n                }\n            } else {\n                delete tmp[a[i]];\n            }\n            s.setItem(a0, JSON.stringify(to_store));\n            return true;\n        }\n    }\n\n    // Remove all items from a storage\n    function _removeAll(reinit_ns) {\n        var keys = _keys.call(this), i;\n        for (i in keys) {\n            if (keys.hasOwnProperty(i)) {\n                _remove.call(this, keys[i]);\n            }\n        }\n        // Reinitialize all namespace storages\n        if (reinit_ns) {\n            for (i in apis.namespaceStorages) {\n                if (apis.namespaceStorages.hasOwnProperty(i)) {\n                    _createNamespace(i);\n                }\n            }\n        }\n    }\n\n    // Check if items of a storage are empty\n    function _isEmpty() {\n        var l = arguments.length, a = arguments, a0 = a[0], i;\n        if (l == 0) {\n            // If no argument, test if storage is empty\n            return (_keys.call(this).length == 0);\n        } else if (Array.isArray(a0)) {\n            // If first argument is an array, test each item of this array and return true only if all items are empty\n            for (i = 0; i < a0.length; i++) {\n                if (!_isEmpty.call(this, a0[i])) {\n                    return false;\n                }\n            }\n            return true;\n        } else {\n            // If at least 1 argument, try to get value and test it\n            try {\n                var v = _get.apply(this, arguments);\n                // Convert result to an object (if last argument is an array, _get return already an object) and test each item\n                if (!Array.isArray(a[l - 1])) {\n                    v = {'totest': v};\n                }\n                for (i in v) {\n                    if (v.hasOwnProperty(i) && !(\n                        (_isPlainObject(v[i]) && _isEmptyObject(v[i])) ||\n                        (Array.isArray(v[i]) && !v[i].length) ||\n                        (typeof v[i] !== 'boolean' && !v[i])\n                    )) {\n                        return false;\n                    }\n                }\n                return true;\n            } catch (e) {\n                return true;\n            }\n        }\n    }\n\n    // Check if items of a storage exist\n    function _isSet() {\n        var l = arguments.length, a = arguments, a0 = a[0], i;\n        if (l < 1) {\n            throw new Error('Minimum 1 argument must be given');\n        }\n        if (Array.isArray(a0)) {\n            // If first argument is an array, test each item of this array and return true only if all items exist\n            for (i = 0; i < a0.length; i++) {\n                if (!_isSet.call(this, a0[i])) {\n                    return false;\n                }\n            }\n            return true;\n        } else {\n            // For other case, try to get value and test it\n            try {\n                var v = _get.apply(this, arguments);\n                // Convert result to an object (if last argument is an array, _get return already an object) and test each item\n                if (!Array.isArray(a[l - 1])) {\n                    v = {'totest': v};\n                }\n                for (i in v) {\n                    if (v.hasOwnProperty(i) && !(v[i] !== undefined && v[i] !== null)) {\n                        return false;\n                    }\n                }\n                return true;\n            } catch (e) {\n                return false;\n            }\n        }\n    }\n\n    // Get keys of a storage or of an item of the storage\n    function _keys() {\n        var storage = this._type, l = arguments.length, s = window[storage], keys = [], o = {};\n        // If at least 1 argument, get value from storage to retrieve keys\n        // Else, use storage to retrieve keys\n        if (l > 0) {\n            o = _get.apply(this, arguments);\n        } else {\n            o = s;\n        }\n        if (o && o._cookie) {\n            // If storage is a cookie, use js-cookie to retrieve keys\n            var cookies = Cookies.get();\n            for (var key in cookies) {\n                if (cookies.hasOwnProperty(key) && key != '') {\n                    keys.push(key.replace(o._prefix, ''));\n                }\n            }\n        } else {\n            for (var i in o) {\n                if (o.hasOwnProperty(i)) {\n                    keys.push(i);\n                }\n            }\n        }\n        return keys;\n    }\n\n    // Create new namespace storage\n    function _createNamespace(name) {\n        if (!name || typeof name != \"string\") {\n            throw new Error('First parameter must be a string');\n        }\n        if (storage_available) {\n            if (!window.localStorage.getItem(name)) {\n                window.localStorage.setItem(name, '{}');\n            }\n            if (!window.sessionStorage.getItem(name)) {\n                window.sessionStorage.setItem(name, '{}');\n            }\n        } else {\n            if (!window.localCookieStorage.getItem(name)) {\n                window.localCookieStorage.setItem(name, '{}');\n            }\n            if (!window.sessionCookieStorage.getItem(name)) {\n                window.sessionCookieStorage.setItem(name, '{}');\n            }\n        }\n        var ns = {\n            localStorage: _extend({}, apis.localStorage, {_ns: name}),\n            sessionStorage: _extend({}, apis.sessionStorage, {_ns: name})\n        };\n        if (cookies_available) {\n            if (!window.cookieStorage.getItem(name)) {\n                window.cookieStorage.setItem(name, '{}');\n            }\n            ns.cookieStorage = _extend({}, apis.cookieStorage, {_ns: name});\n        }\n        apis.namespaceStorages[name] = ns;\n        return ns;\n    }\n\n    // Test if storage is natively available on browser\n    function _testStorage(name) {\n        var foo = 'jsapi';\n        try {\n            if (!window[name]) {\n                return false;\n            }\n            window[name].setItem(foo, foo);\n            window[name].removeItem(foo);\n            return true;\n        } catch (e) {\n            return false;\n        }\n    }\n\n    // Test if a variable is a plain object (from jQuery)\n    function _isPlainObject(obj) {\n        var proto, Ctor;\n\n        // Detect obvious negatives\n        // Use toString instead of jQuery.type to catch host objects\n        if (!obj || toString.call(obj) !== \"[object Object]\") {\n            return false;\n        }\n\n        proto = getProto(obj);\n\n        // Objects with no prototype (e.g., `Object.create( null )`) are plain\n        if (!proto) {\n            return true;\n        }\n\n        // Objects with prototype are plain iff they were constructed by a global Object function\n        Ctor = hasOwn.call(proto, \"constructor\") && proto.constructor;\n        return typeof Ctor === \"function\" && fnToString.call(Ctor) === ObjectFunctionString;\n    }\n\n    // Test if a variable is an empty object (from jQuery)\n    function _isEmptyObject(obj) {\n        var name;\n\n        for (name in obj) {\n            return false;\n        }\n        return true;\n    }\n\n    // Merge objects\n    function _extend() {\n        var i = 1;\n        var result = arguments[0];\n        for (; i < arguments.length; i++) {\n            var attributes = arguments[i];\n            for (var key in attributes) {\n                if (attributes.hasOwnProperty(key)) {\n                    result[key] = attributes[key];\n                }\n            }\n        }\n        return result;\n    }\n\n    // Check if storages are natively available on browser and check is js-cookie is present\n    var storage_available = _testStorage('localStorage');\n    var cookies_available = typeof Cookies !== 'undefined';\n\n    // Namespace object\n    var storage = {\n        _type: '',\n        _ns: '',\n        _callMethod: function (f, a) {\n            a = Array.prototype.slice.call(a);\n            var p = [], a0 = a[0];\n            if (this._ns) {\n                p.push(this._ns);\n            }\n            if (typeof a0 === 'string' && a0.indexOf('.') !== -1) {\n                a.shift();\n                [].unshift.apply(a, a0.split('.'));\n            }\n            [].push.apply(p, a);\n            return f.apply(this, p);\n        },\n        // Define if plugin always use JSON to store values (even to store simple values like string, int...) or not\n        alwaysUseJson: false,\n        // Get items. If no parameters and storage have a namespace, return all namespace\n        get: function () {\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            return this._callMethod(_get, arguments);\n        },\n        // Set items\n        set: function () {\n            var l = arguments.length, a = arguments, a0 = a[0];\n            if (l < 1 || !_isPlainObject(a0) && l < 2) {\n                throw new Error('Minimum 2 arguments must be given or first parameter must be an object');\n            }\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            // If first argument is an object and storage is a namespace storage, set values individually\n            if (_isPlainObject(a0) && this._ns) {\n                for (var i in a0) {\n                    if (a0.hasOwnProperty(i)) {\n                        this._callMethod(_set, [i, a0[i]]);\n                    }\n                }\n                return a0;\n            } else {\n                var r = this._callMethod(_set, a);\n                if (this._ns) {\n                    return r[a0.split('.')[0]];\n                } else {\n                    return r;\n                }\n            }\n        },\n        // Delete items\n        remove: function () {\n            if (arguments.length < 1) {\n                throw new Error('Minimum 1 argument must be given');\n            }\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            return this._callMethod(_remove, arguments);\n        },\n        // Delete all items\n        removeAll: function (reinit_ns) {\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            if (this._ns) {\n                this._callMethod(_set, [{}]);\n                return true;\n            } else {\n                return this._callMethod(_removeAll, [reinit_ns]);\n            }\n        },\n        // Items empty\n        isEmpty: function () {\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            return this._callMethod(_isEmpty, arguments);\n        },\n        // Items exists\n        isSet: function () {\n            if (arguments.length < 1) {\n                throw new Error('Minimum 1 argument must be given');\n            }\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            return this._callMethod(_isSet, arguments);\n        },\n        // Get keys of items\n        keys: function () {\n            if (!storage_available && !cookies_available){\n                return null;\n            }\n            return this._callMethod(_keys, arguments);\n        }\n    };\n\n    // Use js-cookie for compatibility with old browsers and give access to cookieStorage\n    if (cookies_available) {\n        // sessionStorage is valid for one window/tab. To simulate that with cookie, we set a name for the window and use it for the name of the cookie\n        if (!window.name) {\n            window.name = Math.floor(Math.random() * 100000000);\n        }\n        var cookie_storage = {\n            _cookie: true,\n            _prefix: '',\n            _expires: null,\n            _path: null,\n            _domain: null,\n            _secure: false,\n            setItem: function (n, v) {\n                Cookies.set(this._prefix + n, v, {expires: this._expires, path: this._path, domain: this._domain, secure: this._secure});\n            },\n            getItem: function (n) {\n                return Cookies.get(this._prefix + n);\n            },\n            removeItem: function (n) {\n                return Cookies.remove(this._prefix + n, {path: this._path});\n            },\n            clear: function () {\n                var cookies = Cookies.get();\n                for (var key in cookies) {\n                    if (cookies.hasOwnProperty(key) && key != '') {\n                        if (!this._prefix && key.indexOf(cookie_local_prefix) === -1 && key.indexOf(cookie_session_prefix) === -1 || this._prefix && key.indexOf(this._prefix) === 0) {\n                            Cookies.remove(key);\n                        }\n                    }\n                }\n            },\n            setExpires: function (e) {\n                this._expires = e;\n                return this;\n            },\n            setPath: function (p) {\n                this._path = p;\n                return this;\n            },\n            setDomain: function (d) {\n                this._domain = d;\n                return this;\n            },\n            setSecure: function (s) {\n                this._secure = s;\n                return this;\n            },\n            setConf: function (c) {\n                if (c.path) {\n                    this._path = c.path;\n                }\n                if (c.domain) {\n                    this._domain = c.domain;\n                }\n                if (c.secure) {\n                    this._secure = c.secure;\n                }\n                if (c.expires) {\n                    this._expires = c.expires;\n                }\n                return this;\n            },\n            setDefaultConf: function () {\n                this._path = this._domain = this._expires = null;\n                this._secure = false;\n            }\n        };\n        if (!storage_available) {\n            window.localCookieStorage = _extend({}, cookie_storage, {\n                _prefix: cookie_local_prefix,\n                _expires: 365 * 10,\n                _secure: true\n            });\n            window.sessionCookieStorage = _extend({}, cookie_storage, {\n                _prefix: cookie_session_prefix + window.name + '_',\n                _secure: true\n            });\n        }\n        window.cookieStorage = _extend({}, cookie_storage);\n        // cookieStorage API\n        apis.cookieStorage = _extend({}, storage, {\n            _type: 'cookieStorage',\n            setExpires: function (e) {\n                window.cookieStorage.setExpires(e);\n                return this;\n            },\n            setPath: function (p) {\n                window.cookieStorage.setPath(p);\n                return this;\n            },\n            setDomain: function (d) {\n                window.cookieStorage.setDomain(d);\n                return this;\n            },\n            setSecure: function (s) {\n                window.cookieStorage.setSecure(s);\n                return this;\n            },\n            setConf: function (c) {\n                window.cookieStorage.setConf(c);\n                return this;\n            },\n            setDefaultConf: function () {\n                window.cookieStorage.setDefaultConf();\n                return this;\n            }\n        });\n    }\n\n    // Get a new API on a namespace\n    apis.initNamespaceStorage = function (ns) {\n        return _createNamespace(ns);\n    };\n    if (storage_available) {\n        // localStorage API\n        apis.localStorage = _extend({}, storage, {_type: 'localStorage'});\n        // sessionStorage API\n        apis.sessionStorage = _extend({}, storage, {_type: 'sessionStorage'});\n    } else {\n        // localStorage API\n        apis.localStorage = _extend({}, storage, {_type: 'localCookieStorage'});\n        // sessionStorage API\n        apis.sessionStorage = _extend({}, storage, {_type: 'sessionCookieStorage'});\n    }\n    // List of all namespace storage\n    apis.namespaceStorages = {};\n    // Remove all items in all storages\n    apis.removeAllStorages = function (reinit_ns) {\n        apis.localStorage.removeAll(reinit_ns);\n        apis.sessionStorage.removeAll(reinit_ns);\n        if (apis.cookieStorage) {\n            apis.cookieStorage.removeAll(reinit_ns);\n        }\n        if (!reinit_ns) {\n            apis.namespaceStorages = {};\n        }\n    };\n    // About alwaysUseJson\n    // By default, all values are string on html storages and the plugin don't use json to store simple values (strings, int, float...)\n    // So by default, if you do storage.setItem('test',2), value in storage will be \"2\", not 2\n    // If you set this property to true, all values set with the plugin will be stored as json to have typed values in any cases\n    apis.alwaysUseJsonInStorage = function (value) {\n        storage.alwaysUseJson = value;\n        apis.localStorage.alwaysUseJson = value;\n        apis.sessionStorage.alwaysUseJson = value;\n        if (apis.cookieStorage) {\n            apis.cookieStorage.alwaysUseJson = value;\n        }\n    };\n\n    return apis;\n}));\n","jquery/jquery.parsequery.js":"/**\n * Copyright (c) 2010 Conrad Irwin <[email protected]> MIT license.\n * Based loosely on original: Copyright (c) 2008 mkmanning MIT license.\n *\n * Parses CGI query strings into javascript objects.\n *\n * See the README for details.\n **/\ndefine([\n    \"jquery\"\n], function($){\n    $.parseQuery = function (options) {\n\n        var config = {query: window.location.search || \"\"},\n            params = {};\n\n        if (typeof options === 'string') {\n            options = {query: options};\n        }\n        $.extend(config, $.parseQuery, options);\n        config.query = config.query.replace(/^\\?/, '');\n\n        if (config.query.length > 0) {\n            $.each(config.query.split(config.separator), function (i, param) {\n                var pair = param.split('='),\n                    key = config.decode(pair.shift(), null).toString(),\n                    value = config.decode(pair.length ? pair.join('=') : null, key);\n\n                if (config.array_keys.test ? config.array_keys.test(key) : config.array_keys(key)) {\n                    params[key] = params[key] || [];\n                    params[key].push(value);\n                } else {\n                    params[key] = value;\n                }\n            });\n        }\n        return params;\n    };\n    $.parseQuery.decode = $.parseQuery.default_decode = function (string) {\n        return decodeURIComponent((string || \"\").replace(/\\+/g, ' '));\n    };\n    $.parseQuery.array_keys = function () {\n        return false;\n    };\n    $.parseQuery.separator = \"&\";\n});\n","jquery/compat.js":"// Import every plugin under the sun. Bad for performance,\n// but prevents the store from breaking in situations\n// where a dependency was missed during the migration from\n// a monolith build of jQueryUI to a modular one\n\ndefine([\n    'jquery-ui-modules/core',\n    'jquery-ui-modules/accordion',\n    'jquery-ui-modules/autocomplete',\n    'jquery-ui-modules/button',\n    'jquery-ui-modules/datepicker',\n    'jquery-ui-modules/dialog',\n    'jquery-ui-modules/draggable',\n    'jquery-ui-modules/droppable',\n    'jquery-ui-modules/effect-blind',\n    'jquery-ui-modules/effect-bounce',\n    'jquery-ui-modules/effect-clip',\n    'jquery-ui-modules/effect-drop',\n    'jquery-ui-modules/effect-explode',\n    'jquery-ui-modules/effect-fade',\n    'jquery-ui-modules/effect-fold',\n    'jquery-ui-modules/effect-highlight',\n    'jquery-ui-modules/effect-scale',\n    'jquery-ui-modules/effect-pulsate',\n    'jquery-ui-modules/effect-shake',\n    'jquery-ui-modules/effect-slide',\n    'jquery-ui-modules/effect-transfer',\n    'jquery-ui-modules/effect',\n    'jquery-ui-modules/menu',\n    'jquery-ui-modules/mouse',\n    'jquery-ui-modules/position',\n    'jquery-ui-modules/progressbar',\n    'jquery-ui-modules/resizable',\n    'jquery-ui-modules/selectable',\n    'jquery-ui-modules/slider',\n    'jquery-ui-modules/sortable',\n    'jquery-ui-modules/spinner',\n    'jquery-ui-modules/tabs',\n    'jquery-ui-modules/timepicker',\n    'jquery-ui-modules/tooltip',\n    'jquery-ui-modules/widget'\n], function() {\n    console.warn(\n        'Fallback to JQueryUI Compat activated. ' +\n        'Your store is missing a dependency for a ' +\n        'jQueryUI widget. Identifying and addressing the dependency ' +\n        'will drastically improve the performance of your site.'\n    );\n});\n","jquery/timepicker.js":"/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20\n* http://trentrichardson.com/examples/timepicker\n* Copyright (c) 2016 Trent Richardson; Licensed MIT */\n(function (factory) {\n    if (typeof define === 'function' && define.amd) {\n        define(['jquery', 'jquery-ui-modules/datepicker', 'jquery-ui-modules/slider'], factory);\n    } else {\n        factory(jQuery);\n    }\n}(function ($) {\n\n    /*\n    * Lets not redefine timepicker, Prevent \"Uncaught RangeError: Maximum call stack size exceeded\"\n    */\n    $.ui.timepicker = $.ui.timepicker || {};\n    if ($.ui.timepicker.version) {\n        return;\n    }\n\n    /*\n    * Extend jQueryUI, get it started with our version number\n    */\n    $.extend($.ui, {\n        timepicker: {\n            version: \"1.6.3\"\n        }\n    });\n\n    /*\n    * Timepicker manager.\n    * Use the singleton instance of this class, $.timepicker, to interact with the time picker.\n    * Settings for (groups of) time pickers are maintained in an instance object,\n    * allowing multiple different settings on the same page.\n    */\n    var Timepicker = function () {\n        this.regional = []; // Available regional settings, indexed by language code\n        this.regional[''] = { // Default regional settings\n            currentText: 'Now',\n            closeText: 'Done',\n            amNames: ['AM', 'A'],\n            pmNames: ['PM', 'P'],\n            timeFormat: 'HH:mm',\n            timeSuffix: '',\n            timeOnlyTitle: 'Choose Time',\n            timeText: 'Time',\n            hourText: 'Hour',\n            minuteText: 'Minute',\n            secondText: 'Second',\n            millisecText: 'Millisecond',\n            microsecText: 'Microsecond',\n            timezoneText: 'Time Zone',\n            isRTL: false\n        };\n        this._defaults = { // Global defaults for all the datetime picker instances\n            showButtonPanel: true,\n            timeOnly: false,\n            timeOnlyShowDate: false,\n            showHour: null,\n            showMinute: null,\n            showSecond: null,\n            showMillisec: null,\n            showMicrosec: null,\n            showTimezone: null,\n            showTime: true,\n            stepHour: 1,\n            stepMinute: 1,\n            stepSecond: 1,\n            stepMillisec: 1,\n            stepMicrosec: 1,\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null,\n            hourMin: 0,\n            minuteMin: 0,\n            secondMin: 0,\n            millisecMin: 0,\n            microsecMin: 0,\n            hourMax: 23,\n            minuteMax: 59,\n            secondMax: 59,\n            millisecMax: 999,\n            microsecMax: 999,\n            minDateTime: null,\n            maxDateTime: null,\n            maxTime: null,\n            minTime: null,\n            onSelect: null,\n            hourGrid: 0,\n            minuteGrid: 0,\n            secondGrid: 0,\n            millisecGrid: 0,\n            microsecGrid: 0,\n            alwaysSetTime: true,\n            separator: ' ',\n            altFieldTimeOnly: true,\n            altTimeFormat: null,\n            altSeparator: null,\n            altTimeSuffix: null,\n            altRedirectFocus: true,\n            pickerTimeFormat: null,\n            pickerTimeSuffix: null,\n            showTimepicker: true,\n            timezoneList: null,\n            addSliderAccess: false,\n            sliderAccessArgs: null,\n            controlType: 'slider',\n            oneLine: false,\n            defaultValue: null,\n            parse: 'strict',\n            afterInject: null\n        };\n        $.extend(this._defaults, this.regional['']);\n    };\n\n    $.extend(Timepicker.prototype, {\n        $input: null,\n        $altInput: null,\n        $timeObj: null,\n        inst: null,\n        hour_slider: null,\n        minute_slider: null,\n        second_slider: null,\n        millisec_slider: null,\n        microsec_slider: null,\n        timezone_select: null,\n        maxTime: null,\n        minTime: null,\n        hour: 0,\n        minute: 0,\n        second: 0,\n        millisec: 0,\n        microsec: 0,\n        timezone: null,\n        hourMinOriginal: null,\n        minuteMinOriginal: null,\n        secondMinOriginal: null,\n        millisecMinOriginal: null,\n        microsecMinOriginal: null,\n        hourMaxOriginal: null,\n        minuteMaxOriginal: null,\n        secondMaxOriginal: null,\n        millisecMaxOriginal: null,\n        microsecMaxOriginal: null,\n        ampm: '',\n        formattedDate: '',\n        formattedTime: '',\n        formattedDateTime: '',\n        timezoneList: null,\n        units: ['hour', 'minute', 'second', 'millisec', 'microsec'],\n        support: {},\n        control: null,\n\n        /*\n        * Override the default settings for all instances of the time picker.\n        * @param  {Object} settings  object - the new settings to use as defaults (anonymous object)\n        * @return {Object} the manager object\n        */\n        setDefaults: function (settings) {\n            extendRemove(this._defaults, settings || {});\n            return this;\n        },\n\n        /*\n        * Create a new Timepicker instance\n        */\n        _newInst: function ($input, opts) {\n            var tp_inst = new Timepicker(),\n                inlineSettings = {},\n                fns = {},\n                overrides, i;\n\n            for (var attrName in this._defaults) {\n                if (this._defaults.hasOwnProperty(attrName)) {\n                    var attrValue = $input.attr('time:' + attrName);\n                    if (attrValue) {\n                        try {\n                            inlineSettings[attrName] = eval(attrValue);\n                        } catch (err) {\n                            inlineSettings[attrName] = attrValue;\n                        }\n                    }\n                }\n            }\n\n            overrides = {\n                beforeShow: function (input, dp_inst) {\n                    if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {\n                        return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);\n                    }\n                },\n                onChangeMonthYear: function (year, month, dp_inst) {\n                    // Update the time as well : this prevents the time from disappearing from the $input field.\n                    // tp_inst._updateDateTime(dp_inst);\n                    if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {\n                        tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);\n                    }\n                },\n                onClose: function (dateText, dp_inst) {\n                    if (tp_inst.timeDefined === true && $input.val() !== '') {\n                        tp_inst._updateDateTime(dp_inst);\n                    }\n                    if ($.isFunction(tp_inst._defaults.evnts.onClose)) {\n                        tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);\n                    }\n                }\n            };\n            for (i in overrides) {\n                if (overrides.hasOwnProperty(i)) {\n                    fns[i] = opts[i] || this._defaults[i] || null;\n                }\n            }\n\n            tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {\n                evnts: fns,\n                timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');\n            });\n            tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {\n                return val.toUpperCase();\n            });\n            tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {\n                return val.toUpperCase();\n            });\n\n            // detect which units are supported\n            tp_inst.support = detectSupport(\n                tp_inst._defaults.timeFormat +\n                (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +\n                (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));\n\n            // controlType is string - key to our this._controls\n            if (typeof(tp_inst._defaults.controlType) === 'string') {\n                if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {\n                    tp_inst._defaults.controlType = 'select';\n                }\n                tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];\n            }\n            // controlType is an object and must implement create, options, value methods\n            else {\n                tp_inst.control = tp_inst._defaults.controlType;\n            }\n\n            // prep the timezone options\n            var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,\n                0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];\n            if (tp_inst._defaults.timezoneList !== null) {\n                timezoneList = tp_inst._defaults.timezoneList;\n            }\n            var tzl = timezoneList.length, tzi = 0, tzv = null;\n            if (tzl > 0 && typeof timezoneList[0] !== 'object') {\n                for (; tzi < tzl; tzi++) {\n                    tzv = timezoneList[tzi];\n                    timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };\n                }\n            }\n            tp_inst._defaults.timezoneList = timezoneList;\n\n            // set the default units\n            tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :\n                ((new Date()).getTimezoneOffset() * -1);\n            tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :\n                tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;\n            tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :\n                tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;\n            tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :\n                tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;\n            tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :\n                tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;\n            tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :\n                tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;\n            tp_inst.ampm = '';\n            tp_inst.$input = $input;\n\n            if (tp_inst._defaults.altField) {\n                tp_inst.$altInput = $(tp_inst._defaults.altField);\n                if (tp_inst._defaults.altRedirectFocus === true) {\n                    tp_inst.$altInput.css({\n                        cursor: 'pointer'\n                    }).focus(function () {\n                        $input.trigger(\"focus\");\n                    });\n                }\n            }\n\n            if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {\n                tp_inst._defaults.minDate = new Date();\n            }\n            if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {\n                tp_inst._defaults.maxDate = new Date();\n            }\n\n            // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..\n            if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {\n                tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());\n            }\n            if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {\n                tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());\n            }\n            if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {\n                tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());\n            }\n            if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {\n                tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());\n            }\n            tp_inst.$input.bind('focus', function () {\n                tp_inst._onFocus();\n            });\n\n            return tp_inst;\n        },\n\n        /*\n        * add our sliders to the calendar\n        */\n        _addTimePicker: function (dp_inst) {\n            var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val());\n\n            this.timeDefined = this._parseTime(currDT);\n            this._limitMinMaxDateTime(dp_inst, false);\n            this._injectTimePicker();\n            this._afterInject();\n        },\n\n        /*\n        * parse the time string from input value or _setTime\n        */\n        _parseTime: function (timeString, withDate) {\n            if (!this.inst) {\n                this.inst = $.datepicker._getInst(this.$input[0]);\n            }\n\n            if (withDate || !this._defaults.timeOnly) {\n                var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');\n                try {\n                    var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);\n                    if (!parseRes.timeObj) {\n                        return false;\n                    }\n                    $.extend(this, parseRes.timeObj);\n                } catch (err) {\n                    $.timepicker.log(\"Error parsing the date/time string: \" + err +\n                        \"\\ndate/time string = \" + timeString +\n                        \"\\ntimeFormat = \" + this._defaults.timeFormat +\n                        \"\\ndateFormat = \" + dp_dateFormat);\n                    return false;\n                }\n                return true;\n            } else {\n                var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);\n                if (!timeObj) {\n                    return false;\n                }\n                $.extend(this, timeObj);\n                return true;\n            }\n        },\n\n        /*\n        * Handle callback option after injecting timepicker\n        */\n        _afterInject: function() {\n            var o = this.inst.settings;\n            if ($.isFunction(o.afterInject)) {\n                o.afterInject.call(this);\n            }\n        },\n\n        /*\n        * generate and inject html for timepicker into ui datepicker\n        */\n        _injectTimePicker: function () {\n            var $dp = this.inst.dpDiv,\n                o = this.inst.settings,\n                tp_inst = this,\n                litem = '',\n                uitem = '',\n                show = null,\n                max = {},\n                gridSize = {},\n                size = null,\n                i = 0,\n                l = 0;\n\n            // Prevent displaying twice\n            if ($dp.find(\"div.ui-timepicker-div\").length === 0 && o.showTimepicker) {\n                var noDisplay = ' ui_tpicker_unit_hide',\n                    html = '<div class=\"ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + (o.oneLine && o.controlType === 'select' ? ' ui-timepicker-oneLine' : '') + '\"><dl>' + '<dt class=\"ui_tpicker_time_label' + ((o.showTime) ? '' : noDisplay) + '\">' + o.timeText + '</dt>' +\n                        '<dd class=\"ui_tpicker_time '+ ((o.showTime) ? '' : noDisplay) + '\"><input class=\"ui_tpicker_time_input\" ' + (o.timeInput ? '' : 'disabled') + '/></dd>';\n\n                // Create the markup\n                for (i = 0, l = this.units.length; i < l; i++) {\n                    litem = this.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // Added by Peter Medeiros:\n                    // - Figure out what the hour/minute/second max should be based on the step values.\n                    // - Example: if stepMinute is 15, then minMax is 45.\n                    max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);\n                    gridSize[litem] = 0;\n\n                    html += '<dt class=\"ui_tpicker_' + litem + '_label' + (show ? '' : noDisplay) + '\">' + o[litem + 'Text'] + '</dt>' +\n                        '<dd class=\"ui_tpicker_' + litem + (show ? '' : noDisplay) + '\"><div class=\"ui_tpicker_' + litem + '_slider' + (show ? '' : noDisplay) + '\"></div>';\n\n                    if (show && o[litem + 'Grid'] > 0) {\n                        html += '<div style=\"padding-left: 1px\"><table class=\"ui-tpicker-grid-label\"><tr>';\n\n                        if (litem === 'hour') {\n                            for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);\n                                html += '<td data-for=\"' + litem + '\">' + tmph + '</td>';\n                            }\n                        }\n                        else {\n                            for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                html += '<td data-for=\"' + litem + '\">' + ((m < 10) ? '0' : '') + m + '</td>';\n                            }\n                        }\n\n                        html += '</tr></table></div>';\n                    }\n                    html += '</dd>';\n                }\n\n                // Timezone\n                var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;\n                html += '<dt class=\"ui_tpicker_timezone_label' + (showTz ? '' : noDisplay) + '\">' + o.timezoneText + '</dt>';\n                html += '<dd class=\"ui_tpicker_timezone' + (showTz ? '' : noDisplay) + '\"></dd>';\n\n                // Create the elements from string\n                html += '</dl></div>';\n                var $tp = $(html);\n\n                // if we only want time picker...\n                if (o.timeOnly === true) {\n                    $tp.prepend('<div class=\"ui-widget-header ui-helper-clearfix ui-corner-all\">' + '<div class=\"ui-datepicker-title\">' + o.timeOnlyTitle + '</div>' + '</div>');\n                    $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();\n                }\n\n                // add sliders, adjust grids, add events\n                for (i = 0, l = tp_inst.units.length; i < l; i++) {\n                    litem = tp_inst.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // add the slider\n                    tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);\n\n                    // adjust the grid and add click event\n                    if (show && o[litem + 'Grid'] > 0) {\n                        size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);\n                        $tp.find('.ui_tpicker_' + litem + ' table').css({\n                            width: size + \"%\",\n                            marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + \"%\"),\n                            marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + \"%\") : '0',\n                            borderCollapse: 'collapse'\n                        }).find(\"td\").click(function (e) {\n                            var $t = $(this),\n                                h = $t.html(),\n                                n = parseInt(h.replace(/[^0-9]/g), 10),\n                                ap = h.replace(/[^apm]/ig),\n                                f = $t.data('for'); // loses scope, so we use data-for\n\n                            if (f === 'hour') {\n                                if (ap.indexOf('p') !== -1 && n < 12) {\n                                    n += 12;\n                                }\n                                else {\n                                    if (ap.indexOf('a') !== -1 && n === 12) {\n                                        n = 0;\n                                    }\n                                }\n                            }\n\n                            tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);\n\n                            tp_inst._onTimeChange();\n                            tp_inst._onSelectHandler();\n                        }).css({\n                            cursor: 'pointer',\n                            width: (100 / gridSize[litem]) + '%',\n                            textAlign: 'center',\n                            overflow: 'hidden'\n                        });\n                    } // end if grid > 0\n                } // end for loop\n\n                // Add timezone options\n                this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find(\"select\");\n                $.fn.append.apply(this.timezone_select,\n                    $.map(o.timezoneList, function (val, idx) {\n                        return $(\"<option />\").val(typeof val === \"object\" ? val.value : val).text(typeof val === \"object\" ? val.label : val);\n                    }));\n                if (typeof(this.timezone) !== \"undefined\" && this.timezone !== null && this.timezone !== \"\") {\n                    var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;\n                    if (local_timezone === this.timezone) {\n                        selectLocalTimezone(tp_inst);\n                    } else {\n                        this.timezone_select.val(this.timezone);\n                    }\n                } else {\n                    if (typeof(this.hour) !== \"undefined\" && this.hour !== null && this.hour !== \"\") {\n                        this.timezone_select.val(o.timezone);\n                    } else {\n                        selectLocalTimezone(tp_inst);\n                    }\n                }\n                this.timezone_select.change(function () {\n                    tp_inst._onTimeChange();\n                    tp_inst._onSelectHandler();\n                    tp_inst._afterInject();\n                });\n                // End timezone options\n\n                // inject timepicker into datepicker\n                var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');\n                if ($buttonPanel.length) {\n                    $buttonPanel.before($tp);\n                } else {\n                    $dp.append($tp);\n                }\n\n                this.$timeObj = $tp.find('.ui_tpicker_time_input');\n                this.$timeObj.change(function () {\n                    var timeFormat = tp_inst.inst.settings.timeFormat;\n                    var parsedTime = $.datepicker.parseTime(timeFormat, this.value);\n                    var update = new Date();\n                    if (parsedTime) {\n                        update.setHours(parsedTime.hour);\n                        update.setMinutes(parsedTime.minute);\n                        update.setSeconds(parsedTime.second);\n                        $.datepicker._setTime(tp_inst.inst, update);\n                    } else {\n                        this.value = tp_inst.formattedTime;\n                        this.blur();\n                    }\n                });\n\n                if (this.inst !== null) {\n                    var timeDefined = this.timeDefined;\n                    this._onTimeChange();\n                    this.timeDefined = timeDefined;\n                }\n\n                // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/\n                if (this._defaults.addSliderAccess) {\n                    var sliderAccessArgs = this._defaults.sliderAccessArgs,\n                        rtl = this._defaults.isRTL;\n                    sliderAccessArgs.isRTL = rtl;\n\n                    setTimeout(function () { // fix for inline mode\n                        if ($tp.find('.ui-slider-access').length === 0) {\n                            $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);\n\n                            // fix any grids since sliders are shorter\n                            var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);\n                            if (sliderAccessWidth) {\n                                $tp.find('table:visible').each(function () {\n                                    var $g = $(this),\n                                        oldWidth = $g.outerWidth(),\n                                        oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),\n                                        newWidth = oldWidth - sliderAccessWidth,\n                                        newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',\n                                        css = { width: newWidth, marginRight: 0, marginLeft: 0 };\n                                    css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;\n                                    $g.css(css);\n                                });\n                            }\n                        }\n                    }, 10);\n                }\n                // end slideAccess integration\n\n                tp_inst._limitMinMaxDateTime(this.inst, true);\n            }\n        },\n\n        /*\n        * This function tries to limit the ability to go outside the\n        * min/max date range\n        */\n        _limitMinMaxDateTime: function (dp_inst, adjustSliders) {\n            var o = this._defaults,\n                dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);\n\n            if (!this._defaults.showTimepicker) {\n                return;\n            } // No time so nothing to check here\n\n            if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {\n                var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),\n                    minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {\n                    this.hourMinOriginal = o.hourMin;\n                    this.minuteMinOriginal = o.minuteMin;\n                    this.secondMinOriginal = o.secondMin;\n                    this.millisecMinOriginal = o.millisecMin;\n                    this.microsecMinOriginal = o.microsecMin;\n                }\n\n                if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMin = minDateTime.getHours();\n                    if (this.hour <= this._defaults.hourMin) {\n                        this.hour = this._defaults.hourMin;\n                        this._defaults.minuteMin = minDateTime.getMinutes();\n                        if (this.minute <= this._defaults.minuteMin) {\n                            this.minute = this._defaults.minuteMin;\n                            this._defaults.secondMin = minDateTime.getSeconds();\n                            if (this.second <= this._defaults.secondMin) {\n                                this.second = this._defaults.secondMin;\n                                this._defaults.millisecMin = minDateTime.getMilliseconds();\n                                if (this.millisec <= this._defaults.millisecMin) {\n                                    this.millisec = this._defaults.millisecMin;\n                                    this._defaults.microsecMin = minDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec < this._defaults.microsecMin) {\n                                        this.microsec = this._defaults.microsecMin;\n                                    }\n                                    this._defaults.microsecMin = this.microsecMinOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMin = this.millisecMinOriginal;\n                                this._defaults.microsecMin = this.microsecMinOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMin = this.secondMinOriginal;\n                            this._defaults.millisecMin = this.millisecMinOriginal;\n                            this._defaults.microsecMin = this.microsecMinOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMin = this.minuteMinOriginal;\n                        this._defaults.secondMin = this.secondMinOriginal;\n                        this._defaults.millisecMin = this.millisecMinOriginal;\n                        this._defaults.microsecMin = this.microsecMinOriginal;\n                    }\n                } else {\n                    this._defaults.hourMin = this.hourMinOriginal;\n                    this._defaults.minuteMin = this.minuteMinOriginal;\n                    this._defaults.secondMin = this.secondMinOriginal;\n                    this._defaults.millisecMin = this.millisecMinOriginal;\n                    this._defaults.microsecMin = this.microsecMinOriginal;\n                }\n            }\n\n            if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {\n                var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),\n                    maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {\n                    this.hourMaxOriginal = o.hourMax;\n                    this.minuteMaxOriginal = o.minuteMax;\n                    this.secondMaxOriginal = o.secondMax;\n                    this.millisecMaxOriginal = o.millisecMax;\n                    this.microsecMaxOriginal = o.microsecMax;\n                }\n\n                if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMax = maxDateTime.getHours();\n                    if (this.hour >= this._defaults.hourMax) {\n                        this.hour = this._defaults.hourMax;\n                        this._defaults.minuteMax = maxDateTime.getMinutes();\n                        if (this.minute >= this._defaults.minuteMax) {\n                            this.minute = this._defaults.minuteMax;\n                            this._defaults.secondMax = maxDateTime.getSeconds();\n                            if (this.second >= this._defaults.secondMax) {\n                                this.second = this._defaults.secondMax;\n                                this._defaults.millisecMax = maxDateTime.getMilliseconds();\n                                if (this.millisec >= this._defaults.millisecMax) {\n                                    this.millisec = this._defaults.millisecMax;\n                                    this._defaults.microsecMax = maxDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec > this._defaults.microsecMax) {\n                                        this.microsec = this._defaults.microsecMax;\n                                    }\n                                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMax = this.millisecMaxOriginal;\n                                this._defaults.microsecMax = this.microsecMaxOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMax = this.secondMaxOriginal;\n                            this._defaults.millisecMax = this.millisecMaxOriginal;\n                            this._defaults.microsecMax = this.microsecMaxOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMax = this.minuteMaxOriginal;\n                        this._defaults.secondMax = this.secondMaxOriginal;\n                        this._defaults.millisecMax = this.millisecMaxOriginal;\n                        this._defaults.microsecMax = this.microsecMaxOriginal;\n                    }\n                } else {\n                    this._defaults.hourMax = this.hourMaxOriginal;\n                    this._defaults.minuteMax = this.minuteMaxOriginal;\n                    this._defaults.secondMax = this.secondMaxOriginal;\n                    this._defaults.millisecMax = this.millisecMaxOriginal;\n                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                }\n            }\n\n            if (dp_inst.settings.minTime!==null) {\n                var tempMinTime=new Date(\"01/01/1970 \" + dp_inst.settings.minTime);\n                if (this.hour<tempMinTime.getHours()) {\n                    this.hour=this._defaults.hourMin=tempMinTime.getHours();\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMin<tempMinTime.getHours()) {\n                        this._defaults.hourMin=tempMinTime.getHours();\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMin=0;\n                    }\n                }\n            }\n\n            if (dp_inst.settings.maxTime!==null) {\n                var tempMaxTime=new Date(\"01/01/1970 \" + dp_inst.settings.maxTime);\n                if (this.hour>tempMaxTime.getHours()) {\n                    this.hour=this._defaults.hourMax=tempMaxTime.getHours();\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMax>tempMaxTime.getHours()) {\n                        this._defaults.hourMax=tempMaxTime.getHours();\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMax=59;\n                    }\n                }\n            }\n\n            if (adjustSliders !== undefined && adjustSliders === true) {\n                var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),\n                    minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),\n                    secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),\n                    millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),\n                    microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);\n\n                if (this.hour_slider) {\n                    this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });\n                    this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));\n                }\n                if (this.minute_slider) {\n                    this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });\n                    this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));\n                }\n                if (this.second_slider) {\n                    this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });\n                    this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));\n                }\n                if (this.millisec_slider) {\n                    this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });\n                    this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));\n                }\n                if (this.microsec_slider) {\n                    this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });\n                    this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));\n                }\n            }\n\n        },\n\n        /*\n        * when a slider moves, set the internal time...\n        * on time change is also called when the time is updated in the text field\n        */\n        _onTimeChange: function () {\n            if (!this._defaults.showTimepicker) {\n                return;\n            }\n            var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,\n                minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,\n                second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,\n                millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,\n                microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,\n                timezone = (this.timezone_select) ? this.timezone_select.val() : false,\n                o = this._defaults,\n                pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,\n                pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;\n\n            if (typeof(hour) === 'object') {\n                hour = false;\n            }\n            if (typeof(minute) === 'object') {\n                minute = false;\n            }\n            if (typeof(second) === 'object') {\n                second = false;\n            }\n            if (typeof(millisec) === 'object') {\n                millisec = false;\n            }\n            if (typeof(microsec) === 'object') {\n                microsec = false;\n            }\n            if (typeof(timezone) === 'object') {\n                timezone = false;\n            }\n\n            if (hour !== false) {\n                hour = parseInt(hour, 10);\n            }\n            if (minute !== false) {\n                minute = parseInt(minute, 10);\n            }\n            if (second !== false) {\n                second = parseInt(second, 10);\n            }\n            if (millisec !== false) {\n                millisec = parseInt(millisec, 10);\n            }\n            if (microsec !== false) {\n                microsec = parseInt(microsec, 10);\n            }\n            if (timezone !== false) {\n                timezone = timezone.toString();\n            }\n\n            var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];\n\n            // If the update was done in the input field, the input field should not be updated.\n            // If the update was done using the sliders, update the input field.\n            var hasChanged = (\n                hour !== parseInt(this.hour,10) || // sliders should all be numeric\n                minute !== parseInt(this.minute,10) ||\n                second !== parseInt(this.second,10) ||\n                millisec !== parseInt(this.millisec,10) ||\n                microsec !== parseInt(this.microsec,10) ||\n                (this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) ||\n                (this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or \"EST\" format, so use toString()\n            );\n\n            if (hasChanged) {\n\n                if (hour !== false) {\n                    this.hour = hour;\n                }\n                if (minute !== false) {\n                    this.minute = minute;\n                }\n                if (second !== false) {\n                    this.second = second;\n                }\n                if (millisec !== false) {\n                    this.millisec = millisec;\n                }\n                if (microsec !== false) {\n                    this.microsec = microsec;\n                }\n                if (timezone !== false) {\n                    this.timezone = timezone;\n                }\n\n                if (!this.inst) {\n                    this.inst = $.datepicker._getInst(this.$input[0]);\n                }\n\n                this._limitMinMaxDateTime(this.inst, true);\n            }\n            if (this.support.ampm) {\n                this.ampm = ampm;\n            }\n\n            // Updates the time within the timepicker\n            this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);\n            if (this.$timeObj) {\n                if (pickerTimeFormat === o.timeFormat) {\n                    this.$timeObj.val(this.formattedTime + pickerTimeSuffix);\n                }\n                else {\n                    this.$timeObj.val($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);\n                }\n                if (this.$timeObj[0].setSelectionRange) {\n                    var sPos = this.$timeObj[0].selectionStart;\n                    var ePos = this.$timeObj[0].selectionEnd;\n                    this.$timeObj[0].setSelectionRange(sPos, ePos);\n                }\n            }\n\n            this.timeDefined = true;\n            if (hasChanged) {\n                this._updateDateTime();\n                //this.$input.focus(); // may automatically open the picker on setDate\n            }\n        },\n\n        /*\n        * call custom onSelect.\n        * bind to sliders slidestop, and grid click.\n        */\n        _onSelectHandler: function () {\n            var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;\n            var inputEl = this.$input ? this.$input[0] : null;\n            if (onSelect && inputEl) {\n                onSelect.apply(inputEl, [this.formattedDateTime, this]);\n            }\n        },\n\n        /*\n        * update our input with the new date time..\n        */\n        _updateDateTime: function (dp_inst) {\n            dp_inst = this.inst || dp_inst;\n            var dtTmp = (dp_inst.currentYear > 0?\n                new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :\n                new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                dt = $.datepicker._daylightSavingAdjust(dtTmp),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),\n                dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),\n                formatCfg = $.datepicker._getFormatConfig(dp_inst),\n                timeAvailable = dt !== null && this.timeDefined;\n            this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);\n            var formattedDateTime = this.formattedDate;\n\n            // if a slider was changed but datepicker doesn't have a value yet, set it\n            if (dp_inst.lastVal === \"\") {\n                dp_inst.currentYear = dp_inst.selectedYear;\n                dp_inst.currentMonth = dp_inst.selectedMonth;\n                dp_inst.currentDay = dp_inst.selectedDay;\n            }\n\n            /*\n            * remove following lines to force every changes in date picker to change the input value\n            * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.\n            * If the user manually empty the value in the input field, the date picker will never change selected value.\n            */\n            //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {\n            //\treturn;\n            //}\n\n            if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {\n                formattedDateTime = this.formattedTime;\n            } else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {\n                formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;\n            }\n\n            this.formattedDateTime = formattedDateTime;\n\n            if (!this._defaults.showTimepicker) {\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {\n                this.$altInput.val(this.formattedTime);\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput) {\n                this.$input.val(formattedDateTime);\n                var altFormattedDateTime = '',\n                    altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,\n                    altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;\n\n                if (!this._defaults.timeOnly) {\n                    if (this._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);\n                    }\n                    else {\n                        altFormattedDateTime = this.formattedDate;\n                    }\n\n                    if (altFormattedDateTime) {\n                        altFormattedDateTime += altSeparator;\n                    }\n                }\n\n                if (this._defaults.altTimeFormat !== null) {\n                    altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;\n                }\n                else {\n                    altFormattedDateTime += this.formattedTime + altTimeSuffix;\n                }\n                this.$altInput.val(altFormattedDateTime);\n            } else {\n                this.$input.val(formattedDateTime);\n            }\n\n            this.$input.trigger(\"change\");\n        },\n\n        _onFocus: function () {\n            if (!this.$input.val() && this._defaults.defaultValue) {\n                this.$input.val(this._defaults.defaultValue);\n                var inst = $.datepicker._getInst(this.$input.get(0)),\n                    tp_inst = $.datepicker._get(inst, 'timepicker');\n                if (tp_inst) {\n                    if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                        try {\n                            $.datepicker._updateDatepicker(inst);\n                        } catch (err) {\n                            $.timepicker.log(err);\n                        }\n                    }\n                }\n            }\n        },\n\n        /*\n        * Small abstraction to control types\n        * We can add more, just be sure to follow the pattern: create, options, value\n        */\n        _controls: {\n            // slider methods\n            slider: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60\n                    return obj.prop('slide', null).slider({\n                        orientation: \"horizontal\",\n                        value: rtl ? val * -1 : val,\n                        min: rtl ? max * -1 : min,\n                        max: rtl ? min * -1 : max,\n                        step: step,\n                        slide: function (event, ui) {\n                            tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);\n                            tp_inst._onTimeChange();\n                        },\n                        stop: function (event, ui) {\n                            tp_inst._onSelectHandler();\n                        }\n                    });\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (typeof(opts) === 'string') {\n                            if (opts === 'min' || opts === 'max') {\n                                if (val !== undefined) {\n                                    return obj.slider(opts, val * -1);\n                                }\n                                return Math.abs(obj.slider(opts));\n                            }\n                            return obj.slider(opts);\n                        }\n                        var min = opts.min,\n                            max = opts.max;\n                        opts.min = opts.max = null;\n                        if (min !== undefined) {\n                            opts.max = min * -1;\n                        }\n                        if (max !== undefined) {\n                            opts.min = max * -1;\n                        }\n                        return obj.slider(opts);\n                    }\n                    if (typeof(opts) === 'string' && val !== undefined) {\n                        return obj.slider(opts, val);\n                    }\n                    return obj.slider(opts);\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (val !== undefined) {\n                            return obj.slider('value', val * -1);\n                        }\n                        return Math.abs(obj.slider('value'));\n                    }\n                    if (val !== undefined) {\n                        return obj.slider('value', val);\n                    }\n                    return obj.slider('value');\n                }\n            },\n            // select methods\n            select: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var sel = '<select class=\"ui-timepicker-select ui-state-default ui-corner-all\" data-unit=\"' + unit + '\" data-min=\"' + min + '\" data-max=\"' + max + '\" data-step=\"' + step + '\">',\n                        format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;\n\n                    for (var i = min; i <= max; i += step) {\n                        sel += '<option value=\"' + i + '\"' + (i === val ? ' selected' : '') + '>';\n                        if (unit === 'hour') {\n                            sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);\n                        }\n                        else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }\n                        else {sel += '0' + i.toString(); }\n                        sel += '</option>';\n                    }\n                    sel += '</select>';\n\n                    obj.children('select').remove();\n\n                    $(sel).appendTo(obj).change(function (e) {\n                        tp_inst._onTimeChange();\n                        tp_inst._onSelectHandler();\n                        tp_inst._afterInject();\n                    });\n\n                    return obj;\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    var o = {},\n                        $t = obj.children('select');\n                    if (typeof(opts) === 'string') {\n                        if (val === undefined) {\n                            return $t.data(opts);\n                        }\n                        o[opts] = val;\n                    }\n                    else { o = opts; }\n                    return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min>=0 ? o.min : $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    var $t = obj.children('select');\n                    if (val !== undefined) {\n                        return $t.val(val);\n                    }\n                    return $t.val();\n                }\n            }\n        } // end _controls\n\n    });\n\n    $.fn.extend({\n        /*\n        * shorthand just to use timepicker.\n        */\n        timepicker: function (o) {\n            o = o || {};\n            var tmp_args = Array.prototype.slice.call(arguments);\n\n            if (typeof o === 'object') {\n                tmp_args[0] = $.extend(o, {\n                    timeOnly: true\n                });\n            }\n\n            return $(this).each(function () {\n                $.fn.datetimepicker.apply($(this), tmp_args);\n            });\n        },\n\n        /*\n        * extend timepicker to datepicker\n        */\n        datetimepicker: function (o) {\n            o = o || {};\n            var tmp_args = arguments;\n\n            if (typeof(o) === 'string') {\n                if (o === 'getDate'  || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {\n                    return $.fn.datepicker.apply($(this[0]), tmp_args);\n                } else {\n                    return this.each(function () {\n                        var $t = $(this);\n                        $t.datepicker.apply($t, tmp_args);\n                    });\n                }\n            } else {\n                return this.each(function () {\n                    var $t = $(this);\n                    $t.datepicker($.timepicker._newInst($t, o)._defaults);\n                });\n            }\n        }\n    });\n\n    /*\n    * Public Utility to parse date and time\n    */\n    $.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);\n        if (parseRes.timeObj) {\n            var t = parseRes.timeObj;\n            parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);\n            parseRes.date.setMicroseconds(t.microsec);\n        }\n\n        return parseRes.date;\n    };\n\n    /*\n    * Public utility to parse time\n    */\n    $.datepicker.parseTime = function (timeFormat, timeString, options) {\n        var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),\n            iso8601 = (timeFormat.replace(/\\'.*?\\'/g, '').indexOf('Z') !== -1);\n\n        // Strict parse requires the timeString to match the timeFormat exactly\n        var strictParse = function (f, s, o) {\n\n            // pattern for standard and localized AM/PM markers\n            var getPatternAmpm = function (amNames, pmNames) {\n                var markers = [];\n                if (amNames) {\n                    $.merge(markers, amNames);\n                }\n                if (pmNames) {\n                    $.merge(markers, pmNames);\n                }\n                markers = $.map(markers, function (val) {\n                    return val.replace(/[.*+?|()\\[\\]{}\\\\]/g, '\\\\$&');\n                });\n                return '(' + markers.join('|') + ')?';\n            };\n\n            // figure out position of time elements.. cause js cant do named captures\n            var getFormatPositions = function (timeFormat) {\n                var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),\n                    orders = {\n                        h: -1,\n                        m: -1,\n                        s: -1,\n                        l: -1,\n                        c: -1,\n                        t: -1,\n                        z: -1\n                    };\n\n                if (finds) {\n                    for (var i = 0; i < finds.length; i++) {\n                        if (orders[finds[i].toString().charAt(0)] === -1) {\n                            orders[finds[i].toString().charAt(0)] = i + 1;\n                        }\n                    }\n                }\n                return orders;\n            };\n\n            var regstr = '^' + f.toString()\n                    .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n                        var ml = match.length;\n                        switch (match.charAt(0).toLowerCase()) {\n                            case 'h':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'm':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 's':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'l':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'c':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'z':\n                                return '(z|[-+]\\\\d\\\\d:?\\\\d\\\\d|\\\\S+)?';\n                            case 't':\n                                return getPatternAmpm(o.amNames, o.pmNames);\n                            default:    // literal escaped in quotes\n                                return '(' + match.replace(/\\'/g, \"\").replace(/(\\.|\\$|\\^|\\\\|\\/|\\(|\\)|\\[|\\]|\\?|\\+|\\*)/g, function (m) { return \"\\\\\" + m; }) + ')?';\n                        }\n                    })\n                    .replace(/\\s/g, '\\\\s?') +\n                o.timeSuffix + '$',\n                order = getFormatPositions(f),\n                ampm = '',\n                treg;\n\n            treg = s.match(new RegExp(regstr, 'i'));\n\n            var resTime = {\n                hour: 0,\n                minute: 0,\n                second: 0,\n                millisec: 0,\n                microsec: 0\n            };\n\n            if (treg) {\n                if (order.t !== -1) {\n                    if (treg[order.t] === undefined || treg[order.t].length === 0) {\n                        ampm = '';\n                        resTime.ampm = '';\n                    } else {\n                        ampm = $.inArray(treg[order.t].toUpperCase(), $.map(o.amNames, function (x,i) { return x.toUpperCase(); })) !== -1 ? 'AM' : 'PM';\n                        resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];\n                    }\n                }\n\n                if (order.h !== -1) {\n                    if (ampm === 'AM' && treg[order.h] === '12') {\n                        resTime.hour = 0; // 12am = 0 hour\n                    } else {\n                        if (ampm === 'PM' && treg[order.h] !== '12') {\n                            resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12\n                        } else {\n                            resTime.hour = Number(treg[order.h]);\n                        }\n                    }\n                }\n\n                if (order.m !== -1) {\n                    resTime.minute = Number(treg[order.m]);\n                }\n                if (order.s !== -1) {\n                    resTime.second = Number(treg[order.s]);\n                }\n                if (order.l !== -1) {\n                    resTime.millisec = Number(treg[order.l]);\n                }\n                if (order.c !== -1) {\n                    resTime.microsec = Number(treg[order.c]);\n                }\n                if (order.z !== -1 && treg[order.z] !== undefined) {\n                    resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);\n                }\n\n\n                return resTime;\n            }\n            return false;\n        };// end strictParse\n\n        // First try JS Date, if that fails, use strictParse\n        var looseParse = function (f, s, o) {\n            try {\n                var d = new Date('2012-01-01 ' + s);\n                if (isNaN(d.getTime())) {\n                    d = new Date('2012-01-01T' + s);\n                    if (isNaN(d.getTime())) {\n                        d = new Date('01/01/2012 ' + s);\n                        if (isNaN(d.getTime())) {\n                            throw \"Unable to parse time with native Date: \" + s;\n                        }\n                    }\n                }\n\n                return {\n                    hour: d.getHours(),\n                    minute: d.getMinutes(),\n                    second: d.getSeconds(),\n                    millisec: d.getMilliseconds(),\n                    microsec: d.getMicroseconds(),\n                    timezone: d.getTimezoneOffset() * -1\n                };\n            }\n            catch (err) {\n                try {\n                    return strictParse(f, s, o);\n                }\n                catch (err2) {\n                    $.timepicker.log(\"Unable to parse \\ntimeString: \" + s + \"\\ntimeFormat: \" + f);\n                }\n            }\n            return false;\n        }; // end looseParse\n\n        if (typeof o.parse === \"function\") {\n            return o.parse(timeFormat, timeString, o);\n        }\n        if (o.parse === 'loose') {\n            return looseParse(timeFormat, timeString, o);\n        }\n        return strictParse(timeFormat, timeString, o);\n    };\n\n    /**\n     * Public utility to format the time\n     * @param {string} format format of the time\n     * @param {Object} time Object not a Date for timezones\n     * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm\n     * @returns {string} the formatted time\n     */\n    $.datepicker.formatTime = function (format, time, options) {\n        options = options || {};\n        options = $.extend({}, $.timepicker._defaults, options);\n        time = $.extend({\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null\n        }, time);\n\n        var tmptime = format,\n            ampmName = options.amNames[0],\n            hour = parseInt(time.hour, 10);\n\n        if (hour > 11) {\n            ampmName = options.pmNames[0];\n        }\n\n        tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n            switch (match) {\n                case 'HH':\n                    return ('0' + hour).slice(-2);\n                case 'H':\n                    return hour;\n                case 'hh':\n                    return ('0' + convert24to12(hour)).slice(-2);\n                case 'h':\n                    return convert24to12(hour);\n                case 'mm':\n                    return ('0' + time.minute).slice(-2);\n                case 'm':\n                    return time.minute;\n                case 'ss':\n                    return ('0' + time.second).slice(-2);\n                case 's':\n                    return time.second;\n                case 'l':\n                    return ('00' + time.millisec).slice(-3);\n                case 'c':\n                    return ('00' + time.microsec).slice(-3);\n                case 'z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);\n                case 'Z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);\n                case 'T':\n                    return ampmName.charAt(0).toUpperCase();\n                case 'TT':\n                    return ampmName.toUpperCase();\n                case 't':\n                    return ampmName.charAt(0).toLowerCase();\n                case 'tt':\n                    return ampmName.toLowerCase();\n                default:\n                    return match.replace(/'/g, \"\");\n            }\n        });\n\n        return tmptime;\n    };\n\n    /*\n    * the bad hack :/ override datepicker so it doesn't close on select\n    // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378\n    */\n    $.datepicker._base_selectDate = $.datepicker._selectDate;\n    $.datepicker._selectDate = function (id, dateStr) {\n        var inst = this._getInst($(id)[0]),\n            tp_inst = this._get(inst, 'timepicker'),\n            was_inline;\n\n        if (tp_inst && inst.settings.showTimepicker) {\n            tp_inst._limitMinMaxDateTime(inst, true);\n            was_inline = inst.inline;\n            inst.inline = inst.stay_open = true;\n            //This way the onSelect handler called from calendarpicker get the full dateTime\n            this._base_selectDate(id, dateStr);\n            inst.inline = was_inline;\n            inst.stay_open = false;\n            this._notifyChange(inst);\n            this._updateDatepicker(inst);\n        } else {\n            this._base_selectDate(id, dateStr);\n        }\n    };\n\n    /*\n    * second bad hack :/ override datepicker so it triggers an event when changing the input field\n    * and does not redraw the datepicker on every selectDate event\n    */\n    $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;\n    $.datepicker._updateDatepicker = function (inst) {\n\n        // don't popup the datepicker if there is another instance already opened\n        var input = inst.input[0];\n        if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {\n            return;\n        }\n\n        if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {\n\n            this._base_updateDatepicker(inst);\n\n            // Reload the time control when changing something in the input text field.\n            var tp_inst = this._get(inst, 'timepicker');\n            if (tp_inst) {\n                tp_inst._addTimePicker(inst);\n            }\n        }\n    };\n\n    /*\n    * third bad hack :/ override datepicker so it allows spaces and colon in the input field\n    */\n    $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;\n    $.datepicker._doKeyPress = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if ($.datepicker._get(inst, 'constrainInput')) {\n                var ampm = tp_inst.support.ampm,\n                    tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,\n                    dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),\n                    datetimeChars = tp_inst._defaults.timeFormat.toString()\n                            .replace(/[hms]/g, '')\n                            .replace(/TT/g, ampm ? 'APM' : '')\n                            .replace(/Tt/g, ampm ? 'AaPpMm' : '')\n                            .replace(/tT/g, ampm ? 'AaPpMm' : '')\n                            .replace(/T/g, ampm ? 'AP' : '')\n                            .replace(/tt/g, ampm ? 'apm' : '')\n                            .replace(/t/g, ampm ? 'ap' : '') +\n                        \" \" + tp_inst._defaults.separator +\n                        tp_inst._defaults.timeSuffix +\n                        (tz ? tp_inst._defaults.timezoneList.join('') : '') +\n                        (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +\n                        dateChars,\n                    chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);\n                return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);\n            }\n        }\n\n        return $.datepicker._base_doKeyPress(event);\n    };\n\n    /*\n    * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField\n    * Update any alternate field to synchronise with the main field.\n    */\n    $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;\n    $.datepicker._updateAlternate = function (inst) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var altField = tp_inst._defaults.altField;\n            if (altField) { // update alternate field too\n                var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,\n                    date = this._getDate(inst),\n                    formatCfg = $.datepicker._getFormatConfig(inst),\n                    altFormattedDateTime = '',\n                    altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,\n                    altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,\n                    altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;\n\n                altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;\n                if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {\n                    if (tp_inst._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;\n                    }\n                    else {\n                        altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;\n                    }\n                }\n                $(altField).val( inst.input.val() ? altFormattedDateTime : \"\");\n            }\n        }\n        else {\n            $.datepicker._base_updateAlternate(inst);\n        }\n    };\n\n    /*\n    * Override key up event to sync manual input changes.\n    */\n    $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;\n    $.datepicker._doKeyUp = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                try {\n                    $.datepicker._updateDatepicker(inst);\n                } catch (err) {\n                    $.timepicker.log(err);\n                }\n            }\n        }\n\n        return $.datepicker._base_doKeyUp(event);\n    };\n\n    /*\n    * override \"Today\" button to also grab the time and set it to input field.\n    */\n    $.datepicker._base_gotoToday = $.datepicker._gotoToday;\n    $.datepicker._gotoToday = function (id) {\n        var inst = this._getInst($(id)[0]);\n        this._base_gotoToday(id);\n        var tp_inst = this._get(inst, 'timepicker');\n        if (!tp_inst) {\n            return;\n        }\n\n        var tzoffset = $.timepicker.timezoneOffsetNumber(tp_inst.timezone);\n        var now = new Date();\n        now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + parseInt(tzoffset, 10));\n        this._setTime(inst, now);\n        this._setDate(inst, now);\n        tp_inst._onSelectHandler();\n    };\n\n    /*\n    * Disable & enable the Time in the datetimepicker\n    */\n    $.datepicker._disableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = false;\n            tp_inst._defaults.showTimepicker = false;\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    $.datepicker._enableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = true;\n            tp_inst._defaults.showTimepicker = true;\n            tp_inst._addTimePicker(inst); // Could be disabled on page load\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create our own set time function\n    */\n    $.datepicker._setTime = function (inst, date) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var defaults = tp_inst._defaults;\n\n            // calling _setTime with no date sets time to defaults\n            tp_inst.hour = date ? date.getHours() : defaults.hour;\n            tp_inst.minute = date ? date.getMinutes() : defaults.minute;\n            tp_inst.second = date ? date.getSeconds() : defaults.second;\n            tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;\n            tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;\n\n            //check if within min/max times..\n            tp_inst._limitMinMaxDateTime(inst, true);\n\n            tp_inst._onTimeChange();\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create new public method to set only time, callable as $().datepicker('setTime', date)\n    */\n    $.datepicker._setTimeDatepicker = function (target, date, withDate) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            this._setDateFromField(inst);\n            var tp_date;\n            if (date) {\n                if (typeof date === \"string\") {\n                    tp_inst._parseTime(date, withDate);\n                    tp_date = new Date();\n                    tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                    tp_date.setMicroseconds(tp_inst.microsec);\n                } else {\n                    tp_date = new Date(date.getTime());\n                    tp_date.setMicroseconds(date.getMicroseconds());\n                }\n                if (tp_date.toString() === 'Invalid Date') {\n                    tp_date = undefined;\n                }\n                this._setTime(inst, tp_date);\n            }\n        }\n\n    };\n\n    /*\n    * override setDate() to allow setting time too within Date object\n    */\n    $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;\n    $.datepicker._setDateDatepicker = function (target, _date) {\n        var inst = this._getInst(target);\n        var date = _date;\n        if (!inst) {\n            return;\n        }\n\n        if (typeof(_date) === 'string') {\n            date = new Date(_date);\n            if (!date.getTime()) {\n                this._base_setDateDatepicker.apply(this, arguments);\n                date = $(target).datepicker('getDate');\n            }\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        var tp_date;\n        if (date instanceof Date) {\n            tp_date = new Date(date.getTime());\n            tp_date.setMicroseconds(date.getMicroseconds());\n        } else {\n            tp_date = date;\n        }\n\n        // This is important if you are using the timezone option, javascript's Date\n        // object will only return the timezone offset for the current locale, so we\n        // adjust it accordingly.  If not using timezone option this won't matter..\n        // If a timezone is different in tp, keep the timezone as is\n        if (tp_inst && tp_date) {\n            // look out for DST if tz wasn't specified\n            if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                tp_inst.timezone = tp_date.getTimezoneOffset() * -1;\n            }\n            date = $.timepicker.timezoneAdjust(date, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()), tp_inst.timezone);\n            tp_date = $.timepicker.timezoneAdjust(tp_date, $.timepicker.timezoneOffsetString(-tp_date.getTimezoneOffset()), tp_inst.timezone);\n        }\n\n        this._updateDatepicker(inst);\n        this._base_setDateDatepicker.apply(this, arguments);\n        this._setTimeDatepicker(target, tp_date, true);\n    };\n\n    /*\n    * override getDate() to allow getting time too within Date object\n    */\n    $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;\n    $.datepicker._getDateDatepicker = function (target, noDefault) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            // if it hasn't yet been defined, grab from field\n            if (inst.lastVal === undefined) {\n                this._setDateFromField(inst, noDefault);\n            }\n\n            var date = this._getDate(inst);\n\n            var currDT = null;\n\n            if (tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly) {\n                currDT = tp_inst.$input.val() + ' ' + tp_inst.$altInput.val();\n            }\n            else if (tp_inst.$input.get(0).tagName !== 'INPUT' && tp_inst.$altInput) {\n                /**\n                 * in case the datetimepicker has been applied to a non-input tag for inline UI,\n                 * and the user has not configured the plugin to display only time in altInput,\n                 * pick current date time from the altInput (and hope for the best, for now, until \"ER1\" is applied)\n                 *\n                 * @todo ER1. Since altInput can have a totally difference format, convert it to standard format by reading input format from \"altFormat\" and \"altTimeFormat\" option values\n                 */\n                currDT = tp_inst.$altInput.val();\n            }\n            else {\n                currDT = tp_inst.$input.val();\n            }\n\n            if (date && tp_inst._parseTime(currDT, !inst.settings.timeOnly)) {\n                date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                date.setMicroseconds(tp_inst.microsec);\n\n                // This is important if you are using the timezone option, javascript's Date\n                // object will only return the timezone offset for the current locale, so we\n                // adjust it accordingly.  If not using timezone option this won't matter..\n                if (tp_inst.timezone != null) {\n                    // look out for DST if tz wasn't specified\n                    if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                        tp_inst.timezone = date.getTimezoneOffset() * -1;\n                    }\n                    date = $.timepicker.timezoneAdjust(date, tp_inst.timezone, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()));\n                }\n            }\n            return date;\n        }\n        return this._base_getDateDatepicker(target, noDefault);\n    };\n\n    /*\n    * override parseDate() because UI 1.8.14 throws an error about \"Extra characters\"\n    * An option in datapicker to ignore extra format characters would be nicer.\n    */\n    $.datepicker._base_parseDate = $.datepicker.parseDate;\n    $.datepicker.parseDate = function (format, value, settings) {\n        var date;\n        try {\n            date = this._base_parseDate(format, value, settings);\n        } catch (err) {\n            // Hack!  The error message ends with a colon, a space, and\n            // the \"extra\" characters.  We rely on that instead of\n            // attempting to perfectly reproduce the parsing algorithm.\n            if (err.indexOf(\":\") >= 0) {\n                date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);\n                $.timepicker.log(\"Error parsing the date string: \" + err + \"\\ndate string = \" + value + \"\\ndate format = \" + format);\n            } else {\n                throw err;\n            }\n        }\n        return date;\n    };\n\n    /*\n    * override formatDate to set date with time to the input\n    */\n    $.datepicker._base_formatDate = $.datepicker._formatDate;\n    $.datepicker._formatDate = function (inst, day, month, year) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            tp_inst._updateDateTime(inst);\n            return tp_inst.$input.val();\n        }\n        return this._base_formatDate(inst);\n    };\n\n    /*\n    * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate\n    */\n    $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;\n    $.datepicker._optionDatepicker = function (target, name, value) {\n        var inst = this._getInst(target),\n            name_clone;\n        if (!inst) {\n            return null;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var min = null,\n                max = null,\n                onselect = null,\n                overrides = tp_inst._defaults.evnts,\n                fns = {},\n                prop,\n                ret,\n                oldVal,\n                $target;\n            if (typeof name === 'string') { // if min/max was set with the string\n                if (name === 'minDate' || name === 'minDateTime') {\n                    min = value;\n                } else if (name === 'maxDate' || name === 'maxDateTime') {\n                    max = value;\n                } else if (name === 'onSelect') {\n                    onselect = value;\n                } else if (overrides.hasOwnProperty(name)) {\n                    if (typeof (value) === 'undefined') {\n                        return overrides[name];\n                    }\n                    fns[name] = value;\n                    name_clone = {}; //empty results in exiting function after overrides updated\n                }\n            } else if (typeof name === 'object') { //if min/max was set with the JSON\n                if (name.minDate) {\n                    min = name.minDate;\n                } else if (name.minDateTime) {\n                    min = name.minDateTime;\n                } else if (name.maxDate) {\n                    max = name.maxDate;\n                } else if (name.maxDateTime) {\n                    max = name.maxDateTime;\n                }\n                for (prop in overrides) {\n                    if (overrides.hasOwnProperty(prop) && name[prop]) {\n                        fns[prop] = name[prop];\n                    }\n                }\n            }\n            for (prop in fns) {\n                if (fns.hasOwnProperty(prop)) {\n                    overrides[prop] = fns[prop];\n                    if (!name_clone) { name_clone = $.extend({}, name); }\n                    delete name_clone[prop];\n                }\n            }\n            if (name_clone && isEmptyObject(name_clone)) { return; }\n            if (min) { //if min was set\n                if (min === 0) {\n                    min = new Date();\n                } else {\n                    min = new Date(min);\n                }\n                tp_inst._defaults.minDate = min;\n                tp_inst._defaults.minDateTime = min;\n            } else if (max) { //if max was set\n                if (max === 0) {\n                    max = new Date();\n                } else {\n                    max = new Date(max);\n                }\n                tp_inst._defaults.maxDate = max;\n                tp_inst._defaults.maxDateTime = max;\n            } else if (onselect) {\n                tp_inst._defaults.onSelect = onselect;\n            }\n\n            // Datepicker will override our date when we call _base_optionDatepicker when\n            // calling minDate/maxDate, so we will first grab the value, call\n            // _base_optionDatepicker, then set our value back.\n            if(min || max){\n                $target = $(target);\n                oldVal = $target.datetimepicker('getDate');\n                ret = this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n                $target.datetimepicker('setDate', oldVal);\n                return ret;\n            }\n        }\n        if (value === undefined) {\n            return this._base_optionDatepicker.call($.datepicker, target, name);\n        }\n        return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n    };\n\n    /*\n    * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,\n    * it will return false for all objects\n    */\n    var isEmptyObject = function (obj) {\n        var prop;\n        for (prop in obj) {\n            if (obj.hasOwnProperty(prop)) {\n                return false;\n            }\n        }\n        return true;\n    };\n\n    /*\n    * jQuery extend now ignores nulls!\n    */\n    var extendRemove = function (target, props) {\n        $.extend(target, props);\n        for (var name in props) {\n            if (props[name] === null || props[name] === undefined) {\n                target[name] = props[name];\n            }\n        }\n        return target;\n    };\n\n    /*\n    * Determine by the time format which units are supported\n    * Returns an object of booleans for each unit\n    */\n    var detectSupport = function (timeFormat) {\n        var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals\n            isIn = function (f, t) { // does the format contain the token?\n                return f.indexOf(t) !== -1 ? true : false;\n            };\n        return {\n            hour: isIn(tf, 'h'),\n            minute: isIn(tf, 'm'),\n            second: isIn(tf, 's'),\n            millisec: isIn(tf, 'l'),\n            microsec: isIn(tf, 'c'),\n            timezone: isIn(tf, 'z'),\n            ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),\n            iso8601: isIn(timeFormat, 'Z')\n        };\n    };\n\n    /*\n    * Converts 24 hour format into 12 hour\n    * Returns 12 hour without leading 0\n    */\n    var convert24to12 = function (hour) {\n        hour %= 12;\n\n        if (hour === 0) {\n            hour = 12;\n        }\n\n        return String(hour);\n    };\n\n    var computeEffectiveSetting = function (settings, property) {\n        return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];\n    };\n\n    /*\n    * Splits datetime string into date and time substrings.\n    * Throws exception when date can't be parsed\n    * Returns {dateString: dateString, timeString: timeString}\n    */\n    var splitDateTime = function (dateTimeString, timeSettings) {\n        // The idea is to get the number separator occurrences in datetime and the time format requested (since time has\n        // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.\n        var separator = computeEffectiveSetting(timeSettings, 'separator'),\n            format = computeEffectiveSetting(timeSettings, 'timeFormat'),\n            timeParts = format.split(separator), // how many occurrences of separator may be in our format?\n            timePartsLen = timeParts.length,\n            allParts = dateTimeString.split(separator),\n            allPartsLen = allParts.length;\n\n        if (allPartsLen > 1) {\n            return {\n                dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),\n                timeString: allParts.splice(0, timePartsLen).join(separator)\n            };\n        }\n\n        return {\n            dateString: dateTimeString,\n            timeString: ''\n        };\n    };\n\n    /*\n    * Internal function to parse datetime interval\n    * Returns: {date: Date, timeObj: Object}, where\n    *   date - parsed date without time (type Date)\n    *   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional\n    */\n    var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var date,\n            parts,\n            parsedTime;\n\n        parts = splitDateTime(dateTimeString, timeSettings);\n        date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);\n\n        if (parts.timeString === '') {\n            return {\n                date: date\n            };\n        }\n\n        parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);\n\n        if (!parsedTime) {\n            throw 'Wrong time format';\n        }\n\n        return {\n            date: date,\n            timeObj: parsedTime\n        };\n    };\n\n    /*\n    * Internal function to set timezone_select to the local timezone\n    */\n    var selectLocalTimezone = function (tp_inst, date) {\n        if (tp_inst && tp_inst.timezone_select) {\n            var now = date || new Date();\n            tp_inst.timezone_select.val(-now.getTimezoneOffset());\n        }\n    };\n\n    /*\n    * Create a Singleton Instance\n    */\n    $.timepicker = new Timepicker();\n\n    /**\n     * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)\n     * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned\n     * @param {boolean} iso8601 if true formats in accordance to iso8601 \"+12:45\"\n     * @return {string}\n     */\n    $.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {\n        if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {\n            return tzMinutes;\n        }\n\n        var off = tzMinutes,\n            minutes = off % 60,\n            hours = (off - minutes) / 60,\n            iso = iso8601 ? ':' : '',\n            tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);\n\n        if (tz === '+00:00') {\n            return 'Z';\n        }\n        return tz;\n    };\n\n    /**\n     * Get the number in minutes that represents a timezone string\n     * @param  {string} tzString formatted like \"+0500\", \"-1245\", \"Z\"\n     * @return {number} the offset minutes or the original string if it doesn't match expectations\n     */\n    $.timepicker.timezoneOffsetNumber = function (tzString) {\n        var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with \"+1245\"\n\n        if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset\n            return 0;\n        }\n\n        if (!/^(\\-|\\+)\\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back\n            return parseInt(tzString, 10);\n        }\n\n        return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus\n            ((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)\n                parseInt(normalized.substr(3, 2), 10))); // minutes\n    };\n\n    /**\n     * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)\n     * @param  {Date} date\n     * @param  {string} fromTimezone formatted like \"+0500\", \"-1245\"\n     * @param  {string} toTimezone formatted like \"+0500\", \"-1245\"\n     * @return {Date}\n     */\n    $.timepicker.timezoneAdjust = function (date, fromTimezone, toTimezone) {\n        var fromTz = $.timepicker.timezoneOffsetNumber(fromTimezone);\n        var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);\n        if (!isNaN(toTz)) {\n            date.setMinutes(date.getMinutes() + (-fromTz) - (-toTz));\n        }\n        return date;\n    };\n\n    /**\n     * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * n.b. The input value must be correctly formatted (reformatting is not supported)\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the timepicker() call\n     * @return {jQuery}\n     */\n    $.timepicker.timeRange = function (startTime, endTime, options) {\n        return $.timepicker.handleRange('timepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @return {jQuery}\n     */\n    $.timepicker.datetimeRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datetimepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.dateRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `method` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.handleRange = function (method, startTime, endTime, options) {\n        options = $.extend({}, {\n            minInterval: 0, // min allowed interval in milliseconds\n            maxInterval: 0, // max allowed interval in milliseconds\n            start: {},      // options for start picker\n            end: {}         // options for end picker\n        }, options);\n\n        // for the mean time this fixes an issue with calling getDate with timepicker()\n        var timeOnly = false;\n        if(method === 'timepicker'){\n            timeOnly = true;\n            method = 'datetimepicker';\n        }\n\n        function checkDates(changed, other) {\n            var startdt = startTime[method]('getDate'),\n                enddt = endTime[method]('getDate'),\n                changeddt = changed[method]('getDate');\n\n            if (startdt !== null) {\n                var minDate = new Date(startdt.getTime()),\n                    maxDate = new Date(startdt.getTime());\n\n                minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);\n                maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);\n\n                if (options.minInterval > 0 && minDate > enddt) { // minInterval check\n                    endTime[method]('setDate', minDate);\n                }\n                else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check\n                    endTime[method]('setDate', maxDate);\n                }\n                else if (startdt > enddt) {\n                    other[method]('setDate', changeddt);\n                }\n            }\n        }\n\n        function selected(changed, other, option) {\n            if (!changed.val()) {\n                return;\n            }\n            var date = changed[method].call(changed, 'getDate');\n            if (date !== null && options.minInterval > 0) {\n                if (option === 'minDate') {\n                    date.setMilliseconds(date.getMilliseconds() + options.minInterval);\n                }\n                if (option === 'maxDate') {\n                    date.setMilliseconds(date.getMilliseconds() - options.minInterval);\n                }\n            }\n\n            if (date.getTime) {\n                other[method].call(other, 'option', option, date);\n            }\n        }\n\n        $.fn[method].call(startTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), endTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), endTime, 'minDate');\n            }\n        }, options, options.start));\n        $.fn[method].call(endTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), startTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), startTime, 'maxDate');\n            }\n        }, options, options.end));\n\n        checkDates(startTime, endTime);\n\n        selected(startTime, endTime, 'minDate');\n        selected(endTime, startTime, 'maxDate');\n\n        return $([startTime.get(0), endTime.get(0)]);\n    };\n\n    /**\n     * Log error or data to the console during error or debugging\n     * @param  {Object} err pass any type object to log to the console during error or debugging\n     * @return {void}\n     */\n    $.timepicker.log = function () {\n        // Older IE (9, maybe 10) throw error on accessing `window.console.log.apply`, so check first.\n        if (window.console && window.console.log && window.console.log.apply) {\n            window.console.log.apply(window.console, Array.prototype.slice.call(arguments));\n        }\n    };\n\n    /*\n     * Add util object to allow access to private methods for testability.\n     */\n    $.timepicker._util = {\n        _extendRemove: extendRemove,\n        _isEmptyObject: isEmptyObject,\n        _convert24to12: convert24to12,\n        _detectSupport: detectSupport,\n        _selectLocalTimezone: selectLocalTimezone,\n        _computeEffectiveSetting: computeEffectiveSetting,\n        _splitDateTime: splitDateTime,\n        _parseDateTimeInternal: parseDateTimeInternal\n    };\n\n    /*\n    * Microsecond support\n    */\n    if (!Date.prototype.getMicroseconds) {\n        Date.prototype.microseconds = 0;\n        Date.prototype.getMicroseconds = function () { return this.microseconds; };\n        Date.prototype.setMicroseconds = function (m) {\n            this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));\n            this.microseconds = m % 1000;\n            return this;\n        };\n    }\n\n    /*\n    * Keep up with the version\n    */\n    $.timepicker.version = \"1.6.3\";\n\n}));\n","jquery/jquery-ui-timepicker-addon.js":"/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20\n* http://trentrichardson.com/examples/timepicker\n* Copyright (c) 2016 Trent Richardson; Licensed MIT */\n(function (factory) {\n    if (typeof define === 'function' && define.amd) {\n        define(['jquery', 'jquery/ui'], factory);\n    } else {\n        factory(jQuery);\n    }\n}(function ($) {\n\n    /*\n    * Lets not redefine timepicker, Prevent \"Uncaught RangeError: Maximum call stack size exceeded\"\n    */\n    $.ui.timepicker = $.ui.timepicker || {};\n    if ($.ui.timepicker.version) {\n        return;\n    }\n\n    /*\n    * Extend jQueryUI, get it started with our version number\n    */\n    $.extend($.ui, {\n        timepicker: {\n            version: \"1.6.3\"\n        }\n    });\n\n    /*\n    * Timepicker manager.\n    * Use the singleton instance of this class, $.timepicker, to interact with the time picker.\n    * Settings for (groups of) time pickers are maintained in an instance object,\n    * allowing multiple different settings on the same page.\n    */\n    var Timepicker = function () {\n        this.regional = []; // Available regional settings, indexed by language code\n        this.regional[''] = { // Default regional settings\n            currentText: 'Now',\n            closeText: 'Done',\n            amNames: ['AM', 'A'],\n            pmNames: ['PM', 'P'],\n            timeFormat: 'HH:mm',\n            timeSuffix: '',\n            timeOnlyTitle: 'Choose Time',\n            timeText: 'Time',\n            hourText: 'Hour',\n            minuteText: 'Minute',\n            secondText: 'Second',\n            millisecText: 'Millisecond',\n            microsecText: 'Microsecond',\n            timezoneText: 'Time Zone',\n            isRTL: false\n        };\n        this._defaults = { // Global defaults for all the datetime picker instances\n            showButtonPanel: true,\n            timeOnly: false,\n            timeOnlyShowDate: false,\n            showHour: null,\n            showMinute: null,\n            showSecond: null,\n            showMillisec: null,\n            showMicrosec: null,\n            showTimezone: null,\n            showTime: true,\n            stepHour: 1,\n            stepMinute: 1,\n            stepSecond: 1,\n            stepMillisec: 1,\n            stepMicrosec: 1,\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null,\n            hourMin: 0,\n            minuteMin: 0,\n            secondMin: 0,\n            millisecMin: 0,\n            microsecMin: 0,\n            hourMax: 23,\n            minuteMax: 59,\n            secondMax: 59,\n            millisecMax: 999,\n            microsecMax: 999,\n            minDateTime: null,\n            maxDateTime: null,\n            maxTime: null,\n            minTime: null,\n            onSelect: null,\n            hourGrid: 0,\n            minuteGrid: 0,\n            secondGrid: 0,\n            millisecGrid: 0,\n            microsecGrid: 0,\n            alwaysSetTime: true,\n            separator: ' ',\n            altFieldTimeOnly: true,\n            altTimeFormat: null,\n            altSeparator: null,\n            altTimeSuffix: null,\n            altRedirectFocus: true,\n            pickerTimeFormat: null,\n            pickerTimeSuffix: null,\n            showTimepicker: true,\n            timezoneList: null,\n            addSliderAccess: false,\n            sliderAccessArgs: null,\n            controlType: 'slider',\n            oneLine: false,\n            defaultValue: null,\n            parse: 'strict',\n            afterInject: null\n        };\n        $.extend(this._defaults, this.regional['']);\n    };\n\n    $.extend(Timepicker.prototype, {\n        $input: null,\n        $altInput: null,\n        $timeObj: null,\n        inst: null,\n        hour_slider: null,\n        minute_slider: null,\n        second_slider: null,\n        millisec_slider: null,\n        microsec_slider: null,\n        timezone_select: null,\n        maxTime: null,\n        minTime: null,\n        hour: 0,\n        minute: 0,\n        second: 0,\n        millisec: 0,\n        microsec: 0,\n        timezone: null,\n        hourMinOriginal: null,\n        minuteMinOriginal: null,\n        secondMinOriginal: null,\n        millisecMinOriginal: null,\n        microsecMinOriginal: null,\n        hourMaxOriginal: null,\n        minuteMaxOriginal: null,\n        secondMaxOriginal: null,\n        millisecMaxOriginal: null,\n        microsecMaxOriginal: null,\n        ampm: '',\n        formattedDate: '',\n        formattedTime: '',\n        formattedDateTime: '',\n        timezoneList: null,\n        units: ['hour', 'minute', 'second', 'millisec', 'microsec'],\n        support: {},\n        control: null,\n\n        /*\n        * Override the default settings for all instances of the time picker.\n        * @param  {Object} settings  object - the new settings to use as defaults (anonymous object)\n        * @return {Object} the manager object\n        */\n        setDefaults: function (settings) {\n            extendRemove(this._defaults, settings || {});\n            return this;\n        },\n\n        /*\n        * Create a new Timepicker instance\n        */\n        _newInst: function ($input, opts) {\n            var tp_inst = new Timepicker(),\n                inlineSettings = {},\n                fns = {},\n                overrides, i;\n\n            for (var attrName in this._defaults) {\n                if (this._defaults.hasOwnProperty(attrName)) {\n                    var attrValue = $input.attr('time:' + attrName);\n                    if (attrValue) {\n                        try {\n                            inlineSettings[attrName] = eval(attrValue);\n                        } catch (err) {\n                            inlineSettings[attrName] = attrValue;\n                        }\n                    }\n                }\n            }\n\n            overrides = {\n                beforeShow: function (input, dp_inst) {\n                    if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {\n                        return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);\n                    }\n                },\n                onChangeMonthYear: function (year, month, dp_inst) {\n                    // Update the time as well : this prevents the time from disappearing from the $input field.\n                    // tp_inst._updateDateTime(dp_inst);\n                    if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {\n                        tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);\n                    }\n                },\n                onClose: function (dateText, dp_inst) {\n                    if (tp_inst.timeDefined === true && $input.val() !== '') {\n                        tp_inst._updateDateTime(dp_inst);\n                    }\n                    if ($.isFunction(tp_inst._defaults.evnts.onClose)) {\n                        tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);\n                    }\n                }\n            };\n            for (i in overrides) {\n                if (overrides.hasOwnProperty(i)) {\n                    fns[i] = opts[i] || this._defaults[i] || null;\n                }\n            }\n\n            tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {\n                evnts: fns,\n                timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');\n            });\n            tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {\n                return val.toUpperCase();\n            });\n            tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {\n                return val.toUpperCase();\n            });\n\n            // detect which units are supported\n            tp_inst.support = detectSupport(\n                tp_inst._defaults.timeFormat +\n                (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +\n                (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));\n\n            // controlType is string - key to our this._controls\n            if (typeof(tp_inst._defaults.controlType) === 'string') {\n                if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {\n                    tp_inst._defaults.controlType = 'select';\n                }\n                tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];\n            }\n            // controlType is an object and must implement create, options, value methods\n            else {\n                tp_inst.control = tp_inst._defaults.controlType;\n            }\n\n            // prep the timezone options\n            var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,\n                0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];\n            if (tp_inst._defaults.timezoneList !== null) {\n                timezoneList = tp_inst._defaults.timezoneList;\n            }\n            var tzl = timezoneList.length, tzi = 0, tzv = null;\n            if (tzl > 0 && typeof timezoneList[0] !== 'object') {\n                for (; tzi < tzl; tzi++) {\n                    tzv = timezoneList[tzi];\n                    timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };\n                }\n            }\n            tp_inst._defaults.timezoneList = timezoneList;\n\n            // set the default units\n            tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :\n                ((new Date()).getTimezoneOffset() * -1);\n            tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :\n                tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;\n            tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :\n                tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;\n            tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :\n                tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;\n            tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :\n                tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;\n            tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :\n                tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;\n            tp_inst.ampm = '';\n            tp_inst.$input = $input;\n\n            if (tp_inst._defaults.altField) {\n                tp_inst.$altInput = $(tp_inst._defaults.altField);\n                if (tp_inst._defaults.altRedirectFocus === true) {\n                    tp_inst.$altInput.css({\n                        cursor: 'pointer'\n                    }).focus(function () {\n                        $input.trigger(\"focus\");\n                    });\n                }\n            }\n\n            if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {\n                tp_inst._defaults.minDate = new Date();\n            }\n            if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {\n                tp_inst._defaults.maxDate = new Date();\n            }\n\n            // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..\n            if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {\n                tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());\n            }\n            if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {\n                tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());\n            }\n            if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {\n                tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());\n            }\n            if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {\n                tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());\n            }\n            tp_inst.$input.bind('focus', function () {\n                tp_inst._onFocus();\n            });\n\n            return tp_inst;\n        },\n\n        /*\n        * add our sliders to the calendar\n        */\n        _addTimePicker: function (dp_inst) {\n            var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val());\n\n            this.timeDefined = this._parseTime(currDT);\n            this._limitMinMaxDateTime(dp_inst, false);\n            this._injectTimePicker();\n            this._afterInject();\n        },\n\n        /*\n        * parse the time string from input value or _setTime\n        */\n        _parseTime: function (timeString, withDate) {\n            if (!this.inst) {\n                this.inst = $.datepicker._getInst(this.$input[0]);\n            }\n\n            if (withDate || !this._defaults.timeOnly) {\n                var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');\n                try {\n                    var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);\n                    if (!parseRes.timeObj) {\n                        return false;\n                    }\n                    $.extend(this, parseRes.timeObj);\n                } catch (err) {\n                    $.timepicker.log(\"Error parsing the date/time string: \" + err +\n                        \"\\ndate/time string = \" + timeString +\n                        \"\\ntimeFormat = \" + this._defaults.timeFormat +\n                        \"\\ndateFormat = \" + dp_dateFormat);\n                    return false;\n                }\n                return true;\n            } else {\n                var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);\n                if (!timeObj) {\n                    return false;\n                }\n                $.extend(this, timeObj);\n                return true;\n            }\n        },\n\n        /*\n        * Handle callback option after injecting timepicker\n        */\n        _afterInject: function() {\n            var o = this.inst.settings;\n            if ($.isFunction(o.afterInject)) {\n                o.afterInject.call(this);\n            }\n        },\n\n        /*\n        * generate and inject html for timepicker into ui datepicker\n        */\n        _injectTimePicker: function () {\n            var $dp = this.inst.dpDiv,\n                o = this.inst.settings,\n                tp_inst = this,\n                litem = '',\n                uitem = '',\n                show = null,\n                max = {},\n                gridSize = {},\n                size = null,\n                i = 0,\n                l = 0;\n\n            // Prevent displaying twice\n            if ($dp.find(\"div.ui-timepicker-div\").length === 0 && o.showTimepicker) {\n                var noDisplay = ' ui_tpicker_unit_hide',\n                    html = '<div class=\"ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + (o.oneLine && o.controlType === 'select' ? ' ui-timepicker-oneLine' : '') + '\"><dl>' + '<dt class=\"ui_tpicker_time_label' + ((o.showTime) ? '' : noDisplay) + '\">' + o.timeText + '</dt>' +\n                        '<dd class=\"ui_tpicker_time '+ ((o.showTime) ? '' : noDisplay) + '\"><input class=\"ui_tpicker_time_input\" ' + (o.timeInput ? '' : 'disabled') + '/></dd>';\n\n                // Create the markup\n                for (i = 0, l = this.units.length; i < l; i++) {\n                    litem = this.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // Added by Peter Medeiros:\n                    // - Figure out what the hour/minute/second max should be based on the step values.\n                    // - Example: if stepMinute is 15, then minMax is 45.\n                    max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);\n                    gridSize[litem] = 0;\n\n                    html += '<dt class=\"ui_tpicker_' + litem + '_label' + (show ? '' : noDisplay) + '\">' + o[litem + 'Text'] + '</dt>' +\n                        '<dd class=\"ui_tpicker_' + litem + (show ? '' : noDisplay) + '\"><div class=\"ui_tpicker_' + litem + '_slider' + (show ? '' : noDisplay) + '\"></div>';\n\n                    if (show && o[litem + 'Grid'] > 0) {\n                        html += '<div style=\"padding-left: 1px\"><table class=\"ui-tpicker-grid-label\"><tr>';\n\n                        if (litem === 'hour') {\n                            for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);\n                                html += '<td data-for=\"' + litem + '\">' + tmph + '</td>';\n                            }\n                        }\n                        else {\n                            for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {\n                                gridSize[litem]++;\n                                html += '<td data-for=\"' + litem + '\">' + ((m < 10) ? '0' : '') + m + '</td>';\n                            }\n                        }\n\n                        html += '</tr></table></div>';\n                    }\n                    html += '</dd>';\n                }\n\n                // Timezone\n                var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;\n                html += '<dt class=\"ui_tpicker_timezone_label' + (showTz ? '' : noDisplay) + '\">' + o.timezoneText + '</dt>';\n                html += '<dd class=\"ui_tpicker_timezone' + (showTz ? '' : noDisplay) + '\"></dd>';\n\n                // Create the elements from string\n                html += '</dl></div>';\n                var $tp = $(html);\n\n                // if we only want time picker...\n                if (o.timeOnly === true) {\n                    $tp.prepend('<div class=\"ui-widget-header ui-helper-clearfix ui-corner-all\">' + '<div class=\"ui-datepicker-title\">' + o.timeOnlyTitle + '</div>' + '</div>');\n                    $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();\n                }\n\n                // add sliders, adjust grids, add events\n                for (i = 0, l = tp_inst.units.length; i < l; i++) {\n                    litem = tp_inst.units[i];\n                    uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);\n                    show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];\n\n                    // add the slider\n                    tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);\n\n                    // adjust the grid and add click event\n                    if (show && o[litem + 'Grid'] > 0) {\n                        size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);\n                        $tp.find('.ui_tpicker_' + litem + ' table').css({\n                            width: size + \"%\",\n                            marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + \"%\"),\n                            marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + \"%\") : '0',\n                            borderCollapse: 'collapse'\n                        }).find(\"td\").click(function (e) {\n                            var $t = $(this),\n                                h = $t.html(),\n                                n = parseInt(h.replace(/[^0-9]/g), 10),\n                                ap = h.replace(/[^apm]/ig),\n                                f = $t.data('for'); // loses scope, so we use data-for\n\n                            if (f === 'hour') {\n                                if (ap.indexOf('p') !== -1 && n < 12) {\n                                    n += 12;\n                                }\n                                else {\n                                    if (ap.indexOf('a') !== -1 && n === 12) {\n                                        n = 0;\n                                    }\n                                }\n                            }\n\n                            tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);\n\n                            tp_inst._onTimeChange();\n                            tp_inst._onSelectHandler();\n                        }).css({\n                            cursor: 'pointer',\n                            width: (100 / gridSize[litem]) + '%',\n                            textAlign: 'center',\n                            overflow: 'hidden'\n                        });\n                    } // end if grid > 0\n                } // end for loop\n\n                // Add timezone options\n                this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find(\"select\");\n                $.fn.append.apply(this.timezone_select,\n                    $.map(o.timezoneList, function (val, idx) {\n                        return $(\"<option />\").val(typeof val === \"object\" ? val.value : val).text(typeof val === \"object\" ? val.label : val);\n                    }));\n                if (typeof(this.timezone) !== \"undefined\" && this.timezone !== null && this.timezone !== \"\") {\n                    var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;\n                    if (local_timezone === this.timezone) {\n                        selectLocalTimezone(tp_inst);\n                    } else {\n                        this.timezone_select.val(this.timezone);\n                    }\n                } else {\n                    if (typeof(this.hour) !== \"undefined\" && this.hour !== null && this.hour !== \"\") {\n                        this.timezone_select.val(o.timezone);\n                    } else {\n                        selectLocalTimezone(tp_inst);\n                    }\n                }\n                this.timezone_select.change(function () {\n                    tp_inst._onTimeChange();\n                    tp_inst._onSelectHandler();\n                    tp_inst._afterInject();\n                });\n                // End timezone options\n\n                // inject timepicker into datepicker\n                var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');\n                if ($buttonPanel.length) {\n                    $buttonPanel.before($tp);\n                } else {\n                    $dp.append($tp);\n                }\n\n                this.$timeObj = $tp.find('.ui_tpicker_time_input');\n                this.$timeObj.change(function () {\n                    var timeFormat = tp_inst.inst.settings.timeFormat;\n                    var parsedTime = $.datepicker.parseTime(timeFormat, this.value);\n                    var update = new Date();\n                    if (parsedTime) {\n                        update.setHours(parsedTime.hour);\n                        update.setMinutes(parsedTime.minute);\n                        update.setSeconds(parsedTime.second);\n                        $.datepicker._setTime(tp_inst.inst, update);\n                    } else {\n                        this.value = tp_inst.formattedTime;\n                        this.blur();\n                    }\n                });\n\n                if (this.inst !== null) {\n                    var timeDefined = this.timeDefined;\n                    this._onTimeChange();\n                    this.timeDefined = timeDefined;\n                }\n\n                // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/\n                if (this._defaults.addSliderAccess) {\n                    var sliderAccessArgs = this._defaults.sliderAccessArgs,\n                        rtl = this._defaults.isRTL;\n                    sliderAccessArgs.isRTL = rtl;\n\n                    setTimeout(function () { // fix for inline mode\n                        if ($tp.find('.ui-slider-access').length === 0) {\n                            $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);\n\n                            // fix any grids since sliders are shorter\n                            var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);\n                            if (sliderAccessWidth) {\n                                $tp.find('table:visible').each(function () {\n                                    var $g = $(this),\n                                        oldWidth = $g.outerWidth(),\n                                        oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),\n                                        newWidth = oldWidth - sliderAccessWidth,\n                                        newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',\n                                        css = { width: newWidth, marginRight: 0, marginLeft: 0 };\n                                    css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;\n                                    $g.css(css);\n                                });\n                            }\n                        }\n                    }, 10);\n                }\n                // end slideAccess integration\n\n                tp_inst._limitMinMaxDateTime(this.inst, true);\n            }\n        },\n\n        /*\n        * This function tries to limit the ability to go outside the\n        * min/max date range\n        */\n        _limitMinMaxDateTime: function (dp_inst, adjustSliders) {\n            var o = this._defaults,\n                dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);\n\n            if (!this._defaults.showTimepicker) {\n                return;\n            } // No time so nothing to check here\n\n            if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {\n                var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),\n                    minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {\n                    this.hourMinOriginal = o.hourMin;\n                    this.minuteMinOriginal = o.minuteMin;\n                    this.secondMinOriginal = o.secondMin;\n                    this.millisecMinOriginal = o.millisecMin;\n                    this.microsecMinOriginal = o.microsecMin;\n                }\n\n                if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMin = minDateTime.getHours();\n                    if (this.hour <= this._defaults.hourMin) {\n                        this.hour = this._defaults.hourMin;\n                        this._defaults.minuteMin = minDateTime.getMinutes();\n                        if (this.minute <= this._defaults.minuteMin) {\n                            this.minute = this._defaults.minuteMin;\n                            this._defaults.secondMin = minDateTime.getSeconds();\n                            if (this.second <= this._defaults.secondMin) {\n                                this.second = this._defaults.secondMin;\n                                this._defaults.millisecMin = minDateTime.getMilliseconds();\n                                if (this.millisec <= this._defaults.millisecMin) {\n                                    this.millisec = this._defaults.millisecMin;\n                                    this._defaults.microsecMin = minDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec < this._defaults.microsecMin) {\n                                        this.microsec = this._defaults.microsecMin;\n                                    }\n                                    this._defaults.microsecMin = this.microsecMinOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMin = this.millisecMinOriginal;\n                                this._defaults.microsecMin = this.microsecMinOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMin = this.secondMinOriginal;\n                            this._defaults.millisecMin = this.millisecMinOriginal;\n                            this._defaults.microsecMin = this.microsecMinOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMin = this.minuteMinOriginal;\n                        this._defaults.secondMin = this.secondMinOriginal;\n                        this._defaults.millisecMin = this.millisecMinOriginal;\n                        this._defaults.microsecMin = this.microsecMinOriginal;\n                    }\n                } else {\n                    this._defaults.hourMin = this.hourMinOriginal;\n                    this._defaults.minuteMin = this.minuteMinOriginal;\n                    this._defaults.secondMin = this.secondMinOriginal;\n                    this._defaults.millisecMin = this.millisecMinOriginal;\n                    this._defaults.microsecMin = this.microsecMinOriginal;\n                }\n            }\n\n            if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {\n                var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),\n                    maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);\n\n                if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {\n                    this.hourMaxOriginal = o.hourMax;\n                    this.minuteMaxOriginal = o.minuteMax;\n                    this.secondMaxOriginal = o.secondMax;\n                    this.millisecMaxOriginal = o.millisecMax;\n                    this.microsecMaxOriginal = o.microsecMax;\n                }\n\n                if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {\n                    this._defaults.hourMax = maxDateTime.getHours();\n                    if (this.hour >= this._defaults.hourMax) {\n                        this.hour = this._defaults.hourMax;\n                        this._defaults.minuteMax = maxDateTime.getMinutes();\n                        if (this.minute >= this._defaults.minuteMax) {\n                            this.minute = this._defaults.minuteMax;\n                            this._defaults.secondMax = maxDateTime.getSeconds();\n                            if (this.second >= this._defaults.secondMax) {\n                                this.second = this._defaults.secondMax;\n                                this._defaults.millisecMax = maxDateTime.getMilliseconds();\n                                if (this.millisec >= this._defaults.millisecMax) {\n                                    this.millisec = this._defaults.millisecMax;\n                                    this._defaults.microsecMax = maxDateTime.getMicroseconds();\n                                } else {\n                                    if (this.microsec > this._defaults.microsecMax) {\n                                        this.microsec = this._defaults.microsecMax;\n                                    }\n                                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                                }\n                            } else {\n                                this._defaults.millisecMax = this.millisecMaxOriginal;\n                                this._defaults.microsecMax = this.microsecMaxOriginal;\n                            }\n                        } else {\n                            this._defaults.secondMax = this.secondMaxOriginal;\n                            this._defaults.millisecMax = this.millisecMaxOriginal;\n                            this._defaults.microsecMax = this.microsecMaxOriginal;\n                        }\n                    } else {\n                        this._defaults.minuteMax = this.minuteMaxOriginal;\n                        this._defaults.secondMax = this.secondMaxOriginal;\n                        this._defaults.millisecMax = this.millisecMaxOriginal;\n                        this._defaults.microsecMax = this.microsecMaxOriginal;\n                    }\n                } else {\n                    this._defaults.hourMax = this.hourMaxOriginal;\n                    this._defaults.minuteMax = this.minuteMaxOriginal;\n                    this._defaults.secondMax = this.secondMaxOriginal;\n                    this._defaults.millisecMax = this.millisecMaxOriginal;\n                    this._defaults.microsecMax = this.microsecMaxOriginal;\n                }\n            }\n\n            if (dp_inst.settings.minTime!==null) {\n                var tempMinTime=new Date(\"01/01/1970 \" + dp_inst.settings.minTime);\n                if (this.hour<tempMinTime.getHours()) {\n                    this.hour=this._defaults.hourMin=tempMinTime.getHours();\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMin<tempMinTime.getHours()) {\n                        this._defaults.hourMin=tempMinTime.getHours();\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {\n                        this._defaults.minuteMin=tempMinTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMin=0;\n                    }\n                }\n            }\n\n            if (dp_inst.settings.maxTime!==null) {\n                var tempMaxTime=new Date(\"01/01/1970 \" + dp_inst.settings.maxTime);\n                if (this.hour>tempMaxTime.getHours()) {\n                    this.hour=this._defaults.hourMax=tempMaxTime.getHours();\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {\n                    this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();\n                } else {\n                    if (this._defaults.hourMax>tempMaxTime.getHours()) {\n                        this._defaults.hourMax=tempMaxTime.getHours();\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {\n                        this._defaults.minuteMax=tempMaxTime.getMinutes();\n                    } else {\n                        this._defaults.minuteMax=59;\n                    }\n                }\n            }\n\n            if (adjustSliders !== undefined && adjustSliders === true) {\n                var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),\n                    minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),\n                    secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),\n                    millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),\n                    microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);\n\n                if (this.hour_slider) {\n                    this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });\n                    this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));\n                }\n                if (this.minute_slider) {\n                    this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });\n                    this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));\n                }\n                if (this.second_slider) {\n                    this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });\n                    this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));\n                }\n                if (this.millisec_slider) {\n                    this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });\n                    this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));\n                }\n                if (this.microsec_slider) {\n                    this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });\n                    this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));\n                }\n            }\n\n        },\n\n        /*\n        * when a slider moves, set the internal time...\n        * on time change is also called when the time is updated in the text field\n        */\n        _onTimeChange: function () {\n            if (!this._defaults.showTimepicker) {\n                return;\n            }\n            var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,\n                minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,\n                second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,\n                millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,\n                microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,\n                timezone = (this.timezone_select) ? this.timezone_select.val() : false,\n                o = this._defaults,\n                pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,\n                pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;\n\n            if (typeof(hour) === 'object') {\n                hour = false;\n            }\n            if (typeof(minute) === 'object') {\n                minute = false;\n            }\n            if (typeof(second) === 'object') {\n                second = false;\n            }\n            if (typeof(millisec) === 'object') {\n                millisec = false;\n            }\n            if (typeof(microsec) === 'object') {\n                microsec = false;\n            }\n            if (typeof(timezone) === 'object') {\n                timezone = false;\n            }\n\n            if (hour !== false) {\n                hour = parseInt(hour, 10);\n            }\n            if (minute !== false) {\n                minute = parseInt(minute, 10);\n            }\n            if (second !== false) {\n                second = parseInt(second, 10);\n            }\n            if (millisec !== false) {\n                millisec = parseInt(millisec, 10);\n            }\n            if (microsec !== false) {\n                microsec = parseInt(microsec, 10);\n            }\n            if (timezone !== false) {\n                timezone = timezone.toString();\n            }\n\n            var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];\n\n            // If the update was done in the input field, the input field should not be updated.\n            // If the update was done using the sliders, update the input field.\n            var hasChanged = (\n                hour !== parseInt(this.hour,10) || // sliders should all be numeric\n                minute !== parseInt(this.minute,10) ||\n                second !== parseInt(this.second,10) ||\n                millisec !== parseInt(this.millisec,10) ||\n                microsec !== parseInt(this.microsec,10) ||\n                (this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) ||\n                (this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or \"EST\" format, so use toString()\n            );\n\n            if (hasChanged) {\n\n                if (hour !== false) {\n                    this.hour = hour;\n                }\n                if (minute !== false) {\n                    this.minute = minute;\n                }\n                if (second !== false) {\n                    this.second = second;\n                }\n                if (millisec !== false) {\n                    this.millisec = millisec;\n                }\n                if (microsec !== false) {\n                    this.microsec = microsec;\n                }\n                if (timezone !== false) {\n                    this.timezone = timezone;\n                }\n\n                if (!this.inst) {\n                    this.inst = $.datepicker._getInst(this.$input[0]);\n                }\n\n                this._limitMinMaxDateTime(this.inst, true);\n            }\n            if (this.support.ampm) {\n                this.ampm = ampm;\n            }\n\n            // Updates the time within the timepicker\n            this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);\n            if (this.$timeObj) {\n                if (pickerTimeFormat === o.timeFormat) {\n                    this.$timeObj.val(this.formattedTime + pickerTimeSuffix);\n                }\n                else {\n                    this.$timeObj.val($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);\n                }\n                if (this.$timeObj[0].setSelectionRange) {\n                    var sPos = this.$timeObj[0].selectionStart;\n                    var ePos = this.$timeObj[0].selectionEnd;\n                    this.$timeObj[0].setSelectionRange(sPos, ePos);\n                }\n            }\n\n            this.timeDefined = true;\n            if (hasChanged) {\n                this._updateDateTime();\n                //this.$input.focus(); // may automatically open the picker on setDate\n            }\n        },\n\n        /*\n        * call custom onSelect.\n        * bind to sliders slidestop, and grid click.\n        */\n        _onSelectHandler: function () {\n            var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;\n            var inputEl = this.$input ? this.$input[0] : null;\n            if (onSelect && inputEl) {\n                onSelect.apply(inputEl, [this.formattedDateTime, this]);\n            }\n        },\n\n        /*\n        * update our input with the new date time..\n        */\n        _updateDateTime: function (dp_inst) {\n            dp_inst = this.inst || dp_inst;\n            var dtTmp = (dp_inst.currentYear > 0?\n                new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :\n                new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                dt = $.datepicker._daylightSavingAdjust(dtTmp),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),\n                //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),\n                dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),\n                formatCfg = $.datepicker._getFormatConfig(dp_inst),\n                timeAvailable = dt !== null && this.timeDefined;\n            this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);\n            var formattedDateTime = this.formattedDate;\n\n            // if a slider was changed but datepicker doesn't have a value yet, set it\n            if (dp_inst.lastVal === \"\") {\n                dp_inst.currentYear = dp_inst.selectedYear;\n                dp_inst.currentMonth = dp_inst.selectedMonth;\n                dp_inst.currentDay = dp_inst.selectedDay;\n            }\n\n            /*\n            * remove following lines to force every changes in date picker to change the input value\n            * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.\n            * If the user manually empty the value in the input field, the date picker will never change selected value.\n            */\n            //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {\n            //\treturn;\n            //}\n\n            if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {\n                formattedDateTime = this.formattedTime;\n            } else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {\n                formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;\n            }\n\n            this.formattedDateTime = formattedDateTime;\n\n            if (!this._defaults.showTimepicker) {\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {\n                this.$altInput.val(this.formattedTime);\n                this.$input.val(this.formattedDate);\n            } else if (this.$altInput) {\n                this.$input.val(formattedDateTime);\n                var altFormattedDateTime = '',\n                    altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,\n                    altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;\n\n                if (!this._defaults.timeOnly) {\n                    if (this._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);\n                    }\n                    else {\n                        altFormattedDateTime = this.formattedDate;\n                    }\n\n                    if (altFormattedDateTime) {\n                        altFormattedDateTime += altSeparator;\n                    }\n                }\n\n                if (this._defaults.altTimeFormat !== null) {\n                    altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;\n                }\n                else {\n                    altFormattedDateTime += this.formattedTime + altTimeSuffix;\n                }\n                this.$altInput.val(altFormattedDateTime);\n            } else {\n                this.$input.val(formattedDateTime);\n            }\n\n            this.$input.trigger(\"change\");\n        },\n\n        _onFocus: function () {\n            if (!this.$input.val() && this._defaults.defaultValue) {\n                this.$input.val(this._defaults.defaultValue);\n                var inst = $.datepicker._getInst(this.$input.get(0)),\n                    tp_inst = $.datepicker._get(inst, 'timepicker');\n                if (tp_inst) {\n                    if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                        try {\n                            $.datepicker._updateDatepicker(inst);\n                        } catch (err) {\n                            $.timepicker.log(err);\n                        }\n                    }\n                }\n            }\n        },\n\n        /*\n        * Small abstraction to control types\n        * We can add more, just be sure to follow the pattern: create, options, value\n        */\n        _controls: {\n            // slider methods\n            slider: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60\n                    return obj.prop('slide', null).slider({\n                        orientation: \"horizontal\",\n                        value: rtl ? val * -1 : val,\n                        min: rtl ? max * -1 : min,\n                        max: rtl ? min * -1 : max,\n                        step: step,\n                        slide: function (event, ui) {\n                            tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);\n                            tp_inst._onTimeChange();\n                        },\n                        stop: function (event, ui) {\n                            tp_inst._onSelectHandler();\n                        }\n                    });\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (typeof(opts) === 'string') {\n                            if (opts === 'min' || opts === 'max') {\n                                if (val !== undefined) {\n                                    return obj.slider(opts, val * -1);\n                                }\n                                return Math.abs(obj.slider(opts));\n                            }\n                            return obj.slider(opts);\n                        }\n                        var min = opts.min,\n                            max = opts.max;\n                        opts.min = opts.max = null;\n                        if (min !== undefined) {\n                            opts.max = min * -1;\n                        }\n                        if (max !== undefined) {\n                            opts.min = max * -1;\n                        }\n                        return obj.slider(opts);\n                    }\n                    if (typeof(opts) === 'string' && val !== undefined) {\n                        return obj.slider(opts, val);\n                    }\n                    return obj.slider(opts);\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    if (tp_inst._defaults.isRTL) {\n                        if (val !== undefined) {\n                            return obj.slider('value', val * -1);\n                        }\n                        return Math.abs(obj.slider('value'));\n                    }\n                    if (val !== undefined) {\n                        return obj.slider('value', val);\n                    }\n                    return obj.slider('value');\n                }\n            },\n            // select methods\n            select: {\n                create: function (tp_inst, obj, unit, val, min, max, step) {\n                    var sel = '<select class=\"ui-timepicker-select ui-state-default ui-corner-all\" data-unit=\"' + unit + '\" data-min=\"' + min + '\" data-max=\"' + max + '\" data-step=\"' + step + '\">',\n                        format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;\n\n                    for (var i = min; i <= max; i += step) {\n                        sel += '<option value=\"' + i + '\"' + (i === val ? ' selected' : '') + '>';\n                        if (unit === 'hour') {\n                            sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);\n                        }\n                        else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }\n                        else {sel += '0' + i.toString(); }\n                        sel += '</option>';\n                    }\n                    sel += '</select>';\n\n                    obj.children('select').remove();\n\n                    $(sel).appendTo(obj).change(function (e) {\n                        tp_inst._onTimeChange();\n                        tp_inst._onSelectHandler();\n                        tp_inst._afterInject();\n                    });\n\n                    return obj;\n                },\n                options: function (tp_inst, obj, unit, opts, val) {\n                    var o = {},\n                        $t = obj.children('select');\n                    if (typeof(opts) === 'string') {\n                        if (val === undefined) {\n                            return $t.data(opts);\n                        }\n                        o[opts] = val;\n                    }\n                    else { o = opts; }\n                    return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min>=0 ? o.min : $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));\n                },\n                value: function (tp_inst, obj, unit, val) {\n                    var $t = obj.children('select');\n                    if (val !== undefined) {\n                        return $t.val(val);\n                    }\n                    return $t.val();\n                }\n            }\n        } // end _controls\n\n    });\n\n    $.fn.extend({\n        /*\n        * shorthand just to use timepicker.\n        */\n        timepicker: function (o) {\n            o = o || {};\n            var tmp_args = Array.prototype.slice.call(arguments);\n\n            if (typeof o === 'object') {\n                tmp_args[0] = $.extend(o, {\n                    timeOnly: true\n                });\n            }\n\n            return $(this).each(function () {\n                $.fn.datetimepicker.apply($(this), tmp_args);\n            });\n        },\n\n        /*\n        * extend timepicker to datepicker\n        */\n        datetimepicker: function (o) {\n            o = o || {};\n            var tmp_args = arguments;\n\n            if (typeof(o) === 'string') {\n                if (o === 'getDate'  || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {\n                    return $.fn.datepicker.apply($(this[0]), tmp_args);\n                } else {\n                    return this.each(function () {\n                        var $t = $(this);\n                        $t.datepicker.apply($t, tmp_args);\n                    });\n                }\n            } else {\n                return this.each(function () {\n                    var $t = $(this);\n                    $t.datepicker($.timepicker._newInst($t, o)._defaults);\n                });\n            }\n        }\n    });\n\n    /*\n    * Public Utility to parse date and time\n    */\n    $.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);\n        if (parseRes.timeObj) {\n            var t = parseRes.timeObj;\n            parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);\n            parseRes.date.setMicroseconds(t.microsec);\n        }\n\n        return parseRes.date;\n    };\n\n    /*\n    * Public utility to parse time\n    */\n    $.datepicker.parseTime = function (timeFormat, timeString, options) {\n        var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),\n            iso8601 = (timeFormat.replace(/\\'.*?\\'/g, '').indexOf('Z') !== -1);\n\n        // Strict parse requires the timeString to match the timeFormat exactly\n        var strictParse = function (f, s, o) {\n\n            // pattern for standard and localized AM/PM markers\n            var getPatternAmpm = function (amNames, pmNames) {\n                var markers = [];\n                if (amNames) {\n                    $.merge(markers, amNames);\n                }\n                if (pmNames) {\n                    $.merge(markers, pmNames);\n                }\n                markers = $.map(markers, function (val) {\n                    return val.replace(/[.*+?|()\\[\\]{}\\\\]/g, '\\\\$&');\n                });\n                return '(' + markers.join('|') + ')?';\n            };\n\n            // figure out position of time elements.. cause js cant do named captures\n            var getFormatPositions = function (timeFormat) {\n                var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),\n                    orders = {\n                        h: -1,\n                        m: -1,\n                        s: -1,\n                        l: -1,\n                        c: -1,\n                        t: -1,\n                        z: -1\n                    };\n\n                if (finds) {\n                    for (var i = 0; i < finds.length; i++) {\n                        if (orders[finds[i].toString().charAt(0)] === -1) {\n                            orders[finds[i].toString().charAt(0)] = i + 1;\n                        }\n                    }\n                }\n                return orders;\n            };\n\n            var regstr = '^' + f.toString()\n                    .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n                        var ml = match.length;\n                        switch (match.charAt(0).toLowerCase()) {\n                            case 'h':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'm':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 's':\n                                return ml === 1 ? '(\\\\d?\\\\d)' : '(\\\\d{' + ml + '})';\n                            case 'l':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'c':\n                                return '(\\\\d?\\\\d?\\\\d)';\n                            case 'z':\n                                return '(z|[-+]\\\\d\\\\d:?\\\\d\\\\d|\\\\S+)?';\n                            case 't':\n                                return getPatternAmpm(o.amNames, o.pmNames);\n                            default:    // literal escaped in quotes\n                                return '(' + match.replace(/\\'/g, \"\").replace(/(\\.|\\$|\\^|\\\\|\\/|\\(|\\)|\\[|\\]|\\?|\\+|\\*)/g, function (m) { return \"\\\\\" + m; }) + ')?';\n                        }\n                    })\n                    .replace(/\\s/g, '\\\\s?') +\n                o.timeSuffix + '$',\n                order = getFormatPositions(f),\n                ampm = '',\n                treg;\n\n            treg = s.match(new RegExp(regstr, 'i'));\n\n            var resTime = {\n                hour: 0,\n                minute: 0,\n                second: 0,\n                millisec: 0,\n                microsec: 0\n            };\n\n            if (treg) {\n                if (order.t !== -1) {\n                    if (treg[order.t] === undefined || treg[order.t].length === 0) {\n                        ampm = '';\n                        resTime.ampm = '';\n                    } else {\n                        ampm = $.inArray(treg[order.t].toUpperCase(), $.map(o.amNames, function (x,i) { return x.toUpperCase(); })) !== -1 ? 'AM' : 'PM';\n                        resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];\n                    }\n                }\n\n                if (order.h !== -1) {\n                    if (ampm === 'AM' && treg[order.h] === '12') {\n                        resTime.hour = 0; // 12am = 0 hour\n                    } else {\n                        if (ampm === 'PM' && treg[order.h] !== '12') {\n                            resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12\n                        } else {\n                            resTime.hour = Number(treg[order.h]);\n                        }\n                    }\n                }\n\n                if (order.m !== -1) {\n                    resTime.minute = Number(treg[order.m]);\n                }\n                if (order.s !== -1) {\n                    resTime.second = Number(treg[order.s]);\n                }\n                if (order.l !== -1) {\n                    resTime.millisec = Number(treg[order.l]);\n                }\n                if (order.c !== -1) {\n                    resTime.microsec = Number(treg[order.c]);\n                }\n                if (order.z !== -1 && treg[order.z] !== undefined) {\n                    resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);\n                }\n\n\n                return resTime;\n            }\n            return false;\n        };// end strictParse\n\n        // First try JS Date, if that fails, use strictParse\n        var looseParse = function (f, s, o) {\n            try {\n                var d = new Date('2012-01-01 ' + s);\n                if (isNaN(d.getTime())) {\n                    d = new Date('2012-01-01T' + s);\n                    if (isNaN(d.getTime())) {\n                        d = new Date('01/01/2012 ' + s);\n                        if (isNaN(d.getTime())) {\n                            throw \"Unable to parse time with native Date: \" + s;\n                        }\n                    }\n                }\n\n                return {\n                    hour: d.getHours(),\n                    minute: d.getMinutes(),\n                    second: d.getSeconds(),\n                    millisec: d.getMilliseconds(),\n                    microsec: d.getMicroseconds(),\n                    timezone: d.getTimezoneOffset() * -1\n                };\n            }\n            catch (err) {\n                try {\n                    return strictParse(f, s, o);\n                }\n                catch (err2) {\n                    $.timepicker.log(\"Unable to parse \\ntimeString: \" + s + \"\\ntimeFormat: \" + f);\n                }\n            }\n            return false;\n        }; // end looseParse\n\n        if (typeof o.parse === \"function\") {\n            return o.parse(timeFormat, timeString, o);\n        }\n        if (o.parse === 'loose') {\n            return looseParse(timeFormat, timeString, o);\n        }\n        return strictParse(timeFormat, timeString, o);\n    };\n\n    /**\n     * Public utility to format the time\n     * @param {string} format format of the time\n     * @param {Object} time Object not a Date for timezones\n     * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm\n     * @returns {string} the formatted time\n     */\n    $.datepicker.formatTime = function (format, time, options) {\n        options = options || {};\n        options = $.extend({}, $.timepicker._defaults, options);\n        time = $.extend({\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisec: 0,\n            microsec: 0,\n            timezone: null\n        }, time);\n\n        var tmptime = format,\n            ampmName = options.amNames[0],\n            hour = parseInt(time.hour, 10);\n\n        if (hour > 11) {\n            ampmName = options.pmNames[0];\n        }\n\n        tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {\n            switch (match) {\n                case 'HH':\n                    return ('0' + hour).slice(-2);\n                case 'H':\n                    return hour;\n                case 'hh':\n                    return ('0' + convert24to12(hour)).slice(-2);\n                case 'h':\n                    return convert24to12(hour);\n                case 'mm':\n                    return ('0' + time.minute).slice(-2);\n                case 'm':\n                    return time.minute;\n                case 'ss':\n                    return ('0' + time.second).slice(-2);\n                case 's':\n                    return time.second;\n                case 'l':\n                    return ('00' + time.millisec).slice(-3);\n                case 'c':\n                    return ('00' + time.microsec).slice(-3);\n                case 'z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);\n                case 'Z':\n                    return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);\n                case 'T':\n                    return ampmName.charAt(0).toUpperCase();\n                case 'TT':\n                    return ampmName.toUpperCase();\n                case 't':\n                    return ampmName.charAt(0).toLowerCase();\n                case 'tt':\n                    return ampmName.toLowerCase();\n                default:\n                    return match.replace(/'/g, \"\");\n            }\n        });\n\n        return tmptime;\n    };\n\n    /*\n    * the bad hack :/ override datepicker so it doesn't close on select\n    // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378\n    */\n    $.datepicker._base_selectDate = $.datepicker._selectDate;\n    $.datepicker._selectDate = function (id, dateStr) {\n        var inst = this._getInst($(id)[0]),\n            tp_inst = this._get(inst, 'timepicker'),\n            was_inline;\n\n        if (tp_inst && inst.settings.showTimepicker) {\n            tp_inst._limitMinMaxDateTime(inst, true);\n            was_inline = inst.inline;\n            inst.inline = inst.stay_open = true;\n            //This way the onSelect handler called from calendarpicker get the full dateTime\n            this._base_selectDate(id, dateStr);\n            inst.inline = was_inline;\n            inst.stay_open = false;\n            this._notifyChange(inst);\n            this._updateDatepicker(inst);\n        } else {\n            this._base_selectDate(id, dateStr);\n        }\n    };\n\n    /*\n    * second bad hack :/ override datepicker so it triggers an event when changing the input field\n    * and does not redraw the datepicker on every selectDate event\n    */\n    $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;\n    $.datepicker._updateDatepicker = function (inst) {\n\n        // don't popup the datepicker if there is another instance already opened\n        var input = inst.input[0];\n        if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {\n            return;\n        }\n\n        if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {\n\n            this._base_updateDatepicker(inst);\n\n            // Reload the time control when changing something in the input text field.\n            var tp_inst = this._get(inst, 'timepicker');\n            if (tp_inst) {\n                tp_inst._addTimePicker(inst);\n            }\n        }\n    };\n\n    /*\n    * third bad hack :/ override datepicker so it allows spaces and colon in the input field\n    */\n    $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;\n    $.datepicker._doKeyPress = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if ($.datepicker._get(inst, 'constrainInput')) {\n                var ampm = tp_inst.support.ampm,\n                    tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,\n                    dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),\n                    datetimeChars = tp_inst._defaults.timeFormat.toString()\n                            .replace(/[hms]/g, '')\n                            .replace(/TT/g, ampm ? 'APM' : '')\n                            .replace(/Tt/g, ampm ? 'AaPpMm' : '')\n                            .replace(/tT/g, ampm ? 'AaPpMm' : '')\n                            .replace(/T/g, ampm ? 'AP' : '')\n                            .replace(/tt/g, ampm ? 'apm' : '')\n                            .replace(/t/g, ampm ? 'ap' : '') +\n                        \" \" + tp_inst._defaults.separator +\n                        tp_inst._defaults.timeSuffix +\n                        (tz ? tp_inst._defaults.timezoneList.join('') : '') +\n                        (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +\n                        dateChars,\n                    chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);\n                return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);\n            }\n        }\n\n        return $.datepicker._base_doKeyPress(event);\n    };\n\n    /*\n    * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField\n    * Update any alternate field to synchronise with the main field.\n    */\n    $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;\n    $.datepicker._updateAlternate = function (inst) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var altField = tp_inst._defaults.altField;\n            if (altField) { // update alternate field too\n                var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,\n                    date = this._getDate(inst),\n                    formatCfg = $.datepicker._getFormatConfig(inst),\n                    altFormattedDateTime = '',\n                    altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,\n                    altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,\n                    altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;\n\n                altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;\n                if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {\n                    if (tp_inst._defaults.altFormat) {\n                        altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;\n                    }\n                    else {\n                        altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;\n                    }\n                }\n                $(altField).val( inst.input.val() ? altFormattedDateTime : \"\");\n            }\n        }\n        else {\n            $.datepicker._base_updateAlternate(inst);\n        }\n    };\n\n    /*\n    * Override key up event to sync manual input changes.\n    */\n    $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;\n    $.datepicker._doKeyUp = function (event) {\n        var inst = $.datepicker._getInst(event.target),\n            tp_inst = $.datepicker._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {\n                try {\n                    $.datepicker._updateDatepicker(inst);\n                } catch (err) {\n                    $.timepicker.log(err);\n                }\n            }\n        }\n\n        return $.datepicker._base_doKeyUp(event);\n    };\n\n    /*\n    * override \"Today\" button to also grab the time and set it to input field.\n    */\n    $.datepicker._base_gotoToday = $.datepicker._gotoToday;\n    $.datepicker._gotoToday = function (id) {\n        var inst = this._getInst($(id)[0]);\n        this._base_gotoToday(id);\n        var tp_inst = this._get(inst, 'timepicker');\n        if (!tp_inst) {\n            return;\n        }\n\n        var tzoffset = $.timepicker.timezoneOffsetNumber(tp_inst.timezone);\n        var now = new Date();\n        now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + parseInt(tzoffset, 10));\n        this._setTime(inst, now);\n        this._setDate(inst, now);\n        tp_inst._onSelectHandler();\n    };\n\n    /*\n    * Disable & enable the Time in the datetimepicker\n    */\n    $.datepicker._disableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = false;\n            tp_inst._defaults.showTimepicker = false;\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    $.datepicker._enableTimepickerDatepicker = function (target) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        $(target).datepicker('getDate'); // Init selected[Year|Month|Day]\n        if (tp_inst) {\n            inst.settings.showTimepicker = true;\n            tp_inst._defaults.showTimepicker = true;\n            tp_inst._addTimePicker(inst); // Could be disabled on page load\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create our own set time function\n    */\n    $.datepicker._setTime = function (inst, date) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var defaults = tp_inst._defaults;\n\n            // calling _setTime with no date sets time to defaults\n            tp_inst.hour = date ? date.getHours() : defaults.hour;\n            tp_inst.minute = date ? date.getMinutes() : defaults.minute;\n            tp_inst.second = date ? date.getSeconds() : defaults.second;\n            tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;\n            tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;\n\n            //check if within min/max times..\n            tp_inst._limitMinMaxDateTime(inst, true);\n\n            tp_inst._onTimeChange();\n            tp_inst._updateDateTime(inst);\n        }\n    };\n\n    /*\n    * Create new public method to set only time, callable as $().datepicker('setTime', date)\n    */\n    $.datepicker._setTimeDatepicker = function (target, date, withDate) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            this._setDateFromField(inst);\n            var tp_date;\n            if (date) {\n                if (typeof date === \"string\") {\n                    tp_inst._parseTime(date, withDate);\n                    tp_date = new Date();\n                    tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                    tp_date.setMicroseconds(tp_inst.microsec);\n                } else {\n                    tp_date = new Date(date.getTime());\n                    tp_date.setMicroseconds(date.getMicroseconds());\n                }\n                if (tp_date.toString() === 'Invalid Date') {\n                    tp_date = undefined;\n                }\n                this._setTime(inst, tp_date);\n            }\n        }\n\n    };\n\n    /*\n    * override setDate() to allow setting time too within Date object\n    */\n    $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;\n    $.datepicker._setDateDatepicker = function (target, _date) {\n        var inst = this._getInst(target);\n        var date = _date;\n        if (!inst) {\n            return;\n        }\n\n        if (typeof(_date) === 'string') {\n            date = new Date(_date);\n            if (!date.getTime()) {\n                this._base_setDateDatepicker.apply(this, arguments);\n                date = $(target).datepicker('getDate');\n            }\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        var tp_date;\n        if (date instanceof Date) {\n            tp_date = new Date(date.getTime());\n            tp_date.setMicroseconds(date.getMicroseconds());\n        } else {\n            tp_date = date;\n        }\n\n        // This is important if you are using the timezone option, javascript's Date\n        // object will only return the timezone offset for the current locale, so we\n        // adjust it accordingly.  If not using timezone option this won't matter..\n        // If a timezone is different in tp, keep the timezone as is\n        if (tp_inst && tp_date) {\n            // look out for DST if tz wasn't specified\n            if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                tp_inst.timezone = tp_date.getTimezoneOffset() * -1;\n            }\n            date = $.timepicker.timezoneAdjust(date, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()), tp_inst.timezone);\n            tp_date = $.timepicker.timezoneAdjust(tp_date, $.timepicker.timezoneOffsetString(-tp_date.getTimezoneOffset()), tp_inst.timezone);\n        }\n\n        this._updateDatepicker(inst);\n        this._base_setDateDatepicker.apply(this, arguments);\n        this._setTimeDatepicker(target, tp_date, true);\n    };\n\n    /*\n    * override getDate() to allow getting time too within Date object\n    */\n    $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;\n    $.datepicker._getDateDatepicker = function (target, noDefault) {\n        var inst = this._getInst(target);\n        if (!inst) {\n            return;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n\n        if (tp_inst) {\n            // if it hasn't yet been defined, grab from field\n            if (inst.lastVal === undefined) {\n                this._setDateFromField(inst, noDefault);\n            }\n\n            var date = this._getDate(inst);\n\n            var currDT = null;\n\n            if (tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly) {\n                currDT = tp_inst.$input.val() + ' ' + tp_inst.$altInput.val();\n            }\n            else if (tp_inst.$input.get(0).tagName !== 'INPUT' && tp_inst.$altInput) {\n                /**\n                 * in case the datetimepicker has been applied to a non-input tag for inline UI,\n                 * and the user has not configured the plugin to display only time in altInput,\n                 * pick current date time from the altInput (and hope for the best, for now, until \"ER1\" is applied)\n                 *\n                 * @todo ER1. Since altInput can have a totally difference format, convert it to standard format by reading input format from \"altFormat\" and \"altTimeFormat\" option values\n                 */\n                currDT = tp_inst.$altInput.val();\n            }\n            else {\n                currDT = tp_inst.$input.val();\n            }\n\n            if (date && tp_inst._parseTime(currDT, !inst.settings.timeOnly)) {\n                date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);\n                date.setMicroseconds(tp_inst.microsec);\n\n                // This is important if you are using the timezone option, javascript's Date\n                // object will only return the timezone offset for the current locale, so we\n                // adjust it accordingly.  If not using timezone option this won't matter..\n                if (tp_inst.timezone != null) {\n                    // look out for DST if tz wasn't specified\n                    if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {\n                        tp_inst.timezone = date.getTimezoneOffset() * -1;\n                    }\n                    date = $.timepicker.timezoneAdjust(date, tp_inst.timezone, $.timepicker.timezoneOffsetString(-date.getTimezoneOffset()));\n                }\n            }\n            return date;\n        }\n        return this._base_getDateDatepicker(target, noDefault);\n    };\n\n    /*\n    * override parseDate() because UI 1.8.14 throws an error about \"Extra characters\"\n    * An option in datapicker to ignore extra format characters would be nicer.\n    */\n    $.datepicker._base_parseDate = $.datepicker.parseDate;\n    $.datepicker.parseDate = function (format, value, settings) {\n        var date;\n        try {\n            date = this._base_parseDate(format, value, settings);\n        } catch (err) {\n            // Hack!  The error message ends with a colon, a space, and\n            // the \"extra\" characters.  We rely on that instead of\n            // attempting to perfectly reproduce the parsing algorithm.\n            if (err.indexOf(\":\") >= 0) {\n                date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);\n                $.timepicker.log(\"Error parsing the date string: \" + err + \"\\ndate string = \" + value + \"\\ndate format = \" + format);\n            } else {\n                throw err;\n            }\n        }\n        return date;\n    };\n\n    /*\n    * override formatDate to set date with time to the input\n    */\n    $.datepicker._base_formatDate = $.datepicker._formatDate;\n    $.datepicker._formatDate = function (inst, day, month, year) {\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            tp_inst._updateDateTime(inst);\n            return tp_inst.$input.val();\n        }\n        return this._base_formatDate(inst);\n    };\n\n    /*\n    * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate\n    */\n    $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;\n    $.datepicker._optionDatepicker = function (target, name, value) {\n        var inst = this._getInst(target),\n            name_clone;\n        if (!inst) {\n            return null;\n        }\n\n        var tp_inst = this._get(inst, 'timepicker');\n        if (tp_inst) {\n            var min = null,\n                max = null,\n                onselect = null,\n                overrides = tp_inst._defaults.evnts,\n                fns = {},\n                prop,\n                ret,\n                oldVal,\n                $target;\n            if (typeof name === 'string') { // if min/max was set with the string\n                if (name === 'minDate' || name === 'minDateTime') {\n                    min = value;\n                } else if (name === 'maxDate' || name === 'maxDateTime') {\n                    max = value;\n                } else if (name === 'onSelect') {\n                    onselect = value;\n                } else if (overrides.hasOwnProperty(name)) {\n                    if (typeof (value) === 'undefined') {\n                        return overrides[name];\n                    }\n                    fns[name] = value;\n                    name_clone = {}; //empty results in exiting function after overrides updated\n                }\n            } else if (typeof name === 'object') { //if min/max was set with the JSON\n                if (name.minDate) {\n                    min = name.minDate;\n                } else if (name.minDateTime) {\n                    min = name.minDateTime;\n                } else if (name.maxDate) {\n                    max = name.maxDate;\n                } else if (name.maxDateTime) {\n                    max = name.maxDateTime;\n                }\n                for (prop in overrides) {\n                    if (overrides.hasOwnProperty(prop) && name[prop]) {\n                        fns[prop] = name[prop];\n                    }\n                }\n            }\n            for (prop in fns) {\n                if (fns.hasOwnProperty(prop)) {\n                    overrides[prop] = fns[prop];\n                    if (!name_clone) { name_clone = $.extend({}, name); }\n                    delete name_clone[prop];\n                }\n            }\n            if (name_clone && isEmptyObject(name_clone)) { return; }\n            if (min) { //if min was set\n                if (min === 0) {\n                    min = new Date();\n                } else {\n                    min = new Date(min);\n                }\n                tp_inst._defaults.minDate = min;\n                tp_inst._defaults.minDateTime = min;\n            } else if (max) { //if max was set\n                if (max === 0) {\n                    max = new Date();\n                } else {\n                    max = new Date(max);\n                }\n                tp_inst._defaults.maxDate = max;\n                tp_inst._defaults.maxDateTime = max;\n            } else if (onselect) {\n                tp_inst._defaults.onSelect = onselect;\n            }\n\n            // Datepicker will override our date when we call _base_optionDatepicker when\n            // calling minDate/maxDate, so we will first grab the value, call\n            // _base_optionDatepicker, then set our value back.\n            if(min || max){\n                $target = $(target);\n                oldVal = $target.datetimepicker('getDate');\n                ret = this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n                $target.datetimepicker('setDate', oldVal);\n                return ret;\n            }\n        }\n        if (value === undefined) {\n            return this._base_optionDatepicker.call($.datepicker, target, name);\n        }\n        return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);\n    };\n\n    /*\n    * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,\n    * it will return false for all objects\n    */\n    var isEmptyObject = function (obj) {\n        var prop;\n        for (prop in obj) {\n            if (obj.hasOwnProperty(prop)) {\n                return false;\n            }\n        }\n        return true;\n    };\n\n    /*\n    * jQuery extend now ignores nulls!\n    */\n    var extendRemove = function (target, props) {\n        $.extend(target, props);\n        for (var name in props) {\n            if (props[name] === null || props[name] === undefined) {\n                target[name] = props[name];\n            }\n        }\n        return target;\n    };\n\n    /*\n    * Determine by the time format which units are supported\n    * Returns an object of booleans for each unit\n    */\n    var detectSupport = function (timeFormat) {\n        var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals\n            isIn = function (f, t) { // does the format contain the token?\n                return f.indexOf(t) !== -1 ? true : false;\n            };\n        return {\n            hour: isIn(tf, 'h'),\n            minute: isIn(tf, 'm'),\n            second: isIn(tf, 's'),\n            millisec: isIn(tf, 'l'),\n            microsec: isIn(tf, 'c'),\n            timezone: isIn(tf, 'z'),\n            ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),\n            iso8601: isIn(timeFormat, 'Z')\n        };\n    };\n\n    /*\n    * Converts 24 hour format into 12 hour\n    * Returns 12 hour without leading 0\n    */\n    var convert24to12 = function (hour) {\n        hour %= 12;\n\n        if (hour === 0) {\n            hour = 12;\n        }\n\n        return String(hour);\n    };\n\n    var computeEffectiveSetting = function (settings, property) {\n        return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];\n    };\n\n    /*\n    * Splits datetime string into date and time substrings.\n    * Throws exception when date can't be parsed\n    * Returns {dateString: dateString, timeString: timeString}\n    */\n    var splitDateTime = function (dateTimeString, timeSettings) {\n        // The idea is to get the number separator occurrences in datetime and the time format requested (since time has\n        // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.\n        var separator = computeEffectiveSetting(timeSettings, 'separator'),\n            format = computeEffectiveSetting(timeSettings, 'timeFormat'),\n            timeParts = format.split(separator), // how many occurrences of separator may be in our format?\n            timePartsLen = timeParts.length,\n            allParts = dateTimeString.split(separator),\n            allPartsLen = allParts.length;\n\n        if (allPartsLen > 1) {\n            return {\n                dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),\n                timeString: allParts.splice(0, timePartsLen).join(separator)\n            };\n        }\n\n        return {\n            dateString: dateTimeString,\n            timeString: ''\n        };\n    };\n\n    /*\n    * Internal function to parse datetime interval\n    * Returns: {date: Date, timeObj: Object}, where\n    *   date - parsed date without time (type Date)\n    *   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional\n    */\n    var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {\n        var date,\n            parts,\n            parsedTime;\n\n        parts = splitDateTime(dateTimeString, timeSettings);\n        date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);\n\n        if (parts.timeString === '') {\n            return {\n                date: date\n            };\n        }\n\n        parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);\n\n        if (!parsedTime) {\n            throw 'Wrong time format';\n        }\n\n        return {\n            date: date,\n            timeObj: parsedTime\n        };\n    };\n\n    /*\n    * Internal function to set timezone_select to the local timezone\n    */\n    var selectLocalTimezone = function (tp_inst, date) {\n        if (tp_inst && tp_inst.timezone_select) {\n            var now = date || new Date();\n            tp_inst.timezone_select.val(-now.getTimezoneOffset());\n        }\n    };\n\n    /*\n    * Create a Singleton Instance\n    */\n    $.timepicker = new Timepicker();\n\n    /**\n     * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)\n     * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned\n     * @param {boolean} iso8601 if true formats in accordance to iso8601 \"+12:45\"\n     * @return {string}\n     */\n    $.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {\n        if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {\n            return tzMinutes;\n        }\n\n        var off = tzMinutes,\n            minutes = off % 60,\n            hours = (off - minutes) / 60,\n            iso = iso8601 ? ':' : '',\n            tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);\n\n        if (tz === '+00:00') {\n            return 'Z';\n        }\n        return tz;\n    };\n\n    /**\n     * Get the number in minutes that represents a timezone string\n     * @param  {string} tzString formatted like \"+0500\", \"-1245\", \"Z\"\n     * @return {number} the offset minutes or the original string if it doesn't match expectations\n     */\n    $.timepicker.timezoneOffsetNumber = function (tzString) {\n        var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with \"+1245\"\n\n        if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset\n            return 0;\n        }\n\n        if (!/^(\\-|\\+)\\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back\n            return parseInt(tzString, 10);\n        }\n\n        return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus\n            ((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)\n                parseInt(normalized.substr(3, 2), 10))); // minutes\n    };\n\n    /**\n     * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)\n     * @param  {Date} date\n     * @param  {string} fromTimezone formatted like \"+0500\", \"-1245\"\n     * @param  {string} toTimezone formatted like \"+0500\", \"-1245\"\n     * @return {Date}\n     */\n    $.timepicker.timezoneAdjust = function (date, fromTimezone, toTimezone) {\n        var fromTz = $.timepicker.timezoneOffsetNumber(fromTimezone);\n        var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);\n        if (!isNaN(toTz)) {\n            date.setMinutes(date.getMinutes() + (-fromTz) - (-toTz));\n        }\n        return date;\n    };\n\n    /**\n     * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * n.b. The input value must be correctly formatted (reformatting is not supported)\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the timepicker() call\n     * @return {jQuery}\n     */\n    $.timepicker.timeRange = function (startTime, endTime, options) {\n        return $.timepicker.handleRange('timepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @return {jQuery}\n     */\n    $.timepicker.datetimeRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datetimepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.dateRange = function (startTime, endTime, options) {\n        $.timepicker.handleRange('datepicker', startTime, endTime, options);\n    };\n\n    /**\n     * Calls `method` on the `startTime` and `endTime` elements, and configures them to\n     * enforce date range limits.\n     * @param  {string} method Can be used to specify the type of picker to be added\n     * @param  {Element} startTime\n     * @param  {Element} endTime\n     * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,\n     *   a boolean value that can be used to reformat the input values to the `dateFormat`.\n     * @return {jQuery}\n     */\n    $.timepicker.handleRange = function (method, startTime, endTime, options) {\n        options = $.extend({}, {\n            minInterval: 0, // min allowed interval in milliseconds\n            maxInterval: 0, // max allowed interval in milliseconds\n            start: {},      // options for start picker\n            end: {}         // options for end picker\n        }, options);\n\n        // for the mean time this fixes an issue with calling getDate with timepicker()\n        var timeOnly = false;\n        if(method === 'timepicker'){\n            timeOnly = true;\n            method = 'datetimepicker';\n        }\n\n        function checkDates(changed, other) {\n            var startdt = startTime[method]('getDate'),\n                enddt = endTime[method]('getDate'),\n                changeddt = changed[method]('getDate');\n\n            if (startdt !== null) {\n                var minDate = new Date(startdt.getTime()),\n                    maxDate = new Date(startdt.getTime());\n\n                minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);\n                maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);\n\n                if (options.minInterval > 0 && minDate > enddt) { // minInterval check\n                    endTime[method]('setDate', minDate);\n                }\n                else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check\n                    endTime[method]('setDate', maxDate);\n                }\n                else if (startdt > enddt) {\n                    other[method]('setDate', changeddt);\n                }\n            }\n        }\n\n        function selected(changed, other, option) {\n            if (!changed.val()) {\n                return;\n            }\n            var date = changed[method].call(changed, 'getDate');\n            if (date !== null && options.minInterval > 0) {\n                if (option === 'minDate') {\n                    date.setMilliseconds(date.getMilliseconds() + options.minInterval);\n                }\n                if (option === 'maxDate') {\n                    date.setMilliseconds(date.getMilliseconds() - options.minInterval);\n                }\n            }\n\n            if (date.getTime) {\n                other[method].call(other, 'option', option, date);\n            }\n        }\n\n        $.fn[method].call(startTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), endTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), endTime, 'minDate');\n            }\n        }, options, options.start));\n        $.fn[method].call(endTime, $.extend({\n            timeOnly: timeOnly,\n            onClose: function (dateText, inst) {\n                checkDates($(this), startTime);\n            },\n            onSelect: function (selectedDateTime) {\n                selected($(this), startTime, 'maxDate');\n            }\n        }, options, options.end));\n\n        checkDates(startTime, endTime);\n\n        selected(startTime, endTime, 'minDate');\n        selected(endTime, startTime, 'maxDate');\n\n        return $([startTime.get(0), endTime.get(0)]);\n    };\n\n    /**\n     * Log error or data to the console during error or debugging\n     * @param  {Object} err pass any type object to log to the console during error or debugging\n     * @return {void}\n     */\n    $.timepicker.log = function () {\n        // Older IE (9, maybe 10) throw error on accessing `window.console.log.apply`, so check first.\n        if (window.console && window.console.log && window.console.log.apply) {\n            window.console.log.apply(window.console, Array.prototype.slice.call(arguments));\n        }\n    };\n\n    /*\n     * Add util object to allow access to private methods for testability.\n     */\n    $.timepicker._util = {\n        _extendRemove: extendRemove,\n        _isEmptyObject: isEmptyObject,\n        _convert24to12: convert24to12,\n        _detectSupport: detectSupport,\n        _selectLocalTimezone: selectLocalTimezone,\n        _computeEffectiveSetting: computeEffectiveSetting,\n        _splitDateTime: splitDateTime,\n        _parseDateTimeInternal: parseDateTimeInternal\n    };\n\n    /*\n    * Microsecond support\n    */\n    if (!Date.prototype.getMicroseconds) {\n        Date.prototype.microseconds = 0;\n        Date.prototype.getMicroseconds = function () { return this.microseconds; };\n        Date.prototype.setMicroseconds = function (m) {\n            this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));\n            this.microseconds = m % 1000;\n            return this;\n        };\n    }\n\n    /*\n    * Keep up with the version\n    */\n    $.timepicker.version = \"1.6.3\";\n\n}));\n","jquery/jquery.validate.js":"/*!\n * jQuery Validation Plugin v1.19.5\n *\n * https://jqueryvalidation.org/\n *\n * Copyright (c) 2022 J\u00f6rn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n    if ( typeof define === \"function\" && define.amd ) {\n        define( [\"jquery\", \"jquery/jquery.metadata\"], factory );\n    } else if (typeof module === \"object\" && module.exports) {\n        module.exports = factory( require( \"jquery\" ) );\n    } else {\n        factory( jQuery );\n    }\n}(function( $ ) {\n\n    $.extend( $.fn, {\n\n        // https://jqueryvalidation.org/validate/\n        validate: function( options ) {\n\n            // If nothing is selected, return nothing; can't chain anyway\n            if ( !this.length ) {\n                if ( options && options.debug && window.console ) {\n                    console.warn( \"Nothing selected, can't validate, returning nothing.\" );\n                }\n                return;\n            }\n\n            // Check if a validator for this form was already created\n            var validator = $.data( this[ 0 ], \"validator\" );\n            if ( validator ) {\n                return validator;\n            }\n\n            // Add novalidate tag if HTML5.\n            this.attr( \"novalidate\", \"novalidate\" );\n\n            validator = new $.validator( options, this[ 0 ] );\n            $.data( this[ 0 ], \"validator\", validator );\n\n            if ( validator.settings.onsubmit ) {\n\n                this.on( \"click.validate\", \":submit\", function( event ) {\n\n                    // Track the used submit button to properly handle scripted\n                    // submits later.\n                    validator.submitButton = event.currentTarget;\n\n                    // Allow suppressing validation by adding a cancel class to the submit button\n                    if ( $( this ).hasClass( \"cancel\" ) ) {\n                        validator.cancelSubmit = true;\n                    }\n\n                    // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button\n                    if ( $( this ).attr( \"formnovalidate\" ) !== undefined ) {\n                        validator.cancelSubmit = true;\n                    }\n                } );\n\n                // Validate the form on submit\n                this.on( \"submit.validate\", function( event ) {\n                    if ( validator.settings.debug ) {\n\n                        // Prevent form submit to be able to see console output\n                        event.preventDefault();\n                    }\n\n                    function handle() {\n                        var hidden, result;\n\n                        // Insert a hidden input as a replacement for the missing submit button\n                        // The hidden input is inserted in two cases:\n                        //   - A user defined a `submitHandler`\n                        //   - There was a pending request due to `remote` method and `stopRequest()`\n                        //     was called to submit the form in case it's valid\n                        if ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) {\n                            hidden = $( \"<input type='hidden'/>\" )\n                                .attr( \"name\", validator.submitButton.name )\n                                .val( $( validator.submitButton ).val() )\n                                .appendTo( validator.currentForm );\n                        }\n\n                        if ( validator.settings.submitHandler && !validator.settings.debug ) {\n                            result = validator.settings.submitHandler.call( validator, validator.currentForm, event );\n                            if ( hidden ) {\n\n                                // And clean up afterwards; thanks to no-block-scope, hidden can be referenced\n                                hidden.remove();\n                            }\n                            if ( result !== undefined ) {\n                                return result;\n                            }\n                            return false;\n                        }\n                        return true;\n                    }\n\n                    // Prevent submit for invalid forms or custom submit handlers\n                    if ( validator.cancelSubmit ) {\n                        validator.cancelSubmit = false;\n                        return handle();\n                    }\n                    if ( validator.form() ) {\n                        if ( validator.pendingRequest ) {\n                            validator.formSubmitted = true;\n                            return false;\n                        }\n                        return handle();\n                    } else {\n                        validator.focusInvalid();\n                        return false;\n                    }\n                } );\n            }\n\n            return validator;\n        },\n\n        // https://jqueryvalidation.org/valid/\n        valid: function() {\n            var valid, validator, errorList;\n\n            if ( $( this[ 0 ] ).is( \"form\" ) ) {\n                valid = this.validate().form();\n            } else {\n                errorList = [];\n                valid = true;\n                validator = $( this[ 0 ].form ).validate();\n                this.each( function() {\n                    valid = validator.element( this ) && valid;\n                    if ( !valid ) {\n                        errorList = errorList.concat( validator.errorList );\n                    }\n                } );\n                validator.errorList = errorList;\n            }\n            return valid;\n        },\n\n        // https://jqueryvalidation.org/rules/\n        rules: function( command, argument ) {\n            var element = this[ 0 ],\n                isContentEditable = typeof this.attr( \"contenteditable\" ) !== \"undefined\" && this.attr( \"contenteditable\" ) !== \"false\",\n                settings, staticRules, existingRules, data, param, filtered;\n\n            // If nothing is selected, return empty object; can't chain anyway\n            if ( element == null ) {\n                return;\n            }\n\n            if ( !element.form && isContentEditable ) {\n                element.form = this.closest( \"form\" )[ 0 ];\n                element.name = this.attr( \"name\" );\n            }\n\n            if ( element.form == null ) {\n                return;\n            }\n\n            if ( command ) {\n                settings = $.data( element.form, \"validator\" ).settings;\n                staticRules = settings.rules;\n                existingRules = $.validator.staticRules( element );\n                switch ( command ) {\n                    case \"add\":\n                        $.extend( existingRules, $.validator.normalizeRule( argument ) );\n\n                        // Remove messages from rules, but allow them to be set separately\n                        delete existingRules.messages;\n                        staticRules[ element.name ] = existingRules;\n                        if ( argument.messages ) {\n                            settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );\n                        }\n                        break;\n                    case \"remove\":\n                        if ( !argument ) {\n                            delete staticRules[ element.name ];\n                            return existingRules;\n                        }\n                        filtered = {};\n                        $.each( argument.split( /\\s/ ), function( index, method ) {\n                            filtered[ method ] = existingRules[ method ];\n                            delete existingRules[ method ];\n                        } );\n                        return filtered;\n                }\n            }\n\n            data = $.validator.normalizeRules(\n                $.extend(\n                    {},\n                    $.validator.metadataRules(element),\n                    $.validator.classRules( element ),\n                    $.validator.attributeRules( element ),\n                    $.validator.dataRules( element ),\n                    $.validator.staticRules( element )\n                ), element );\n\n            // Make sure required is at front\n            if ( data.required ) {\n                param = data.required;\n                delete data.required;\n                data = $.extend( { required: param }, data );\n            }\n\n            // Make sure remote is at back\n            if ( data.remote ) {\n                param = data.remote;\n                delete data.remote;\n                data = $.extend( data, { remote: param } );\n            }\n\n            return data;\n        }\n    } );\n\n// JQuery trim is deprecated, provide a trim method based on String.prototype.trim\n    var trim = function( str ) {\n\n        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill\n        return str.replace( /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, \"\" );\n    };\n\n// Custom selectors\n    $.extend( $.expr.pseudos || $.expr[ \":\" ], {\t\t// '|| $.expr[ \":\" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support\n\n        // https://jqueryvalidation.org/blank-selector/\n        blank: function( a ) {\n            return !trim( \"\" + $( a ).val() );\n        },\n\n        // https://jqueryvalidation.org/filled-selector/\n        filled: function( a ) {\n            var val = $( a ).val();\n            return val !== null && !!trim( \"\" + val );\n        },\n\n        // https://jqueryvalidation.org/unchecked-selector/\n        unchecked: function( a ) {\n            return !$( a ).prop( \"checked\" );\n        }\n    } );\n\n// Constructor for validator\n    $.validator = function( options, form ) {\n        this.settings = $.extend( true, {}, $.validator.defaults, options );\n        this.currentForm = form;\n        this.init();\n    };\n\n// https://jqueryvalidation.org/jQuery.validator.format/\n    $.validator.format = function( source, params ) {\n        if ( arguments.length === 1 ) {\n            return function() {\n                var args = $.makeArray( arguments );\n                args.unshift( source );\n                return $.validator.format.apply( this, args );\n            };\n        }\n        if ( params === undefined ) {\n            return source;\n        }\n        if ( arguments.length > 2 && params.constructor !== Array  ) {\n            params = $.makeArray( arguments ).slice( 1 );\n        }\n        if ( params.constructor !== Array ) {\n            params = [ params ];\n        }\n        $.each( params, function( i, n ) {\n            source = source.replace( new RegExp( \"\\\\{\" + i + \"\\\\}\", \"g\" ), function() {\n                return n;\n            } );\n        } );\n        return source;\n    };\n\n    $.extend( $.validator, {\n\n        defaults: {\n            messages: {},\n            groups: {},\n            rules: {},\n            errorClass: \"error\",\n            pendingClass: \"pending\",\n            validClass: \"valid\",\n            errorElement: \"label\",\n            focusCleanup: false,\n            focusInvalid: true,\n            errorContainer: $( [] ),\n            errorLabelContainer: $( [] ),\n            onsubmit: true,\n            ignore: \":hidden\",\n            ignoreTitle: false,\n            onfocusin: function( element ) {\n                this.lastActive = element;\n\n                // Hide error label and remove error class on focus if enabled\n                if ( this.settings.focusCleanup ) {\n                    if ( this.settings.unhighlight ) {\n                        this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );\n                    }\n                    this.hideThese( this.errorsFor( element ) );\n                }\n            },\n            onfocusout: function( element ) {\n                if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {\n                    this.element( element );\n                }\n            },\n            onkeyup: function( element, event ) {\n\n                // Avoid revalidate the field when pressing one of the following keys\n                // Shift       => 16\n                // Ctrl        => 17\n                // Alt         => 18\n                // Caps lock   => 20\n                // End         => 35\n                // Home        => 36\n                // Left arrow  => 37\n                // Up arrow    => 38\n                // Right arrow => 39\n                // Down arrow  => 40\n                // Insert      => 45\n                // Num lock    => 144\n                // AltGr key   => 225\n                var excludedKeys = [\n                    16, 17, 18, 20, 35, 36, 37,\n                    38, 39, 40, 45, 144, 225\n                ];\n\n                if ( event.which === 9 && this.elementValue( element ) === \"\" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {\n                    return;\n                } else if ( element.name in this.submitted || element.name in this.invalid ) {\n                    this.element( element );\n                }\n            },\n            onclick: function( element ) {\n\n                // Click on selects, radiobuttons and checkboxes\n                if ( element.name in this.submitted ) {\n                    this.element( element );\n\n                    // Or option elements, check parent select in that case\n                } else if ( element.parentNode.name in this.submitted ) {\n                    this.element( element.parentNode );\n                }\n            },\n            highlight: function( element, errorClass, validClass ) {\n                if ( element.type === \"radio\" ) {\n                    this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );\n                } else {\n                    $( element ).addClass( errorClass ).removeClass( validClass );\n                }\n            },\n            unhighlight: function( element, errorClass, validClass ) {\n                if ( element.type === \"radio\" ) {\n                    this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );\n                } else {\n                    $( element ).removeClass( errorClass ).addClass( validClass );\n                }\n            }\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.setDefaults/\n        setDefaults: function( settings ) {\n            $.extend( $.validator.defaults, settings );\n        },\n\n        messages: {\n            required: \"This field is required.\",\n            remote: \"Please fix this field.\",\n            email: \"Please enter a valid email address.\",\n            url: \"Please enter a valid URL.\",\n            date: \"Please enter a valid date.\",\n            dateISO: \"Please enter a valid date (ISO).\",\n            number: \"Please enter a valid number.\",\n            digits: \"Please enter only digits.\",\n            equalTo: \"Please enter the same value again.\",\n            maxlength: $.validator.format( \"Please enter no more than {0} characters.\" ),\n            minlength: $.validator.format( \"Please enter at least {0} characters.\" ),\n            rangelength: $.validator.format( \"Please enter a value between {0} and {1} characters long.\" ),\n            range: $.validator.format( \"Please enter a value between {0} and {1}.\" ),\n            max: $.validator.format( \"Please enter a value less than or equal to {0}.\" ),\n            min: $.validator.format( \"Please enter a value greater than or equal to {0}.\" ),\n            step: $.validator.format( \"Please enter a multiple of {0}.\" )\n        },\n\n        autoCreateRanges: false,\n\n        prototype: {\n\n            init: function() {\n                this.labelContainer = $( this.settings.errorLabelContainer );\n                this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );\n                this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );\n                this.submitted = {};\n                this.valueCache = {};\n                this.pendingRequest = 0;\n                this.pending = {};\n                this.invalid = {};\n                this.reset();\n\n                var currentForm = this.currentForm,\n                    groups = ( this.groups = {} ),\n                    rules;\n                $.each( this.settings.groups, function( key, value ) {\n                    if ( typeof value === \"string\" ) {\n                        value = value.split( /\\s/ );\n                    }\n                    $.each( value, function( index, name ) {\n                        groups[ name ] = key;\n                    } );\n                } );\n                rules = this.settings.rules;\n                $.each( rules, function( key, value ) {\n                    rules[ key ] = $.validator.normalizeRule( value );\n                } );\n\n                function delegate( event ) {\n                    var isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n                    // Set form expando on contenteditable\n                    if ( !this.form && isContentEditable ) {\n                        this.form = $( this ).closest( \"form\" )[ 0 ];\n                        this.name = $( this ).attr( \"name\" );\n                    }\n\n                    // Ignore the element if it belongs to another form. This will happen mainly\n                    // when setting the `form` attribute of an input to the id of another form.\n                    if ( currentForm !== this.form ) {\n                        return;\n                    }\n\n                    var validator = $.data( this.form, \"validator\" ),\n                        eventType = \"on\" + event.type.replace( /^validate/, \"\" ),\n                        settings = validator.settings;\n                    if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {\n                        settings[ eventType ].call( validator, this, event );\n                    }\n                }\n\n                $( this.currentForm )\n                    .on( \"focusin.validate focusout.validate keyup.validate\",\n                        \":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], \" +\n                        \"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], \" +\n                        \"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], \" +\n                        \"[type='radio'], [type='checkbox'], [contenteditable], [type='button']\", delegate )\n\n                    // Support: Chrome, oldIE\n                    // \"select\" is provided as event.target when clicking a option\n                    .on( \"click.validate\", \"select, option, [type='radio'], [type='checkbox']\", delegate );\n\n                if ( this.settings.invalidHandler ) {\n                    $( this.currentForm ).on( \"invalid-form.validate\", this.settings.invalidHandler );\n                }\n            },\n\n            // https://jqueryvalidation.org/Validator.form/\n            form: function() {\n                this.checkForm();\n                $.extend( this.submitted, this.errorMap );\n                this.invalid = $.extend( {}, this.errorMap );\n                if ( !this.valid() ) {\n                    $( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n                }\n                this.showErrors();\n                return this.valid();\n            },\n\n            checkForm: function() {\n                this.prepareForm();\n                for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {\n                    this.check( elements[ i ] );\n                }\n                return this.valid();\n            },\n\n            // https://jqueryvalidation.org/Validator.element/\n            element: function( element ) {\n                var cleanElement = this.clean( element ),\n                    checkElement = this.validationTargetFor( cleanElement ),\n                    v = this,\n                    result = true,\n                    rs, group;\n\n                if ( checkElement === undefined ) {\n                    delete this.invalid[ cleanElement.name ];\n                } else {\n                    this.prepareElement( checkElement );\n                    this.currentElements = $( checkElement );\n\n                    // If this element is grouped, then validate all group elements already\n                    // containing a value\n                    group = this.groups[ checkElement.name ];\n                    if ( group ) {\n                        $.each( this.groups, function( name, testgroup ) {\n                            if ( testgroup === group && name !== checkElement.name ) {\n                                cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );\n                                if ( cleanElement && cleanElement.name in v.invalid ) {\n                                    v.currentElements.push( cleanElement );\n                                    result = v.check( cleanElement ) && result;\n                                }\n                            }\n                        } );\n                    }\n\n                    rs = this.check( checkElement ) !== false;\n                    result = result && rs;\n                    if ( rs ) {\n                        this.invalid[ checkElement.name ] = false;\n                    } else {\n                        this.invalid[ checkElement.name ] = true;\n                    }\n\n                    if ( !this.numberOfInvalids() ) {\n\n                        // Hide error containers on last error\n                        this.toHide = this.toHide.add( this.containers );\n                    }\n                    this.showErrors();\n\n                    // Add aria-invalid status for screen readers\n                    $( element ).attr( \"aria-invalid\", !rs );\n                }\n\n                return result;\n            },\n\n            // https://jqueryvalidation.org/Validator.showErrors/\n            showErrors: function( errors ) {\n                if ( errors ) {\n                    var validator = this;\n\n                    // Add items to error list and map\n                    $.extend( this.errorMap, errors );\n                    this.errorList = $.map( this.errorMap, function( message, name ) {\n                        return {\n                            message: message,\n                            element: validator.findByName( name )[ 0 ]\n                        };\n                    } );\n\n                    // Remove items from success list\n                    this.successList = $.grep( this.successList, function( element ) {\n                        return !( element.name in errors );\n                    } );\n                }\n                if ( this.settings.showErrors ) {\n                    this.settings.showErrors.call( this, this.errorMap, this.errorList );\n                } else {\n                    this.defaultShowErrors();\n                }\n            },\n\n            // https://jqueryvalidation.org/Validator.resetForm/\n            resetForm: function() {\n                if ( $.fn.resetForm ) {\n                    $( this.currentForm ).resetForm();\n                }\n                this.invalid = {};\n                this.submitted = {};\n                this.prepareForm();\n                this.hideErrors();\n                var elements = this.elements()\n                    .removeData( \"previousValue\" )\n                    .removeAttr( \"aria-invalid\" );\n\n                this.resetElements( elements );\n            },\n\n            resetElements: function( elements ) {\n                var i;\n\n                if ( this.settings.unhighlight ) {\n                    for ( i = 0; elements[ i ]; i++ ) {\n                        this.settings.unhighlight.call( this, elements[ i ],\n                            this.settings.errorClass, \"\" );\n                        this.findByName( elements[ i ].name ).removeClass( this.settings.validClass );\n                    }\n                } else {\n                    elements\n                        .removeClass( this.settings.errorClass )\n                        .removeClass( this.settings.validClass );\n                }\n            },\n\n            numberOfInvalids: function() {\n                return this.objectLength( this.invalid );\n            },\n\n            objectLength: function( obj ) {\n                /* jshint unused: false */\n                var count = 0,\n                    i;\n                for ( i in obj ) {\n\n                    // This check allows counting elements with empty error\n                    // message as invalid elements\n                    if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {\n                        count++;\n                    }\n                }\n                return count;\n            },\n\n            hideErrors: function() {\n                this.hideThese( this.toHide );\n            },\n\n            hideThese: function( errors ) {\n                errors.not( this.containers ).text( \"\" );\n                this.addWrapper( errors ).hide();\n            },\n\n            valid: function() {\n                return this.size() === 0;\n            },\n\n            size: function() {\n                return this.errorList.length;\n            },\n\n            focusInvalid: function() {\n                if ( this.settings.focusInvalid ) {\n                    try {\n                        $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )\n                            .filter( \":visible\" )\n                            .trigger( \"focus\" )\n\n                            // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find\n                            .trigger( \"focusin\" );\n                    } catch ( e ) {\n\n                        // Ignore IE throwing errors when focusing hidden elements\n                    }\n                }\n            },\n\n            findLastActive: function() {\n                var lastActive = this.lastActive;\n                return lastActive && $.grep( this.errorList, function( n ) {\n                    return n.element.name === lastActive.name;\n                } ).length === 1 && lastActive;\n            },\n\n            elements: function() {\n                var validator = this,\n                    rulesCache = {};\n\n                // Select all valid inputs inside the form (no submit or reset buttons)\n                return $( this.currentForm )\n                    .find( \"input, select, textarea, [contenteditable]\" )\n                    .not( \":submit, :reset, :image, :disabled\" )\n                    .not( this.settings.ignore )\n                    .filter( function() {\n                        var name = this.name || $( this ).attr( \"name\" ); // For contenteditable\n                        var isContentEditable = typeof $( this ).attr( \"contenteditable\" ) !== \"undefined\" && $( this ).attr( \"contenteditable\" ) !== \"false\";\n\n                        if ( !name && validator.settings.debug && window.console ) {\n                            console.error( \"%o has no name assigned\", this );\n                        }\n\n                        // Set form expando on contenteditable\n                        if ( isContentEditable ) {\n                            this.form = $( this ).closest( \"form\" )[ 0 ];\n                            this.name = name;\n                        }\n\n                        // Ignore elements that belong to other/nested forms\n                        if ( this.form !== validator.currentForm ) {\n                            return false;\n                        }\n\n                        // Select only the first element for each name, and only those with rules specified\n                        if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {\n                            return false;\n                        }\n\n                        rulesCache[ name ] = true;\n                        return true;\n                    } );\n            },\n\n            clean: function( selector ) {\n                return $( selector )[ 0 ];\n            },\n\n            errors: function() {\n                var errorClass = this.settings.errorClass.split( \" \" ).join( \".\" );\n                return $( this.settings.errorElement + \".\" + errorClass, this.errorContext );\n            },\n\n            resetInternals: function() {\n                this.successList = [];\n                this.errorList = [];\n                this.errorMap = {};\n                this.toShow = $( [] );\n                this.toHide = $( [] );\n            },\n\n            reset: function() {\n                this.resetInternals();\n                this.currentElements = $( [] );\n            },\n\n            prepareForm: function() {\n                this.reset();\n                this.toHide = this.errors().add( this.containers );\n            },\n\n            prepareElement: function( element ) {\n                this.reset();\n                this.toHide = this.errorsFor( element );\n            },\n\n            elementValue: function( element ) {\n                var $element = $( element ),\n                    type = element.type,\n                    isContentEditable = typeof $element.attr( \"contenteditable\" ) !== \"undefined\" && $element.attr( \"contenteditable\" ) !== \"false\",\n                    val, idx;\n\n                if ( type === \"radio\" || type === \"checkbox\" ) {\n                    return this.findByName( element.name ).filter( \":checked\" ).val();\n                } else if ( type === \"number\" && typeof element.validity !== \"undefined\" ) {\n                    return element.validity.badInput ? \"NaN\" : $element.val();\n                }\n\n                if ( isContentEditable ) {\n                    val = $element.text();\n                } else {\n                    val = $element.val();\n                }\n\n                if ( type === \"file\" ) {\n\n                    // Modern browser (chrome & safari)\n                    if ( val.substr( 0, 12 ) === \"C:\\\\fakepath\\\\\" ) {\n                        return val.substr( 12 );\n                    }\n\n                    // Legacy browsers\n                    // Unix-based path\n                    idx = val.lastIndexOf( \"/\" );\n                    if ( idx >= 0 ) {\n                        return val.substr( idx + 1 );\n                    }\n\n                    // Windows-based path\n                    idx = val.lastIndexOf( \"\\\\\" );\n                    if ( idx >= 0 ) {\n                        return val.substr( idx + 1 );\n                    }\n\n                    // Just the file name\n                    return val;\n                }\n\n                if ( typeof val === \"string\" ) {\n                    return val.replace( /\\r/g, \"\" );\n                }\n                return val;\n            },\n\n            check: function( element ) {\n                element = this.validationTargetFor( this.clean( element ) );\n\n                var rules = $( element ).rules(),\n                    rulesCount = $.map( rules, function( n, i ) {\n                        return i;\n                    } ).length,\n                    dependencyMismatch = false,\n                    val = this.elementValue( element ),\n                    result, method, rule, normalizer;\n\n                // Prioritize the local normalizer defined for this element over the global one\n                // if the former exists, otherwise user the global one in case it exists.\n                if ( typeof rules.normalizer === \"function\" ) {\n                    normalizer = rules.normalizer;\n                } else if (\ttypeof this.settings.normalizer === \"function\" ) {\n                    normalizer = this.settings.normalizer;\n                }\n\n                // If normalizer is defined, then call it to the changed value instead\n                // of using the real one.\n                // Note that `this` in the normalizer is `element`.\n                if ( normalizer ) {\n                    val = normalizer.call( element, val );\n\n                    // Delete the normalizer from rules to avoid treating it as a pre-defined method.\n                    delete rules.normalizer;\n                }\n\n                for ( method in rules ) {\n                    rule = { method: method, parameters: rules[ method ] };\n                    try {\n                        result = $.validator.methods[ method ].call( this, val, element, rule.parameters );\n\n                        // If a method indicates that the field is optional and therefore valid,\n                        // don't mark it as valid when there are no other rules\n                        if ( result === \"dependency-mismatch\" && rulesCount === 1 ) {\n                            dependencyMismatch = true;\n                            continue;\n                        }\n                        dependencyMismatch = false;\n\n                        if ( result === \"pending\" ) {\n                            this.toHide = this.toHide.not( this.errorsFor( element ) );\n                            return;\n                        }\n\n                        if ( !result ) {\n                            this.formatAndAdd( element, rule );\n                            return false;\n                        }\n                    } catch ( e ) {\n                        if ( this.settings.debug && window.console ) {\n                            console.log( \"Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\", e );\n                        }\n                        if ( e instanceof TypeError ) {\n                            e.message += \".  Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\";\n                        }\n\n                        throw e;\n                    }\n                }\n                if ( dependencyMismatch ) {\n                    return;\n                }\n                if ( this.objectLength( rules ) ) {\n                    this.successList.push( element );\n                }\n                return true;\n            },\n\n            // Return the custom message for the given element and validation method\n            // specified in the element's HTML5 data attribute\n            // return the generic message if present and no method specific message is present\n            customDataMessage: function( element, method ) {\n                return $( element ).data( \"msg\" + method.charAt( 0 ).toUpperCase() +\n                    method.substring( 1 ).toLowerCase() ) || $( element ).data( \"msg\" );\n            },\n\n            // Return the custom message for the given element name and validation method\n            customMessage: function( name, method ) {\n                var m = this.settings.messages[ name ];\n                return m && ( m.constructor === String ? m : m[ method ] );\n            },\n\n            // Return the first defined argument, allowing empty strings\n            findDefined: function() {\n                for ( var i = 0; i < arguments.length; i++ ) {\n                    if ( arguments[ i ] !== undefined ) {\n                        return arguments[ i ];\n                    }\n                }\n                return undefined;\n            },\n\n            // The second parameter 'rule' used to be a string, and extended to an object literal\n            // of the following form:\n            // rule = {\n            //     method: \"method name\",\n            //     parameters: \"the given method parameters\"\n            // }\n            //\n            // The old behavior still supported, kept to maintain backward compatibility with\n            // old code, and will be removed in the next major release.\n            defaultMessage: function( element, rule ) {\n                if ( typeof rule === \"string\" ) {\n                    rule = { method: rule };\n                }\n\n                var message = this.findDefined(\n                        this.customMessage( element.name, rule.method ),\n                        this.customDataMessage( element, rule.method ),\n\n                        // 'title' is never undefined, so handle empty string as undefined\n                        !this.settings.ignoreTitle && element.title || undefined,\n                        $.validator.messages[ rule.method ],\n                        \"<strong>Warning: No message defined for \" + element.name + \"</strong>\"\n                    ),\n                    theregex = /\\$?\\{(\\d+)\\}/g;\n                if ( typeof message === \"function\" ) {\n                    message = message.call( this, rule.parameters, element );\n                } else if ( theregex.test( message ) ) {\n                    message = $.validator.format( message.replace( theregex, \"{$1}\" ), rule.parameters );\n                }\n\n                return message;\n            },\n\n            formatAndAdd: function( element, rule ) {\n                var message = this.defaultMessage( element, rule );\n\n                this.errorList.push( {\n                    message: message,\n                    element: element,\n                    method: rule.method\n                } );\n\n                this.errorMap[ element.name ] = message;\n                this.submitted[ element.name ] = message;\n            },\n\n            addWrapper: function( toToggle ) {\n                if ( this.settings.wrapper ) {\n                    toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );\n                }\n                return toToggle;\n            },\n\n            defaultShowErrors: function() {\n                var i, elements, error;\n                for ( i = 0; this.errorList[ i ]; i++ ) {\n                    error = this.errorList[ i ];\n                    if ( this.settings.highlight ) {\n                        this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );\n                    }\n                    this.showLabel( error.element, error.message );\n                }\n                if ( this.errorList.length ) {\n                    this.toShow = this.toShow.add( this.containers );\n                }\n                if ( this.settings.success ) {\n                    for ( i = 0; this.successList[ i ]; i++ ) {\n                        this.showLabel( this.successList[ i ] );\n                    }\n                }\n                if ( this.settings.unhighlight ) {\n                    for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {\n                        this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );\n                    }\n                }\n                this.toHide = this.toHide.not( this.toShow );\n                this.hideErrors();\n                this.addWrapper( this.toShow ).show();\n            },\n\n            validElements: function() {\n                return this.currentElements.not( this.invalidElements() );\n            },\n\n            invalidElements: function() {\n                return $( this.errorList ).map( function() {\n                    return this.element;\n                } );\n            },\n\n            showLabel: function( element, message ) {\n                var place, group, errorID, v,\n                    error = this.errorsFor( element ),\n                    elementID = this.idOrName( element ),\n                    describedBy = $( element ).attr( \"aria-describedby\" );\n\n                if ( error.length ) {\n\n                    // Refresh error/success class\n                    error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );\n\n                    // Replace message on existing label\n                    error.html( message );\n                } else {\n\n                    // Create error element\n                    error = $( \"<\" + this.settings.errorElement + \">\" )\n                        .attr( \"id\", elementID + \"-error\" )\n                        .addClass( this.settings.errorClass )\n                        .html( message || \"\" );\n\n                    // Maintain reference to the element to be placed into the DOM\n                    place = error;\n                    if ( this.settings.wrapper ) {\n\n                        // Make sure the element is visible, even in IE\n                        // actually showing the wrapped element is handled elsewhere\n                        place = error.hide().show().wrap( \"<\" + this.settings.wrapper + \"/>\" ).parent();\n                    }\n                    if ( this.labelContainer.length ) {\n                        this.labelContainer.append( place );\n                    } else if ( this.settings.errorPlacement ) {\n                        this.settings.errorPlacement.call( this, place, $( element ) );\n                    } else {\n                        place.insertAfter( element );\n                    }\n\n                    // Link error back to the element\n                    if ( error.is( \"label\" ) ) {\n\n                        // If the error is a label, then associate using 'for'\n                        error.attr( \"for\", elementID );\n\n                        // If the element is not a child of an associated label, then it's necessary\n                        // to explicitly apply aria-describedby\n                    } else if ( error.parents( \"label[for='\" + this.escapeCssMeta( elementID ) + \"']\" ).length === 0 ) {\n                        errorID = error.attr( \"id\" );\n\n                        // Respect existing non-error aria-describedby\n                        if ( !describedBy ) {\n                            describedBy = errorID;\n                        } else if ( !describedBy.match( new RegExp( \"\\\\b\" + this.escapeCssMeta( errorID ) + \"\\\\b\" ) ) ) {\n\n                            // Add to end of list if not already present\n                            describedBy += \" \" + errorID;\n                        }\n                        $( element ).attr( \"aria-describedby\", describedBy );\n\n                        // If this element is grouped, then assign to all elements in the same group\n                        group = this.groups[ element.name ];\n                        if ( group ) {\n                            v = this;\n                            $.each( v.groups, function( name, testgroup ) {\n                                if ( testgroup === group ) {\n                                    $( \"[name='\" + v.escapeCssMeta( name ) + \"']\", v.currentForm )\n                                        .attr( \"aria-describedby\", error.attr( \"id\" ) );\n                                }\n                            } );\n                        }\n                    }\n                }\n                if ( !message && this.settings.success ) {\n                    error.text( \"\" );\n                    if ( typeof this.settings.success === \"string\" ) {\n                        error.addClass( this.settings.success );\n                    } else {\n                        this.settings.success( error, element );\n                    }\n                }\n                this.toShow = this.toShow.add( error );\n            },\n\n            errorsFor: function( element ) {\n                var name = this.escapeCssMeta( this.idOrName( element ) ),\n                    describer = $( element ).attr( \"aria-describedby\" ),\n                    selector = \"label[for='\" + name + \"'], label[for='\" + name + \"'] *\";\n\n                // 'aria-describedby' should directly reference the error element\n                if ( describer ) {\n                    selector = selector + \", #\" + this.escapeCssMeta( describer )\n                        .replace( /\\s+/g, \", #\" ) + \":visible\";\n                }\n\n                return this\n                    .errors()\n                    .filter( selector );\n            },\n\n            // See https://api.jquery.com/category/selectors/, for CSS\n            // meta-characters that should be escaped in order to be used with JQuery\n            // as a literal part of a name/id or any selector.\n            escapeCssMeta: function( string ) {\n                if ( string === undefined ) {\n                    return \"\";\n                }\n\n                return string.replace( /([\\\\!\"#$%&'()*+,./:;<=>?@\\[\\]^`{|}~])/g, \"\\\\$1\" );\n            },\n\n            idOrName: function( element ) {\n                return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );\n            },\n\n            validationTargetFor: function( element ) {\n\n                // If radio/checkbox, validate first element in group instead\n                if ( this.checkable( element ) ) {\n                    element = this.findByName( element.name );\n                }\n\n                // Always apply ignore filter\n                return $( element ).not( this.settings.ignore )[ 0 ];\n            },\n\n            checkable: function( element ) {\n                return ( /radio|checkbox/i ).test( element.type );\n            },\n\n            findByName: function( name ) {\n                return $( this.currentForm ).find( \"[name='\" + this.escapeCssMeta( name ) + \"']\" );\n            },\n\n            getLength: function( value, element ) {\n                switch ( element.nodeName.toLowerCase() ) {\n                    case \"select\":\n                        return $( \"option:selected\", element ).length;\n                    case \"input\":\n                        if ( this.checkable( element ) ) {\n                            return this.findByName( element.name ).filter( \":checked\" ).length;\n                        }\n                }\n                return value.length;\n            },\n\n            depend: function( param, element ) {\n                return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;\n            },\n\n            dependTypes: {\n                \"boolean\": function( param ) {\n                    return param;\n                },\n                \"string\": function( param, element ) {\n                    return !!$( param, element.form ).length;\n                },\n                \"function\": function( param, element ) {\n                    return param( element );\n                }\n            },\n\n            optional: function( element ) {\n                var val = this.elementValue( element );\n                return !$.validator.methods.required.call( this, val, element ) && \"dependency-mismatch\";\n            },\n\n            startRequest: function( element ) {\n                if ( !this.pending[ element.name ] ) {\n                    this.pendingRequest++;\n                    $( element ).addClass( this.settings.pendingClass );\n                    this.pending[ element.name ] = true;\n                }\n            },\n\n            stopRequest: function( element, valid ) {\n                this.pendingRequest--;\n\n                // Sometimes synchronization fails, make sure pendingRequest is never < 0\n                if ( this.pendingRequest < 0 ) {\n                    this.pendingRequest = 0;\n                }\n                delete this.pending[ element.name ];\n                $( element ).removeClass( this.settings.pendingClass );\n                if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() && this.pendingRequest === 0 ) {\n                    $( this.currentForm ).trigger( \"submit\" );\n\n                    // Remove the hidden input that was used as a replacement for the\n                    // missing submit button. The hidden input is added by `handle()`\n                    // to ensure that the value of the used submit button is passed on\n                    // for scripted submits triggered by this method\n                    if ( this.submitButton ) {\n                        $( \"input:hidden[name='\" + this.submitButton.name + \"']\", this.currentForm ).remove();\n                    }\n\n                    this.formSubmitted = false;\n                } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {\n                    $( this.currentForm ).triggerHandler( \"invalid-form\", [ this ] );\n                    this.formSubmitted = false;\n                }\n            },\n\n            previousValue: function( element, method ) {\n                method = typeof method === \"string\" && method || \"remote\";\n\n                return $.data( element, \"previousValue\" ) || $.data( element, \"previousValue\", {\n                    old: null,\n                    valid: true,\n                    message: this.defaultMessage( element, { method: method } )\n                } );\n            },\n\n            // Cleans up all forms and elements, removes validator-specific events\n            destroy: function() {\n                this.resetForm();\n\n                $( this.currentForm )\n                    .off( \".validate\" )\n                    .removeData( \"validator\" )\n                    .find( \".validate-equalTo-blur\" )\n                    .off( \".validate-equalTo\" )\n                    .removeClass( \"validate-equalTo-blur\" )\n                    .find( \".validate-lessThan-blur\" )\n                    .off( \".validate-lessThan\" )\n                    .removeClass( \"validate-lessThan-blur\" )\n                    .find( \".validate-lessThanEqual-blur\" )\n                    .off( \".validate-lessThanEqual\" )\n                    .removeClass( \"validate-lessThanEqual-blur\" )\n                    .find( \".validate-greaterThanEqual-blur\" )\n                    .off( \".validate-greaterThanEqual\" )\n                    .removeClass( \"validate-greaterThanEqual-blur\" )\n                    .find( \".validate-greaterThan-blur\" )\n                    .off( \".validate-greaterThan\" )\n                    .removeClass( \"validate-greaterThan-blur\" );\n            }\n\n        },\n\n        classRuleSettings: {\n            required: { required: true },\n            email: { email: true },\n            url: { url: true },\n            date: { date: true },\n            dateISO: { dateISO: true },\n            number: { number: true },\n            digits: { digits: true },\n            creditcard: { creditcard: true }\n        },\n\n        addClassRules: function( className, rules ) {\n            if ( className.constructor === String ) {\n                this.classRuleSettings[ className ] = rules;\n            } else {\n                $.extend( this.classRuleSettings, className );\n            }\n        },\n\n        classRules: function( element ) {\n            var rules = {},\n                classes = $( element ).attr( \"class\" );\n\n            if ( classes ) {\n                $.each( classes.split( \" \" ), function() {\n                    if ( this in $.validator.classRuleSettings ) {\n                        $.extend( rules, $.validator.classRuleSettings[ this ] );\n                    }\n                } );\n            }\n            return rules;\n        },\n\n        normalizeAttributeRule: function( rules, type, method, value ) {\n\n            // Convert the value to a number for number inputs, and for text for backwards compability\n            // allows type=\"date\" and others to be compared as strings\n            if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {\n                value = Number( value );\n\n                // Support Opera Mini, which returns NaN for undefined minlength\n                if ( isNaN( value ) ) {\n                    value = undefined;\n                }\n            }\n\n            if ( value || value === 0 ) {\n                rules[ method ] = value;\n            } else if ( type === method && type !== \"range\" ) {\n\n                // Exception: the jquery validate 'range' method\n                // does not test for the html5 'range' type\n                rules[ type === \"date\" ? \"dateISO\" : method ] = true;\n            }\n        },\n\n        attributeRules: function( element ) {\n            var rules = {},\n                $element = $( element ),\n                type = element.getAttribute( \"type\" ),\n                method, value;\n\n            for ( method in $.validator.methods ) {\n\n                // Support for <input required> in both html5 and older browsers\n                if ( method === \"required\" ) {\n                    value = element.getAttribute( method );\n\n                    // Some browsers return an empty string for the required attribute\n                    // and non-HTML5 browsers might have required=\"\" markup\n                    if ( value === \"\" ) {\n                        value = true;\n                    }\n\n                    // Force non-HTML5 browsers to return bool\n                    value = !!value;\n                } else {\n                    value = $element.attr( method );\n                }\n\n                this.normalizeAttributeRule( rules, type, method, value );\n            }\n\n            // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs\n            if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {\n                delete rules.maxlength;\n            }\n\n            return rules;\n        },\n\n        metadataRules: function (element) {\n            if (!$.metadata) {\n                return {};\n            }\n\n            var meta = $.data(element.form, 'validator').settings.meta;\n            return meta ?\n                $(element).metadata()[meta] :\n                $(element).metadata();\n        },\n\n        dataRules: function( element ) {\n            var rules = {},\n                $element = $( element ),\n                type = element.getAttribute( \"type\" ),\n                method, value;\n\n            for ( method in $.validator.methods ) {\n                value = $element.data( \"rule\" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );\n\n                // Cast empty attributes like `data-rule-required` to `true`\n                if ( value === \"\" ) {\n                    value = true;\n                }\n\n                this.normalizeAttributeRule( rules, type, method, value );\n            }\n            return rules;\n        },\n\n        staticRules: function( element ) {\n            var rules = {},\n                validator = $.data( element.form, \"validator\" );\n\n            if ( validator.settings.rules ) {\n                rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};\n            }\n            return rules;\n        },\n\n        normalizeRules: function( rules, element ) {\n\n            // Handle dependency check\n            $.each( rules, function( prop, val ) {\n\n                // Ignore rule when param is explicitly false, eg. required:false\n                if ( val === false ) {\n                    delete rules[ prop ];\n                    return;\n                }\n                if ( val.param || val.depends ) {\n                    var keepRule = true;\n                    switch ( typeof val.depends ) {\n                        case \"string\":\n                            keepRule = !!$( val.depends, element.form ).length;\n                            break;\n                        case \"function\":\n                            keepRule = val.depends.call( element, element );\n                            break;\n                    }\n                    if ( keepRule ) {\n                        rules[ prop ] = val.param !== undefined ? val.param : true;\n                    } else {\n                        $.data( element.form, \"validator\" ).resetElements( $( element ) );\n                        delete rules[ prop ];\n                    }\n                }\n            } );\n\n            // Evaluate parameters\n            $.each( rules, function( rule, parameter ) {\n                rules[ rule ] = typeof parameter === \"function\" && rule !== \"normalizer\" ? parameter( element ) : parameter;\n            } );\n\n            // Clean number parameters\n            $.each( [ \"minlength\", \"maxlength\" ], function() {\n                if ( rules[ this ] ) {\n                    rules[ this ] = Number( rules[ this ] );\n                }\n            } );\n            $.each( [ \"rangelength\", \"range\" ], function() {\n                var parts;\n                if ( rules[ this ] ) {\n                    if ( Array.isArray( rules[ this ] ) ) {\n                        rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];\n                    } else if ( typeof rules[ this ] === \"string\" ) {\n                        parts = rules[ this ].replace( /[\\[\\]]/g, \"\" ).split( /[\\s,]+/ );\n                        rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];\n                    }\n                }\n            } );\n\n            if ( $.validator.autoCreateRanges ) {\n\n                // Auto-create ranges\n                if ( rules.min != null && rules.max != null ) {\n                    rules.range = [ rules.min, rules.max ];\n                    delete rules.min;\n                    delete rules.max;\n                }\n                if ( rules.minlength != null && rules.maxlength != null ) {\n                    rules.rangelength = [ rules.minlength, rules.maxlength ];\n                    delete rules.minlength;\n                    delete rules.maxlength;\n                }\n            }\n\n            return rules;\n        },\n\n        // Converts a simple string to a {string: true} rule, e.g., \"required\" to {required:true}\n        normalizeRule: function( data ) {\n            if ( typeof data === \"string\" ) {\n                var transformed = {};\n                $.each( data.split( /\\s/ ), function() {\n                    transformed[ this ] = true;\n                } );\n                data = transformed;\n            }\n            return data;\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.addMethod/\n        addMethod: function( name, method, message ) {\n            $.validator.methods[ name ] = method;\n            $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];\n            if ( method.length < 3 ) {\n                $.validator.addClassRules( name, $.validator.normalizeRule( name ) );\n            }\n        },\n\n        // https://jqueryvalidation.org/jQuery.validator.methods/\n        methods: {\n\n            // https://jqueryvalidation.org/required-method/\n            required: function( value, element, param ) {\n\n                // Check if dependency is met\n                if ( !this.depend( param, element ) ) {\n                    return \"dependency-mismatch\";\n                }\n                if ( element.nodeName.toLowerCase() === \"select\" ) {\n\n                    // Could be an array for select-multiple or a string, both are fine this way\n                    var val = $( element ).val();\n                    return val && val.length > 0;\n                }\n                if ( this.checkable( element ) ) {\n                    return this.getLength( value, element ) > 0;\n                }\n                return value !== undefined && value !== null && value.length > 0;\n            },\n\n            // https://jqueryvalidation.org/email-method/\n            email: function( value, element ) {\n\n                // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address\n                // Retrieved 2014-01-14\n                // If you have a problem with this implementation, report a bug against the above spec\n                // Or use custom methods to implement your own email validation\n                return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/url-method/\n            url: function( value, element ) {\n\n                // Copyright (c) 2010-2013 Diego Perini, MIT licensed\n                // https://gist.github.com/dperini/729294\n                // see also https://mathiasbynens.be/demo/url-regex\n                // modified to allow protocol-relative URLs\n                return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\\/\\/)(?:(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\\]\\[?\\/<~#`!@$^&*()+=}|:\";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$/i.test( value );\n            },\n\n            // https://jqueryvalidation.org/date-method/\n            date: ( function() {\n                var called = false;\n\n                return function( value, element ) {\n                    if ( !called ) {\n                        called = true;\n                        if ( this.settings.debug && window.console ) {\n                            console.warn(\n                                \"The `date` method is deprecated and will be removed in version '2.0.0'.\\n\" +\n                                \"Please don't use it, since it relies on the Date constructor, which\\n\" +\n                                \"behaves very differently across browsers and locales. Use `dateISO`\\n\" +\n                                \"instead or one of the locale specific methods in `localizations/`\\n\" +\n                                \"and `additional-methods.js`.\"\n                            );\n                        }\n                    }\n\n                    return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );\n                };\n            }() ),\n\n            // https://jqueryvalidation.org/dateISO-method/\n            dateISO: function( value, element ) {\n                return this.optional( element ) || /^\\d{4}[\\/\\-](0?[1-9]|1[012])[\\/\\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/number-method/\n            number: function( value, element ) {\n                return this.optional( element ) || /^(?:-?\\d+|-?\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/digits-method/\n            digits: function( value, element ) {\n                return this.optional( element ) || /^\\d+$/.test( value );\n            },\n\n            // https://jqueryvalidation.org/minlength-method/\n            minlength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || length >= param;\n            },\n\n            // https://jqueryvalidation.org/maxlength-method/\n            maxlength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || length <= param;\n            },\n\n            // https://jqueryvalidation.org/rangelength-method/\n            rangelength: function( value, element, param ) {\n                var length = Array.isArray( value ) ? value.length : this.getLength( value, element );\n                return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );\n            },\n\n            // https://jqueryvalidation.org/min-method/\n            min: function( value, element, param ) {\n                return this.optional( element ) || value >= param;\n            },\n\n            // https://jqueryvalidation.org/max-method/\n            max: function( value, element, param ) {\n                return this.optional( element ) || value <= param;\n            },\n\n            // https://jqueryvalidation.org/range-method/\n            range: function( value, element, param ) {\n                return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );\n            },\n\n            // https://jqueryvalidation.org/step-method/\n            step: function( value, element, param ) {\n                var type = $( element ).attr( \"type\" ),\n                    errorMessage = \"Step attribute on input type \" + type + \" is not supported.\",\n                    supportedTypes = [ \"text\", \"number\", \"range\" ],\n                    re = new RegExp( \"\\\\b\" + type + \"\\\\b\" ),\n                    notSupported = type && !re.test( supportedTypes.join() ),\n                    decimalPlaces = function( num ) {\n                        var match = ( \"\" + num ).match( /(?:\\.(\\d+))?$/ );\n                        if ( !match ) {\n                            return 0;\n                        }\n\n                        // Number of digits right of decimal point.\n                        return match[ 1 ] ? match[ 1 ].length : 0;\n                    },\n                    toInt = function( num ) {\n                        return Math.round( num * Math.pow( 10, decimals ) );\n                    },\n                    valid = true,\n                    decimals;\n\n                // Works only for text, number and range input types\n                // TODO find a way to support input types date, datetime, datetime-local, month, time and week\n                if ( notSupported ) {\n                    throw new Error( errorMessage );\n                }\n\n                decimals = decimalPlaces( param );\n\n                // Value can't have too many decimals\n                if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {\n                    valid = false;\n                }\n\n                return this.optional( element ) || valid;\n            },\n\n            // https://jqueryvalidation.org/equalTo-method/\n            equalTo: function( value, element, param ) {\n\n                // Bind to the blur event of the target in order to revalidate whenever the target field is updated\n                var target = $( param );\n                if ( this.settings.onfocusout && target.not( \".validate-equalTo-blur\" ).length ) {\n                    target.addClass( \"validate-equalTo-blur\" ).on( \"blur.validate-equalTo\", function() {\n                        $( element ).valid();\n                    } );\n                }\n                return value === target.val();\n            },\n\n            // https://jqueryvalidation.org/remote-method/\n            remote: function( value, element, param, method ) {\n                if ( this.optional( element ) ) {\n                    return \"dependency-mismatch\";\n                }\n\n                method = typeof method === \"string\" && method || \"remote\";\n\n                var previous = this.previousValue( element, method ),\n                    validator, data, optionDataString;\n\n                if ( !this.settings.messages[ element.name ] ) {\n                    this.settings.messages[ element.name ] = {};\n                }\n                previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];\n                this.settings.messages[ element.name ][ method ] = previous.message;\n\n                param = typeof param === \"string\" && { url: param } || param;\n                optionDataString = $.param( $.extend( { data: value }, param.data ) );\n                if ( previous.old === optionDataString ) {\n                    return previous.valid;\n                }\n\n                previous.old = optionDataString;\n                validator = this;\n                this.startRequest( element );\n                data = {};\n                data[ element.name ] = value;\n                $.ajax( $.extend( true, {\n                    mode: \"abort\",\n                    port: \"validate\" + element.name,\n                    dataType: \"json\",\n                    data: data,\n                    context: validator.currentForm,\n                    success: function( response ) {\n                        var valid = response === true || response === \"true\",\n                            errors, message, submitted;\n\n                        validator.settings.messages[ element.name ][ method ] = previous.originalMessage;\n                        if ( valid ) {\n                            submitted = validator.formSubmitted;\n                            validator.resetInternals();\n                            validator.toHide = validator.errorsFor( element );\n                            validator.formSubmitted = submitted;\n                            validator.successList.push( element );\n                            validator.invalid[ element.name ] = false;\n                            validator.showErrors();\n                        } else {\n                            errors = {};\n                            message = response || validator.defaultMessage( element, { method: method, parameters: value } );\n                            errors[ element.name ] = previous.message = message;\n                            validator.invalid[ element.name ] = true;\n                            validator.showErrors( errors );\n                        }\n                        previous.valid = valid;\n                        validator.stopRequest( element, valid );\n                    }\n                }, param ) );\n                return \"pending\";\n            }\n        }\n\n    } );\n\n// Ajax mode: abort\n// usage: $.ajax({ mode: \"abort\"[, port: \"uniqueport\"]});\n// if mode:\"abort\" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()\n\n    var pendingRequests = {},\n        ajax;\n\n// Use a prefilter if available (1.5+)\n    if ( $.ajaxPrefilter ) {\n        $.ajaxPrefilter( function( settings, _, xhr ) {\n            var port = settings.port;\n            if ( settings.mode === \"abort\" ) {\n                if ( pendingRequests[ port ] ) {\n                    pendingRequests[ port ].abort();\n                }\n                pendingRequests[ port ] = xhr;\n            }\n        } );\n    } else {\n\n        // Proxy ajax\n        ajax = $.ajax;\n        $.ajax = function( settings ) {\n            var mode = ( \"mode\" in settings ? settings : $.ajaxSettings ).mode,\n                port = ( \"port\" in settings ? settings : $.ajaxSettings ).port;\n            if ( mode === \"abort\" ) {\n                if ( pendingRequests[ port ] ) {\n                    pendingRequests[ port ].abort();\n                }\n                pendingRequests[ port ] = ajax.apply( this, arguments );\n                return pendingRequests[ port ];\n            }\n            return ajax.apply( this, arguments );\n        };\n    }\n    return $;\n}));\n","jquery/z-index.js":"/*!\n * zIndex plugin from jQuery UI Core - v1.10.4\n * http://jqueryui.com\n *\n * Copyright 2014 jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n *\n * http://api.jqueryui.com/category/ui-core/\n */\ndefine([\n    'jquery'\n], function ($, undefined) {\n\n// plugins\n    $.fn.extend({\n        zIndex: function (zIndex) {\n            if (zIndex !== undefined) {\n                return this.css(\"zIndex\", zIndex);\n            }\n\n            if (this.length) {\n                var elem = $(this[0]), position, value;\n                while (elem.length && elem[0] !== document) {\n                    // Ignore z-index if position is set to a value where z-index is ignored by the browser\n                    // This makes behavior of this function consistent across browsers\n                    // WebKit always returns auto if the element is positioned\n                    position = elem.css(\"position\");\n                    if (position === \"absolute\" || position === \"relative\" || position === \"fixed\") {\n                        // IE returns 0 when zIndex is not specified\n                        // other browsers return a string\n                        // we ignore the case of nested elements with an explicit value of 0\n                        // <div style=\"z-index: -10;\"><div style=\"z-index: 0;\"></div></div>\n                        value = parseInt(elem.css(\"zIndex\"), 10);\n                        if (!isNaN(value) && value !== 0) {\n                            return value;\n                        }\n                    }\n                    elem = elem.parent();\n                }\n            }\n\n            return 0;\n        }\n    });\n});\n","jquery/jquery.tabs.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    \"jquery\",\n    \"jquery/bootstrap/tab\",\n    \"jquery/bootstrap/collapse\",\n], function () {\n\n});\n","jquery/jquery.metadata.js":"/*\n * Metadata - jQuery plugin for parsing metadata from elements\n *\n * Copyright (c) 2006 John Resig, Yehuda Katz, J\u00ef\u00bf\u00bd\u00c3\u00b6rn Zaefferer, Paul McLanahan\n *\n * Dual licensed under the MIT and GPL licenses:\n *   http://www.opensource.org/licenses/mit-license.php\n *   http://www.gnu.org/licenses/gpl.html\n *\n * Revision: $Id: jquery.metadata.js 3640 2007-10-11 18:34:38Z pmclanahan $\n *\n */\n\n/**\n * Sets the type of metadata to use. Metadata is encoded in JSON, and each property\n * in the JSON will become a property of the element itself.\n *\n * There are four supported types of metadata storage:\n *\n *   attr:  Inside an attribute. The name parameter indicates *which* attribute.\n *\n *   class: Inside the class attribute, wrapped in curly braces: { }\n *\n *   elem:  Inside a child element (e.g. a script tag). The\n *          name parameter indicates *which* element.\n *   html5: Values are stored in data-* attributes.\n *\n * The metadata for an element is loaded the first time the element is accessed via jQuery.\n *\n * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements\n * matched by expr, then redefine the metadata type and run another $(expr) for other elements.\n *\n * @name $.metadata.setType\n *\n * @example <p id=\"one\" class=\"some_class {item_id: 1, item_label: 'Label'}\">This is a p</p>\n * @before $.metadata.setType(\"class\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from the class attribute\n *\n * @example <p id=\"one\" class=\"some_class\" data=\"{item_id: 1, item_label: 'Label'}\">This is a p</p>\n * @before $.metadata.setType(\"attr\", \"data\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a \"data\" attribute\n *\n * @example <p id=\"one\" class=\"some_class\"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>\n * @before $.metadata.setType(\"elem\", \"script\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a nested script element\n *\n * @example <p id=\"one\" class=\"some_class\" data-item_id=\"1\" data-item_label=\"Label\">This is a p</p>\n * @before $.metadata.setType(\"html5\")\n * @after $(\"#one\").metadata().item_id == 1; $(\"#one\").metadata().item_label == \"Label\"\n * @desc Reads metadata from a series of data-* attributes\n *\n * @param String type The encoding type\n * @param String name The name of the attribute to be used to get metadata (optional)\n * @cat Plugins/Metadata\n * @descr Sets the type of encoding to be used when loading metadata for the first time\n * @type undefined\n * @see metadata()\n */\n(function (factory) {\n    if (typeof define === 'function' && define.amd) {\n        define([\"jquery\"], factory);\n    } else {\n        factory(jQuery);\n    }\n}(function ($) {\n\n\n    $.extend({\n        metadata : {\n            defaults : {\n                type: 'class',\n                name: 'metadata',\n                cre: /({.*})/,\n                single: 'metadata',\n                meta:'validate'\n            },\n            setType: function( type, name ){\n                this.defaults.type = type;\n                this.defaults.name = name;\n            },\n            get: function( elem, opts ){\n                var settings = $.extend({},this.defaults,opts);\n                // check for empty string in single property\n                if (!settings.single.length) {\n                    settings.single = 'metadata';\n                }\n                if (!settings.meta.length) {\n                    settings.meta = 'validate';\n                }\n\n                var data = $.data(elem, settings.single);\n                // returned cached data if it already exists\n                if ( data ) return data;\n\n                data = \"{}\";\n\n                var getData = function(data) {\n                    if(typeof data != \"string\") return data;\n\n                    if( data.indexOf('{') < 0 ) {\n                        data = eval(\"(\" + data + \")\");\n                    }\n                }\n\n                var getObject = function(data) {\n                    if(typeof data != \"string\") return data;\n\n                    data = eval(\"(\" + data + \")\");\n                    return data;\n                }\n\n                if ( settings.type == \"html5\" ) {\n                    var object = {};\n                    $( elem.attributes ).each(function() {\n                        var name = this.nodeName;\n                        if (name.indexOf('data-' + settings.meta) === 0) {\n                            name = name.replace(/^data-/, '');\n                        }\n                        else {\n                            return true;\n                        }\n                        object[name] = getObject(this.value);\n                    });\n                } else {\n                    if ( settings.type == \"class\" ) {\n                        var m = settings.cre.exec( elem.className );\n                        if ( m )\n                            data = m[1];\n                    } else if ( settings.type == \"elem\" ) {\n                        if( !elem.getElementsByTagName ) return;\n                        var e = elem.getElementsByTagName(settings.name);\n                        if ( e.length )\n                            data = $.trim(e[0].innerHTML);\n                    } else if ( elem.getAttribute != undefined ) {\n                        var attr = elem.getAttribute( settings.name );\n                        if ( attr )\n                            data = attr;\n                    }\n                    object = getObject(data.indexOf(\"{\") < 0 ? \"{\" + data + \"}\" : data);\n                }\n\n                $.data( elem, settings.single, object );\n                return object;\n            }\n        }\n    });\n\n    /**\n     * Returns the metadata object for the first member of the jQuery object.\n     *\n     * @name metadata\n     * @descr Returns element's metadata object\n     * @param Object opts An object contianing settings to override the defaults\n     * @type jQuery\n     * @cat Plugins/Metadata\n     */\n    $.fn.metadata = function( opts ){\n        return $.metadata.get( this[0], opts );\n    };\n\n}));","jquery/jquery.cookie.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'js-cookie/cookie-wrapper'\n], function () {\n\n});\n","jquery/spectrum/spectrum.js":"// Spectrum Colorpicker v1.8.1\n// https://github.com/bgrins/spectrum\n// Author: Brian Grinstead\n// License: MIT\n\n(function (factory) {\n    \"use strict\";\n\n    if (typeof define === 'function' && define.amd) { // AMD\n        define(['jquery'], factory);\n    }\n    else if (typeof exports == \"object\" && typeof module == \"object\") { // CommonJS\n        module.exports = factory(require('jquery'));\n    }\n    else { // Browser\n        factory(jQuery);\n    }\n})(function($, undefined) {\n    \"use strict\";\n\n    var defaultOpts = {\n\n            // Callbacks\n            beforeShow: noop,\n            move: noop,\n            change: noop,\n            show: noop,\n            hide: noop,\n\n            // Options\n            color: false,\n            flat: false,\n            showInput: false,\n            allowEmpty: false,\n            showButtons: true,\n            clickoutFiresChange: true,\n            showInitial: false,\n            showPalette: false,\n            showPaletteOnly: false,\n            hideAfterPaletteSelect: false,\n            togglePaletteOnly: false,\n            showSelectionPalette: true,\n            localStorageKey: false,\n            appendTo: \"body\",\n            maxSelectionSize: 7,\n            cancelText: \"cancel\",\n            chooseText: \"choose\",\n            togglePaletteMoreText: \"more\",\n            togglePaletteLessText: \"less\",\n            clearText: \"Clear Color Selection\",\n            noColorSelectedText: \"No Color Selected\",\n            preferredFormat: false,\n            className: \"\", // Deprecated - use containerClassName and replacerClassName instead.\n            containerClassName: \"\",\n            replacerClassName: \"\",\n            showAlpha: false,\n            theme: \"sp-light\",\n            palette: [[\"#ffffff\", \"#000000\", \"#ff0000\", \"#ff8000\", \"#ffff00\", \"#008000\", \"#0000ff\", \"#4b0082\", \"#9400d3\"]],\n            selectionPalette: [],\n            disabled: false,\n            offset: null\n        },\n        spectrums = [],\n        IE = !!/msie/i.exec( window.navigator.userAgent ),\n        rgbaSupport = (function() {\n            function contains( str, substr ) {\n                return !!~('' + str).indexOf(substr);\n            }\n\n            var elem = document.createElement('div');\n            var style = elem.style;\n            style.cssText = 'background-color:rgba(0,0,0,.5)';\n            return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');\n        })(),\n        replaceInput = [\n            \"<div class='sp-replacer'>\",\n            \"<div class='sp-preview'><div class='sp-preview-inner'></div></div>\",\n            \"<div class='sp-dd'>&#9660;</div>\",\n            \"</div>\"\n        ].join(''),\n        markup = (function () {\n\n            // IE does not support gradients with multiple stops, so we need to simulate\n            //  that for the rainbow slider with 8 divs that each have a single gradient\n            var gradientFix = \"\";\n            if (IE) {\n                for (var i = 1; i <= 6; i++) {\n                    gradientFix += \"<div class='sp-\" + i + \"'></div>\";\n                }\n            }\n\n            return [\n                \"<div class='sp-container sp-hidden'>\",\n                \"<div class='sp-palette-container'>\",\n                \"<div class='sp-palette sp-thumb sp-cf'></div>\",\n                \"<div class='sp-palette-button-container sp-cf'>\",\n                \"<button type='button' class='sp-palette-toggle'></button>\",\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-picker-container'>\",\n                \"<div class='sp-top sp-cf'>\",\n                \"<div class='sp-fill'></div>\",\n                \"<div class='sp-top-inner'>\",\n                \"<div class='sp-color'>\",\n                \"<div class='sp-sat'>\",\n                \"<div class='sp-val'>\",\n                \"<div class='sp-dragger'></div>\",\n                \"</div>\",\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-clear sp-clear-display'>\",\n                \"</div>\",\n                \"<div class='sp-hue'>\",\n                \"<div class='sp-slider'></div>\",\n                gradientFix,\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>\",\n                \"</div>\",\n                \"<div class='sp-input-container sp-cf'>\",\n                \"<input class='sp-input' type='text' spellcheck='false'  />\",\n                \"</div>\",\n                \"<div class='sp-initial sp-thumb sp-cf'></div>\",\n                \"<div class='sp-button-container sp-cf'>\",\n                \"<a class='sp-cancel' href='#'></a>\",\n                \"<button type='button' class='sp-choose'></button>\",\n                \"</div>\",\n                \"</div>\",\n                \"</div>\"\n            ].join(\"\");\n        })();\n\n    function paletteTemplate (p, color, className, opts) {\n        var html = [];\n        for (var i = 0; i < p.length; i++) {\n            var current = p[i];\n            if(current) {\n                var tiny = tinycolor(current);\n                var c = tiny.toHsl().l < 0.5 ? \"sp-thumb-el sp-thumb-dark\" : \"sp-thumb-el sp-thumb-light\";\n                c += (tinycolor.equals(color, current)) ? \" sp-thumb-active\" : \"\";\n                var formattedString = tiny.toString(opts.preferredFormat || \"rgb\");\n                var swatchStyle = rgbaSupport ? (\"background-color:\" + tiny.toRgbString()) : \"filter:\" + tiny.toFilter();\n                html.push('<span title=\"' + formattedString + '\" data-color=\"' + tiny.toRgbString() + '\" class=\"' + c + '\"><span class=\"sp-thumb-inner\" style=\"' + swatchStyle + ';\"></span></span>');\n            } else {\n                var cls = 'sp-clear-display';\n                html.push($('<div />')\n                    .append($('<span data-color=\"\" style=\"background-color:transparent;\" class=\"' + cls + '\"></span>')\n                        .attr('title', opts.noColorSelectedText)\n                    )\n                    .html()\n                );\n            }\n        }\n        return \"<div class='sp-cf \" + className + \"'>\" + html.join('') + \"</div>\";\n    }\n\n    function hideAll() {\n        for (var i = 0; i < spectrums.length; i++) {\n            if (spectrums[i]) {\n                spectrums[i].hide();\n            }\n        }\n    }\n\n    function instanceOptions(o, callbackContext) {\n        var opts = $.extend({}, defaultOpts, o);\n        opts.callbacks = {\n            'move': bind(opts.move, callbackContext),\n            'change': bind(opts.change, callbackContext),\n            'show': bind(opts.show, callbackContext),\n            'hide': bind(opts.hide, callbackContext),\n            'beforeShow': bind(opts.beforeShow, callbackContext)\n        };\n\n        return opts;\n    }\n\n    function spectrum(element, o) {\n\n        var opts = instanceOptions(o, element),\n            flat = opts.flat,\n            showSelectionPalette = opts.showSelectionPalette,\n            localStorageKey = opts.localStorageKey,\n            theme = opts.theme,\n            callbacks = opts.callbacks,\n            resize = throttle(reflow, 10),\n            visible = false,\n            isDragging = false,\n            dragWidth = 0,\n            dragHeight = 0,\n            dragHelperHeight = 0,\n            slideHeight = 0,\n            slideWidth = 0,\n            alphaWidth = 0,\n            alphaSlideHelperWidth = 0,\n            slideHelperHeight = 0,\n            currentHue = 0,\n            currentSaturation = 0,\n            currentValue = 0,\n            currentAlpha = 1,\n            palette = [],\n            paletteArray = [],\n            paletteLookup = {},\n            selectionPalette = opts.selectionPalette.slice(0),\n            maxSelectionSize = opts.maxSelectionSize,\n            draggingClass = \"sp-dragging\",\n            shiftMovementDirection = null;\n\n        var doc = element.ownerDocument,\n            body = doc.body,\n            boundElement = $(element),\n            disabled = false,\n            container = $(markup, doc).addClass(theme),\n            pickerContainer = container.find(\".sp-picker-container\"),\n            dragger = container.find(\".sp-color\"),\n            dragHelper = container.find(\".sp-dragger\"),\n            slider = container.find(\".sp-hue\"),\n            slideHelper = container.find(\".sp-slider\"),\n            alphaSliderInner = container.find(\".sp-alpha-inner\"),\n            alphaSlider = container.find(\".sp-alpha\"),\n            alphaSlideHelper = container.find(\".sp-alpha-handle\"),\n            textInput = container.find(\".sp-input\"),\n            paletteContainer = container.find(\".sp-palette\"),\n            initialColorContainer = container.find(\".sp-initial\"),\n            cancelButton = container.find(\".sp-cancel\"),\n            clearButton = container.find(\".sp-clear\"),\n            chooseButton = container.find(\".sp-choose\"),\n            toggleButton = container.find(\".sp-palette-toggle\"),\n            isInput = boundElement.is(\"input\"),\n            isInputTypeColor = isInput && boundElement.attr(\"type\") === \"color\" && inputTypeColorSupport(),\n            shouldReplace = isInput && !flat,\n            replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),\n            offsetElement = (shouldReplace) ? replacer : boundElement,\n            previewElement = replacer.find(\".sp-preview-inner\"),\n            initialColor = opts.color || (isInput && boundElement.val()),\n            colorOnShow = false,\n            currentPreferredFormat = opts.preferredFormat,\n            clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,\n            isEmpty = !initialColor,\n            allowEmpty = opts.allowEmpty && !isInputTypeColor;\n\n        function applyOptions() {\n\n            if (opts.showPaletteOnly) {\n                opts.showPalette = true;\n            }\n\n            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);\n\n            if (opts.palette) {\n                palette = opts.palette.slice(0);\n                paletteArray = $.isArray(palette[0]) ? palette : [palette];\n                paletteLookup = {};\n                for (var i = 0; i < paletteArray.length; i++) {\n                    for (var j = 0; j < paletteArray[i].length; j++) {\n                        var rgb = tinycolor(paletteArray[i][j]).toRgbString();\n                        paletteLookup[rgb] = true;\n                    }\n                }\n            }\n\n            container.toggleClass(\"sp-flat\", flat);\n            container.toggleClass(\"sp-input-disabled\", !opts.showInput);\n            container.toggleClass(\"sp-alpha-enabled\", opts.showAlpha);\n            container.toggleClass(\"sp-clear-enabled\", allowEmpty);\n            container.toggleClass(\"sp-buttons-disabled\", !opts.showButtons);\n            container.toggleClass(\"sp-palette-buttons-disabled\", !opts.togglePaletteOnly);\n            container.toggleClass(\"sp-palette-disabled\", !opts.showPalette);\n            container.toggleClass(\"sp-palette-only\", opts.showPaletteOnly);\n            container.toggleClass(\"sp-initial-disabled\", !opts.showInitial);\n            container.addClass(opts.className).addClass(opts.containerClassName);\n\n            reflow();\n        }\n\n        function initialize() {\n\n            if (IE) {\n                container.find(\"*:not(input)\").attr(\"unselectable\", \"on\");\n            }\n\n            applyOptions();\n\n            if (shouldReplace) {\n                boundElement.after(replacer).hide();\n            }\n\n            if (!allowEmpty) {\n                clearButton.hide();\n            }\n\n            if (flat) {\n                boundElement.after(container).hide();\n            }\n            else {\n\n                var appendTo = opts.appendTo === \"parent\" ? boundElement.parent() : $(opts.appendTo);\n                if (appendTo.length !== 1) {\n                    appendTo = $(\"body\");\n                }\n\n                appendTo.append(container);\n            }\n\n            updateSelectionPaletteFromStorage();\n\n            offsetElement.on(\"click.spectrum touchstart.spectrum\", function (e) {\n                if (!disabled) {\n                    toggle();\n                }\n\n                e.stopPropagation();\n\n                if (!$(e.target).is(\"input\")) {\n                    e.preventDefault();\n                }\n            });\n\n            if(boundElement.is(\":disabled\") || (opts.disabled === true)) {\n                disable();\n            }\n\n            // Prevent clicks from bubbling up to document.  This would cause it to be hidden.\n            container.click(stopPropagation);\n\n            // Handle user typed input\n            textInput.change(setFromTextInput);\n            textInput.on(\"paste\", function () {\n                setTimeout(setFromTextInput, 1);\n            });\n            textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });\n\n            cancelButton.text(opts.cancelText);\n            cancelButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n                revert();\n                hide();\n            });\n\n            clearButton.attr(\"title\", opts.clearText);\n            clearButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n                isEmpty = true;\n                move();\n\n                if(flat) {\n                    //for the flat style, this is a change event\n                    updateOriginalInput(true);\n                }\n            });\n\n            chooseButton.text(opts.chooseText);\n            chooseButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n\n                if (IE && textInput.is(\":focus\")) {\n                    textInput.trigger('change');\n                }\n\n                if (isValid()) {\n                    updateOriginalInput(true);\n                    hide();\n                }\n            });\n\n            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);\n            toggleButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n\n                opts.showPaletteOnly = !opts.showPaletteOnly;\n\n                // To make sure the Picker area is drawn on the right, next to the\n                // Palette area (and not below the palette), first move the Palette\n                // to the left to make space for the picker, plus 5px extra.\n                // The 'applyOptions' function puts the whole container back into place\n                // and takes care of the button-text and the sp-palette-only CSS class.\n                if (!opts.showPaletteOnly && !flat) {\n                    container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));\n                }\n                applyOptions();\n            });\n\n            draggable(alphaSlider, function (dragX, dragY, e) {\n                currentAlpha = (dragX / alphaWidth);\n                isEmpty = false;\n                if (e.shiftKey) {\n                    currentAlpha = Math.round(currentAlpha * 10) / 10;\n                }\n\n                move();\n            }, dragStart, dragStop);\n\n            draggable(slider, function (dragX, dragY) {\n                currentHue = parseFloat(dragY / slideHeight);\n                isEmpty = false;\n                if (!opts.showAlpha) {\n                    currentAlpha = 1;\n                }\n                move();\n            }, dragStart, dragStop);\n\n            draggable(dragger, function (dragX, dragY, e) {\n\n                // shift+drag should snap the movement to either the x or y axis.\n                if (!e.shiftKey) {\n                    shiftMovementDirection = null;\n                }\n                else if (!shiftMovementDirection) {\n                    var oldDragX = currentSaturation * dragWidth;\n                    var oldDragY = dragHeight - (currentValue * dragHeight);\n                    var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);\n\n                    shiftMovementDirection = furtherFromX ? \"x\" : \"y\";\n                }\n\n                var setSaturation = !shiftMovementDirection || shiftMovementDirection === \"x\";\n                var setValue = !shiftMovementDirection || shiftMovementDirection === \"y\";\n\n                if (setSaturation) {\n                    currentSaturation = parseFloat(dragX / dragWidth);\n                }\n                if (setValue) {\n                    currentValue = parseFloat((dragHeight - dragY) / dragHeight);\n                }\n\n                isEmpty = false;\n                if (!opts.showAlpha) {\n                    currentAlpha = 1;\n                }\n\n                move();\n\n            }, dragStart, dragStop);\n\n            if (!!initialColor) {\n                set(initialColor);\n\n                // In case color was black - update the preview UI and set the format\n                // since the set function will not run (default color is black).\n                updateUI();\n                currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;\n\n                addColorToSelectionPalette(initialColor);\n            }\n            else {\n                updateUI();\n            }\n\n            if (flat) {\n                show();\n            }\n\n            function paletteElementClick(e) {\n                if (e.data && e.data.ignore) {\n                    set($(e.target).closest(\".sp-thumb-el\").data(\"color\"));\n                    move();\n                }\n                else {\n                    set($(e.target).closest(\".sp-thumb-el\").data(\"color\"));\n                    move();\n\n                    updateOriginalInput(true);\n                    if (opts.hideAfterPaletteSelect) {\n                        hide();\n                    }\n                }\n\n                return false;\n            }\n\n            var paletteEvent = IE ? \"mousedown.spectrum\" : \"click.spectrum touchstart.spectrum\";\n            paletteContainer.on(paletteEvent, \".sp-thumb-el\", paletteElementClick);\n            initialColorContainer.on(paletteEvent, \".sp-thumb-el:nth-child(1)\", { ignore: true }, paletteElementClick);\n        }\n\n        function updateSelectionPaletteFromStorage() {\n\n            if (localStorageKey && window.localStorage) {\n\n                // Migrate old palettes over to new format.  May want to remove this eventually.\n                try {\n                    var oldPalette = window.localStorage[localStorageKey].split(\",#\");\n                    if (oldPalette.length > 1) {\n                        delete window.localStorage[localStorageKey];\n                        $.each(oldPalette, function(i, c) {\n                            addColorToSelectionPalette(c);\n                        });\n                    }\n                }\n                catch(e) { }\n\n                try {\n                    selectionPalette = window.localStorage[localStorageKey].split(\";\");\n                }\n                catch (e) { }\n            }\n        }\n\n        function addColorToSelectionPalette(color) {\n            if (showSelectionPalette) {\n                var rgb = tinycolor(color).toRgbString();\n                if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {\n                    selectionPalette.push(rgb);\n                    while(selectionPalette.length > maxSelectionSize) {\n                        selectionPalette.shift();\n                    }\n                }\n\n                if (localStorageKey && window.localStorage) {\n                    try {\n                        window.localStorage[localStorageKey] = selectionPalette.join(\";\");\n                    }\n                    catch(e) { }\n                }\n            }\n        }\n\n        function getUniqueSelectionPalette() {\n            var unique = [];\n            if (opts.showPalette) {\n                for (var i = 0; i < selectionPalette.length; i++) {\n                    var rgb = tinycolor(selectionPalette[i]).toRgbString();\n\n                    if (!paletteLookup[rgb]) {\n                        unique.push(selectionPalette[i]);\n                    }\n                }\n            }\n\n            return unique.reverse().slice(0, opts.maxSelectionSize);\n        }\n\n        function drawPalette() {\n\n            var currentColor = get();\n\n            var html = $.map(paletteArray, function (palette, i) {\n                return paletteTemplate(palette, currentColor, \"sp-palette-row sp-palette-row-\" + i, opts);\n            });\n\n            updateSelectionPaletteFromStorage();\n\n            if (selectionPalette) {\n                html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, \"sp-palette-row sp-palette-row-selection\", opts));\n            }\n\n            paletteContainer.html(html.join(\"\"));\n        }\n\n        function drawInitial() {\n            if (opts.showInitial) {\n                var initial = colorOnShow;\n                var current = get();\n                initialColorContainer.html(paletteTemplate([initial, current], current, \"sp-palette-row-initial\", opts));\n            }\n        }\n\n        function dragStart() {\n            if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {\n                reflow();\n            }\n            isDragging = true;\n            container.addClass(draggingClass);\n            shiftMovementDirection = null;\n            boundElement.trigger('dragstart.spectrum', [ get() ]);\n        }\n\n        function dragStop() {\n            isDragging = false;\n            container.removeClass(draggingClass);\n            boundElement.trigger('dragstop.spectrum', [ get() ]);\n        }\n\n        function setFromTextInput() {\n\n            var value = textInput.val();\n\n            if ((value === null || value === \"\") && allowEmpty) {\n                set(null);\n                move();\n                updateOriginalInput();\n            }\n            else {\n                var tiny = tinycolor(value);\n                if (tiny.isValid()) {\n                    set(tiny);\n                    move();\n                    updateOriginalInput(true);\n                }\n                else {\n                    textInput.addClass(\"sp-validation-error\");\n                }\n            }\n        }\n\n        function toggle() {\n            if (visible) {\n                hide();\n            }\n            else {\n                show();\n            }\n        }\n\n        function show() {\n            var event = $.Event('beforeShow.spectrum');\n\n            if (visible) {\n                reflow();\n                return;\n            }\n\n            boundElement.trigger(event, [ get() ]);\n\n            if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {\n                return;\n            }\n\n            hideAll();\n            visible = true;\n\n            $(doc).on(\"keydown.spectrum\", onkeydown);\n            $(doc).on(\"click.spectrum\", clickout);\n            $(window).on(\"resize.spectrum\", resize);\n            replacer.addClass(\"sp-active\");\n            container.removeClass(\"sp-hidden\");\n\n            reflow();\n            updateUI();\n\n            colorOnShow = get();\n\n            drawInitial();\n            callbacks.show(colorOnShow);\n            boundElement.trigger('show.spectrum', [ colorOnShow ]);\n        }\n\n        function onkeydown(e) {\n            // Close on ESC\n            if (e.keyCode === 27) {\n                hide();\n            }\n        }\n\n        function clickout(e) {\n            // Return on right click.\n            if (e.button == 2) { return; }\n\n            // If a drag event was happening during the mouseup, don't hide\n            // on click.\n            if (isDragging) { return; }\n\n            if (clickoutFiresChange) {\n                updateOriginalInput(true);\n            }\n            else {\n                revert();\n            }\n            hide();\n        }\n\n        function hide() {\n            // Return if hiding is unnecessary\n            if (!visible || flat) { return; }\n            visible = false;\n\n            $(doc).off(\"keydown.spectrum\", onkeydown);\n            $(doc).off(\"click.spectrum\", clickout);\n            $(window).off(\"resize.spectrum\", resize);\n\n            replacer.removeClass(\"sp-active\");\n            container.addClass(\"sp-hidden\");\n\n            callbacks.hide(get());\n            boundElement.trigger('hide.spectrum', [ get() ]);\n        }\n\n        function revert() {\n            set(colorOnShow, true);\n            updateOriginalInput(true);\n        }\n\n        function set(color, ignoreFormatChange) {\n            if (tinycolor.equals(color, get())) {\n                // Update UI just in case a validation error needs\n                // to be cleared.\n                updateUI();\n                return;\n            }\n\n            var newColor, newHsv;\n            if (!color && allowEmpty) {\n                isEmpty = true;\n            } else {\n                isEmpty = false;\n                newColor = tinycolor(color);\n                newHsv = newColor.toHsv();\n\n                currentHue = (newHsv.h % 360) / 360;\n                currentSaturation = newHsv.s;\n                currentValue = newHsv.v;\n                currentAlpha = newHsv.a;\n            }\n            updateUI();\n\n            if (newColor && newColor.isValid() && !ignoreFormatChange) {\n                currentPreferredFormat = opts.preferredFormat || newColor.getFormat();\n            }\n        }\n\n        function get(opts) {\n            opts = opts || { };\n\n            if (allowEmpty && isEmpty) {\n                return null;\n            }\n\n            return tinycolor.fromRatio({\n                h: currentHue,\n                s: currentSaturation,\n                v: currentValue,\n                a: Math.round(currentAlpha * 1000) / 1000\n            }, { format: opts.format || currentPreferredFormat });\n        }\n\n        function isValid() {\n            return !textInput.hasClass(\"sp-validation-error\");\n        }\n\n        function move() {\n            updateUI();\n\n            callbacks.move(get());\n            boundElement.trigger('move.spectrum', [ get() ]);\n        }\n\n        function updateUI() {\n\n            textInput.removeClass(\"sp-validation-error\");\n\n            updateHelperLocations();\n\n            // Update dragger background color (gradients take care of saturation and value).\n            var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });\n            dragger.css(\"background-color\", flatColor.toHexString());\n\n            // Get a format that alpha will be included in (hex and names ignore alpha)\n            var format = currentPreferredFormat;\n            if (currentAlpha < 1 && !(currentAlpha === 0 && format === \"name\")) {\n                if (format === \"hex\" || format === \"hex3\" || format === \"hex6\" || format === \"name\") {\n                    format = \"rgb\";\n                }\n            }\n\n            var realColor = get({ format: format }),\n                displayColor = '';\n\n            //reset background info for preview element\n            previewElement.removeClass(\"sp-clear-display\");\n            previewElement.css('background-color', 'transparent');\n\n            if (!realColor && allowEmpty) {\n                // Update the replaced elements background with icon indicating no color selection\n                previewElement.addClass(\"sp-clear-display\");\n            }\n            else {\n                var realHex = realColor.toHexString(),\n                    realRgb = realColor.toRgbString();\n\n                // Update the replaced elements background color (with actual selected color)\n                if (rgbaSupport || realColor.alpha === 1) {\n                    previewElement.css(\"background-color\", realRgb);\n                }\n                else {\n                    previewElement.css(\"background-color\", \"transparent\");\n                    previewElement.css(\"filter\", realColor.toFilter());\n                }\n\n                if (opts.showAlpha) {\n                    var rgb = realColor.toRgb();\n                    rgb.a = 0;\n                    var realAlpha = tinycolor(rgb).toRgbString();\n                    var gradient = \"linear-gradient(left, \" + realAlpha + \", \" + realHex + \")\";\n\n                    if (IE) {\n                        alphaSliderInner.css(\"filter\", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));\n                    }\n                    else {\n                        alphaSliderInner.css(\"background\", \"-webkit-\" + gradient);\n                        alphaSliderInner.css(\"background\", \"-moz-\" + gradient);\n                        alphaSliderInner.css(\"background\", \"-ms-\" + gradient);\n                        // Use current syntax gradient on unprefixed property.\n                        alphaSliderInner.css(\"background\",\n                            \"linear-gradient(to right, \" + realAlpha + \", \" + realHex + \")\");\n                    }\n                }\n\n                displayColor = realColor.toString(format);\n            }\n\n            // Update the text entry input as it changes happen\n            if (opts.showInput) {\n                textInput.val(displayColor);\n            }\n\n            if (opts.showPalette) {\n                drawPalette();\n            }\n\n            drawInitial();\n        }\n\n        function updateHelperLocations() {\n            var s = currentSaturation;\n            var v = currentValue;\n\n            if(allowEmpty && isEmpty) {\n                //if selected color is empty, hide the helpers\n                alphaSlideHelper.hide();\n                slideHelper.hide();\n                dragHelper.hide();\n            }\n            else {\n                //make sure helpers are visible\n                alphaSlideHelper.show();\n                slideHelper.show();\n                dragHelper.show();\n\n                // Where to show the little circle in that displays your current selected color\n                var dragX = s * dragWidth;\n                var dragY = dragHeight - (v * dragHeight);\n                dragX = Math.max(\n                    -dragHelperHeight,\n                    Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)\n                );\n                dragY = Math.max(\n                    -dragHelperHeight,\n                    Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)\n                );\n                dragHelper.css({\n                    \"top\": dragY + \"px\",\n                    \"left\": dragX + \"px\"\n                });\n\n                var alphaX = currentAlpha * alphaWidth;\n                alphaSlideHelper.css({\n                    \"left\": (alphaX - (alphaSlideHelperWidth / 2)) + \"px\"\n                });\n\n                // Where to show the bar that displays your current selected hue\n                var slideY = (currentHue) * slideHeight;\n                slideHelper.css({\n                    \"top\": (slideY - slideHelperHeight) + \"px\"\n                });\n            }\n        }\n\n        function updateOriginalInput(fireCallback) {\n            var color = get(),\n                displayColor = '',\n                hasChanged = !tinycolor.equals(color, colorOnShow);\n\n            if (color) {\n                displayColor = color.toString(currentPreferredFormat);\n                // Update the selection palette with the current color\n                addColorToSelectionPalette(color);\n            }\n\n            if (isInput) {\n                boundElement.val(displayColor);\n            }\n\n            if (fireCallback && hasChanged) {\n                callbacks.change(color);\n                boundElement.trigger('change', [ color ]);\n            }\n        }\n\n        function reflow() {\n            if (!visible) {\n                return; // Calculations would be useless and wouldn't be reliable anyways\n            }\n            dragWidth = dragger.width();\n            dragHeight = dragger.height();\n            dragHelperHeight = dragHelper.height();\n            slideWidth = slider.width();\n            slideHeight = slider.height();\n            slideHelperHeight = slideHelper.height();\n            alphaWidth = alphaSlider.width();\n            alphaSlideHelperWidth = alphaSlideHelper.width();\n\n            if (!flat) {\n                container.css(\"position\", \"absolute\");\n                if (opts.offset) {\n                    container.offset(opts.offset);\n                } else {\n                    container.offset(getOffset(container, offsetElement));\n                }\n            }\n\n            updateHelperLocations();\n\n            if (opts.showPalette) {\n                drawPalette();\n            }\n\n            boundElement.trigger('reflow.spectrum');\n        }\n\n        function destroy() {\n            boundElement.show();\n            offsetElement.off(\"click.spectrum touchstart.spectrum\");\n            container.remove();\n            replacer.remove();\n            spectrums[spect.id] = null;\n        }\n\n        function option(optionName, optionValue) {\n            if (optionName === undefined) {\n                return $.extend({}, opts);\n            }\n            if (optionValue === undefined) {\n                return opts[optionName];\n            }\n\n            opts[optionName] = optionValue;\n\n            if (optionName === \"preferredFormat\") {\n                currentPreferredFormat = opts.preferredFormat;\n            }\n            applyOptions();\n        }\n\n        function enable() {\n            disabled = false;\n            boundElement.attr(\"disabled\", false);\n            offsetElement.removeClass(\"sp-disabled\");\n        }\n\n        function disable() {\n            hide();\n            disabled = true;\n            boundElement.attr(\"disabled\", true);\n            offsetElement.addClass(\"sp-disabled\");\n        }\n\n        function setOffset(coord) {\n            opts.offset = coord;\n            reflow();\n        }\n\n        initialize();\n\n        var spect = {\n            show: show,\n            hide: hide,\n            toggle: toggle,\n            reflow: reflow,\n            option: option,\n            enable: enable,\n            disable: disable,\n            offset: setOffset,\n            set: function (c) {\n                set(c);\n                updateOriginalInput();\n            },\n            get: get,\n            destroy: destroy,\n            container: container\n        };\n\n        spect.id = spectrums.push(spect) - 1;\n\n        return spect;\n    }\n\n    /**\n     * checkOffset - get the offset below/above and left/right element depending on screen position\n     * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js\n     */\n    function getOffset(picker, input) {\n        var extraY = 0;\n        var dpWidth = picker.outerWidth();\n        var dpHeight = picker.outerHeight();\n        var inputHeight = input.outerHeight();\n        var doc = picker[0].ownerDocument;\n        var docElem = doc.documentElement;\n        var viewWidth = docElem.clientWidth + $(doc).scrollLeft();\n        var viewHeight = docElem.clientHeight + $(doc).scrollTop();\n        var offset = input.offset();\n        var offsetLeft = offset.left;\n        var offsetTop = offset.top;\n\n        offsetTop += inputHeight;\n\n        offsetLeft -=\n            Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ?\n                Math.abs(offsetLeft + dpWidth - viewWidth) : 0);\n\n        offsetTop -=\n            Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ?\n                Math.abs(dpHeight + inputHeight - extraY) : extraY));\n\n        return {\n            top: offsetTop,\n            bottom: offset.bottom,\n            left: offsetLeft,\n            right: offset.right,\n            width: offset.width,\n            height: offset.height\n        };\n    }\n\n    /**\n     * noop - do nothing\n     */\n    function noop() {\n\n    }\n\n    /**\n     * stopPropagation - makes the code only doing this a little easier to read in line\n     */\n    function stopPropagation(e) {\n        e.stopPropagation();\n    }\n\n    /**\n     * Create a function bound to a given object\n     * Thanks to underscore.js\n     */\n    function bind(func, obj) {\n        var slice = Array.prototype.slice;\n        var args = slice.call(arguments, 2);\n        return function () {\n            return func.apply(obj, args.concat(slice.call(arguments)));\n        };\n    }\n\n    /**\n     * Lightweight drag helper.  Handles containment within the element, so that\n     * when dragging, the x is within [0,element.width] and y is within [0,element.height]\n     */\n    function draggable(element, onmove, onstart, onstop) {\n        onmove = onmove || function () { };\n        onstart = onstart || function () { };\n        onstop = onstop || function () { };\n        var doc = document;\n        var dragging = false;\n        var offset = {};\n        var maxHeight = 0;\n        var maxWidth = 0;\n        var hasTouch = ('ontouchstart' in window);\n\n        var duringDragEvents = {};\n        duringDragEvents[\"selectstart\"] = prevent;\n        duringDragEvents[\"dragstart\"] = prevent;\n        duringDragEvents[\"touchmove mousemove\"] = move;\n        duringDragEvents[\"touchend mouseup\"] = stop;\n\n        function prevent(e) {\n            if (e.stopPropagation) {\n                e.stopPropagation();\n            }\n            if (e.preventDefault) {\n                e.preventDefault();\n            }\n            e.returnValue = false;\n        }\n\n        function move(e) {\n            if (dragging) {\n                // Mouseup happened outside of window\n                if (IE && doc.documentMode < 9 && !e.button) {\n                    return stop();\n                }\n\n                var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];\n                var pageX = t0 && t0.pageX || e.pageX;\n                var pageY = t0 && t0.pageY || e.pageY;\n\n                var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));\n                var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));\n\n                if (hasTouch) {\n                    // Stop scrolling in iOS\n                    prevent(e);\n                }\n\n                onmove.apply(element, [dragX, dragY, e]);\n            }\n        }\n\n        function start(e) {\n            var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);\n\n            if (!rightclick && !dragging) {\n                if (onstart.apply(element, arguments) !== false) {\n                    dragging = true;\n                    maxHeight = $(element).height();\n                    maxWidth = $(element).width();\n                    offset = $(element).offset();\n\n                    $(doc).on(duringDragEvents);\n                    $(doc.body).addClass(\"sp-dragging\");\n\n                    move(e);\n\n                    prevent(e);\n                }\n            }\n        }\n\n        function stop() {\n            if (dragging) {\n                $(doc).off(duringDragEvents);\n                $(doc.body).removeClass(\"sp-dragging\");\n\n                // Wait a tick before notifying observers to allow the click event\n                // to fire in Chrome.\n                setTimeout(function() {\n                    onstop.apply(element, arguments);\n                }, 0);\n            }\n            dragging = false;\n        }\n\n        $(element).on(\"touchstart mousedown\", start);\n    }\n\n    function throttle(func, wait, debounce) {\n        var timeout;\n        return function () {\n            var context = this, args = arguments;\n            var throttler = function () {\n                timeout = null;\n                func.apply(context, args);\n            };\n            if (debounce) clearTimeout(timeout);\n            if (debounce || !timeout) timeout = setTimeout(throttler, wait);\n        };\n    }\n\n    function inputTypeColorSupport() {\n        return $.fn.spectrum.inputTypeColorSupport();\n    }\n\n    /**\n     * Define a jQuery plugin\n     */\n    var dataID = \"spectrum.id\";\n    $.fn.spectrum = function (opts, extra) {\n\n        if (typeof opts == \"string\") {\n\n            var returnValue = this;\n            var args = Array.prototype.slice.call( arguments, 1 );\n\n            this.each(function () {\n                var spect = spectrums[$(this).data(dataID)];\n                if (spect) {\n                    var method = spect[opts];\n                    if (!method) {\n                        throw new Error( \"Spectrum: no such method: '\" + opts + \"'\" );\n                    }\n\n                    if (opts == \"get\") {\n                        returnValue = spect.get();\n                    }\n                    else if (opts == \"container\") {\n                        returnValue = spect.container;\n                    }\n                    else if (opts == \"option\") {\n                        returnValue = spect.option.apply(spect, args);\n                    }\n                    else if (opts == \"destroy\") {\n                        spect.destroy();\n                        $(this).removeData(dataID);\n                    }\n                    else {\n                        method.apply(spect, args);\n                    }\n                }\n            });\n\n            return returnValue;\n        }\n\n        // Initializing a new instance of spectrum\n        return this.spectrum(\"destroy\").each(function () {\n            var options = $.extend({}, $(this).data(), opts);\n            var spect = spectrum(this, options);\n            $(this).data(dataID, spect.id);\n        });\n    };\n\n    $.fn.spectrum.load = true;\n    $.fn.spectrum.loadOpts = {};\n    $.fn.spectrum.draggable = draggable;\n    $.fn.spectrum.defaults = defaultOpts;\n    $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {\n        if (typeof inputTypeColorSupport._cachedResult === \"undefined\") {\n            var colorInput = $(\"<input type='color'/>\")[0]; // if color element is supported, value will default to not null\n            inputTypeColorSupport._cachedResult = colorInput.type === \"color\" && colorInput.value !== \"\";\n        }\n        return inputTypeColorSupport._cachedResult;\n    };\n\n    $.spectrum = { };\n    $.spectrum.localization = { };\n    $.spectrum.palettes = { };\n\n    $.fn.spectrum.processNativeColorInputs = function () {\n        var colorInputs = $(\"input[type=color]\");\n        if (colorInputs.length && !inputTypeColorSupport()) {\n            colorInputs.spectrum({\n                preferredFormat: \"hex6\"\n            });\n        }\n    };\n\n    // TinyColor v1.1.2\n    // https://github.com/bgrins/TinyColor\n    // Brian Grinstead, MIT License\n\n    (function() {\n\n        var trimLeft = /^[\\s,#]+/,\n            trimRight = /\\s+$/,\n            tinyCounter = 0,\n            math = Math,\n            mathRound = math.round,\n            mathMin = math.min,\n            mathMax = math.max,\n            mathRandom = math.random;\n\n        var tinycolor = function(color, opts) {\n\n            color = (color) ? color : '';\n            opts = opts || { };\n\n            // If input is already a tinycolor, return itself\n            if (color instanceof tinycolor) {\n                return color;\n            }\n            // If we are called as a function, call using new instead\n            if (!(this instanceof tinycolor)) {\n                return new tinycolor(color, opts);\n            }\n\n            var rgb = inputToRGB(color);\n            this._originalInput = color;\n            this._r = rgb.r;\n            this._g = rgb.g;\n            this._b = rgb.b;\n            this._a = rgb.a;\n            this._roundA = mathRound(1000 * this._a) / 1000;\n            this._format = opts.format || rgb.format;\n            this._gradientType = opts.gradientType;\n\n            // Don't let the range of [0,255] come back in [0,1].\n            // Potentially lose a little bit of precision here, but will fix issues where\n            // .5 gets interpreted as half of the total, instead of half of 1\n            // If it was supposed to be 128, this was already taken care of by `inputToRgb`\n            if (this._r < 1) { this._r = mathRound(this._r); }\n            if (this._g < 1) { this._g = mathRound(this._g); }\n            if (this._b < 1) { this._b = mathRound(this._b); }\n\n            this._ok = rgb.ok;\n            this._tc_id = tinyCounter++;\n        };\n\n        tinycolor.prototype = {\n            isDark: function() {\n                return this.getBrightness() < 128;\n            },\n            isLight: function() {\n                return !this.isDark();\n            },\n            isValid: function() {\n                return this._ok;\n            },\n            getOriginalInput: function() {\n                return this._originalInput;\n            },\n            getFormat: function() {\n                return this._format;\n            },\n            getAlpha: function() {\n                return this._a;\n            },\n            getBrightness: function() {\n                var rgb = this.toRgb();\n                return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;\n            },\n            setAlpha: function(value) {\n                this._a = boundAlpha(value);\n                this._roundA = mathRound(1000 * this._a) / 1000;\n                return this;\n            },\n            toHsv: function() {\n                var hsv = rgbToHsv(this._r, this._g, this._b);\n                return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };\n            },\n            toHsvString: function() {\n                var hsv = rgbToHsv(this._r, this._g, this._b);\n                var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);\n                return (this._a == 1) ?\n                    \"hsv(\"  + h + \", \" + s + \"%, \" + v + \"%)\" :\n                    \"hsva(\" + h + \", \" + s + \"%, \" + v + \"%, \"+ this._roundA + \")\";\n            },\n            toHsl: function() {\n                var hsl = rgbToHsl(this._r, this._g, this._b);\n                return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };\n            },\n            toHslString: function() {\n                var hsl = rgbToHsl(this._r, this._g, this._b);\n                var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);\n                return (this._a == 1) ?\n                    \"hsl(\"  + h + \", \" + s + \"%, \" + l + \"%)\" :\n                    \"hsla(\" + h + \", \" + s + \"%, \" + l + \"%, \"+ this._roundA + \")\";\n            },\n            toHex: function(allow3Char) {\n                return rgbToHex(this._r, this._g, this._b, allow3Char);\n            },\n            toHexString: function(allow3Char) {\n                return '#' + this.toHex(allow3Char);\n            },\n            toHex8: function() {\n                return rgbaToHex(this._r, this._g, this._b, this._a);\n            },\n            toHex8String: function() {\n                return '#' + this.toHex8();\n            },\n            toRgb: function() {\n                return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };\n            },\n            toRgbString: function() {\n                return (this._a == 1) ?\n                    \"rgb(\"  + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \")\" :\n                    \"rgba(\" + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \", \" + this._roundA + \")\";\n            },\n            toPercentageRgb: function() {\n                return { r: mathRound(bound01(this._r, 255) * 100) + \"%\", g: mathRound(bound01(this._g, 255) * 100) + \"%\", b: mathRound(bound01(this._b, 255) * 100) + \"%\", a: this._a };\n            },\n            toPercentageRgbString: function() {\n                return (this._a == 1) ?\n                    \"rgb(\"  + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%)\" :\n                    \"rgba(\" + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%, \" + this._roundA + \")\";\n            },\n            toName: function() {\n                if (this._a === 0) {\n                    return \"transparent\";\n                }\n\n                if (this._a < 1) {\n                    return false;\n                }\n\n                return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;\n            },\n            toFilter: function(secondColor) {\n                var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);\n                var secondHex8String = hex8String;\n                var gradientType = this._gradientType ? \"GradientType = 1, \" : \"\";\n\n                if (secondColor) {\n                    var s = tinycolor(secondColor);\n                    secondHex8String = s.toHex8String();\n                }\n\n                return \"progid:DXImageTransform.Microsoft.gradient(\"+gradientType+\"startColorstr=\"+hex8String+\",endColorstr=\"+secondHex8String+\")\";\n            },\n            toString: function(format) {\n                var formatSet = !!format;\n                format = format || this._format;\n\n                var formattedString = false;\n                var hasAlpha = this._a < 1 && this._a >= 0;\n                var needsAlphaFormat = !formatSet && hasAlpha && (format === \"hex\" || format === \"hex6\" || format === \"hex3\" || format === \"name\");\n\n                if (needsAlphaFormat) {\n                    // Special case for \"transparent\", all other non-alpha formats\n                    // will return rgba when there is transparency.\n                    if (format === \"name\" && this._a === 0) {\n                        return this.toName();\n                    }\n                    return this.toRgbString();\n                }\n                if (format === \"rgb\") {\n                    formattedString = this.toRgbString();\n                }\n                if (format === \"prgb\") {\n                    formattedString = this.toPercentageRgbString();\n                }\n                if (format === \"hex\" || format === \"hex6\") {\n                    formattedString = this.toHexString();\n                }\n                if (format === \"hex3\") {\n                    formattedString = this.toHexString(true);\n                }\n                if (format === \"hex8\") {\n                    formattedString = this.toHex8String();\n                }\n                if (format === \"name\") {\n                    formattedString = this.toName();\n                }\n                if (format === \"hsl\") {\n                    formattedString = this.toHslString();\n                }\n                if (format === \"hsv\") {\n                    formattedString = this.toHsvString();\n                }\n\n                return formattedString || this.toHexString();\n            },\n\n            _applyModification: function(fn, args) {\n                var color = fn.apply(null, [this].concat([].slice.call(args)));\n                this._r = color._r;\n                this._g = color._g;\n                this._b = color._b;\n                this.setAlpha(color._a);\n                return this;\n            },\n            lighten: function() {\n                return this._applyModification(lighten, arguments);\n            },\n            brighten: function() {\n                return this._applyModification(brighten, arguments);\n            },\n            darken: function() {\n                return this._applyModification(darken, arguments);\n            },\n            desaturate: function() {\n                return this._applyModification(desaturate, arguments);\n            },\n            saturate: function() {\n                return this._applyModification(saturate, arguments);\n            },\n            greyscale: function() {\n                return this._applyModification(greyscale, arguments);\n            },\n            spin: function() {\n                return this._applyModification(spin, arguments);\n            },\n\n            _applyCombination: function(fn, args) {\n                return fn.apply(null, [this].concat([].slice.call(args)));\n            },\n            analogous: function() {\n                return this._applyCombination(analogous, arguments);\n            },\n            complement: function() {\n                return this._applyCombination(complement, arguments);\n            },\n            monochromatic: function() {\n                return this._applyCombination(monochromatic, arguments);\n            },\n            splitcomplement: function() {\n                return this._applyCombination(splitcomplement, arguments);\n            },\n            triad: function() {\n                return this._applyCombination(triad, arguments);\n            },\n            tetrad: function() {\n                return this._applyCombination(tetrad, arguments);\n            }\n        };\n\n        // If input is an object, force 1 into \"1.0\" to handle ratios properly\n        // String input requires \"1.0\" as input, so 1 will be treated as 1\n        tinycolor.fromRatio = function(color, opts) {\n            if (typeof color == \"object\") {\n                var newColor = {};\n                for (var i in color) {\n                    if (color.hasOwnProperty(i)) {\n                        if (i === \"a\") {\n                            newColor[i] = color[i];\n                        }\n                        else {\n                            newColor[i] = convertToPercentage(color[i]);\n                        }\n                    }\n                }\n                color = newColor;\n            }\n\n            return tinycolor(color, opts);\n        };\n\n        // Given a string or object, convert that input to RGB\n        // Possible string inputs:\n        //\n        //     \"red\"\n        //     \"#f00\" or \"f00\"\n        //     \"#ff0000\" or \"ff0000\"\n        //     \"#ff000000\" or \"ff000000\"\n        //     \"rgb 255 0 0\" or \"rgb (255, 0, 0)\"\n        //     \"rgb 1.0 0 0\" or \"rgb (1, 0, 0)\"\n        //     \"rgba (255, 0, 0, 1)\" or \"rgba 255, 0, 0, 1\"\n        //     \"rgba (1.0, 0, 0, 1)\" or \"rgba 1.0, 0, 0, 1\"\n        //     \"hsl(0, 100%, 50%)\" or \"hsl 0 100% 50%\"\n        //     \"hsla(0, 100%, 50%, 1)\" or \"hsla 0 100% 50%, 1\"\n        //     \"hsv(0, 100%, 100%)\" or \"hsv 0 100% 100%\"\n        //\n        function inputToRGB(color) {\n\n            var rgb = { r: 0, g: 0, b: 0 };\n            var a = 1;\n            var ok = false;\n            var format = false;\n\n            if (typeof color == \"string\") {\n                color = stringInputToObject(color);\n            }\n\n            if (typeof color == \"object\") {\n                if (color.hasOwnProperty(\"r\") && color.hasOwnProperty(\"g\") && color.hasOwnProperty(\"b\")) {\n                    rgb = rgbToRgb(color.r, color.g, color.b);\n                    ok = true;\n                    format = String(color.r).substr(-1) === \"%\" ? \"prgb\" : \"rgb\";\n                }\n                else if (color.hasOwnProperty(\"h\") && color.hasOwnProperty(\"s\") && color.hasOwnProperty(\"v\")) {\n                    color.s = convertToPercentage(color.s);\n                    color.v = convertToPercentage(color.v);\n                    rgb = hsvToRgb(color.h, color.s, color.v);\n                    ok = true;\n                    format = \"hsv\";\n                }\n                else if (color.hasOwnProperty(\"h\") && color.hasOwnProperty(\"s\") && color.hasOwnProperty(\"l\")) {\n                    color.s = convertToPercentage(color.s);\n                    color.l = convertToPercentage(color.l);\n                    rgb = hslToRgb(color.h, color.s, color.l);\n                    ok = true;\n                    format = \"hsl\";\n                }\n\n                if (color.hasOwnProperty(\"a\")) {\n                    a = color.a;\n                }\n            }\n\n            a = boundAlpha(a);\n\n            return {\n                ok: ok,\n                format: color.format || format,\n                r: mathMin(255, mathMax(rgb.r, 0)),\n                g: mathMin(255, mathMax(rgb.g, 0)),\n                b: mathMin(255, mathMax(rgb.b, 0)),\n                a: a\n            };\n        }\n\n\n        // Conversion Functions\n        // --------------------\n\n        // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:\n        // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>\n\n        // `rgbToRgb`\n        // Handle bounds / percentage checking to conform to CSS color spec\n        // <http://www.w3.org/TR/css3-color/>\n        // *Assumes:* r, g, b in [0, 255] or [0, 1]\n        // *Returns:* { r, g, b } in [0, 255]\n        function rgbToRgb(r, g, b){\n            return {\n                r: bound01(r, 255) * 255,\n                g: bound01(g, 255) * 255,\n                b: bound01(b, 255) * 255\n            };\n        }\n\n        // `rgbToHsl`\n        // Converts an RGB color value to HSL.\n        // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]\n        // *Returns:* { h, s, l } in [0,1]\n        function rgbToHsl(r, g, b) {\n\n            r = bound01(r, 255);\n            g = bound01(g, 255);\n            b = bound01(b, 255);\n\n            var max = mathMax(r, g, b), min = mathMin(r, g, b);\n            var h, s, l = (max + min) / 2;\n\n            if(max == min) {\n                h = s = 0; // achromatic\n            }\n            else {\n                var d = max - min;\n                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n                switch(max) {\n                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                    case g: h = (b - r) / d + 2; break;\n                    case b: h = (r - g) / d + 4; break;\n                }\n\n                h /= 6;\n            }\n\n            return { h: h, s: s, l: l };\n        }\n\n        // `hslToRgb`\n        // Converts an HSL color value to RGB.\n        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]\n        // *Returns:* { r, g, b } in the set [0, 255]\n        function hslToRgb(h, s, l) {\n            var r, g, b;\n\n            h = bound01(h, 360);\n            s = bound01(s, 100);\n            l = bound01(l, 100);\n\n            function hue2rgb(p, q, t) {\n                if(t < 0) t += 1;\n                if(t > 1) t -= 1;\n                if(t < 1/6) return p + (q - p) * 6 * t;\n                if(t < 1/2) return q;\n                if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;\n                return p;\n            }\n\n            if(s === 0) {\n                r = g = b = l; // achromatic\n            }\n            else {\n                var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n                var p = 2 * l - q;\n                r = hue2rgb(p, q, h + 1/3);\n                g = hue2rgb(p, q, h);\n                b = hue2rgb(p, q, h - 1/3);\n            }\n\n            return { r: r * 255, g: g * 255, b: b * 255 };\n        }\n\n        // `rgbToHsv`\n        // Converts an RGB color value to HSV\n        // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]\n        // *Returns:* { h, s, v } in [0,1]\n        function rgbToHsv(r, g, b) {\n\n            r = bound01(r, 255);\n            g = bound01(g, 255);\n            b = bound01(b, 255);\n\n            var max = mathMax(r, g, b), min = mathMin(r, g, b);\n            var h, s, v = max;\n\n            var d = max - min;\n            s = max === 0 ? 0 : d / max;\n\n            if(max == min) {\n                h = 0; // achromatic\n            }\n            else {\n                switch(max) {\n                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                    case g: h = (b - r) / d + 2; break;\n                    case b: h = (r - g) / d + 4; break;\n                }\n                h /= 6;\n            }\n            return { h: h, s: s, v: v };\n        }\n\n        // `hsvToRgb`\n        // Converts an HSV color value to RGB.\n        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]\n        // *Returns:* { r, g, b } in the set [0, 255]\n        function hsvToRgb(h, s, v) {\n\n            h = bound01(h, 360) * 6;\n            s = bound01(s, 100);\n            v = bound01(v, 100);\n\n            var i = math.floor(h),\n                f = h - i,\n                p = v * (1 - s),\n                q = v * (1 - f * s),\n                t = v * (1 - (1 - f) * s),\n                mod = i % 6,\n                r = [v, q, p, p, t, v][mod],\n                g = [t, v, v, q, p, p][mod],\n                b = [p, p, t, v, v, q][mod];\n\n            return { r: r * 255, g: g * 255, b: b * 255 };\n        }\n\n        // `rgbToHex`\n        // Converts an RGB color to hex\n        // Assumes r, g, and b are contained in the set [0, 255]\n        // Returns a 3 or 6 character hex\n        function rgbToHex(r, g, b, allow3Char) {\n\n            var hex = [\n                pad2(mathRound(r).toString(16)),\n                pad2(mathRound(g).toString(16)),\n                pad2(mathRound(b).toString(16))\n            ];\n\n            // Return a 3 character hex if possible\n            if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {\n                return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);\n            }\n\n            return hex.join(\"\");\n        }\n        // `rgbaToHex`\n        // Converts an RGBA color plus alpha transparency to hex\n        // Assumes r, g, b and a are contained in the set [0, 255]\n        // Returns an 8 character hex\n        function rgbaToHex(r, g, b, a) {\n\n            var hex = [\n                pad2(convertDecimalToHex(a)),\n                pad2(mathRound(r).toString(16)),\n                pad2(mathRound(g).toString(16)),\n                pad2(mathRound(b).toString(16))\n            ];\n\n            return hex.join(\"\");\n        }\n\n        // `equals`\n        // Can be called with any tinycolor input\n        tinycolor.equals = function (color1, color2) {\n            if (!color1 || !color2) { return false; }\n            return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();\n        };\n        tinycolor.random = function() {\n            return tinycolor.fromRatio({\n                r: mathRandom(),\n                g: mathRandom(),\n                b: mathRandom()\n            });\n        };\n\n\n        // Modification Functions\n        // ----------------------\n        // Thanks to less.js for some of the basics here\n        // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>\n\n        function desaturate(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.s -= amount / 100;\n            hsl.s = clamp01(hsl.s);\n            return tinycolor(hsl);\n        }\n\n        function saturate(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.s += amount / 100;\n            hsl.s = clamp01(hsl.s);\n            return tinycolor(hsl);\n        }\n\n        function greyscale(color) {\n            return tinycolor(color).desaturate(100);\n        }\n\n        function lighten (color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.l += amount / 100;\n            hsl.l = clamp01(hsl.l);\n            return tinycolor(hsl);\n        }\n\n        function brighten(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var rgb = tinycolor(color).toRgb();\n            rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));\n            rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));\n            rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));\n            return tinycolor(rgb);\n        }\n\n        function darken (color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.l -= amount / 100;\n            hsl.l = clamp01(hsl.l);\n            return tinycolor(hsl);\n        }\n\n        // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.\n        // Values outside of this range will be wrapped into this range.\n        function spin(color, amount) {\n            var hsl = tinycolor(color).toHsl();\n            var hue = (mathRound(hsl.h) + amount) % 360;\n            hsl.h = hue < 0 ? 360 + hue : hue;\n            return tinycolor(hsl);\n        }\n\n        // Combination Functions\n        // ---------------------\n        // Thanks to jQuery xColor for some of the ideas behind these\n        // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>\n\n        function complement(color) {\n            var hsl = tinycolor(color).toHsl();\n            hsl.h = (hsl.h + 180) % 360;\n            return tinycolor(hsl);\n        }\n\n        function triad(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })\n            ];\n        }\n\n        function tetrad(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })\n            ];\n        }\n\n        function splitcomplement(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),\n                tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})\n            ];\n        }\n\n        function analogous(color, results, slices) {\n            results = results || 6;\n            slices = slices || 30;\n\n            var hsl = tinycolor(color).toHsl();\n            var part = 360 / slices;\n            var ret = [tinycolor(color)];\n\n            for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {\n                hsl.h = (hsl.h + part) % 360;\n                ret.push(tinycolor(hsl));\n            }\n            return ret;\n        }\n\n        function monochromatic(color, results) {\n            results = results || 6;\n            var hsv = tinycolor(color).toHsv();\n            var h = hsv.h, s = hsv.s, v = hsv.v;\n            var ret = [];\n            var modification = 1 / results;\n\n            while (results--) {\n                ret.push(tinycolor({ h: h, s: s, v: v}));\n                v = (v + modification) % 1;\n            }\n\n            return ret;\n        }\n\n        // Utility Functions\n        // ---------------------\n\n        tinycolor.mix = function(color1, color2, amount) {\n            amount = (amount === 0) ? 0 : (amount || 50);\n\n            var rgb1 = tinycolor(color1).toRgb();\n            var rgb2 = tinycolor(color2).toRgb();\n\n            var p = amount / 100;\n            var w = p * 2 - 1;\n            var a = rgb2.a - rgb1.a;\n\n            var w1;\n\n            if (w * a == -1) {\n                w1 = w;\n            } else {\n                w1 = (w + a) / (1 + w * a);\n            }\n\n            w1 = (w1 + 1) / 2;\n\n            var w2 = 1 - w1;\n\n            var rgba = {\n                r: rgb2.r * w1 + rgb1.r * w2,\n                g: rgb2.g * w1 + rgb1.g * w2,\n                b: rgb2.b * w1 + rgb1.b * w2,\n                a: rgb2.a * p  + rgb1.a * (1 - p)\n            };\n\n            return tinycolor(rgba);\n        };\n\n\n        // Readability Functions\n        // ---------------------\n        // <http://www.w3.org/TR/AERT#color-contrast>\n\n        // `readability`\n        // Analyze the 2 colors and returns an object with the following properties:\n        //    `brightness`: difference in brightness between the two colors\n        //    `color`: difference in color/hue between the two colors\n        tinycolor.readability = function(color1, color2) {\n            var c1 = tinycolor(color1);\n            var c2 = tinycolor(color2);\n            var rgb1 = c1.toRgb();\n            var rgb2 = c2.toRgb();\n            var brightnessA = c1.getBrightness();\n            var brightnessB = c2.getBrightness();\n            var colorDiff = (\n                Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +\n                Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +\n                Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)\n            );\n\n            return {\n                brightness: Math.abs(brightnessA - brightnessB),\n                color: colorDiff\n            };\n        };\n\n        // `readable`\n        // http://www.w3.org/TR/AERT#color-contrast\n        // Ensure that foreground and background color combinations provide sufficient contrast.\n        // *Example*\n        //    tinycolor.isReadable(\"#000\", \"#111\") => false\n        tinycolor.isReadable = function(color1, color2) {\n            var readability = tinycolor.readability(color1, color2);\n            return readability.brightness > 125 && readability.color > 500;\n        };\n\n        // `mostReadable`\n        // Given a base color and a list of possible foreground or background\n        // colors for that base, returns the most readable color.\n        // *Example*\n        //    tinycolor.mostReadable(\"#123\", [\"#fff\", \"#000\"]) => \"#000\"\n        tinycolor.mostReadable = function(baseColor, colorList) {\n            var bestColor = null;\n            var bestScore = 0;\n            var bestIsReadable = false;\n            for (var i=0; i < colorList.length; i++) {\n\n                // We normalize both around the \"acceptable\" breaking point,\n                // but rank brightness constrast higher than hue.\n\n                var readability = tinycolor.readability(baseColor, colorList[i]);\n                var readable = readability.brightness > 125 && readability.color > 500;\n                var score = 3 * (readability.brightness / 125) + (readability.color / 500);\n\n                if ((readable && ! bestIsReadable) ||\n                    (readable && bestIsReadable && score > bestScore) ||\n                    ((! readable) && (! bestIsReadable) && score > bestScore)) {\n                    bestIsReadable = readable;\n                    bestScore = score;\n                    bestColor = tinycolor(colorList[i]);\n                }\n            }\n            return bestColor;\n        };\n\n\n        // Big List of Colors\n        // ------------------\n        // <http://www.w3.org/TR/css3-color/#svg-color>\n        var names = tinycolor.names = {\n            aliceblue: \"f0f8ff\",\n            antiquewhite: \"faebd7\",\n            aqua: \"0ff\",\n            aquamarine: \"7fffd4\",\n            azure: \"f0ffff\",\n            beige: \"f5f5dc\",\n            bisque: \"ffe4c4\",\n            black: \"000\",\n            blanchedalmond: \"ffebcd\",\n            blue: \"00f\",\n            blueviolet: \"8a2be2\",\n            brown: \"a52a2a\",\n            burlywood: \"deb887\",\n            burntsienna: \"ea7e5d\",\n            cadetblue: \"5f9ea0\",\n            chartreuse: \"7fff00\",\n            chocolate: \"d2691e\",\n            coral: \"ff7f50\",\n            cornflowerblue: \"6495ed\",\n            cornsilk: \"fff8dc\",\n            crimson: \"dc143c\",\n            cyan: \"0ff\",\n            darkblue: \"00008b\",\n            darkcyan: \"008b8b\",\n            darkgoldenrod: \"b8860b\",\n            darkgray: \"a9a9a9\",\n            darkgreen: \"006400\",\n            darkgrey: \"a9a9a9\",\n            darkkhaki: \"bdb76b\",\n            darkmagenta: \"8b008b\",\n            darkolivegreen: \"556b2f\",\n            darkorange: \"ff8c00\",\n            darkorchid: \"9932cc\",\n            darkred: \"8b0000\",\n            darksalmon: \"e9967a\",\n            darkseagreen: \"8fbc8f\",\n            darkslateblue: \"483d8b\",\n            darkslategray: \"2f4f4f\",\n            darkslategrey: \"2f4f4f\",\n            darkturquoise: \"00ced1\",\n            darkviolet: \"9400d3\",\n            deeppink: \"ff1493\",\n            deepskyblue: \"00bfff\",\n            dimgray: \"696969\",\n            dimgrey: \"696969\",\n            dodgerblue: \"1e90ff\",\n            firebrick: \"b22222\",\n            floralwhite: \"fffaf0\",\n            forestgreen: \"228b22\",\n            fuchsia: \"f0f\",\n            gainsboro: \"dcdcdc\",\n            ghostwhite: \"f8f8ff\",\n            gold: \"ffd700\",\n            goldenrod: \"daa520\",\n            gray: \"808080\",\n            green: \"008000\",\n            greenyellow: \"adff2f\",\n            grey: \"808080\",\n            honeydew: \"f0fff0\",\n            hotpink: \"ff69b4\",\n            indianred: \"cd5c5c\",\n            indigo: \"4b0082\",\n            ivory: \"fffff0\",\n            khaki: \"f0e68c\",\n            lavender: \"e6e6fa\",\n            lavenderblush: \"fff0f5\",\n            lawngreen: \"7cfc00\",\n            lemonchiffon: \"fffacd\",\n            lightblue: \"add8e6\",\n            lightcoral: \"f08080\",\n            lightcyan: \"e0ffff\",\n            lightgoldenrodyellow: \"fafad2\",\n            lightgray: \"d3d3d3\",\n            lightgreen: \"90ee90\",\n            lightgrey: \"d3d3d3\",\n            lightpink: \"ffb6c1\",\n            lightsalmon: \"ffa07a\",\n            lightseagreen: \"20b2aa\",\n            lightskyblue: \"87cefa\",\n            lightslategray: \"789\",\n            lightslategrey: \"789\",\n            lightsteelblue: \"b0c4de\",\n            lightyellow: \"ffffe0\",\n            lime: \"0f0\",\n            limegreen: \"32cd32\",\n            linen: \"faf0e6\",\n            magenta: \"f0f\",\n            maroon: \"800000\",\n            mediumaquamarine: \"66cdaa\",\n            mediumblue: \"0000cd\",\n            mediumorchid: \"ba55d3\",\n            mediumpurple: \"9370db\",\n            mediumseagreen: \"3cb371\",\n            mediumslateblue: \"7b68ee\",\n            mediumspringgreen: \"00fa9a\",\n            mediumturquoise: \"48d1cc\",\n            mediumvioletred: \"c71585\",\n            midnightblue: \"191970\",\n            mintcream: \"f5fffa\",\n            mistyrose: \"ffe4e1\",\n            moccasin: \"ffe4b5\",\n            navajowhite: \"ffdead\",\n            navy: \"000080\",\n            oldlace: \"fdf5e6\",\n            olive: \"808000\",\n            olivedrab: \"6b8e23\",\n            orange: \"ffa500\",\n            orangered: \"ff4500\",\n            orchid: \"da70d6\",\n            palegoldenrod: \"eee8aa\",\n            palegreen: \"98fb98\",\n            paleturquoise: \"afeeee\",\n            palevioletred: \"db7093\",\n            papayawhip: \"ffefd5\",\n            peachpuff: \"ffdab9\",\n            peru: \"cd853f\",\n            pink: \"ffc0cb\",\n            plum: \"dda0dd\",\n            powderblue: \"b0e0e6\",\n            purple: \"800080\",\n            rebeccapurple: \"663399\",\n            red: \"f00\",\n            rosybrown: \"bc8f8f\",\n            royalblue: \"4169e1\",\n            saddlebrown: \"8b4513\",\n            salmon: \"fa8072\",\n            sandybrown: \"f4a460\",\n            seagreen: \"2e8b57\",\n            seashell: \"fff5ee\",\n            sienna: \"a0522d\",\n            silver: \"c0c0c0\",\n            skyblue: \"87ceeb\",\n            slateblue: \"6a5acd\",\n            slategray: \"708090\",\n            slategrey: \"708090\",\n            snow: \"fffafa\",\n            springgreen: \"00ff7f\",\n            steelblue: \"4682b4\",\n            tan: \"d2b48c\",\n            teal: \"008080\",\n            thistle: \"d8bfd8\",\n            tomato: \"ff6347\",\n            turquoise: \"40e0d0\",\n            violet: \"ee82ee\",\n            wheat: \"f5deb3\",\n            white: \"fff\",\n            whitesmoke: \"f5f5f5\",\n            yellow: \"ff0\",\n            yellowgreen: \"9acd32\"\n        };\n\n        // Make it easy to access colors via `hexNames[hex]`\n        var hexNames = tinycolor.hexNames = flip(names);\n\n\n        // Utilities\n        // ---------\n\n        // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`\n        function flip(o) {\n            var flipped = { };\n            for (var i in o) {\n                if (o.hasOwnProperty(i)) {\n                    flipped[o[i]] = i;\n                }\n            }\n            return flipped;\n        }\n\n        // Return a valid alpha value [0,1] with all invalid values being set to 1\n        function boundAlpha(a) {\n            a = parseFloat(a);\n\n            if (isNaN(a) || a < 0 || a > 1) {\n                a = 1;\n            }\n\n            return a;\n        }\n\n        // Take input from [0, n] and return it as [0, 1]\n        function bound01(n, max) {\n            if (isOnePointZero(n)) { n = \"100%\"; }\n\n            var processPercent = isPercentage(n);\n            n = mathMin(max, mathMax(0, parseFloat(n)));\n\n            // Automatically convert percentage into number\n            if (processPercent) {\n                n = parseInt(n * max, 10) / 100;\n            }\n\n            // Handle floating point rounding errors\n            if ((math.abs(n - max) < 0.000001)) {\n                return 1;\n            }\n\n            // Convert into [0, 1] range if it isn't already\n            return (n % max) / parseFloat(max);\n        }\n\n        // Force a number between 0 and 1\n        function clamp01(val) {\n            return mathMin(1, mathMax(0, val));\n        }\n\n        // Parse a base-16 hex value into a base-10 integer\n        function parseIntFromHex(val) {\n            return parseInt(val, 16);\n        }\n\n        // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1\n        // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>\n        function isOnePointZero(n) {\n            return typeof n == \"string\" && n.indexOf('.') != -1 && parseFloat(n) === 1;\n        }\n\n        // Check to see if string passed in is a percentage\n        function isPercentage(n) {\n            return typeof n === \"string\" && n.indexOf('%') != -1;\n        }\n\n        // Force a hex value to have 2 characters\n        function pad2(c) {\n            return c.length == 1 ? '0' + c : '' + c;\n        }\n\n        // Replace a decimal with it's percentage value\n        function convertToPercentage(n) {\n            if (n <= 1) {\n                n = (n * 100) + \"%\";\n            }\n\n            return n;\n        }\n\n        // Converts a decimal to a hex value\n        function convertDecimalToHex(d) {\n            return Math.round(parseFloat(d) * 255).toString(16);\n        }\n        // Converts a hex value to a decimal\n        function convertHexToDecimal(h) {\n            return (parseIntFromHex(h) / 255);\n        }\n\n        var matchers = (function() {\n\n            // <http://www.w3.org/TR/css3-values/#integers>\n            var CSS_INTEGER = \"[-\\\\+]?\\\\d+%?\";\n\n            // <http://www.w3.org/TR/css3-values/#number-value>\n            var CSS_NUMBER = \"[-\\\\+]?\\\\d*\\\\.\\\\d+%?\";\n\n            // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.\n            var CSS_UNIT = \"(?:\" + CSS_NUMBER + \")|(?:\" + CSS_INTEGER + \")\";\n\n            // Actual matching.\n            // Parentheses and commas are optional, but not required.\n            // Whitespace can take the place of commas or opening paren\n            var PERMISSIVE_MATCH3 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n            var PERMISSIVE_MATCH4 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n\n            return {\n                rgb: new RegExp(\"rgb\" + PERMISSIVE_MATCH3),\n                rgba: new RegExp(\"rgba\" + PERMISSIVE_MATCH4),\n                hsl: new RegExp(\"hsl\" + PERMISSIVE_MATCH3),\n                hsla: new RegExp(\"hsla\" + PERMISSIVE_MATCH4),\n                hsv: new RegExp(\"hsv\" + PERMISSIVE_MATCH3),\n                hsva: new RegExp(\"hsva\" + PERMISSIVE_MATCH4),\n                hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n                hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n                hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/\n            };\n        })();\n\n        // `stringInputToObject`\n        // Permissive string parsing.  Take in a number of formats, and output an object\n        // based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`\n        function stringInputToObject(color) {\n\n            color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();\n            var named = false;\n            if (names[color]) {\n                color = names[color];\n                named = true;\n            }\n            else if (color == 'transparent') {\n                return { r: 0, g: 0, b: 0, a: 0, format: \"name\" };\n            }\n\n            // Try to match string input using regular expressions.\n            // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]\n            // Just return an object and let the conversion functions handle that.\n            // This way the result will be the same whether the tinycolor is initialized with string or object.\n            var match;\n            if ((match = matchers.rgb.exec(color))) {\n                return { r: match[1], g: match[2], b: match[3] };\n            }\n            if ((match = matchers.rgba.exec(color))) {\n                return { r: match[1], g: match[2], b: match[3], a: match[4] };\n            }\n            if ((match = matchers.hsl.exec(color))) {\n                return { h: match[1], s: match[2], l: match[3] };\n            }\n            if ((match = matchers.hsla.exec(color))) {\n                return { h: match[1], s: match[2], l: match[3], a: match[4] };\n            }\n            if ((match = matchers.hsv.exec(color))) {\n                return { h: match[1], s: match[2], v: match[3] };\n            }\n            if ((match = matchers.hsva.exec(color))) {\n                return { h: match[1], s: match[2], v: match[3], a: match[4] };\n            }\n            if ((match = matchers.hex8.exec(color))) {\n                return {\n                    a: convertHexToDecimal(match[1]),\n                    r: parseIntFromHex(match[2]),\n                    g: parseIntFromHex(match[3]),\n                    b: parseIntFromHex(match[4]),\n                    format: named ? \"name\" : \"hex8\"\n                };\n            }\n            if ((match = matchers.hex6.exec(color))) {\n                return {\n                    r: parseIntFromHex(match[1]),\n                    g: parseIntFromHex(match[2]),\n                    b: parseIntFromHex(match[3]),\n                    format: named ? \"name\" : \"hex\"\n                };\n            }\n            if ((match = matchers.hex3.exec(color))) {\n                return {\n                    r: parseIntFromHex(match[1] + '' + match[1]),\n                    g: parseIntFromHex(match[2] + '' + match[2]),\n                    b: parseIntFromHex(match[3] + '' + match[3]),\n                    format: named ? \"name\" : \"hex\"\n                };\n            }\n\n            return false;\n        }\n\n        window.tinycolor = tinycolor;\n    })();\n\n    $(function () {\n        if ($.fn.spectrum.load) {\n            $.fn.spectrum.processNativeColorInputs();\n        }\n    });\n\n});\n","jquery/spectrum/tinycolor.js":"// TinyColor v1.4.2\n// https://github.com/bgrins/TinyColor\n// Brian Grinstead, MIT License\n\n(function(Math) {\n\n    var trimLeft = /^\\s+/,\n        trimRight = /\\s+$/,\n        tinyCounter = 0,\n        mathRound = Math.round,\n        mathMin = Math.min,\n        mathMax = Math.max,\n        mathRandom = Math.random;\n\n    function tinycolor (color, opts) {\n\n        color = (color) ? color : '';\n        opts = opts || { };\n\n        // If input is already a tinycolor, return itself\n        if (color instanceof tinycolor) {\n            return color;\n        }\n        // If we are called as a function, call using new instead\n        if (!(this instanceof tinycolor)) {\n            return new tinycolor(color, opts);\n        }\n\n        var rgb = inputToRGB(color);\n        this._originalInput = color,\n            this._r = rgb.r,\n            this._g = rgb.g,\n            this._b = rgb.b,\n            this._a = rgb.a,\n            this._roundA = mathRound(100*this._a) / 100,\n            this._format = opts.format || rgb.format;\n        this._gradientType = opts.gradientType;\n\n        // Don't let the range of [0,255] come back in [0,1].\n        // Potentially lose a little bit of precision here, but will fix issues where\n        // .5 gets interpreted as half of the total, instead of half of 1\n        // If it was supposed to be 128, this was already taken care of by `inputToRgb`\n        if (this._r < 1) { this._r = mathRound(this._r); }\n        if (this._g < 1) { this._g = mathRound(this._g); }\n        if (this._b < 1) { this._b = mathRound(this._b); }\n\n        this._ok = rgb.ok;\n        this._tc_id = tinyCounter++;\n    }\n\n    tinycolor.prototype = {\n        isDark: function() {\n            return this.getBrightness() < 128;\n        },\n        isLight: function() {\n            return !this.isDark();\n        },\n        isValid: function() {\n            return this._ok;\n        },\n        getOriginalInput: function() {\n            return this._originalInput;\n        },\n        getFormat: function() {\n            return this._format;\n        },\n        getAlpha: function() {\n            return this._a;\n        },\n        getBrightness: function() {\n            //http://www.w3.org/TR/AERT#color-contrast\n            var rgb = this.toRgb();\n            return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;\n        },\n        getLuminance: function() {\n            //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n            var rgb = this.toRgb();\n            var RsRGB, GsRGB, BsRGB, R, G, B;\n            RsRGB = rgb.r/255;\n            GsRGB = rgb.g/255;\n            BsRGB = rgb.b/255;\n\n            if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}\n            if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}\n            if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}\n            return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);\n        },\n        setAlpha: function(value) {\n            this._a = boundAlpha(value);\n            this._roundA = mathRound(100*this._a) / 100;\n            return this;\n        },\n        toHsv: function() {\n            var hsv = rgbToHsv(this._r, this._g, this._b);\n            return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };\n        },\n        toHsvString: function() {\n            var hsv = rgbToHsv(this._r, this._g, this._b);\n            var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);\n            return (this._a == 1) ?\n                \"hsv(\"  + h + \", \" + s + \"%, \" + v + \"%)\" :\n                \"hsva(\" + h + \", \" + s + \"%, \" + v + \"%, \"+ this._roundA + \")\";\n        },\n        toHsl: function() {\n            var hsl = rgbToHsl(this._r, this._g, this._b);\n            return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };\n        },\n        toHslString: function() {\n            var hsl = rgbToHsl(this._r, this._g, this._b);\n            var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);\n            return (this._a == 1) ?\n                \"hsl(\"  + h + \", \" + s + \"%, \" + l + \"%)\" :\n                \"hsla(\" + h + \", \" + s + \"%, \" + l + \"%, \"+ this._roundA + \")\";\n        },\n        toHex: function(allow3Char) {\n            return rgbToHex(this._r, this._g, this._b, allow3Char);\n        },\n        toHexString: function(allow3Char) {\n            return '#' + this.toHex(allow3Char);\n        },\n        toHex8: function(allow4Char) {\n            return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);\n        },\n        toHex8String: function(allow4Char) {\n            return '#' + this.toHex8(allow4Char);\n        },\n        toRgb: function() {\n            return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };\n        },\n        toRgbString: function() {\n            return (this._a == 1) ?\n                \"rgb(\"  + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \")\" :\n                \"rgba(\" + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \", \" + this._roundA + \")\";\n        },\n        toPercentageRgb: function() {\n            return { r: mathRound(bound01(this._r, 255) * 100) + \"%\", g: mathRound(bound01(this._g, 255) * 100) + \"%\", b: mathRound(bound01(this._b, 255) * 100) + \"%\", a: this._a };\n        },\n        toPercentageRgbString: function() {\n            return (this._a == 1) ?\n                \"rgb(\"  + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%)\" :\n                \"rgba(\" + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%, \" + this._roundA + \")\";\n        },\n        toName: function() {\n            if (this._a === 0) {\n                return \"transparent\";\n            }\n\n            if (this._a < 1) {\n                return false;\n            }\n\n            return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;\n        },\n        toFilter: function(secondColor) {\n            var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);\n            var secondHex8String = hex8String;\n            var gradientType = this._gradientType ? \"GradientType = 1, \" : \"\";\n\n            if (secondColor) {\n                var s = tinycolor(secondColor);\n                secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);\n            }\n\n            return \"progid:DXImageTransform.Microsoft.gradient(\"+gradientType+\"startColorstr=\"+hex8String+\",endColorstr=\"+secondHex8String+\")\";\n        },\n        toString: function(format) {\n            var formatSet = !!format;\n            format = format || this._format;\n\n            var formattedString = false;\n            var hasAlpha = this._a < 1 && this._a >= 0;\n            var needsAlphaFormat = !formatSet && hasAlpha && (format === \"hex\" || format === \"hex6\" || format === \"hex3\" || format === \"hex4\" || format === \"hex8\" || format === \"name\");\n\n            if (needsAlphaFormat) {\n                // Special case for \"transparent\", all other non-alpha formats\n                // will return rgba when there is transparency.\n                if (format === \"name\" && this._a === 0) {\n                    return this.toName();\n                }\n                return this.toRgbString();\n            }\n            if (format === \"rgb\") {\n                formattedString = this.toRgbString();\n            }\n            if (format === \"prgb\") {\n                formattedString = this.toPercentageRgbString();\n            }\n            if (format === \"hex\" || format === \"hex6\") {\n                formattedString = this.toHexString();\n            }\n            if (format === \"hex3\") {\n                formattedString = this.toHexString(true);\n            }\n            if (format === \"hex4\") {\n                formattedString = this.toHex8String(true);\n            }\n            if (format === \"hex8\") {\n                formattedString = this.toHex8String();\n            }\n            if (format === \"name\") {\n                formattedString = this.toName();\n            }\n            if (format === \"hsl\") {\n                formattedString = this.toHslString();\n            }\n            if (format === \"hsv\") {\n                formattedString = this.toHsvString();\n            }\n\n            return formattedString || this.toHexString();\n        },\n        clone: function() {\n            return tinycolor(this.toString());\n        },\n\n        _applyModification: function(fn, args) {\n            var color = fn.apply(null, [this].concat([].slice.call(args)));\n            this._r = color._r;\n            this._g = color._g;\n            this._b = color._b;\n            this.setAlpha(color._a);\n            return this;\n        },\n        lighten: function() {\n            return this._applyModification(lighten, arguments);\n        },\n        brighten: function() {\n            return this._applyModification(brighten, arguments);\n        },\n        darken: function() {\n            return this._applyModification(darken, arguments);\n        },\n        desaturate: function() {\n            return this._applyModification(desaturate, arguments);\n        },\n        saturate: function() {\n            return this._applyModification(saturate, arguments);\n        },\n        greyscale: function() {\n            return this._applyModification(greyscale, arguments);\n        },\n        spin: function() {\n            return this._applyModification(spin, arguments);\n        },\n\n        _applyCombination: function(fn, args) {\n            return fn.apply(null, [this].concat([].slice.call(args)));\n        },\n        analogous: function() {\n            return this._applyCombination(analogous, arguments);\n        },\n        complement: function() {\n            return this._applyCombination(complement, arguments);\n        },\n        monochromatic: function() {\n            return this._applyCombination(monochromatic, arguments);\n        },\n        splitcomplement: function() {\n            return this._applyCombination(splitcomplement, arguments);\n        },\n        triad: function() {\n            return this._applyCombination(triad, arguments);\n        },\n        tetrad: function() {\n            return this._applyCombination(tetrad, arguments);\n        }\n    };\n\n// If input is an object, force 1 into \"1.0\" to handle ratios properly\n// String input requires \"1.0\" as input, so 1 will be treated as 1\n    tinycolor.fromRatio = function(color, opts) {\n        if (typeof color == \"object\") {\n            var newColor = {};\n            for (var i in color) {\n                if (color.hasOwnProperty(i)) {\n                    if (i === \"a\") {\n                        newColor[i] = color[i];\n                    }\n                    else {\n                        newColor[i] = convertToPercentage(color[i]);\n                    }\n                }\n            }\n            color = newColor;\n        }\n\n        return tinycolor(color, opts);\n    };\n\n// Given a string or object, convert that input to RGB\n// Possible string inputs:\n//\n//     \"red\"\n//     \"#f00\" or \"f00\"\n//     \"#ff0000\" or \"ff0000\"\n//     \"#ff000000\" or \"ff000000\"\n//     \"rgb 255 0 0\" or \"rgb (255, 0, 0)\"\n//     \"rgb 1.0 0 0\" or \"rgb (1, 0, 0)\"\n//     \"rgba (255, 0, 0, 1)\" or \"rgba 255, 0, 0, 1\"\n//     \"rgba (1.0, 0, 0, 1)\" or \"rgba 1.0, 0, 0, 1\"\n//     \"hsl(0, 100%, 50%)\" or \"hsl 0 100% 50%\"\n//     \"hsla(0, 100%, 50%, 1)\" or \"hsla 0 100% 50%, 1\"\n//     \"hsv(0, 100%, 100%)\" or \"hsv 0 100% 100%\"\n//\n    function inputToRGB(color) {\n\n        var rgb = { r: 0, g: 0, b: 0 };\n        var a = 1;\n        var s = null;\n        var v = null;\n        var l = null;\n        var ok = false;\n        var format = false;\n\n        if (typeof color == \"string\") {\n            color = stringInputToObject(color);\n        }\n\n        if (typeof color == \"object\") {\n            if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {\n                rgb = rgbToRgb(color.r, color.g, color.b);\n                ok = true;\n                format = String(color.r).substr(-1) === \"%\" ? \"prgb\" : \"rgb\";\n            }\n            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {\n                s = convertToPercentage(color.s);\n                v = convertToPercentage(color.v);\n                rgb = hsvToRgb(color.h, s, v);\n                ok = true;\n                format = \"hsv\";\n            }\n            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {\n                s = convertToPercentage(color.s);\n                l = convertToPercentage(color.l);\n                rgb = hslToRgb(color.h, s, l);\n                ok = true;\n                format = \"hsl\";\n            }\n\n            if (color.hasOwnProperty(\"a\")) {\n                a = color.a;\n            }\n        }\n\n        a = boundAlpha(a);\n\n        return {\n            ok: ok,\n            format: color.format || format,\n            r: mathMin(255, mathMax(rgb.r, 0)),\n            g: mathMin(255, mathMax(rgb.g, 0)),\n            b: mathMin(255, mathMax(rgb.b, 0)),\n            a: a\n        };\n    }\n\n\n// Conversion Functions\n// --------------------\n\n// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:\n// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>\n\n// `rgbToRgb`\n// Handle bounds / percentage checking to conform to CSS color spec\n// <http://www.w3.org/TR/css3-color/>\n// *Assumes:* r, g, b in [0, 255] or [0, 1]\n// *Returns:* { r, g, b } in [0, 255]\n    function rgbToRgb(r, g, b){\n        return {\n            r: bound01(r, 255) * 255,\n            g: bound01(g, 255) * 255,\n            b: bound01(b, 255) * 255\n        };\n    }\n\n// `rgbToHsl`\n// Converts an RGB color value to HSL.\n// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]\n// *Returns:* { h, s, l } in [0,1]\n    function rgbToHsl(r, g, b) {\n\n        r = bound01(r, 255);\n        g = bound01(g, 255);\n        b = bound01(b, 255);\n\n        var max = mathMax(r, g, b), min = mathMin(r, g, b);\n        var h, s, l = (max + min) / 2;\n\n        if(max == min) {\n            h = s = 0; // achromatic\n        }\n        else {\n            var d = max - min;\n            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n            switch(max) {\n                case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                case g: h = (b - r) / d + 2; break;\n                case b: h = (r - g) / d + 4; break;\n            }\n\n            h /= 6;\n        }\n\n        return { h: h, s: s, l: l };\n    }\n\n// `hslToRgb`\n// Converts an HSL color value to RGB.\n// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]\n// *Returns:* { r, g, b } in the set [0, 255]\n    function hslToRgb(h, s, l) {\n        var r, g, b;\n\n        h = bound01(h, 360);\n        s = bound01(s, 100);\n        l = bound01(l, 100);\n\n        function hue2rgb(p, q, t) {\n            if(t < 0) t += 1;\n            if(t > 1) t -= 1;\n            if(t < 1/6) return p + (q - p) * 6 * t;\n            if(t < 1/2) return q;\n            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;\n            return p;\n        }\n\n        if(s === 0) {\n            r = g = b = l; // achromatic\n        }\n        else {\n            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var p = 2 * l - q;\n            r = hue2rgb(p, q, h + 1/3);\n            g = hue2rgb(p, q, h);\n            b = hue2rgb(p, q, h - 1/3);\n        }\n\n        return { r: r * 255, g: g * 255, b: b * 255 };\n    }\n\n// `rgbToHsv`\n// Converts an RGB color value to HSV\n// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]\n// *Returns:* { h, s, v } in [0,1]\n    function rgbToHsv(r, g, b) {\n\n        r = bound01(r, 255);\n        g = bound01(g, 255);\n        b = bound01(b, 255);\n\n        var max = mathMax(r, g, b), min = mathMin(r, g, b);\n        var h, s, v = max;\n\n        var d = max - min;\n        s = max === 0 ? 0 : d / max;\n\n        if(max == min) {\n            h = 0; // achromatic\n        }\n        else {\n            switch(max) {\n                case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                case g: h = (b - r) / d + 2; break;\n                case b: h = (r - g) / d + 4; break;\n            }\n            h /= 6;\n        }\n        return { h: h, s: s, v: v };\n    }\n\n// `hsvToRgb`\n// Converts an HSV color value to RGB.\n// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]\n// *Returns:* { r, g, b } in the set [0, 255]\n    function hsvToRgb(h, s, v) {\n\n        h = bound01(h, 360) * 6;\n        s = bound01(s, 100);\n        v = bound01(v, 100);\n\n        var i = Math.floor(h),\n            f = h - i,\n            p = v * (1 - s),\n            q = v * (1 - f * s),\n            t = v * (1 - (1 - f) * s),\n            mod = i % 6,\n            r = [v, q, p, p, t, v][mod],\n            g = [t, v, v, q, p, p][mod],\n            b = [p, p, t, v, v, q][mod];\n\n        return { r: r * 255, g: g * 255, b: b * 255 };\n    }\n\n// `rgbToHex`\n// Converts an RGB color to hex\n// Assumes r, g, and b are contained in the set [0, 255]\n// Returns a 3 or 6 character hex\n    function rgbToHex(r, g, b, allow3Char) {\n\n        var hex = [\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16))\n        ];\n\n        // Return a 3 character hex if possible\n        if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {\n            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);\n        }\n\n        return hex.join(\"\");\n    }\n\n// `rgbaToHex`\n// Converts an RGBA color plus alpha transparency to hex\n// Assumes r, g, b are contained in the set [0, 255] and\n// a in [0, 1]. Returns a 4 or 8 character rgba hex\n    function rgbaToHex(r, g, b, a, allow4Char) {\n\n        var hex = [\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16)),\n            pad2(convertDecimalToHex(a))\n        ];\n\n        // Return a 4 character hex if possible\n        if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {\n            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);\n        }\n\n        return hex.join(\"\");\n    }\n\n// `rgbaToArgbHex`\n// Converts an RGBA color to an ARGB Hex8 string\n// Rarely used, but required for \"toFilter()\"\n    function rgbaToArgbHex(r, g, b, a) {\n\n        var hex = [\n            pad2(convertDecimalToHex(a)),\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16))\n        ];\n\n        return hex.join(\"\");\n    }\n\n// `equals`\n// Can be called with any tinycolor input\n    tinycolor.equals = function (color1, color2) {\n        if (!color1 || !color2) { return false; }\n        return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();\n    };\n\n    tinycolor.random = function() {\n        return tinycolor.fromRatio({\n            r: mathRandom(),\n            g: mathRandom(),\n            b: mathRandom()\n        });\n    };\n\n\n// Modification Functions\n// ----------------------\n// Thanks to less.js for some of the basics here\n// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>\n\n    function desaturate(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.s -= amount / 100;\n        hsl.s = clamp01(hsl.s);\n        return tinycolor(hsl);\n    }\n\n    function saturate(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.s += amount / 100;\n        hsl.s = clamp01(hsl.s);\n        return tinycolor(hsl);\n    }\n\n    function greyscale(color) {\n        return tinycolor(color).desaturate(100);\n    }\n\n    function lighten (color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.l += amount / 100;\n        hsl.l = clamp01(hsl.l);\n        return tinycolor(hsl);\n    }\n\n    function brighten(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var rgb = tinycolor(color).toRgb();\n        rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));\n        rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));\n        rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));\n        return tinycolor(rgb);\n    }\n\n    function darken (color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.l -= amount / 100;\n        hsl.l = clamp01(hsl.l);\n        return tinycolor(hsl);\n    }\n\n// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.\n// Values outside of this range will be wrapped into this range.\n    function spin(color, amount) {\n        var hsl = tinycolor(color).toHsl();\n        var hue = (hsl.h + amount) % 360;\n        hsl.h = hue < 0 ? 360 + hue : hue;\n        return tinycolor(hsl);\n    }\n\n// Combination Functions\n// ---------------------\n// Thanks to jQuery xColor for some of the ideas behind these\n// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>\n\n    function complement(color) {\n        var hsl = tinycolor(color).toHsl();\n        hsl.h = (hsl.h + 180) % 360;\n        return tinycolor(hsl);\n    }\n\n    function triad(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })\n        ];\n    }\n\n    function tetrad(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })\n        ];\n    }\n\n    function splitcomplement(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),\n            tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})\n        ];\n    }\n\n    function analogous(color, results, slices) {\n        results = results || 6;\n        slices = slices || 30;\n\n        var hsl = tinycolor(color).toHsl();\n        var part = 360 / slices;\n        var ret = [tinycolor(color)];\n\n        for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {\n            hsl.h = (hsl.h + part) % 360;\n            ret.push(tinycolor(hsl));\n        }\n        return ret;\n    }\n\n    function monochromatic(color, results) {\n        results = results || 6;\n        var hsv = tinycolor(color).toHsv();\n        var h = hsv.h, s = hsv.s, v = hsv.v;\n        var ret = [];\n        var modification = 1 / results;\n\n        while (results--) {\n            ret.push(tinycolor({ h: h, s: s, v: v}));\n            v = (v + modification) % 1;\n        }\n\n        return ret;\n    }\n\n// Utility Functions\n// ---------------------\n\n    tinycolor.mix = function(color1, color2, amount) {\n        amount = (amount === 0) ? 0 : (amount || 50);\n\n        var rgb1 = tinycolor(color1).toRgb();\n        var rgb2 = tinycolor(color2).toRgb();\n\n        var p = amount / 100;\n\n        var rgba = {\n            r: ((rgb2.r - rgb1.r) * p) + rgb1.r,\n            g: ((rgb2.g - rgb1.g) * p) + rgb1.g,\n            b: ((rgb2.b - rgb1.b) * p) + rgb1.b,\n            a: ((rgb2.a - rgb1.a) * p) + rgb1.a\n        };\n\n        return tinycolor(rgba);\n    };\n\n\n// Readability Functions\n// ---------------------\n// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)\n\n// `contrast`\n// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)\n    tinycolor.readability = function(color1, color2) {\n        var c1 = tinycolor(color1);\n        var c2 = tinycolor(color2);\n        return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);\n    };\n\n// `isReadable`\n// Ensure that foreground and background color combinations meet WCAG2 guidelines.\n// The third argument is an optional Object.\n//      the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';\n//      the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.\n// If the entire object is absent, isReadable defaults to {level:\"AA\",size:\"small\"}.\n\n// *Example*\n//    tinycolor.isReadable(\"#000\", \"#111\") => false\n//    tinycolor.isReadable(\"#000\", \"#111\",{level:\"AA\",size:\"large\"}) => false\n    tinycolor.isReadable = function(color1, color2, wcag2) {\n        var readability = tinycolor.readability(color1, color2);\n        var wcag2Parms, out;\n\n        out = false;\n\n        wcag2Parms = validateWCAG2Parms(wcag2);\n        switch (wcag2Parms.level + wcag2Parms.size) {\n            case \"AAsmall\":\n            case \"AAAlarge\":\n                out = readability >= 4.5;\n                break;\n            case \"AAlarge\":\n                out = readability >= 3;\n                break;\n            case \"AAAsmall\":\n                out = readability >= 7;\n                break;\n        }\n        return out;\n\n    };\n\n// `mostReadable`\n// Given a base color and a list of possible foreground or background\n// colors for that base, returns the most readable color.\n// Optionally returns Black or White if the most readable color is unreadable.\n// *Example*\n//    tinycolor.mostReadable(tinycolor.mostReadable(\"#123\", [\"#124\", \"#125\"],{includeFallbackColors:false}).toHexString(); // \"#112255\"\n//    tinycolor.mostReadable(tinycolor.mostReadable(\"#123\", [\"#124\", \"#125\"],{includeFallbackColors:true}).toHexString();  // \"#ffffff\"\n//    tinycolor.mostReadable(\"#a8015a\", [\"#faf3f3\"],{includeFallbackColors:true,level:\"AAA\",size:\"large\"}).toHexString(); // \"#faf3f3\"\n//    tinycolor.mostReadable(\"#a8015a\", [\"#faf3f3\"],{includeFallbackColors:true,level:\"AAA\",size:\"small\"}).toHexString(); // \"#ffffff\"\n    tinycolor.mostReadable = function(baseColor, colorList, args) {\n        var bestColor = null;\n        var bestScore = 0;\n        var readability;\n        var includeFallbackColors, level, size ;\n        args = args || {};\n        includeFallbackColors = args.includeFallbackColors ;\n        level = args.level;\n        size = args.size;\n\n        for (var i= 0; i < colorList.length ; i++) {\n            readability = tinycolor.readability(baseColor, colorList[i]);\n            if (readability > bestScore) {\n                bestScore = readability;\n                bestColor = tinycolor(colorList[i]);\n            }\n        }\n\n        if (tinycolor.isReadable(baseColor, bestColor, {\"level\":level,\"size\":size}) || !includeFallbackColors) {\n            return bestColor;\n        }\n        else {\n            args.includeFallbackColors=false;\n            return tinycolor.mostReadable(baseColor,[\"#fff\", \"#000\"],args);\n        }\n    };\n\n\n// Big List of Colors\n// ------------------\n// <http://www.w3.org/TR/css3-color/#svg-color>\n    var names = tinycolor.names = {\n        aliceblue: \"f0f8ff\",\n        antiquewhite: \"faebd7\",\n        aqua: \"0ff\",\n        aquamarine: \"7fffd4\",\n        azure: \"f0ffff\",\n        beige: \"f5f5dc\",\n        bisque: \"ffe4c4\",\n        black: \"000\",\n        blanchedalmond: \"ffebcd\",\n        blue: \"00f\",\n        blueviolet: \"8a2be2\",\n        brown: \"a52a2a\",\n        burlywood: \"deb887\",\n        burntsienna: \"ea7e5d\",\n        cadetblue: \"5f9ea0\",\n        chartreuse: \"7fff00\",\n        chocolate: \"d2691e\",\n        coral: \"ff7f50\",\n        cornflowerblue: \"6495ed\",\n        cornsilk: \"fff8dc\",\n        crimson: \"dc143c\",\n        cyan: \"0ff\",\n        darkblue: \"00008b\",\n        darkcyan: \"008b8b\",\n        darkgoldenrod: \"b8860b\",\n        darkgray: \"a9a9a9\",\n        darkgreen: \"006400\",\n        darkgrey: \"a9a9a9\",\n        darkkhaki: \"bdb76b\",\n        darkmagenta: \"8b008b\",\n        darkolivegreen: \"556b2f\",\n        darkorange: \"ff8c00\",\n        darkorchid: \"9932cc\",\n        darkred: \"8b0000\",\n        darksalmon: \"e9967a\",\n        darkseagreen: \"8fbc8f\",\n        darkslateblue: \"483d8b\",\n        darkslategray: \"2f4f4f\",\n        darkslategrey: \"2f4f4f\",\n        darkturquoise: \"00ced1\",\n        darkviolet: \"9400d3\",\n        deeppink: \"ff1493\",\n        deepskyblue: \"00bfff\",\n        dimgray: \"696969\",\n        dimgrey: \"696969\",\n        dodgerblue: \"1e90ff\",\n        firebrick: \"b22222\",\n        floralwhite: \"fffaf0\",\n        forestgreen: \"228b22\",\n        fuchsia: \"f0f\",\n        gainsboro: \"dcdcdc\",\n        ghostwhite: \"f8f8ff\",\n        gold: \"ffd700\",\n        goldenrod: \"daa520\",\n        gray: \"808080\",\n        green: \"008000\",\n        greenyellow: \"adff2f\",\n        grey: \"808080\",\n        honeydew: \"f0fff0\",\n        hotpink: \"ff69b4\",\n        indianred: \"cd5c5c\",\n        indigo: \"4b0082\",\n        ivory: \"fffff0\",\n        khaki: \"f0e68c\",\n        lavender: \"e6e6fa\",\n        lavenderblush: \"fff0f5\",\n        lawngreen: \"7cfc00\",\n        lemonchiffon: \"fffacd\",\n        lightblue: \"add8e6\",\n        lightcoral: \"f08080\",\n        lightcyan: \"e0ffff\",\n        lightgoldenrodyellow: \"fafad2\",\n        lightgray: \"d3d3d3\",\n        lightgreen: \"90ee90\",\n        lightgrey: \"d3d3d3\",\n        lightpink: \"ffb6c1\",\n        lightsalmon: \"ffa07a\",\n        lightseagreen: \"20b2aa\",\n        lightskyblue: \"87cefa\",\n        lightslategray: \"789\",\n        lightslategrey: \"789\",\n        lightsteelblue: \"b0c4de\",\n        lightyellow: \"ffffe0\",\n        lime: \"0f0\",\n        limegreen: \"32cd32\",\n        linen: \"faf0e6\",\n        magenta: \"f0f\",\n        maroon: \"800000\",\n        mediumaquamarine: \"66cdaa\",\n        mediumblue: \"0000cd\",\n        mediumorchid: \"ba55d3\",\n        mediumpurple: \"9370db\",\n        mediumseagreen: \"3cb371\",\n        mediumslateblue: \"7b68ee\",\n        mediumspringgreen: \"00fa9a\",\n        mediumturquoise: \"48d1cc\",\n        mediumvioletred: \"c71585\",\n        midnightblue: \"191970\",\n        mintcream: \"f5fffa\",\n        mistyrose: \"ffe4e1\",\n        moccasin: \"ffe4b5\",\n        navajowhite: \"ffdead\",\n        navy: \"000080\",\n        oldlace: \"fdf5e6\",\n        olive: \"808000\",\n        olivedrab: \"6b8e23\",\n        orange: \"ffa500\",\n        orangered: \"ff4500\",\n        orchid: \"da70d6\",\n        palegoldenrod: \"eee8aa\",\n        palegreen: \"98fb98\",\n        paleturquoise: \"afeeee\",\n        palevioletred: \"db7093\",\n        papayawhip: \"ffefd5\",\n        peachpuff: \"ffdab9\",\n        peru: \"cd853f\",\n        pink: \"ffc0cb\",\n        plum: \"dda0dd\",\n        powderblue: \"b0e0e6\",\n        purple: \"800080\",\n        rebeccapurple: \"663399\",\n        red: \"f00\",\n        rosybrown: \"bc8f8f\",\n        royalblue: \"4169e1\",\n        saddlebrown: \"8b4513\",\n        salmon: \"fa8072\",\n        sandybrown: \"f4a460\",\n        seagreen: \"2e8b57\",\n        seashell: \"fff5ee\",\n        sienna: \"a0522d\",\n        silver: \"c0c0c0\",\n        skyblue: \"87ceeb\",\n        slateblue: \"6a5acd\",\n        slategray: \"708090\",\n        slategrey: \"708090\",\n        snow: \"fffafa\",\n        springgreen: \"00ff7f\",\n        steelblue: \"4682b4\",\n        tan: \"d2b48c\",\n        teal: \"008080\",\n        thistle: \"d8bfd8\",\n        tomato: \"ff6347\",\n        turquoise: \"40e0d0\",\n        violet: \"ee82ee\",\n        wheat: \"f5deb3\",\n        white: \"fff\",\n        whitesmoke: \"f5f5f5\",\n        yellow: \"ff0\",\n        yellowgreen: \"9acd32\"\n    };\n\n// Make it easy to access colors via `hexNames[hex]`\n    var hexNames = tinycolor.hexNames = flip(names);\n\n\n// Utilities\n// ---------\n\n// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`\n    function flip(o) {\n        var flipped = { };\n        for (var i in o) {\n            if (o.hasOwnProperty(i)) {\n                flipped[o[i]] = i;\n            }\n        }\n        return flipped;\n    }\n\n// Return a valid alpha value [0,1] with all invalid values being set to 1\n    function boundAlpha(a) {\n        a = parseFloat(a);\n\n        if (isNaN(a) || a < 0 || a > 1) {\n            a = 1;\n        }\n\n        return a;\n    }\n\n// Take input from [0, n] and return it as [0, 1]\n    function bound01(n, max) {\n        if (isOnePointZero(n)) { n = \"100%\"; }\n\n        var processPercent = isPercentage(n);\n        n = mathMin(max, mathMax(0, parseFloat(n)));\n\n        // Automatically convert percentage into number\n        if (processPercent) {\n            n = parseInt(n * max, 10) / 100;\n        }\n\n        // Handle floating point rounding errors\n        if ((Math.abs(n - max) < 0.000001)) {\n            return 1;\n        }\n\n        // Convert into [0, 1] range if it isn't already\n        return (n % max) / parseFloat(max);\n    }\n\n// Force a number between 0 and 1\n    function clamp01(val) {\n        return mathMin(1, mathMax(0, val));\n    }\n\n// Parse a base-16 hex value into a base-10 integer\n    function parseIntFromHex(val) {\n        return parseInt(val, 16);\n    }\n\n// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1\n// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>\n    function isOnePointZero(n) {\n        return typeof n == \"string\" && n.indexOf('.') != -1 && parseFloat(n) === 1;\n    }\n\n// Check to see if string passed in is a percentage\n    function isPercentage(n) {\n        return typeof n === \"string\" && n.indexOf('%') != -1;\n    }\n\n// Force a hex value to have 2 characters\n    function pad2(c) {\n        return c.length == 1 ? '0' + c : '' + c;\n    }\n\n// Replace a decimal with it's percentage value\n    function convertToPercentage(n) {\n        if (n <= 1) {\n            n = (n * 100) + \"%\";\n        }\n\n        return n;\n    }\n\n// Converts a decimal to a hex value\n    function convertDecimalToHex(d) {\n        return Math.round(parseFloat(d) * 255).toString(16);\n    }\n// Converts a hex value to a decimal\n    function convertHexToDecimal(h) {\n        return (parseIntFromHex(h) / 255);\n    }\n\n    var matchers = (function() {\n\n        // <http://www.w3.org/TR/css3-values/#integers>\n        var CSS_INTEGER = \"[-\\\\+]?\\\\d+%?\";\n\n        // <http://www.w3.org/TR/css3-values/#number-value>\n        var CSS_NUMBER = \"[-\\\\+]?\\\\d*\\\\.\\\\d+%?\";\n\n        // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.\n        var CSS_UNIT = \"(?:\" + CSS_NUMBER + \")|(?:\" + CSS_INTEGER + \")\";\n\n        // Actual matching.\n        // Parentheses and commas are optional, but not required.\n        // Whitespace can take the place of commas or opening paren\n        var PERMISSIVE_MATCH3 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n        var PERMISSIVE_MATCH4 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n\n        return {\n            CSS_UNIT: new RegExp(CSS_UNIT),\n            rgb: new RegExp(\"rgb\" + PERMISSIVE_MATCH3),\n            rgba: new RegExp(\"rgba\" + PERMISSIVE_MATCH4),\n            hsl: new RegExp(\"hsl\" + PERMISSIVE_MATCH3),\n            hsla: new RegExp(\"hsla\" + PERMISSIVE_MATCH4),\n            hsv: new RegExp(\"hsv\" + PERMISSIVE_MATCH3),\n            hsva: new RegExp(\"hsva\" + PERMISSIVE_MATCH4),\n            hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n            hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n            hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n            hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/\n        };\n    })();\n\n// `isValidCSSUnit`\n// Take in a single string / number and check to see if it looks like a CSS unit\n// (see `matchers` above for definition).\n    function isValidCSSUnit(color) {\n        return !!matchers.CSS_UNIT.exec(color);\n    }\n\n// `stringInputToObject`\n// Permissive string parsing.  Take in a number of formats, and output an object\n// based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`\n    function stringInputToObject(color) {\n\n        color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();\n        var named = false;\n        if (names[color]) {\n            color = names[color];\n            named = true;\n        }\n        else if (color == 'transparent') {\n            return { r: 0, g: 0, b: 0, a: 0, format: \"name\" };\n        }\n\n        // Try to match string input using regular expressions.\n        // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]\n        // Just return an object and let the conversion functions handle that.\n        // This way the result will be the same whether the tinycolor is initialized with string or object.\n        var match;\n        if ((match = matchers.rgb.exec(color))) {\n            return { r: match[1], g: match[2], b: match[3] };\n        }\n        if ((match = matchers.rgba.exec(color))) {\n            return { r: match[1], g: match[2], b: match[3], a: match[4] };\n        }\n        if ((match = matchers.hsl.exec(color))) {\n            return { h: match[1], s: match[2], l: match[3] };\n        }\n        if ((match = matchers.hsla.exec(color))) {\n            return { h: match[1], s: match[2], l: match[3], a: match[4] };\n        }\n        if ((match = matchers.hsv.exec(color))) {\n            return { h: match[1], s: match[2], v: match[3] };\n        }\n        if ((match = matchers.hsva.exec(color))) {\n            return { h: match[1], s: match[2], v: match[3], a: match[4] };\n        }\n        if ((match = matchers.hex8.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1]),\n                g: parseIntFromHex(match[2]),\n                b: parseIntFromHex(match[3]),\n                a: convertHexToDecimal(match[4]),\n                format: named ? \"name\" : \"hex8\"\n            };\n        }\n        if ((match = matchers.hex6.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1]),\n                g: parseIntFromHex(match[2]),\n                b: parseIntFromHex(match[3]),\n                format: named ? \"name\" : \"hex\"\n            };\n        }\n        if ((match = matchers.hex4.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1] + '' + match[1]),\n                g: parseIntFromHex(match[2] + '' + match[2]),\n                b: parseIntFromHex(match[3] + '' + match[3]),\n                a: convertHexToDecimal(match[4] + '' + match[4]),\n                format: named ? \"name\" : \"hex8\"\n            };\n        }\n        if ((match = matchers.hex3.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1] + '' + match[1]),\n                g: parseIntFromHex(match[2] + '' + match[2]),\n                b: parseIntFromHex(match[3] + '' + match[3]),\n                format: named ? \"name\" : \"hex\"\n            };\n        }\n\n        return false;\n    }\n\n    function validateWCAG2Parms(parms) {\n        // return valid WCAG2 parms for isReadable.\n        // If input parms are invalid, return {\"level\":\"AA\", \"size\":\"small\"}\n        var level, size;\n        parms = parms || {\"level\":\"AA\", \"size\":\"small\"};\n        level = (parms.level || \"AA\").toUpperCase();\n        size = (parms.size || \"small\").toLowerCase();\n        if (level !== \"AA\" && level !== \"AAA\") {\n            level = \"AA\";\n        }\n        if (size !== \"small\" && size !== \"large\") {\n            size = \"small\";\n        }\n        return {\"level\":level, \"size\":size};\n    }\n\n// Node: Export function\n    if (typeof module !== \"undefined\" && module.exports) {\n        module.exports = tinycolor;\n    }\n// AMD/requirejs: Define the module\n    else if (typeof define === 'function' && define.amd) {\n        define(function () {return tinycolor;});\n    }\n// Browser: Expose to window\n    else {\n        window.tinycolor = tinycolor;\n    }\n\n})(Math);\n","jquery/ui-modules/jquery-var-for-color.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\t\"use strict\";\n\n// Create a local jQuery because jQuery Color relies on it and the\n// global may not exist with AMD and a custom build (#10199).\n// This module is a noop if used as a regular AMD module.\n// eslint-disable-next-line no-unused-vars\nvar jQuery = $;\n\n} );\n","jquery/ui-modules/data.js":"/*!\n * jQuery UI :data 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: :data Selector\n//>>group: Core\n//>>description: Selects elements which have data stored under the specified key.\n//>>docs: http://api.jqueryui.com/data-selector/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.extend( $.expr.pseudos, {\n        data: $.expr.createPseudo ?\n            $.expr.createPseudo( function( dataName ) {\n                return function( elem ) {\n                    return !!$.data( elem, dataName );\n                };\n            } ) :\n\n            // Support: jQuery <1.8\n            function( elem, i, match ) {\n                return !!$.data( elem, match[ 3 ] );\n            }\n    } );\n} );\n","jquery/ui-modules/effect.js":"/*!\n * jQuery UI Effects 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: Effects Core\n//>>group: Effects\n/* eslint-disable max-len */\n//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.\n/* eslint-enable max-len */\n//>>docs: http://api.jqueryui.com/category/effects-core/\n//>>demos: http://jqueryui.com/effect/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [\n            \"jquery\",\n            \"./jquery-var-for-color\",\n            \"./vendor/jquery-color/jquery.color\",\n            \"./version\"\n        ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    var dataSpace = \"ui-effects-\",\n        dataSpaceStyle = \"ui-effects-style\",\n        dataSpaceAnimated = \"ui-effects-animated\";\n\n    $.effects = {\n        effect: {}\n    };\n\n    /******************************************************************************/\n    /****************************** CLASS ANIMATIONS ******************************/\n    /******************************************************************************/\n    ( function() {\n\n        var classAnimationActions = [ \"add\", \"remove\", \"toggle\" ],\n            shorthandStyles = {\n                border: 1,\n                borderBottom: 1,\n                borderColor: 1,\n                borderLeft: 1,\n                borderRight: 1,\n                borderTop: 1,\n                borderWidth: 1,\n                margin: 1,\n                padding: 1\n            };\n\n        $.each(\n            [ \"borderLeftStyle\", \"borderRightStyle\", \"borderBottomStyle\", \"borderTopStyle\" ],\n            function( _, prop ) {\n                $.fx.step[ prop ] = function( fx ) {\n                    if ( fx.end !== \"none\" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {\n                        jQuery.style( fx.elem, prop, fx.end );\n                        fx.setAttr = true;\n                    }\n                };\n            }\n        );\n\n        function camelCase( string ) {\n            return string.replace( /-([\\da-z])/gi, function( all, letter ) {\n                return letter.toUpperCase();\n            } );\n        }\n\n        function getElementStyles( elem ) {\n            var key, len,\n                style = elem.ownerDocument.defaultView ?\n                    elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :\n                    elem.currentStyle,\n                styles = {};\n\n            if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {\n                len = style.length;\n                while ( len-- ) {\n                    key = style[ len ];\n                    if ( typeof style[ key ] === \"string\" ) {\n                        styles[ camelCase( key ) ] = style[ key ];\n                    }\n                }\n\n                // Support: Opera, IE <9\n            } else {\n                for ( key in style ) {\n                    if ( typeof style[ key ] === \"string\" ) {\n                        styles[ key ] = style[ key ];\n                    }\n                }\n            }\n\n            return styles;\n        }\n\n        function styleDifference( oldStyle, newStyle ) {\n            var diff = {},\n                name, value;\n\n            for ( name in newStyle ) {\n                value = newStyle[ name ];\n                if ( oldStyle[ name ] !== value ) {\n                    if ( !shorthandStyles[ name ] ) {\n                        if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {\n                            diff[ name ] = value;\n                        }\n                    }\n                }\n            }\n\n            return diff;\n        }\n\n// Support: jQuery <1.8\n        if ( !$.fn.addBack ) {\n            $.fn.addBack = function( selector ) {\n                return this.add( selector == null ?\n                    this.prevObject : this.prevObject.filter( selector )\n                );\n            };\n        }\n\n        $.effects.animateClass = function( value, duration, easing, callback ) {\n            var o = $.speed( duration, easing, callback );\n\n            return this.queue( function() {\n                var animated = $( this ),\n                    baseClass = animated.attr( \"class\" ) || \"\",\n                    applyClassChange,\n                    allAnimations = o.children ? animated.find( \"*\" ).addBack() : animated;\n\n                // Map the animated objects to store the original styles.\n                allAnimations = allAnimations.map( function() {\n                    var el = $( this );\n                    return {\n                        el: el,\n                        start: getElementStyles( this )\n                    };\n                } );\n\n                // Apply class change\n                applyClassChange = function() {\n                    $.each( classAnimationActions, function( i, action ) {\n                        if ( value[ action ] ) {\n                            animated[ action + \"Class\" ]( value[ action ] );\n                        }\n                    } );\n                };\n                applyClassChange();\n\n                // Map all animated objects again - calculate new styles and diff\n                allAnimations = allAnimations.map( function() {\n                    this.end = getElementStyles( this.el[ 0 ] );\n                    this.diff = styleDifference( this.start, this.end );\n                    return this;\n                } );\n\n                // Apply original class\n                animated.attr( \"class\", baseClass );\n\n                // Map all animated objects again - this time collecting a promise\n                allAnimations = allAnimations.map( function() {\n                    var styleInfo = this,\n                        dfd = $.Deferred(),\n                        opts = $.extend( {}, o, {\n                            queue: false,\n                            complete: function() {\n                                dfd.resolve( styleInfo );\n                            }\n                        } );\n\n                    this.el.animate( this.diff, opts );\n                    return dfd.promise();\n                } );\n\n                // Once all animations have completed:\n                $.when.apply( $, allAnimations.get() ).done( function() {\n\n                    // Set the final class\n                    applyClassChange();\n\n                    // For each animated element,\n                    // clear all css properties that were animated\n                    $.each( arguments, function() {\n                        var el = this.el;\n                        $.each( this.diff, function( key ) {\n                            el.css( key, \"\" );\n                        } );\n                    } );\n\n                    // This is guarnteed to be there if you use jQuery.speed()\n                    // it also handles dequeuing the next anim...\n                    o.complete.call( animated[ 0 ] );\n                } );\n            } );\n        };\n\n        $.fn.extend( {\n            addClass: ( function( orig ) {\n                return function( classNames, speed, easing, callback ) {\n                    return speed ?\n                        $.effects.animateClass.call( this,\n                            { add: classNames }, speed, easing, callback ) :\n                        orig.apply( this, arguments );\n                };\n            } )( $.fn.addClass ),\n\n            removeClass: ( function( orig ) {\n                return function( classNames, speed, easing, callback ) {\n                    return arguments.length > 1 ?\n                        $.effects.animateClass.call( this,\n                            { remove: classNames }, speed, easing, callback ) :\n                        orig.apply( this, arguments );\n                };\n            } )( $.fn.removeClass ),\n\n            toggleClass: ( function( orig ) {\n                return function( classNames, force, speed, easing, callback ) {\n                    if ( typeof force === \"boolean\" || force === undefined ) {\n                        if ( !speed ) {\n\n                            // Without speed parameter\n                            return orig.apply( this, arguments );\n                        } else {\n                            return $.effects.animateClass.call( this,\n                                ( force ? { add: classNames } : { remove: classNames } ),\n                                speed, easing, callback );\n                        }\n                    } else {\n\n                        // Without force parameter\n                        return $.effects.animateClass.call( this,\n                            { toggle: classNames }, force, speed, easing );\n                    }\n                };\n            } )( $.fn.toggleClass ),\n\n            switchClass: function( remove, add, speed, easing, callback ) {\n                return $.effects.animateClass.call( this, {\n                    add: add,\n                    remove: remove\n                }, speed, easing, callback );\n            }\n        } );\n\n    } )();\n\n    /******************************************************************************/\n    /*********************************** EFFECTS **********************************/\n    /******************************************************************************/\n\n    ( function() {\n\n        if ( $.expr && $.expr.pseudos && $.expr.pseudos.animated ) {\n            $.expr.pseudos.animated = ( function( orig ) {\n                return function( elem ) {\n                    return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );\n                };\n            } )( $.expr.pseudos.animated );\n        }\n\n        if ( $.uiBackCompat !== false ) {\n            $.extend( $.effects, {\n\n                // Saves a set of properties in a data storage\n                save: function( element, set ) {\n                    var i = 0, length = set.length;\n                    for ( ; i < length; i++ ) {\n                        if ( set[ i ] !== null ) {\n                            element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );\n                        }\n                    }\n                },\n\n                // Restores a set of previously saved properties from a data storage\n                restore: function( element, set ) {\n                    var val, i = 0, length = set.length;\n                    for ( ; i < length; i++ ) {\n                        if ( set[ i ] !== null ) {\n                            val = element.data( dataSpace + set[ i ] );\n                            element.css( set[ i ], val );\n                        }\n                    }\n                },\n\n                setMode: function( el, mode ) {\n                    if ( mode === \"toggle\" ) {\n                        mode = el.is( \":hidden\" ) ? \"show\" : \"hide\";\n                    }\n                    return mode;\n                },\n\n                // Wraps the element around a wrapper that copies position properties\n                createWrapper: function( element ) {\n\n                    // If the element is already wrapped, return it\n                    if ( element.parent().is( \".ui-effects-wrapper\" ) ) {\n                        return element.parent();\n                    }\n\n                    // Wrap the element\n                    var props = {\n                            width: element.outerWidth( true ),\n                            height: element.outerHeight( true ),\n                            \"float\": element.css( \"float\" )\n                        },\n                        wrapper = $( \"<div></div>\" )\n                            .addClass( \"ui-effects-wrapper\" )\n                            .css( {\n                                fontSize: \"100%\",\n                                background: \"transparent\",\n                                border: \"none\",\n                                margin: 0,\n                                padding: 0\n                            } ),\n\n                        // Store the size in case width/height are defined in % - Fixes #5245\n                        size = {\n                            width: element.width(),\n                            height: element.height()\n                        },\n                        active = document.activeElement;\n\n                    // Support: Firefox\n                    // Firefox incorrectly exposes anonymous content\n                    // https://bugzilla.mozilla.org/show_bug.cgi?id=561664\n                    try {\n                        // eslint-disable-next-line no-unused-expressions\n                        active.id;\n                    } catch ( e ) {\n                        active = document.body;\n                    }\n\n                    element.wrap( wrapper );\n\n                    // Fixes #7595 - Elements lose focus when wrapped.\n                    if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {\n                        $( active ).trigger( \"focus\" );\n                    }\n\n                    // Hotfix for jQuery 1.4 since some change in wrap() seems to actually\n                    // lose the reference to the wrapped element\n                    wrapper = element.parent();\n\n                    // Transfer positioning properties to the wrapper\n                    if ( element.css( \"position\" ) === \"static\" ) {\n                        wrapper.css( { position: \"relative\" } );\n                        element.css( { position: \"relative\" } );\n                    } else {\n                        $.extend( props, {\n                            position: element.css( \"position\" ),\n                            zIndex: element.css( \"z-index\" )\n                        } );\n                        $.each( [ \"top\", \"left\", \"bottom\", \"right\" ], function( i, pos ) {\n                            props[ pos ] = element.css( pos );\n                            if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {\n                                props[ pos ] = \"auto\";\n                            }\n                        } );\n                        element.css( {\n                            position: \"relative\",\n                            top: 0,\n                            left: 0,\n                            right: \"auto\",\n                            bottom: \"auto\"\n                        } );\n                    }\n                    element.css( size );\n\n                    return wrapper.css( props ).show();\n                },\n\n                removeWrapper: function( element ) {\n                    var active = document.activeElement;\n\n                    if ( element.parent().is( \".ui-effects-wrapper\" ) ) {\n                        element.parent().replaceWith( element );\n\n                        // Fixes #7595 - Elements lose focus when wrapped.\n                        if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {\n                            $( active ).trigger( \"focus\" );\n                        }\n                    }\n\n                    return element;\n                }\n            } );\n        }\n\n        $.extend( $.effects, {\n            version: \"1.13.2\",\n\n            define: function( name, mode, effect ) {\n                if ( !effect ) {\n                    effect = mode;\n                    mode = \"effect\";\n                }\n\n                $.effects.effect[ name ] = effect;\n                $.effects.effect[ name ].mode = mode;\n\n                return effect;\n            },\n\n            scaledDimensions: function( element, percent, direction ) {\n                if ( percent === 0 ) {\n                    return {\n                        height: 0,\n                        width: 0,\n                        outerHeight: 0,\n                        outerWidth: 0\n                    };\n                }\n\n                var x = direction !== \"horizontal\" ? ( ( percent || 100 ) / 100 ) : 1,\n                    y = direction !== \"vertical\" ? ( ( percent || 100 ) / 100 ) : 1;\n\n                return {\n                    height: element.height() * y,\n                    width: element.width() * x,\n                    outerHeight: element.outerHeight() * y,\n                    outerWidth: element.outerWidth() * x\n                };\n\n            },\n\n            clipToBox: function( animation ) {\n                return {\n                    width: animation.clip.right - animation.clip.left,\n                    height: animation.clip.bottom - animation.clip.top,\n                    left: animation.clip.left,\n                    top: animation.clip.top\n                };\n            },\n\n            // Injects recently queued functions to be first in line (after \"inprogress\")\n            unshift: function( element, queueLength, count ) {\n                var queue = element.queue();\n\n                if ( queueLength > 1 ) {\n                    queue.splice.apply( queue,\n                        [ 1, 0 ].concat( queue.splice( queueLength, count ) ) );\n                }\n                element.dequeue();\n            },\n\n            saveStyle: function( element ) {\n                element.data( dataSpaceStyle, element[ 0 ].style.cssText );\n            },\n\n            restoreStyle: function( element ) {\n                element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || \"\";\n                element.removeData( dataSpaceStyle );\n            },\n\n            mode: function( element, mode ) {\n                var hidden = element.is( \":hidden\" );\n\n                if ( mode === \"toggle\" ) {\n                    mode = hidden ? \"show\" : \"hide\";\n                }\n                if ( hidden ? mode === \"hide\" : mode === \"show\" ) {\n                    mode = \"none\";\n                }\n                return mode;\n            },\n\n            // Translates a [top,left] array into a baseline value\n            getBaseline: function( origin, original ) {\n                var y, x;\n\n                switch ( origin[ 0 ] ) {\n                    case \"top\":\n                        y = 0;\n                        break;\n                    case \"middle\":\n                        y = 0.5;\n                        break;\n                    case \"bottom\":\n                        y = 1;\n                        break;\n                    default:\n                        y = origin[ 0 ] / original.height;\n                }\n\n                switch ( origin[ 1 ] ) {\n                    case \"left\":\n                        x = 0;\n                        break;\n                    case \"center\":\n                        x = 0.5;\n                        break;\n                    case \"right\":\n                        x = 1;\n                        break;\n                    default:\n                        x = origin[ 1 ] / original.width;\n                }\n\n                return {\n                    x: x,\n                    y: y\n                };\n            },\n\n            // Creates a placeholder element so that the original element can be made absolute\n            createPlaceholder: function( element ) {\n                var placeholder,\n                    cssPosition = element.css( \"position\" ),\n                    position = element.position();\n\n                // Lock in margins first to account for form elements, which\n                // will change margin if you explicitly set height\n                // see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380\n                // Support: Safari\n                element.css( {\n                    marginTop: element.css( \"marginTop\" ),\n                    marginBottom: element.css( \"marginBottom\" ),\n                    marginLeft: element.css( \"marginLeft\" ),\n                    marginRight: element.css( \"marginRight\" )\n                } )\n                    .outerWidth( element.outerWidth() )\n                    .outerHeight( element.outerHeight() );\n\n                if ( /^(static|relative)/.test( cssPosition ) ) {\n                    cssPosition = \"absolute\";\n\n                    placeholder = $( \"<\" + element[ 0 ].nodeName + \">\" ).insertAfter( element ).css( {\n\n                        // Convert inline to inline block to account for inline elements\n                        // that turn to inline block based on content (like img)\n                        display: /^(inline|ruby)/.test( element.css( \"display\" ) ) ?\n                            \"inline-block\" :\n                            \"block\",\n                        visibility: \"hidden\",\n\n                        // Margins need to be set to account for margin collapse\n                        marginTop: element.css( \"marginTop\" ),\n                        marginBottom: element.css( \"marginBottom\" ),\n                        marginLeft: element.css( \"marginLeft\" ),\n                        marginRight: element.css( \"marginRight\" ),\n                        \"float\": element.css( \"float\" )\n                    } )\n                        .outerWidth( element.outerWidth() )\n                        .outerHeight( element.outerHeight() )\n                        .addClass( \"ui-effects-placeholder\" );\n\n                    element.data( dataSpace + \"placeholder\", placeholder );\n                }\n\n                element.css( {\n                    position: cssPosition,\n                    left: position.left,\n                    top: position.top\n                } );\n\n                return placeholder;\n            },\n\n            removePlaceholder: function( element ) {\n                var dataKey = dataSpace + \"placeholder\",\n                    placeholder = element.data( dataKey );\n\n                if ( placeholder ) {\n                    placeholder.remove();\n                    element.removeData( dataKey );\n                }\n            },\n\n            // Removes a placeholder if it exists and restores\n            // properties that were modified during placeholder creation\n            cleanUp: function( element ) {\n                $.effects.restoreStyle( element );\n                $.effects.removePlaceholder( element );\n            },\n\n            setTransition: function( element, list, factor, value ) {\n                value = value || {};\n                $.each( list, function( i, x ) {\n                    var unit = element.cssUnit( x );\n                    if ( unit[ 0 ] > 0 ) {\n                        value[ x ] = unit[ 0 ] * factor + unit[ 1 ];\n                    }\n                } );\n                return value;\n            }\n        } );\n\n// Return an effect options object for the given parameters:\n        function _normalizeArguments( effect, options, speed, callback ) {\n\n            // Allow passing all options as the first parameter\n            if ( $.isPlainObject( effect ) ) {\n                options = effect;\n                effect = effect.effect;\n            }\n\n            // Convert to an object\n            effect = { effect: effect };\n\n            // Catch (effect, null, ...)\n            if ( options == null ) {\n                options = {};\n            }\n\n            // Catch (effect, callback)\n            if ( typeof options === \"function\" ) {\n                callback = options;\n                speed = null;\n                options = {};\n            }\n\n            // Catch (effect, speed, ?)\n            if ( typeof options === \"number\" || $.fx.speeds[ options ] ) {\n                callback = speed;\n                speed = options;\n                options = {};\n            }\n\n            // Catch (effect, options, callback)\n            if ( typeof speed === \"function\" ) {\n                callback = speed;\n                speed = null;\n            }\n\n            // Add options to effect\n            if ( options ) {\n                $.extend( effect, options );\n            }\n\n            speed = speed || options.duration;\n            effect.duration = $.fx.off ? 0 :\n                typeof speed === \"number\" ? speed :\n                    speed in $.fx.speeds ? $.fx.speeds[ speed ] :\n                        $.fx.speeds._default;\n\n            effect.complete = callback || options.complete;\n\n            return effect;\n        }\n\n        function standardAnimationOption( option ) {\n\n            // Valid standard speeds (nothing, number, named speed)\n            if ( !option || typeof option === \"number\" || $.fx.speeds[ option ] ) {\n                return true;\n            }\n\n            // Invalid strings - treat as \"normal\" speed\n            if ( typeof option === \"string\" && !$.effects.effect[ option ] ) {\n                return true;\n            }\n\n            // Complete callback\n            if ( typeof option === \"function\" ) {\n                return true;\n            }\n\n            // Options hash (but not naming an effect)\n            if ( typeof option === \"object\" && !option.effect ) {\n                return true;\n            }\n\n            // Didn't match any standard API\n            return false;\n        }\n\n        $.fn.extend( {\n            effect: function( /* effect, options, speed, callback */ ) {\n                var args = _normalizeArguments.apply( this, arguments ),\n                    effectMethod = $.effects.effect[ args.effect ],\n                    defaultMode = effectMethod.mode,\n                    queue = args.queue,\n                    queueName = queue || \"fx\",\n                    complete = args.complete,\n                    mode = args.mode,\n                    modes = [],\n                    prefilter = function( next ) {\n                        var el = $( this ),\n                            normalizedMode = $.effects.mode( el, mode ) || defaultMode;\n\n                        // Sentinel for duck-punching the :animated pseudo-selector\n                        el.data( dataSpaceAnimated, true );\n\n                        // Save effect mode for later use,\n                        // we can't just call $.effects.mode again later,\n                        // as the .show() below destroys the initial state\n                        modes.push( normalizedMode );\n\n                        // See $.uiBackCompat inside of run() for removal of defaultMode in 1.14\n                        if ( defaultMode && ( normalizedMode === \"show\" ||\n                            ( normalizedMode === defaultMode && normalizedMode === \"hide\" ) ) ) {\n                            el.show();\n                        }\n\n                        if ( !defaultMode || normalizedMode !== \"none\" ) {\n                            $.effects.saveStyle( el );\n                        }\n\n                        if ( typeof next === \"function\" ) {\n                            next();\n                        }\n                    };\n\n                if ( $.fx.off || !effectMethod ) {\n\n                    // Delegate to the original method (e.g., .show()) if possible\n                    if ( mode ) {\n                        return this[ mode ]( args.duration, complete );\n                    } else {\n                        return this.each( function() {\n                            if ( complete ) {\n                                complete.call( this );\n                            }\n                        } );\n                    }\n                }\n\n                function run( next ) {\n                    var elem = $( this );\n\n                    function cleanup() {\n                        elem.removeData( dataSpaceAnimated );\n\n                        $.effects.cleanUp( elem );\n\n                        if ( args.mode === \"hide\" ) {\n                            elem.hide();\n                        }\n\n                        done();\n                    }\n\n                    function done() {\n                        if ( typeof complete === \"function\" ) {\n                            complete.call( elem[ 0 ] );\n                        }\n\n                        if ( typeof next === \"function\" ) {\n                            next();\n                        }\n                    }\n\n                    // Override mode option on a per element basis,\n                    // as toggle can be either show or hide depending on element state\n                    args.mode = modes.shift();\n\n                    if ( $.uiBackCompat !== false && !defaultMode ) {\n                        if ( elem.is( \":hidden\" ) ? mode === \"hide\" : mode === \"show\" ) {\n\n                            // Call the core method to track \"olddisplay\" properly\n                            elem[ mode ]();\n                            done();\n                        } else {\n                            effectMethod.call( elem[ 0 ], args, done );\n                        }\n                    } else {\n                        if ( args.mode === \"none\" ) {\n\n                            // Call the core method to track \"olddisplay\" properly\n                            elem[ mode ]();\n                            done();\n                        } else {\n                            effectMethod.call( elem[ 0 ], args, cleanup );\n                        }\n                    }\n                }\n\n                // Run prefilter on all elements first to ensure that\n                // any showing or hiding happens before placeholder creation,\n                // which ensures that any layout changes are correctly captured.\n                return queue === false ?\n                    this.each( prefilter ).each( run ) :\n                    this.queue( queueName, prefilter ).queue( queueName, run );\n            },\n\n            show: ( function( orig ) {\n                return function( option ) {\n                    if ( standardAnimationOption( option ) ) {\n                        return orig.apply( this, arguments );\n                    } else {\n                        var args = _normalizeArguments.apply( this, arguments );\n                        args.mode = \"show\";\n                        return this.effect.call( this, args );\n                    }\n                };\n            } )( $.fn.show ),\n\n            hide: ( function( orig ) {\n                return function( option ) {\n                    if ( standardAnimationOption( option ) ) {\n                        return orig.apply( this, arguments );\n                    } else {\n                        var args = _normalizeArguments.apply( this, arguments );\n                        args.mode = \"hide\";\n                        return this.effect.call( this, args );\n                    }\n                };\n            } )( $.fn.hide ),\n\n            toggle: ( function( orig ) {\n                return function( option ) {\n                    if ( standardAnimationOption( option ) || typeof option === \"boolean\" ) {\n                        return orig.apply( this, arguments );\n                    } else {\n                        var args = _normalizeArguments.apply( this, arguments );\n                        args.mode = \"toggle\";\n                        return this.effect.call( this, args );\n                    }\n                };\n            } )( $.fn.toggle ),\n\n            cssUnit: function( key ) {\n                var style = this.css( key ),\n                    val = [];\n\n                $.each( [ \"em\", \"px\", \"%\", \"pt\" ], function( i, unit ) {\n                    if ( style.indexOf( unit ) > 0 ) {\n                        val = [ parseFloat( style ), unit ];\n                    }\n                } );\n                return val;\n            },\n\n            cssClip: function( clipObj ) {\n                if ( clipObj ) {\n                    return this.css( \"clip\", \"rect(\" + clipObj.top + \"px \" + clipObj.right + \"px \" +\n                        clipObj.bottom + \"px \" + clipObj.left + \"px)\" );\n                }\n                return parseClip( this.css( \"clip\" ), this );\n            },\n\n            transfer: function( options, done ) {\n                var element = $( this ),\n                    target = $( options.to ),\n                    targetFixed = target.css( \"position\" ) === \"fixed\",\n                    body = $( \"body\" ),\n                    fixTop = targetFixed ? body.scrollTop() : 0,\n                    fixLeft = targetFixed ? body.scrollLeft() : 0,\n                    endPosition = target.offset(),\n                    animation = {\n                        top: endPosition.top - fixTop,\n                        left: endPosition.left - fixLeft,\n                        height: target.innerHeight(),\n                        width: target.innerWidth()\n                    },\n                    startPosition = element.offset(),\n                    transfer = $( \"<div class='ui-effects-transfer'></div>\" );\n\n                transfer\n                    .appendTo( \"body\" )\n                    .addClass( options.className )\n                    .css( {\n                        top: startPosition.top - fixTop,\n                        left: startPosition.left - fixLeft,\n                        height: element.innerHeight(),\n                        width: element.innerWidth(),\n                        position: targetFixed ? \"fixed\" : \"absolute\"\n                    } )\n                    .animate( animation, options.duration, options.easing, function() {\n                        transfer.remove();\n                        if ( typeof done === \"function\" ) {\n                            done();\n                        }\n                    } );\n            }\n        } );\n\n        function parseClip( str, element ) {\n            var outerWidth = element.outerWidth(),\n                outerHeight = element.outerHeight(),\n                clipRegex = /^rect\\((-?\\d*\\.?\\d*px|-?\\d+%|auto),?\\s*(-?\\d*\\.?\\d*px|-?\\d+%|auto),?\\s*(-?\\d*\\.?\\d*px|-?\\d+%|auto),?\\s*(-?\\d*\\.?\\d*px|-?\\d+%|auto)\\)$/,\n                values = clipRegex.exec( str ) || [ \"\", 0, outerWidth, outerHeight, 0 ];\n\n            return {\n                top: parseFloat( values[ 1 ] ) || 0,\n                right: values[ 2 ] === \"auto\" ? outerWidth : parseFloat( values[ 2 ] ),\n                bottom: values[ 3 ] === \"auto\" ? outerHeight : parseFloat( values[ 3 ] ),\n                left: parseFloat( values[ 4 ] ) || 0\n            };\n        }\n\n        $.fx.step.clip = function( fx ) {\n            if ( !fx.clipInit ) {\n                fx.start = $( fx.elem ).cssClip();\n                if ( typeof fx.end === \"string\" ) {\n                    fx.end = parseClip( fx.end, fx.elem );\n                }\n                fx.clipInit = true;\n            }\n\n            $( fx.elem ).cssClip( {\n                top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,\n                right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,\n                bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,\n                left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left\n            } );\n        };\n\n    } )();\n\n    /******************************************************************************/\n    /*********************************** EASING ***********************************/\n    /******************************************************************************/\n\n    ( function() {\n\n// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)\n\n        var baseEasings = {};\n\n        $.each( [ \"Quad\", \"Cubic\", \"Quart\", \"Quint\", \"Expo\" ], function( i, name ) {\n            baseEasings[ name ] = function( p ) {\n                return Math.pow( p, i + 2 );\n            };\n        } );\n\n        $.extend( baseEasings, {\n            Sine: function( p ) {\n                return 1 - Math.cos( p * Math.PI / 2 );\n            },\n            Circ: function( p ) {\n                return 1 - Math.sqrt( 1 - p * p );\n            },\n            Elastic: function( p ) {\n                return p === 0 || p === 1 ? p :\n                    -Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );\n            },\n            Back: function( p ) {\n                return p * p * ( 3 * p - 2 );\n            },\n            Bounce: function( p ) {\n                var pow2,\n                    bounce = 4;\n\n                while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}\n                return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );\n            }\n        } );\n\n        $.each( baseEasings, function( name, easeIn ) {\n            $.easing[ \"easeIn\" + name ] = easeIn;\n            $.easing[ \"easeOut\" + name ] = function( p ) {\n                return 1 - easeIn( 1 - p );\n            };\n            $.easing[ \"easeInOut\" + name ] = function( p ) {\n                return p < 0.5 ?\n                    easeIn( p * 2 ) / 2 :\n                    1 - easeIn( p * -2 + 2 ) / 2;\n            };\n        } );\n\n    } )();\n\n    return $.effects;\n\n} );\n","jquery/ui-modules/version.js":"( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    $.ui = $.ui || {};\n\n    return $.ui.version = \"1.13.2\";\n\n} );\n","jquery/ui-modules/safe-active-element.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\"use strict\";\n\nreturn $.ui.safeActiveElement = function( document ) {\n\tvar activeElement;\n\n\t// Support: IE 9 only\n\t// IE9 throws an \"Unspecified error\" accessing document.activeElement from an <iframe>\n\ttry {\n\t\tactiveElement = document.activeElement;\n\t} catch ( error ) {\n\t\tactiveElement = document.body;\n\t}\n\n\t// Support: IE 9 - 11 only\n\t// IE may return null instead of an element\n\t// Interestingly, this only seems to occur when NOT in an iframe\n\tif ( !activeElement ) {\n\t\tactiveElement = document.body;\n\t}\n\n\t// Support: IE 11 only\n\t// IE11 returns a seemingly empty object in some cases when accessing\n\t// document.activeElement from an <iframe>\n\tif ( !activeElement.nodeName ) {\n\t\tactiveElement = document.body;\n\t}\n\n\treturn activeElement;\n};\n\n} );\n","jquery/ui-modules/form.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\"use strict\";\n\n// Support: IE8 Only\n// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop\n// with a string, so we need to find the proper form.\nreturn $.fn._form = function() {\n\treturn typeof this[ 0 ].form === \"string\" ? this.closest( \"form\" ) : $( this[ 0 ].form );\n};\n\n} );\n","jquery/ui-modules/scroll-parent.js":"/*!\n * jQuery UI Scroll Parent 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: scrollParent\n//>>group: Core\n//>>description: Get the closest ancestor element that is scrollable.\n//>>docs: http://api.jqueryui.com/scrollParent/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.fn.scrollParent = function( includeHidden ) {\n        var position = this.css( \"position\" ),\n            excludeStaticParent = position === \"absolute\",\n            overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,\n            scrollParent = this.parents().filter( function() {\n                var parent = $( this );\n                if ( excludeStaticParent && parent.css( \"position\" ) === \"static\" ) {\n                    return false;\n                }\n                return overflowRegex.test( parent.css( \"overflow\" ) + parent.css( \"overflow-y\" ) +\n                    parent.css( \"overflow-x\" ) );\n            } ).eq( 0 );\n\n        return position === \"fixed\" || !scrollParent.length ?\n            $( this[ 0 ].ownerDocument || document ) :\n            scrollParent;\n    };\n\n} );\n","jquery/ui-modules/labels.js":"/*!\n * jQuery UI Labels 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: labels\n//>>group: Core\n//>>description: Find all the labels associated with a given input\n//>>docs: http://api.jqueryui.com/labels/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.fn.labels = function() {\n        var ancestor, selector, id, labels, ancestors;\n\n        if ( !this.length ) {\n            return this.pushStack( [] );\n        }\n\n        // Check control.labels first\n        if ( this[ 0 ].labels && this[ 0 ].labels.length ) {\n            return this.pushStack( this[ 0 ].labels );\n        }\n\n        // Support: IE <= 11, FF <= 37, Android <= 2.3 only\n        // Above browsers do not support control.labels. Everything below is to support them\n        // as well as document fragments. control.labels does not work on document fragments\n        labels = this.eq( 0 ).parents( \"label\" );\n\n        // Look for the label based on the id\n        id = this.attr( \"id\" );\n        if ( id ) {\n\n            // We don't search against the document in case the element\n            // is disconnected from the DOM\n            ancestor = this.eq( 0 ).parents().last();\n\n            // Get a full set of top level ancestors\n            ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );\n\n            // Create a selector for the label based on the id\n            selector = \"label[for='\" + $.escapeSelector( id ) + \"']\";\n\n            labels = labels.add( ancestors.find( selector ).addBack( selector ) );\n\n        }\n\n        // Return whatever we have found for labels\n        return this.pushStack( labels );\n    };\n\n} );\n","jquery/ui-modules/focusable.js":"/*!\n * jQuery UI Focusable 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: :focusable Selector\n//>>group: Core\n//>>description: Selects elements which can be focused.\n//>>docs: http://api.jqueryui.com/focusable-selector/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n// Selectors\n    $.ui.focusable = function( element, hasTabindex ) {\n        var map, mapName, img, focusableIfVisible, fieldset,\n            nodeName = element.nodeName.toLowerCase();\n\n        if ( \"area\" === nodeName ) {\n            map = element.parentNode;\n            mapName = map.name;\n            if ( !element.href || !mapName || map.nodeName.toLowerCase() !== \"map\" ) {\n                return false;\n            }\n            img = $( \"img[usemap='#\" + mapName + \"']\" );\n            return img.length > 0 && img.is( \":visible\" );\n        }\n\n        if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) {\n            focusableIfVisible = !element.disabled;\n\n            if ( focusableIfVisible ) {\n\n                // Form controls within a disabled fieldset are disabled.\n                // However, controls within the fieldset's legend do not get disabled.\n                // Since controls generally aren't placed inside legends, we skip\n                // this portion of the check.\n                fieldset = $( element ).closest( \"fieldset\" )[ 0 ];\n                if ( fieldset ) {\n                    focusableIfVisible = !fieldset.disabled;\n                }\n            }\n        } else if ( \"a\" === nodeName ) {\n            focusableIfVisible = element.href || hasTabindex;\n        } else {\n            focusableIfVisible = hasTabindex;\n        }\n\n        return focusableIfVisible && $( element ).is( \":visible\" ) && visible( $( element ) );\n    };\n\n// Support: IE 8 only\n// IE 8 doesn't resolve inherit to visible/hidden for computed values\n    function visible( element ) {\n        var visibility = element.css( \"visibility\" );\n        while ( visibility === \"inherit\" ) {\n            element = element.parent();\n            visibility = element.css( \"visibility\" );\n        }\n        return visibility === \"visible\";\n    }\n\n    $.extend( $.expr.pseudos, {\n        focusable: function( element ) {\n            return $.ui.focusable( element, $.attr( element, \"tabindex\" ) != null );\n        }\n    } );\n\n    return $.ui.focusable;\n\n} );\n","jquery/ui-modules/core.js":"// This file is deprecated in 1.12.0 to be removed in 1.14\n( function() {\n\"use strict\";\n\ndefine( [\n\t\"jquery\",\n\t\"./data\",\n\t\"./disable-selection\",\n\t\"./focusable\",\n\t\"./form\",\n\t\"./ie\",\n\t\"./keycode\",\n\t\"./labels\",\n\t\"./jquery-patch\",\n\t\"./plugin\",\n\t\"./safe-active-element\",\n\t\"./safe-blur\",\n\t\"./scroll-parent\",\n\t\"./tabbable\",\n\t\"./unique-id\",\n\t\"./version\"\n] );\n} )();\n","jquery/ui-modules/ie.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\"use strict\";\n\n// This file is deprecated\nreturn $.ui.ie = !!/msie [\\w.]+/.exec( navigator.userAgent.toLowerCase() );\n} );\n","jquery/ui-modules/tabbable.js":"/*!\n * jQuery UI Tabbable 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: :tabbable Selector\n//>>group: Core\n//>>description: Selects elements which can be tabbed to.\n//>>docs: http://api.jqueryui.com/tabbable-selector/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\", \"./focusable\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.extend( $.expr.pseudos, {\n        tabbable: function( element ) {\n            var tabIndex = $.attr( element, \"tabindex\" ),\n                hasTabindex = tabIndex != null;\n            return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex );\n        }\n    } );\n\n} );\n","jquery/ui-modules/keycode.js":"/*!\n * jQuery UI Keycode 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: Keycode\n//>>group: Core\n//>>description: Provide keycodes as keynames\n//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.ui.keyCode = {\n        BACKSPACE: 8,\n        COMMA: 188,\n        DELETE: 46,\n        DOWN: 40,\n        END: 35,\n        ENTER: 13,\n        ESCAPE: 27,\n        HOME: 36,\n        LEFT: 37,\n        PAGE_DOWN: 34,\n        PAGE_UP: 33,\n        PERIOD: 190,\n        RIGHT: 39,\n        SPACE: 32,\n        TAB: 9,\n        UP: 38\n    };\n\n} );\n","jquery/ui-modules/unique-id.js":"/*!\n * jQuery UI Unique ID 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: uniqueId\n//>>group: Core\n//>>description: Functions to generate and remove uniqueId's\n//>>docs: http://api.jqueryui.com/uniqueId/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.fn.extend( {\n        uniqueId: ( function() {\n            var uuid = 0;\n\n            return function() {\n                return this.each( function() {\n                    if ( !this.id ) {\n                        this.id = \"ui-id-\" + ( ++uuid );\n                    }\n                } );\n            };\n        } )(),\n\n        removeUniqueId: function() {\n            return this.each( function() {\n                if ( /^ui-id-\\d+$/.test( this.id ) ) {\n                    $( this ).removeAttr( \"id\" );\n                }\n            } );\n        }\n    } );\n\n} );\n","jquery/ui-modules/position.js":"/*!\n * jQuery UI Position 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n *\n * http://api.jqueryui.com/position/\n */\n\n//>>label: Position\n//>>group: Core\n//>>description: Positions elements relative to other elements.\n//>>docs: http://api.jqueryui.com/position/\n//>>demos: http://jqueryui.com/position/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    ( function() {\n        var cachedScrollbarWidth,\n            max = Math.max,\n            abs = Math.abs,\n            rhorizontal = /left|center|right/,\n            rvertical = /top|center|bottom/,\n            roffset = /[\\+\\-]\\d+(\\.[\\d]+)?%?/,\n            rposition = /^\\w+/,\n            rpercent = /%$/,\n            _position = $.fn.position;\n\n        function getOffsets( offsets, width, height ) {\n            return [\n                parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),\n                parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )\n            ];\n        }\n\n        function parseCss( element, property ) {\n            return parseInt( $.css( element, property ), 10 ) || 0;\n        }\n\n        function isWindow( obj ) {\n            return obj != null && obj === obj.window;\n        }\n\n        function getDimensions( elem ) {\n            var raw = elem[ 0 ];\n            if ( raw.nodeType === 9 ) {\n                return {\n                    width: elem.width(),\n                    height: elem.height(),\n                    offset: { top: 0, left: 0 }\n                };\n            }\n            if ( isWindow( raw ) ) {\n                return {\n                    width: elem.width(),\n                    height: elem.height(),\n                    offset: { top: elem.scrollTop(), left: elem.scrollLeft() }\n                };\n            }\n            if ( raw.preventDefault ) {\n                return {\n                    width: 0,\n                    height: 0,\n                    offset: { top: raw.pageY, left: raw.pageX }\n                };\n            }\n            return {\n                width: elem.outerWidth(),\n                height: elem.outerHeight(),\n                offset: elem.offset()\n            };\n        }\n\n        $.position = {\n            scrollbarWidth: function() {\n                if ( cachedScrollbarWidth !== undefined ) {\n                    return cachedScrollbarWidth;\n                }\n                var w1, w2,\n                    div = $( \"<div style=\" +\n                        \"'display:block;position:absolute;width:200px;height:200px;overflow:hidden;'>\" +\n                        \"<div style='height:300px;width:auto;'></div></div>\" ),\n                    innerDiv = div.children()[ 0 ];\n\n                $( \"body\" ).append( div );\n                w1 = innerDiv.offsetWidth;\n                div.css( \"overflow\", \"scroll\" );\n\n                w2 = innerDiv.offsetWidth;\n\n                if ( w1 === w2 ) {\n                    w2 = div[ 0 ].clientWidth;\n                }\n\n                div.remove();\n\n                return ( cachedScrollbarWidth = w1 - w2 );\n            },\n            getScrollInfo: function( within ) {\n                var overflowX = within.isWindow || within.isDocument ? \"\" :\n                        within.element.css( \"overflow-x\" ),\n                    overflowY = within.isWindow || within.isDocument ? \"\" :\n                        within.element.css( \"overflow-y\" ),\n                    hasOverflowX = overflowX === \"scroll\" ||\n                        ( overflowX === \"auto\" && within.width < within.element[ 0 ].scrollWidth ),\n                    hasOverflowY = overflowY === \"scroll\" ||\n                        ( overflowY === \"auto\" && within.height < within.element[ 0 ].scrollHeight );\n                return {\n                    width: hasOverflowY ? $.position.scrollbarWidth() : 0,\n                    height: hasOverflowX ? $.position.scrollbarWidth() : 0\n                };\n            },\n            getWithinInfo: function( element ) {\n                var withinElement = $( element || window ),\n                    isElemWindow = isWindow( withinElement[ 0 ] ),\n                    isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,\n                    hasOffset = !isElemWindow && !isDocument;\n                return {\n                    element: withinElement,\n                    isWindow: isElemWindow,\n                    isDocument: isDocument,\n                    offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },\n                    scrollLeft: withinElement.scrollLeft(),\n                    scrollTop: withinElement.scrollTop(),\n                    width: withinElement.outerWidth(),\n                    height: withinElement.outerHeight()\n                };\n            }\n        };\n\n        $.fn.position = function( options ) {\n            if ( !options || !options.of ) {\n                return _position.apply( this, arguments );\n            }\n\n            // Make a copy, we don't want to modify arguments\n            options = $.extend( {}, options );\n\n            var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,\n\n                // Make sure string options are treated as CSS selectors\n                target = typeof options.of === \"string\" ?\n                    $( document ).find( options.of ) :\n                    $( options.of ),\n\n                within = $.position.getWithinInfo( options.within ),\n                scrollInfo = $.position.getScrollInfo( within ),\n                collision = ( options.collision || \"flip\" ).split( \" \" ),\n                offsets = {};\n\n            dimensions = getDimensions( target );\n            if ( target[ 0 ].preventDefault ) {\n\n                // Force left top to allow flipping\n                options.at = \"left top\";\n            }\n            targetWidth = dimensions.width;\n            targetHeight = dimensions.height;\n            targetOffset = dimensions.offset;\n\n            // Clone to reuse original targetOffset later\n            basePosition = $.extend( {}, targetOffset );\n\n            // Force my and at to have valid horizontal and vertical positions\n            // if a value is missing or invalid, it will be converted to center\n            $.each( [ \"my\", \"at\" ], function() {\n                var pos = ( options[ this ] || \"\" ).split( \" \" ),\n                    horizontalOffset,\n                    verticalOffset;\n\n                if ( pos.length === 1 ) {\n                    pos = rhorizontal.test( pos[ 0 ] ) ?\n                        pos.concat( [ \"center\" ] ) :\n                        rvertical.test( pos[ 0 ] ) ?\n                            [ \"center\" ].concat( pos ) :\n                            [ \"center\", \"center\" ];\n                }\n                pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : \"center\";\n                pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : \"center\";\n\n                // Calculate offsets\n                horizontalOffset = roffset.exec( pos[ 0 ] );\n                verticalOffset = roffset.exec( pos[ 1 ] );\n                offsets[ this ] = [\n                    horizontalOffset ? horizontalOffset[ 0 ] : 0,\n                    verticalOffset ? verticalOffset[ 0 ] : 0\n                ];\n\n                // Reduce to just the positions without the offsets\n                options[ this ] = [\n                    rposition.exec( pos[ 0 ] )[ 0 ],\n                    rposition.exec( pos[ 1 ] )[ 0 ]\n                ];\n            } );\n\n            // Normalize collision option\n            if ( collision.length === 1 ) {\n                collision[ 1 ] = collision[ 0 ];\n            }\n\n            if ( options.at[ 0 ] === \"right\" ) {\n                basePosition.left += targetWidth;\n            } else if ( options.at[ 0 ] === \"center\" ) {\n                basePosition.left += targetWidth / 2;\n            }\n\n            if ( options.at[ 1 ] === \"bottom\" ) {\n                basePosition.top += targetHeight;\n            } else if ( options.at[ 1 ] === \"center\" ) {\n                basePosition.top += targetHeight / 2;\n            }\n\n            atOffset = getOffsets( offsets.at, targetWidth, targetHeight );\n            basePosition.left += atOffset[ 0 ];\n            basePosition.top += atOffset[ 1 ];\n\n            return this.each( function() {\n                var collisionPosition, using,\n                    elem = $( this ),\n                    elemWidth = elem.outerWidth(),\n                    elemHeight = elem.outerHeight(),\n                    marginLeft = parseCss( this, \"marginLeft\" ),\n                    marginTop = parseCss( this, \"marginTop\" ),\n                    collisionWidth = elemWidth + marginLeft + parseCss( this, \"marginRight\" ) +\n                        scrollInfo.width,\n                    collisionHeight = elemHeight + marginTop + parseCss( this, \"marginBottom\" ) +\n                        scrollInfo.height,\n                    position = $.extend( {}, basePosition ),\n                    myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );\n\n                if ( options.my[ 0 ] === \"right\" ) {\n                    position.left -= elemWidth;\n                } else if ( options.my[ 0 ] === \"center\" ) {\n                    position.left -= elemWidth / 2;\n                }\n\n                if ( options.my[ 1 ] === \"bottom\" ) {\n                    position.top -= elemHeight;\n                } else if ( options.my[ 1 ] === \"center\" ) {\n                    position.top -= elemHeight / 2;\n                }\n\n                position.left += myOffset[ 0 ];\n                position.top += myOffset[ 1 ];\n\n                collisionPosition = {\n                    marginLeft: marginLeft,\n                    marginTop: marginTop\n                };\n\n                $.each( [ \"left\", \"top\" ], function( i, dir ) {\n                    if ( $.ui.position[ collision[ i ] ] ) {\n                        $.ui.position[ collision[ i ] ][ dir ]( position, {\n                            targetWidth: targetWidth,\n                            targetHeight: targetHeight,\n                            elemWidth: elemWidth,\n                            elemHeight: elemHeight,\n                            collisionPosition: collisionPosition,\n                            collisionWidth: collisionWidth,\n                            collisionHeight: collisionHeight,\n                            offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],\n                            my: options.my,\n                            at: options.at,\n                            within: within,\n                            elem: elem\n                        } );\n                    }\n                } );\n\n                if ( options.using ) {\n\n                    // Adds feedback as second argument to using callback, if present\n                    using = function( props ) {\n                        var left = targetOffset.left - position.left,\n                            right = left + targetWidth - elemWidth,\n                            top = targetOffset.top - position.top,\n                            bottom = top + targetHeight - elemHeight,\n                            feedback = {\n                                target: {\n                                    element: target,\n                                    left: targetOffset.left,\n                                    top: targetOffset.top,\n                                    width: targetWidth,\n                                    height: targetHeight\n                                },\n                                element: {\n                                    element: elem,\n                                    left: position.left,\n                                    top: position.top,\n                                    width: elemWidth,\n                                    height: elemHeight\n                                },\n                                horizontal: right < 0 ? \"left\" : left > 0 ? \"right\" : \"center\",\n                                vertical: bottom < 0 ? \"top\" : top > 0 ? \"bottom\" : \"middle\"\n                            };\n                        if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {\n                            feedback.horizontal = \"center\";\n                        }\n                        if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {\n                            feedback.vertical = \"middle\";\n                        }\n                        if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {\n                            feedback.important = \"horizontal\";\n                        } else {\n                            feedback.important = \"vertical\";\n                        }\n                        options.using.call( this, props, feedback );\n                    };\n                }\n\n                elem.offset( $.extend( position, { using: using } ) );\n            } );\n        };\n\n        $.ui.position = {\n            fit: {\n                left: function( position, data ) {\n                    var within = data.within,\n                        withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,\n                        outerWidth = within.width,\n                        collisionPosLeft = position.left - data.collisionPosition.marginLeft,\n                        overLeft = withinOffset - collisionPosLeft,\n                        overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,\n                        newOverRight;\n\n                    // Element is wider than within\n                    if ( data.collisionWidth > outerWidth ) {\n\n                        // Element is initially over the left side of within\n                        if ( overLeft > 0 && overRight <= 0 ) {\n                            newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -\n                                withinOffset;\n                            position.left += overLeft - newOverRight;\n\n                            // Element is initially over right side of within\n                        } else if ( overRight > 0 && overLeft <= 0 ) {\n                            position.left = withinOffset;\n\n                            // Element is initially over both left and right sides of within\n                        } else {\n                            if ( overLeft > overRight ) {\n                                position.left = withinOffset + outerWidth - data.collisionWidth;\n                            } else {\n                                position.left = withinOffset;\n                            }\n                        }\n\n                        // Too far left -> align with left edge\n                    } else if ( overLeft > 0 ) {\n                        position.left += overLeft;\n\n                        // Too far right -> align with right edge\n                    } else if ( overRight > 0 ) {\n                        position.left -= overRight;\n\n                        // Adjust based on position and margin\n                    } else {\n                        position.left = max( position.left - collisionPosLeft, position.left );\n                    }\n                },\n                top: function( position, data ) {\n                    var within = data.within,\n                        withinOffset = within.isWindow ? within.scrollTop : within.offset.top,\n                        outerHeight = data.within.height,\n                        collisionPosTop = position.top - data.collisionPosition.marginTop,\n                        overTop = withinOffset - collisionPosTop,\n                        overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,\n                        newOverBottom;\n\n                    // Element is taller than within\n                    if ( data.collisionHeight > outerHeight ) {\n\n                        // Element is initially over the top of within\n                        if ( overTop > 0 && overBottom <= 0 ) {\n                            newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -\n                                withinOffset;\n                            position.top += overTop - newOverBottom;\n\n                            // Element is initially over bottom of within\n                        } else if ( overBottom > 0 && overTop <= 0 ) {\n                            position.top = withinOffset;\n\n                            // Element is initially over both top and bottom of within\n                        } else {\n                            if ( overTop > overBottom ) {\n                                position.top = withinOffset + outerHeight - data.collisionHeight;\n                            } else {\n                                position.top = withinOffset;\n                            }\n                        }\n\n                        // Too far up -> align with top\n                    } else if ( overTop > 0 ) {\n                        position.top += overTop;\n\n                        // Too far down -> align with bottom edge\n                    } else if ( overBottom > 0 ) {\n                        position.top -= overBottom;\n\n                        // Adjust based on position and margin\n                    } else {\n                        position.top = max( position.top - collisionPosTop, position.top );\n                    }\n                }\n            },\n            flip: {\n                left: function( position, data ) {\n                    var within = data.within,\n                        withinOffset = within.offset.left + within.scrollLeft,\n                        outerWidth = within.width,\n                        offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,\n                        collisionPosLeft = position.left - data.collisionPosition.marginLeft,\n                        overLeft = collisionPosLeft - offsetLeft,\n                        overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,\n                        myOffset = data.my[ 0 ] === \"left\" ?\n                            -data.elemWidth :\n                            data.my[ 0 ] === \"right\" ?\n                                data.elemWidth :\n                                0,\n                        atOffset = data.at[ 0 ] === \"left\" ?\n                            data.targetWidth :\n                            data.at[ 0 ] === \"right\" ?\n                                -data.targetWidth :\n                                0,\n                        offset = -2 * data.offset[ 0 ],\n                        newOverRight,\n                        newOverLeft;\n\n                    if ( overLeft < 0 ) {\n                        newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -\n                            outerWidth - withinOffset;\n                        if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {\n                            position.left += myOffset + atOffset + offset;\n                        }\n                    } else if ( overRight > 0 ) {\n                        newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +\n                            atOffset + offset - offsetLeft;\n                        if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {\n                            position.left += myOffset + atOffset + offset;\n                        }\n                    }\n                },\n                top: function( position, data ) {\n                    var within = data.within,\n                        withinOffset = within.offset.top + within.scrollTop,\n                        outerHeight = within.height,\n                        offsetTop = within.isWindow ? within.scrollTop : within.offset.top,\n                        collisionPosTop = position.top - data.collisionPosition.marginTop,\n                        overTop = collisionPosTop - offsetTop,\n                        overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,\n                        top = data.my[ 1 ] === \"top\",\n                        myOffset = top ?\n                            -data.elemHeight :\n                            data.my[ 1 ] === \"bottom\" ?\n                                data.elemHeight :\n                                0,\n                        atOffset = data.at[ 1 ] === \"top\" ?\n                            data.targetHeight :\n                            data.at[ 1 ] === \"bottom\" ?\n                                -data.targetHeight :\n                                0,\n                        offset = -2 * data.offset[ 1 ],\n                        newOverTop,\n                        newOverBottom;\n                    if ( overTop < 0 ) {\n                        newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -\n                            outerHeight - withinOffset;\n                        if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {\n                            position.top += myOffset + atOffset + offset;\n                        }\n                    } else if ( overBottom > 0 ) {\n                        newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +\n                            offset - offsetTop;\n                        if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {\n                            position.top += myOffset + atOffset + offset;\n                        }\n                    }\n                }\n            },\n            flipfit: {\n                left: function() {\n                    $.ui.position.flip.left.apply( this, arguments );\n                    $.ui.position.fit.left.apply( this, arguments );\n                },\n                top: function() {\n                    $.ui.position.flip.top.apply( this, arguments );\n                    $.ui.position.fit.top.apply( this, arguments );\n                }\n            }\n        };\n\n    } )();\n\n    return $.ui.position;\n\n} );\n","jquery/ui-modules/widget.js":"/*!\n * jQuery UI Widget 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: Widget\n//>>group: Core\n//>>description: Provides a factory for creating stateful widgets with a common API.\n//>>docs: http://api.jqueryui.com/jQuery.widget/\n//>>demos: http://jqueryui.com/widget/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    var widgetUuid = 0;\n    var widgetHasOwnProperty = Array.prototype.hasOwnProperty;\n    var widgetSlice = Array.prototype.slice;\n\n    $.cleanData = ( function( orig ) {\n        return function( elems ) {\n            var events, elem, i;\n            for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {\n\n                // Only trigger remove when necessary to save time\n                events = $._data( elem, \"events\" );\n                if ( events && events.remove ) {\n                    $( elem ).triggerHandler( \"remove\" );\n                }\n            }\n            orig( elems );\n        };\n    } )( $.cleanData );\n\n    $.widget = function( name, base, prototype ) {\n        var existingConstructor, constructor, basePrototype;\n\n        // ProxiedPrototype allows the provided prototype to remain unmodified\n        // so that it can be used as a mixin for multiple widgets (#8876)\n        var proxiedPrototype = {};\n\n        var namespace = name.split( \".\" )[ 0 ];\n        name = name.split( \".\" )[ 1 ];\n        var fullName = namespace + \"-\" + name;\n\n        if ( !prototype ) {\n            prototype = base;\n            base = $.Widget;\n        }\n\n        if ( Array.isArray( prototype ) ) {\n            prototype = $.extend.apply( null, [ {} ].concat( prototype ) );\n        }\n\n        // Create selector for plugin\n        $.expr.pseudos[ fullName.toLowerCase() ] = function( elem ) {\n            return !!$.data( elem, fullName );\n        };\n\n        $[ namespace ] = $[ namespace ] || {};\n        existingConstructor = $[ namespace ][ name ];\n        constructor = $[ namespace ][ name ] = function( options, element ) {\n\n            // Allow instantiation without \"new\" keyword\n            if ( !this || !this._createWidget ) {\n                return new constructor( options, element );\n            }\n\n            // Allow instantiation without initializing for simple inheritance\n            // must use \"new\" keyword (the code above always passes args)\n            if ( arguments.length ) {\n                this._createWidget( options, element );\n            }\n        };\n\n        // Extend with the existing constructor to carry over any static properties\n        $.extend( constructor, existingConstructor, {\n            version: prototype.version,\n\n            // Copy the object used to create the prototype in case we need to\n            // redefine the widget later\n            _proto: $.extend( {}, prototype ),\n\n            // Track widgets that inherit from this widget in case this widget is\n            // redefined after a widget inherits from it\n            _childConstructors: []\n        } );\n\n        basePrototype = new base();\n\n        // We need to make the options hash a property directly on the new instance\n        // otherwise we'll modify the options hash on the prototype that we're\n        // inheriting from\n        basePrototype.options = $.widget.extend( {}, basePrototype.options );\n        $.each( prototype, function( prop, value ) {\n            if ( typeof value !== \"function\" ) {\n                proxiedPrototype[ prop ] = value;\n                return;\n            }\n            proxiedPrototype[ prop ] = ( function() {\n                function _super() {\n                    return base.prototype[ prop ].apply( this, arguments );\n                }\n\n                function _superApply( args ) {\n                    return base.prototype[ prop ].apply( this, args );\n                }\n\n                return function() {\n                    var __super = this._super;\n                    var __superApply = this._superApply;\n                    var returnValue;\n\n                    this._super = _super;\n                    this._superApply = _superApply;\n\n                    returnValue = value.apply( this, arguments );\n\n                    this._super = __super;\n                    this._superApply = __superApply;\n\n                    return returnValue;\n                };\n            } )();\n        } );\n        constructor.prototype = $.widget.extend( basePrototype, {\n\n            // TODO: remove support for widgetEventPrefix\n            // always use the name + a colon as the prefix, e.g., draggable:start\n            // don't prefix for widgets that aren't DOM-based\n            widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name\n        }, proxiedPrototype, {\n            constructor: constructor,\n            namespace: namespace,\n            widgetName: name,\n            widgetFullName: fullName\n        } );\n\n        // If this widget is being redefined then we need to find all widgets that\n        // are inheriting from it and redefine all of them so that they inherit from\n        // the new version of this widget. We're essentially trying to replace one\n        // level in the prototype chain.\n        if ( existingConstructor ) {\n            $.each( existingConstructor._childConstructors, function( i, child ) {\n                var childPrototype = child.prototype;\n\n                // Redefine the child widget using the same prototype that was\n                // originally used, but inherit from the new version of the base\n                $.widget( childPrototype.namespace + \".\" + childPrototype.widgetName, constructor,\n                    child._proto );\n            } );\n\n            // Remove the list of existing child constructors from the old constructor\n            // so the old child constructors can be garbage collected\n            delete existingConstructor._childConstructors;\n        } else {\n            base._childConstructors.push( constructor );\n        }\n\n        $.widget.bridge( name, constructor );\n\n        return constructor;\n    };\n\n    $.widget.extend = function( target ) {\n        var input = widgetSlice.call( arguments, 1 );\n        var inputIndex = 0;\n        var inputLength = input.length;\n        var key;\n        var value;\n\n        for ( ; inputIndex < inputLength; inputIndex++ ) {\n            for ( key in input[ inputIndex ] ) {\n                value = input[ inputIndex ][ key ];\n                if ( widgetHasOwnProperty.call( input[ inputIndex ], key ) && value !== undefined ) {\n\n                    // Clone objects\n                    if ( $.isPlainObject( value ) ) {\n                        target[ key ] = $.isPlainObject( target[ key ] ) ?\n                            $.widget.extend( {}, target[ key ], value ) :\n\n                            // Don't extend strings, arrays, etc. with objects\n                            $.widget.extend( {}, value );\n\n                        // Copy everything else by reference\n                    } else {\n                        target[ key ] = value;\n                    }\n                }\n            }\n        }\n        return target;\n    };\n\n    $.widget.bridge = function( name, object ) {\n        var fullName = object.prototype.widgetFullName || name;\n        $.fn[ name ] = function( options ) {\n            var isMethodCall = typeof options === \"string\";\n            var args = widgetSlice.call( arguments, 1 );\n            var returnValue = this;\n\n            if ( isMethodCall ) {\n\n                // If this is an empty collection, we need to have the instance method\n                // return undefined instead of the jQuery instance\n                if ( !this.length && options === \"instance\" ) {\n                    returnValue = undefined;\n                } else {\n                    this.each( function() {\n                        var methodValue;\n                        var instance = $.data( this, fullName );\n\n                        if ( options === \"instance\" ) {\n                            returnValue = instance;\n                            return false;\n                        }\n\n                        if ( !instance ) {\n                            return $.error( \"cannot call methods on \" + name +\n                                \" prior to initialization; \" +\n                                \"attempted to call method '\" + options + \"'\" );\n                        }\n\n                        if ( typeof instance[ options ] !== \"function\" ||\n                            options.charAt( 0 ) === \"_\" ) {\n                            return $.error( \"no such method '\" + options + \"' for \" + name +\n                                \" widget instance\" );\n                        }\n\n                        methodValue = instance[ options ].apply( instance, args );\n\n                        if ( methodValue !== instance && methodValue !== undefined ) {\n                            returnValue = methodValue && methodValue.jquery ?\n                                returnValue.pushStack( methodValue.get() ) :\n                                methodValue;\n                            return false;\n                        }\n                    } );\n                }\n            } else {\n\n                // Allow multiple hashes to be passed on init\n                if ( args.length ) {\n                    options = $.widget.extend.apply( null, [ options ].concat( args ) );\n                }\n\n                this.each( function() {\n                    var instance = $.data( this, fullName );\n                    if ( instance ) {\n                        instance.option( options || {} );\n                        if ( instance._init ) {\n                            instance._init();\n                        }\n                    } else {\n                        $.data( this, fullName, new object( options, this ) );\n                    }\n                } );\n            }\n\n            return returnValue;\n        };\n    };\n\n    $.Widget = function( /* options, element */ ) {};\n    $.Widget._childConstructors = [];\n\n    $.Widget.prototype = {\n        widgetName: \"widget\",\n        widgetEventPrefix: \"\",\n        defaultElement: \"<div>\",\n\n        options: {\n            classes: {},\n            disabled: false,\n\n            // Callbacks\n            create: null\n        },\n\n        _createWidget: function( options, element ) {\n            element = $( element || this.defaultElement || this )[ 0 ];\n            this.element = $( element );\n            this.uuid = widgetUuid++;\n            this.eventNamespace = \".\" + this.widgetName + this.uuid;\n\n            this.bindings = $();\n            this.hoverable = $();\n            this.focusable = $();\n            this.classesElementLookup = {};\n\n            if ( element !== this ) {\n                $.data( element, this.widgetFullName, this );\n                this._on( true, this.element, {\n                    remove: function( event ) {\n                        if ( event.target === element ) {\n                            this.destroy();\n                        }\n                    }\n                } );\n                this.document = $( element.style ?\n\n                    // Element within the document\n                    element.ownerDocument :\n\n                    // Element is window or document\n                    element.document || element );\n                this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );\n            }\n\n            this.options = $.widget.extend( {},\n                this.options,\n                this._getCreateOptions(),\n                options );\n\n            this._create();\n\n            if ( this.options.disabled ) {\n                this._setOptionDisabled( this.options.disabled );\n            }\n\n            this._trigger( \"create\", null, this._getCreateEventData() );\n            this._init();\n        },\n\n        _getCreateOptions: function() {\n            return {};\n        },\n\n        _getCreateEventData: $.noop,\n\n        _create: $.noop,\n\n        _init: $.noop,\n\n        destroy: function() {\n            var that = this;\n\n            this._destroy();\n            $.each( this.classesElementLookup, function( key, value ) {\n                that._removeClass( value, key );\n            } );\n\n            // We can probably remove the unbind calls in 2.0\n            // all event bindings should go through this._on()\n            this.element\n                .off( this.eventNamespace )\n                .removeData( this.widgetFullName );\n            this.widget()\n                .off( this.eventNamespace )\n                .removeAttr( \"aria-disabled\" );\n\n            // Clean up events and states\n            this.bindings.off( this.eventNamespace );\n        },\n\n        _destroy: $.noop,\n\n        widget: function() {\n            return this.element;\n        },\n\n        option: function( key, value ) {\n            var options = key;\n            var parts;\n            var curOption;\n            var i;\n\n            if ( arguments.length === 0 ) {\n\n                // Don't return a reference to the internal hash\n                return $.widget.extend( {}, this.options );\n            }\n\n            if ( typeof key === \"string\" ) {\n\n                // Handle nested keys, e.g., \"foo.bar\" => { foo: { bar: ___ } }\n                options = {};\n                parts = key.split( \".\" );\n                key = parts.shift();\n                if ( parts.length ) {\n                    curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );\n                    for ( i = 0; i < parts.length - 1; i++ ) {\n                        curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};\n                        curOption = curOption[ parts[ i ] ];\n                    }\n                    key = parts.pop();\n                    if ( arguments.length === 1 ) {\n                        return curOption[ key ] === undefined ? null : curOption[ key ];\n                    }\n                    curOption[ key ] = value;\n                } else {\n                    if ( arguments.length === 1 ) {\n                        return this.options[ key ] === undefined ? null : this.options[ key ];\n                    }\n                    options[ key ] = value;\n                }\n            }\n\n            this._setOptions( options );\n\n            return this;\n        },\n\n        _setOptions: function( options ) {\n            var key;\n\n            for ( key in options ) {\n                this._setOption( key, options[ key ] );\n            }\n\n            return this;\n        },\n\n        _setOption: function( key, value ) {\n            if ( key === \"classes\" ) {\n                this._setOptionClasses( value );\n            }\n\n            this.options[ key ] = value;\n\n            if ( key === \"disabled\" ) {\n                this._setOptionDisabled( value );\n            }\n\n            return this;\n        },\n\n        _setOptionClasses: function( value ) {\n            var classKey, elements, currentElements;\n\n            for ( classKey in value ) {\n                currentElements = this.classesElementLookup[ classKey ];\n                if ( value[ classKey ] === this.options.classes[ classKey ] ||\n                    !currentElements ||\n                    !currentElements.length ) {\n                    continue;\n                }\n\n                // We are doing this to create a new jQuery object because the _removeClass() call\n                // on the next line is going to destroy the reference to the current elements being\n                // tracked. We need to save a copy of this collection so that we can add the new classes\n                // below.\n                elements = $( currentElements.get() );\n                this._removeClass( currentElements, classKey );\n\n                // We don't use _addClass() here, because that uses this.options.classes\n                // for generating the string of classes. We want to use the value passed in from\n                // _setOption(), this is the new value of the classes option which was passed to\n                // _setOption(). We pass this value directly to _classes().\n                elements.addClass( this._classes( {\n                    element: elements,\n                    keys: classKey,\n                    classes: value,\n                    add: true\n                } ) );\n            }\n        },\n\n        _setOptionDisabled: function( value ) {\n            this._toggleClass( this.widget(), this.widgetFullName + \"-disabled\", null, !!value );\n\n            // If the widget is becoming disabled, then nothing is interactive\n            if ( value ) {\n                this._removeClass( this.hoverable, null, \"ui-state-hover\" );\n                this._removeClass( this.focusable, null, \"ui-state-focus\" );\n            }\n        },\n\n        enable: function() {\n            return this._setOptions( { disabled: false } );\n        },\n\n        disable: function() {\n            return this._setOptions( { disabled: true } );\n        },\n\n        _classes: function( options ) {\n            var full = [];\n            var that = this;\n\n            options = $.extend( {\n                element: this.element,\n                classes: this.options.classes || {}\n            }, options );\n\n            function bindRemoveEvent() {\n                var nodesToBind = [];\n\n                options.element.each( function( _, element ) {\n                    var isTracked = $.map( that.classesElementLookup, function( elements ) {\n                        return elements;\n                    } )\n                        .some( function( elements ) {\n                            return elements.is( element );\n                        } );\n\n                    if ( !isTracked ) {\n                        nodesToBind.push( element );\n                    }\n                } );\n\n                that._on( $( nodesToBind ), {\n                    remove: \"_untrackClassesElement\"\n                } );\n            }\n\n            function processClassString( classes, checkOption ) {\n                var current, i;\n                for ( i = 0; i < classes.length; i++ ) {\n                    current = that.classesElementLookup[ classes[ i ] ] || $();\n                    if ( options.add ) {\n                        bindRemoveEvent();\n                        current = $( $.uniqueSort( current.get().concat( options.element.get() ) ) );\n                    } else {\n                        current = $( current.not( options.element ).get() );\n                    }\n                    that.classesElementLookup[ classes[ i ] ] = current;\n                    full.push( classes[ i ] );\n                    if ( checkOption && options.classes[ classes[ i ] ] ) {\n                        full.push( options.classes[ classes[ i ] ] );\n                    }\n                }\n            }\n\n            if ( options.keys ) {\n                processClassString( options.keys.match( /\\S+/g ) || [], true );\n            }\n            if ( options.extra ) {\n                processClassString( options.extra.match( /\\S+/g ) || [] );\n            }\n\n            return full.join( \" \" );\n        },\n\n        _untrackClassesElement: function( event ) {\n            var that = this;\n            $.each( that.classesElementLookup, function( key, value ) {\n                if ( $.inArray( event.target, value ) !== -1 ) {\n                    that.classesElementLookup[ key ] = $( value.not( event.target ).get() );\n                }\n            } );\n\n            this._off( $( event.target ) );\n        },\n\n        _removeClass: function( element, keys, extra ) {\n            return this._toggleClass( element, keys, extra, false );\n        },\n\n        _addClass: function( element, keys, extra ) {\n            return this._toggleClass( element, keys, extra, true );\n        },\n\n        _toggleClass: function( element, keys, extra, add ) {\n            add = ( typeof add === \"boolean\" ) ? add : extra;\n            var shift = ( typeof element === \"string\" || element === null ),\n                options = {\n                    extra: shift ? keys : extra,\n                    keys: shift ? element : keys,\n                    element: shift ? this.element : element,\n                    add: add\n                };\n            options.element.toggleClass( this._classes( options ), add );\n            return this;\n        },\n\n        _on: function( suppressDisabledCheck, element, handlers ) {\n            var delegateElement;\n            var instance = this;\n\n            // No suppressDisabledCheck flag, shuffle arguments\n            if ( typeof suppressDisabledCheck !== \"boolean\" ) {\n                handlers = element;\n                element = suppressDisabledCheck;\n                suppressDisabledCheck = false;\n            }\n\n            // No element argument, shuffle and use this.element\n            if ( !handlers ) {\n                handlers = element;\n                element = this.element;\n                delegateElement = this.widget();\n            } else {\n                element = delegateElement = $( element );\n                this.bindings = this.bindings.add( element );\n            }\n\n            $.each( handlers, function( event, handler ) {\n                function handlerProxy() {\n\n                    // Allow widgets to customize the disabled handling\n                    // - disabled as an array instead of boolean\n                    // - disabled class as method for disabling individual parts\n                    if ( !suppressDisabledCheck &&\n                        ( instance.options.disabled === true ||\n                            $( this ).hasClass( \"ui-state-disabled\" ) ) ) {\n                        return;\n                    }\n                    return ( typeof handler === \"string\" ? instance[ handler ] : handler )\n                        .apply( instance, arguments );\n                }\n\n                // Copy the guid so direct unbinding works\n                if ( typeof handler !== \"string\" ) {\n                    handlerProxy.guid = handler.guid =\n                        handler.guid || handlerProxy.guid || $.guid++;\n                }\n\n                var match = event.match( /^([\\w:-]*)\\s*(.*)$/ );\n                var eventName = match[ 1 ] + instance.eventNamespace;\n                var selector = match[ 2 ];\n\n                if ( selector ) {\n                    delegateElement.on( eventName, selector, handlerProxy );\n                } else {\n                    element.on( eventName, handlerProxy );\n                }\n            } );\n        },\n\n        _off: function( element, eventName ) {\n            eventName = ( eventName || \"\" ).split( \" \" ).join( this.eventNamespace + \" \" ) +\n                this.eventNamespace;\n            element.off( eventName );\n\n            // Clear the stack to avoid memory leaks (#10056)\n            this.bindings = $( this.bindings.not( element ).get() );\n            this.focusable = $( this.focusable.not( element ).get() );\n            this.hoverable = $( this.hoverable.not( element ).get() );\n        },\n\n        _delay: function( handler, delay ) {\n            function handlerProxy() {\n                return ( typeof handler === \"string\" ? instance[ handler ] : handler )\n                    .apply( instance, arguments );\n            }\n            var instance = this;\n            return setTimeout( handlerProxy, delay || 0 );\n        },\n\n        _hoverable: function( element ) {\n            this.hoverable = this.hoverable.add( element );\n            this._on( element, {\n                mouseenter: function( event ) {\n                    this._addClass( $( event.currentTarget ), null, \"ui-state-hover\" );\n                },\n                mouseleave: function( event ) {\n                    this._removeClass( $( event.currentTarget ), null, \"ui-state-hover\" );\n                }\n            } );\n        },\n\n        _focusable: function( element ) {\n            this.focusable = this.focusable.add( element );\n            this._on( element, {\n                focusin: function( event ) {\n                    this._addClass( $( event.currentTarget ), null, \"ui-state-focus\" );\n                },\n                focusout: function( event ) {\n                    this._removeClass( $( event.currentTarget ), null, \"ui-state-focus\" );\n                }\n            } );\n        },\n\n        _trigger: function( type, event, data ) {\n            var prop, orig;\n            var callback = this.options[ type ];\n\n            data = data || {};\n            event = $.Event( event );\n            event.type = ( type === this.widgetEventPrefix ?\n                type :\n                this.widgetEventPrefix + type ).toLowerCase();\n\n            // The original event may come from any element\n            // so we need to reset the target on the new event\n            event.target = this.element[ 0 ];\n\n            // Copy original event properties over to the new event\n            orig = event.originalEvent;\n            if ( orig ) {\n                for ( prop in orig ) {\n                    if ( !( prop in event ) ) {\n                        event[ prop ] = orig[ prop ];\n                    }\n                }\n            }\n\n            this.element.trigger( event, data );\n            return !( typeof callback === \"function\" &&\n                callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||\n                event.isDefaultPrevented() );\n        }\n    };\n\n    $.each( { show: \"fadeIn\", hide: \"fadeOut\" }, function( method, defaultEffect ) {\n        $.Widget.prototype[ \"_\" + method ] = function( element, options, callback ) {\n            if ( typeof options === \"string\" ) {\n                options = { effect: options };\n            }\n\n            var hasOptions;\n            var effectName = !options ?\n                method :\n                options === true || typeof options === \"number\" ?\n                    defaultEffect :\n                    options.effect || defaultEffect;\n\n            options = options || {};\n            if ( typeof options === \"number\" ) {\n                options = { duration: options };\n            } else if ( options === true ) {\n                options = {};\n            }\n\n            hasOptions = !$.isEmptyObject( options );\n            options.complete = callback;\n\n            if ( options.delay ) {\n                element.delay( options.delay );\n            }\n\n            if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {\n                element[ method ]( options );\n            } else if ( effectName !== method && element[ effectName ] ) {\n                element[ effectName ]( options.duration, options.easing, callback );\n            } else {\n                element.queue( function( next ) {\n                    $( this )[ method ]();\n                    if ( callback ) {\n                        callback.call( element[ 0 ] );\n                    }\n                    next();\n                } );\n            }\n        };\n    } );\n\n    return $.widget;\n\n} );\n","jquery/ui-modules/safe-blur.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\"use strict\";\n\nreturn $.ui.safeBlur = function( element ) {\n\n\t// Support: IE9 - 10 only\n\t// If the <body> is blurred, IE will switch windows, see #9420\n\tif ( element && element.nodeName.toLowerCase() !== \"body\" ) {\n\t\t$( element ).trigger( \"blur\" );\n\t}\n};\n\n} );\n","jquery/ui-modules/plugin.js":"( function( factory ) {\n\t\"use strict\";\n\n\tif ( typeof define === \"function\" && define.amd ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\tdefine( [ \"jquery\", \"./version\" ], factory );\n\t} else {\n\n\t\t// Browser globals\n\t\tfactory( jQuery );\n\t}\n} )( function( $ ) {\n\"use strict\";\n\n// $.ui.plugin is deprecated. Use $.widget() extensions instead.\nreturn $.ui.plugin = {\n\tadd: function( module, option, set ) {\n\t\tvar i,\n\t\t\tproto = $.ui[ module ].prototype;\n\t\tfor ( i in set ) {\n\t\t\tproto.plugins[ i ] = proto.plugins[ i ] || [];\n\t\t\tproto.plugins[ i ].push( [ option, set[ i ] ] );\n\t\t}\n\t},\n\tcall: function( instance, name, args, allowDisconnected ) {\n\t\tvar i,\n\t\t\tset = instance.plugins[ name ];\n\n\t\tif ( !set ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||\n\t\t\t\tinstance.element[ 0 ].parentNode.nodeType === 11 ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor ( i = 0; i < set.length; i++ ) {\n\t\t\tif ( instance.options[ set[ i ][ 0 ] ] ) {\n\t\t\t\tset[ i ][ 1 ].apply( instance.element, args );\n\t\t\t}\n\t\t}\n\t}\n};\n\n} );\n","jquery/ui-modules/jquery-patch.js":"/*!\n * jQuery UI Support for jQuery core 1.8.x and newer 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n *\n */\n\n//>>label: jQuery 1.8+ Support\n//>>group: Core\n//>>description: Support version 1.8.x and newer of jQuery core\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n// Support: jQuery 1.9.x or older\n// $.expr[ \":\" ] is deprecated.\n    if ( !$.expr.pseudos ) {\n        $.expr.pseudos = $.expr[ \":\" ];\n    }\n\n// Support: jQuery 1.11.x or older\n// $.unique has been renamed to $.uniqueSort\n    if ( !$.uniqueSort ) {\n        $.uniqueSort = $.unique;\n    }\n\n// Support: jQuery 2.2.x or older.\n// This method has been defined in jQuery 3.0.0.\n// Code from https://github.com/jquery/jquery/blob/e539bac79e666bba95bba86d690b4e609dca2286/src/selector/escapeSelector.js\n    if ( !$.escapeSelector ) {\n\n        // CSS string/identifier serialization\n        // https://drafts.csswg.org/cssom/#common-serializing-idioms\n        var rcssescape = /([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\x80-\\uFFFF\\w-]/g;\n\n        var fcssescape = function( ch, asCodePoint ) {\n            if ( asCodePoint ) {\n\n                // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER\n                if ( ch === \"\\0\" ) {\n                    return \"\\uFFFD\";\n                }\n\n                // Control characters and (dependent upon position) numbers get escaped as code points\n                return ch.slice( 0, -1 ) + \"\\\\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + \" \";\n            }\n\n            // Other potentially-special ASCII characters get backslash-escaped\n            return \"\\\\\" + ch;\n        };\n\n        $.escapeSelector = function( sel ) {\n            return ( sel + \"\" ).replace( rcssescape, fcssescape );\n        };\n    }\n\n// Support: jQuery 3.4.x or older\n// These methods have been defined in jQuery 3.5.0.\n    if ( !$.fn.even || !$.fn.odd ) {\n        $.fn.extend( {\n            even: function() {\n                return this.filter( function( i ) {\n                    return i % 2 === 0;\n                } );\n            },\n            odd: function() {\n                return this.filter( function( i ) {\n                    return i % 2 === 1;\n                } );\n            }\n        } );\n    }\n\n} );\n","jquery/ui-modules/disable-selection.js":"/*!\n * jQuery UI Disable Selection 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: disableSelection\n//>>group: Core\n//>>description: Disable selection of text content within the set of matched elements.\n//>>docs: http://api.jqueryui.com/disableSelection/\n\n// This file is deprecated\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [ \"jquery\", \"./version\" ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.fn.extend( {\n        disableSelection: ( function() {\n            var eventType = \"onselectstart\" in document.createElement( \"div\" ) ?\n                \"selectstart\" :\n                \"mousedown\";\n\n            return function() {\n                return this.on( eventType + \".ui-disableSelection\", function( event ) {\n                    event.preventDefault();\n                } );\n            };\n        } )(),\n\n        enableSelection: function() {\n            return this.off( \".ui-disableSelection\" );\n        }\n    } );\n\n} );\n","jquery/ui-modules/form-reset-mixin.js":"/*!\n * jQuery UI Form Reset Mixin 1.13.2\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: Form Reset Mixin\n//>>group: Core\n//>>description: Refresh input widgets when their form is reset\n//>>docs: http://api.jqueryui.com/form-reset-mixin/\n\n( function( factory ) {\n    \"use strict\";\n\n    if ( typeof define === \"function\" && define.amd ) {\n\n        // AMD. Register as an anonymous module.\n        define( [\n            \"jquery\",\n            \"./form\",\n            \"./version\"\n        ], factory );\n    } else {\n\n        // Browser globals\n        factory( jQuery );\n    }\n} )( function( $ ) {\n    \"use strict\";\n\n    return $.ui.formResetMixin = {\n        _formResetHandler: function() {\n            var form = $( this );\n\n            // Wait for the form reset to actually happen before refreshing\n            setTimeout( function() {\n                var instances = form.data( \"ui-form-reset-instances\" );\n                $.each( instances, function() {\n                    this.refresh();\n                } );\n            } );\n        },\n\n        _bindFormResetHandler: function() {\n            this.form = this.element._form();\n            if ( !this.form.length ) {\n                return;\n            }\n\n            var instances = this.form.data( \"ui-form-reset-instances\" ) || [];\n            if ( !instances.length ) {\n\n                // We don't use _on() here because we use a single event handler per form\n                this.form.on( \"reset.ui-form-reset\", this._formResetHandler );\n            }\n            instances.push( this );\n            this.form.data( \"ui-form-reset-instances\", instances );\n        },\n\n        _unbindFormResetHandler: function() {\n            if ( !this.form.length ) {\n                return;\n            }\n\n            var instances = this.form.data( \"ui-form-reset-instances\" );\n            instances.splice( $.inArray( this, instances ), 1 );\n            if ( instances.length ) {\n                this.form.data( \"ui-form-reset-instances\", instances );\n            } else {\n                this.form\n                    .removeData( \"ui-form-reset-instances\" )\n                    .off( \"reset.ui-form-reset\" );\n            }\n        }\n    };\n\n} );\n"}
}});

Spamworldpro Mini