![]() 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/ |
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(/ | /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 ? ' ' : // 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(/<%|%3C%/g, '<%').replace(/%>|%%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, '"') : 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 ''': '\\''\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 = ['­', '<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'>▼</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"} }});