require.config({"config": { "jsbuild":{"Magento_Ui/js/lib/knockout/bindings/mage-init.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'ko',\n 'underscore',\n 'mage/apply/main'\n], function (ko, _, mage) {\n 'use strict';\n\n ko.bindingHandlers.mageInit = {\n /**\n * Initializes components assigned to HTML elements.\n *\n * @param {HTMLElement} el\n * @param {Function} valueAccessor\n */\n init: function (el, valueAccessor) {\n var data = valueAccessor();\n\n _.each(data, function (config, component) {\n mage.applyFor(el, config, component);\n });\n }\n };\n});\n","Magento_Ui/js/lib/knockout/bindings/outer_click.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/** Creates outerClick binding and registers in to ko.bindingHandlers object */\ndefine([\n 'ko',\n 'jquery',\n 'underscore',\n '../template/renderer'\n], function (ko, $, _, renderer) {\n 'use strict';\n\n var defaults = {\n onlyIfVisible: true\n };\n\n /**\n * Checks if element sis visible.\n *\n * @param {Element} el\n * @returns {Boolean}\n */\n function isVisible(el) {\n var style = window.getComputedStyle(el),\n visibility = {\n display: 'none',\n visibility: 'hidden',\n opacity: '0'\n },\n visible = true;\n\n _.each(visibility, function (val, key) {\n if (style[key] === val) {\n visible = false;\n }\n });\n\n return visible;\n }\n\n /**\n * Document click handler which in case if event target is not\n * a descendant of provided container element,\n * invokes specified in configuration callback.\n *\n * @param {HTMLElement} container\n * @param {Object} config\n * @param {EventObject} e\n */\n function onOuterClick(container, config, e) {\n var target = e.target,\n callback = config.callback;\n\n if (container === target || container.contains(target)) {\n return;\n }\n\n if (config.onlyIfVisible) {\n if (!_.isNull(container.offsetParent) && isVisible(container)) {\n callback();\n }\n } else {\n callback();\n }\n }\n\n /**\n * Prepares configuration for the binding based\n * on a default properties and provided options.\n *\n * @param {(Object|Function)} [options={}]\n * @returns {Object}\n */\n function buildConfig(options) {\n var config = {};\n\n if (_.isFunction(options)) {\n options = {\n callback: options\n };\n } else if (!_.isObject(options)) {\n options = {};\n }\n\n return _.extend(config, defaults, options);\n }\n\n ko.bindingHandlers.outerClick = {\n\n /**\n * Initializes outer click binding.\n */\n init: function (element, valueAccessor) {\n var config = buildConfig(valueAccessor()),\n outerClick = onOuterClick.bind(null, element, config),\n isTouchDevice = typeof document.ontouchstart !== 'undefined';\n\n if (isTouchDevice) {\n $(document).on('touchstart', outerClick);\n\n ko.utils.domNodeDisposal.addDisposeCallback(element, function () {\n $(document).off('touchstart', outerClick);\n });\n } else {\n $(document).on('click', outerClick);\n\n ko.utils.domNodeDisposal.addDisposeCallback(element, function () {\n $(document).off('click', outerClick);\n });\n }\n }\n };\n\n renderer.addAttribute('outerClick');\n});\n","Magento_Ui/js/lib/knockout/bindings/resizable.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'ko',\n 'jquery',\n 'Magento_Ui/js/lib/view/utils/async',\n 'uiRegistry',\n 'underscore',\n '../template/renderer',\n 'jquery/ui'\n], function (ko, $, async, registry, _, renderer) {\n 'use strict';\n\n var sizeOptions = [\n 'minHeight',\n 'maxHeight',\n 'minWidth',\n 'maxWidth'\n ],\n\n handles = {\n height: '.ui-resizable-s, .ui-resizable-n',\n width: '.ui-resizable-w, .ui-resizable-e'\n };\n\n /**\n * Recalcs visibility of handles, width and height of resizable based on content\n * @param {HTMLElement} element\n */\n function adjustSize(element) {\n var maxHeight,\n maxWidth;\n\n element = $(element);\n maxHeight = element.resizable('option').maxHeight;\n maxWidth = element.resizable('option').maxWidth;\n\n if (maxHeight && element.height() > maxHeight) {\n element.height(maxHeight + 1);\n $(handles.height).hide();\n } else {\n $(handles.height).show();\n }\n\n if (maxWidth && element.width() > maxWidth) {\n element.width(maxWidth + 1);\n $(handles.width).hide();\n } else {\n $(handles.width).show();\n }\n }\n\n /**\n * Recalcs allowed min, max width and height based on configured selectors\n * @param {Object} sizeConstraints\n * @param {String} componentName\n * @param {HTMLElement} element\n * @param {Boolean} hasWidthUpdate\n */\n function recalcAllowedSize(sizeConstraints, componentName, element, hasWidthUpdate) {\n var size;\n\n element = $(element);\n\n if (!element.data('resizable')) {\n return;\n }\n\n if (!hasWidthUpdate) {\n element.css('width', 'auto');\n }\n\n _.each(sizeConstraints, function (selector, key) {\n async.async({\n component: componentName,\n selector: selector\n }, function (elem) {\n size = key.indexOf('Height') !== -1 ? $(elem).outerHeight(true) : $(elem).outerWidth(true);\n\n if (element.data('resizable')) {\n element.resizable('option', key, size + 1);\n }\n });\n }, this);\n\n adjustSize(element);\n }\n\n /**\n * Preprocess config to separate options,\n * which must be processed further before applying\n *\n * @param {Object} config\n * @param {Object} viewModel\n * @param {*} element\n * @return {Object} config\n */\n function processConfig(config, viewModel, element) {\n var sizeConstraint,\n sizeConstraints = {},\n recalc,\n hasWidthUpdate;\n\n if (_.isEmpty(config)) {\n return {};\n }\n _.each(sizeOptions, function (key) {\n sizeConstraint = config[key];\n\n if (sizeConstraint && !_.isNumber(sizeConstraint)) {\n sizeConstraints[key] = sizeConstraint;\n delete config[key];\n }\n });\n hasWidthUpdate = _.some(sizeConstraints, function (value, key) {\n return key.indexOf('Width') !== -1;\n });\n\n recalc = recalcAllowedSize.bind(null, sizeConstraints, viewModel.name, element, hasWidthUpdate);\n config.start = recalc;\n $(window).on('resize.resizable', recalc);\n registry.get(viewModel.provider).on('reloaded', recalc);\n\n return config;\n }\n\n ko.bindingHandlers.resizable = {\n\n /**\n * Binding init callback.\n *\n * @param {*} element\n * @param {Function} valueAccessor\n * @param {Function} allBindings\n * @param {Object} viewModel\n */\n init: function (element, valueAccessor, allBindings, viewModel) {\n var config = processConfig(valueAccessor(), viewModel, element);\n\n $(element).resizable(config);\n }\n };\n\n renderer.addAttribute('resizable');\n});\n","Magento_Ui/js/lib/knockout/bindings/autoselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'ko',\n 'jquery',\n '../template/renderer'\n], function (ko, $, renderer) {\n 'use strict';\n\n /**\n * 'Focus' event handler.\n *\n * @param {EventObject} e\n */\n function onFocus(e) {\n e.target.select();\n }\n\n ko.bindingHandlers.autoselect = {\n\n /**\n * Adds event handler which automatically\n * selects inputs' element text when field gets focused.\n */\n init: function (element, valueAccessor) {\n var enabled = ko.unwrap(valueAccessor());\n\n if (enabled !== false) {\n $(element).on('focus', onFocus);\n }\n }\n };\n\n renderer.addAttribute('autoselect');\n});\n","Magento_Ui/js/lib/logger/entry.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n './levels-pool'\n], function (logLevels) {\n 'use strict';\n\n /**\n * @param {String} message\n * @param {Number} level\n * @param {Object} [data]\n */\n function LogEntry(message, level, data) {\n /**\n * @readonly\n * @type {Number}\n */\n this.timestamp = Date.now();\n\n /**\n * @readonly\n * @type {Number}\n */\n this.level = level;\n\n /**\n * @readonly\n * @type {String}\n */\n this.levelName = logLevels.getNameByCode(level);\n\n /**\n * @readonly\n * @type {Object}\n */\n this.data = data;\n\n /**\n * @readonly\n * @type {String}\n */\n this.message = message;\n }\n\n return LogEntry;\n});\n","Magento_Ui/js/lib/logger/console-logger.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n './logger',\n './entry-factory',\n './console-output-handler',\n './formatter',\n './message-pool',\n './levels-pool',\n 'Magento_Ui/js/lib/core/storage/local',\n 'underscore',\n './logger-utils'\n], function (Logger, entryFactory, ConsoleHandler, Formatter, messagePoll, levelsPoll, storage, _, LoggerUtils) {\n 'use strict';\n\n var STORAGE_NAMESPACE = 'CONSOLE_LOGGER';\n\n /**\n * Singleton Logger's sub-class instance of which is configured to display its\n * messages to the console. It also provides the support of predefined messages\n * and persists its display level.\n */\n function ConsoleLogger() {\n var formatter = new Formatter(),\n consoleHandler = new ConsoleHandler(formatter),\n savedLevel = storage.get(STORAGE_NAMESPACE),\n utils = new LoggerUtils(this);\n\n Logger.call(this, consoleHandler, entryFactory);\n\n if (savedLevel) {\n this.displayLevel_ = savedLevel;\n }\n\n this.utils = utils;\n this.messages = messagePoll;\n this.levels = levelsPoll.getLevels();\n }\n\n _.extend(ConsoleLogger, Logger);\n\n ConsoleLogger.prototype = Object.create(Logger.prototype);\n ConsoleLogger.prototype.constructor = ConsoleLogger;\n\n /**\n * Overrides parent method to save the provided display level.\n *\n * @override\n */\n ConsoleLogger.prototype.setDisplayLevel = function (level) {\n Logger.prototype.setDisplayLevel.call(this, level);\n\n storage.set(STORAGE_NAMESPACE, level);\n };\n\n /**\n * Adds the support of predefined messages.\n *\n * @protected\n * @override\n */\n ConsoleLogger.prototype.createEntry_ = function (message, level, data) {\n var code;\n\n if (messagePoll.hasMessage(message)) {\n data = data || {};\n code = message;\n message = messagePoll.getMessage(code);\n\n data.messageCode = code;\n }\n\n return Logger.prototype.createEntry_.call(this, message, level, data);\n };\n\n return new ConsoleLogger();\n});\n","Magento_Ui/js/lib/logger/formatter.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'moment',\n 'mage/utils/template'\n], function (moment, mageTemplate) {\n 'use strict';\n\n /**\n * @param {String} dateFormat\n * @param {String} template\n */\n function LogFormatter(dateFormat, template) {\n /**\n * @protected\n * @type {String}\n */\n this.dateFormat_ = 'YYYY-MM-DD hh:mm:ss';\n\n /**\n * @protected\n * @type {String}\n */\n this.template_ = '[${ $.date }] [${ $.entry.levelName }] ${ $.message }';\n\n if (dateFormat) {\n this.dateFormat_ = dateFormat;\n }\n\n if (template) {\n this.template_ = template;\n }\n }\n\n /**\n * @param {LogEntry} entry\n * @returns {String}\n */\n LogFormatter.prototype.process = function (entry) {\n var message = mageTemplate.template(entry.message, entry.data),\n date = moment(entry.timestamp).format(this.dateFormat_);\n\n return mageTemplate.template(this.template_, {\n date: date,\n entry: entry,\n message: message\n });\n };\n\n return LogFormatter;\n});\n","Magento_Ui/js/lib/logger/levels-pool.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 var LEVELS,\n CODE_MAP;\n\n LEVELS = {\n NONE: 0,\n ERROR: 1,\n WARN: 2,\n INFO: 3,\n DEBUG: 4,\n ALL: 5\n };\n\n CODE_MAP = _.invert(LEVELS);\n\n return {\n /**\n * Returns the list of available log levels.\n *\n * @returns {Object}\n */\n getLevels: function () {\n return LEVELS;\n },\n\n /**\n * Returns name of the log level that matches to the provided code.\n *\n * @returns {String}\n */\n getNameByCode: function (code) {\n return CODE_MAP[code];\n }\n };\n});\n","Magento_Ui/js/lib/logger/console-output-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n './levels-pool'\n], function (logLevels) {\n 'use strict';\n\n var levels = logLevels.getLevels();\n\n /**\n * @param {LogFormatter} formatter\n */\n function ConsoleOutputHandler(formatter) {\n /**\n * @protected\n * @type {LogFormatter}\n */\n this.formatter_ = formatter;\n }\n\n /**\n * Display data of the provided entry to the console.\n *\n * @param {LogEntry} entry - Entry to be displayed.\n */\n ConsoleOutputHandler.prototype.show = function (entry) {\n var displayString = this.formatter_.process(entry);\n\n switch (entry.level) {\n case levels.ERROR:\n console.error(displayString);\n break;\n\n case levels.WARN:\n console.warn(displayString);\n break;\n\n case levels.INFO:\n console.info(displayString);\n break;\n\n case levels.DEBUG:\n console.log(displayString);\n break;\n }\n };\n\n /**\n * Displays the array of entries.\n *\n * @param {Array<LogEntry>} entries\n */\n ConsoleOutputHandler.prototype.dump = function (entries) {\n entries.forEach(this.show, this);\n };\n\n return ConsoleOutputHandler;\n});\n","Magento_Ui/js/lib/logger/message-pool.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 var MESSAGES = {\n templateStartLoading:\n 'The \"${ $.template }\" template requested by the \"${$.component}\" component started loading.',\n templateLoadedFromServer:\n 'The \"${ $.template }\" template requested by the \"${$.component}\" component was loaded from server.\"',\n templateLoadedFromCache:\n 'The \"${ $.template }\" template requested by the \"${$.component}\" component was loaded from cache.\"',\n templateLoadingFail: 'Failed to load the \"${ $.template }\" template requested by \"${$.component}\".',\n componentStartInitialization:\n 'Component \"${$.component}\" start initialization with instance name \"${$.componentName}\".',\n componentStartLoading: ' Started loading the \"${$.component}\" component.',\n componentFinishLoading: 'The \"${$.component}\" component was loaded.',\n componentLoadingFail: 'Failed to load the \"${$.component}\" component.',\n depsLoadingFail: 'Could not get the declared \"${$.deps}\" dependency for the \"${$.component}\" instance.',\n depsStartRequesting: 'Requesting the \"${$.deps}\" dependency for the \"${$.component}\" instance.',\n depsFinishRequesting: 'The \"${$.deps}\" dependency for the \"${$.component}\" instance was received.',\n requestingComponent: 'Requesting the \"${$.component}\" component.',\n requestingComponentIsLoaded: 'The requested \"${$.component}\" component was received.',\n requestingComponentIsFailed: 'Could not get the requested \"${$.component}\" component.'\n };\n\n return {\n /**\n * Returns message that matches the provided code.\n *\n * @param {String} code - Message's identifier\n * @returns {String}\n */\n getMessage: function (code) {\n return MESSAGES[code];\n },\n\n /**\n * Adds a new message to the poll.\n *\n * @param {String} code - Message's identifier.\n * @param {String} message - Text of the message\n */\n addMessage: function (code, message) {\n MESSAGES[code] = message;\n },\n\n /**\n * Tells whether message with provide code exists in the poll.\n *\n * @param {String} code - Message's identifier.\n * @returns {Boolean}\n */\n hasMessage: function (code) {\n return MESSAGES.hasOwnProperty(code);\n }\n };\n});\n","Magento_Ui/js/lib/logger/logger-utils.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 /**\n * Utils methods for logger\n * @param {Logger} logger\n */\n function LogUtils(logger) {\n this.logger = logger;\n\n }\n\n /**\n * Method for logging asynchronous operations\n * @param {Promise} promise\n * @param {Object} config\n */\n LogUtils.prototype.asyncLog = function (promise, config) {\n var levels,\n messages,\n wait;\n\n config = config || {};\n levels = config.levels || this.createLevels();\n messages = config.messages || this.createMessages();\n wait = config.wait || 5000;\n\n this.logger[levels.requested](messages.requested, config.data);\n setTimeout(function () {\n promise.state() === 'pending' ?\n this.logger[levels.failed](messages.failed, config.data) :\n this.logger[levels.loaded](messages.loaded, config.data);\n }.bind(this), wait);\n };\n\n /**\n * Method that creates object of messages\n * @param {String} requested - log message that showing that request for class is started\n * @param {String} loaded - log message that show when requested class is loaded\n * @param {String} failded - log message that show when requested class is failed\n * @returns {Object}\n */\n LogUtils.prototype.createMessages = function (requested, loaded, failded) {\n return {\n requested: requested || '',\n loaded: loaded || '',\n failed: failded || ''\n };\n };\n\n /**\n * Method that creates object of log levels\n * @param {String} requested - log message that showing that request for class is started\n * @param {String} loaded - log message that show when requested class is loaded\n * @param {String} failded - log message that show when requested class is failed\n * @returns {Object}\n */\n LogUtils.prototype.createLevels = function (requested, loaded, failded) {\n return {\n requested: requested || 'info',\n loaded: loaded || 'info',\n failed: failded || 'warn'\n };\n };\n\n return LogUtils;\n});\n","Magento_Ui/js/lib/logger/logger.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n './levels-pool'\n], function (logLevels) {\n 'use strict';\n\n var levels = logLevels.getLevels();\n\n /**\n * @param {LogOutputHandler} outputHandler\n * @param {LogEntryFactory} entryFactory\n */\n function Logger(outputHandler, entryFactory) {\n /**\n * An array of log entries.\n *\n * @protected\n * @type {Array<LogEntry>}\n */\n this.entries_ = [];\n\n /**\n * Current display level.\n *\n * @protected\n * @type {Number}\n */\n this.displayLevel_ = levels.ERROR;\n\n /**\n * An array of display criteria.\n *\n * @protected\n * @type {Array<LogCriteria>}\n */\n this.displayCriteria_ = [];\n\n /**\n * @protected\n * @type {LogEntryFactory}\n */\n this.entryFactory_ = entryFactory;\n\n /**\n * @protected\n * @type {Array<LogOutputHandler>}\n */\n this.outputHandlers_ = [outputHandler];\n\n this.addDisplayCriteria(this.matchesLevel_);\n }\n\n /**\n * Swaps current display level with the provided one.\n *\n * @param {Number} level - Level's code.\n */\n Logger.prototype.setDisplayLevel = function (level) {\n var levelName = logLevels.getNameByCode(level);\n\n if (!levelName) {\n throw new TypeError('The provided level is not defined in the levels list.');\n }\n\n this.displayLevel_ = level;\n };\n\n /**\n * Sets up the criteria by which log entries will be filtered out from the output.\n *\n * @param {LogCriteria} criteria\n */\n Logger.prototype.addDisplayCriteria = function (criteria) {\n this.displayCriteria_.push(criteria);\n };\n\n /**\n * Removes previously defined criteria.\n *\n * @param {LogCriteria} criteria\n */\n Logger.prototype.removeDisplayCriteria = function (criteria) {\n var index = this.displayCriteria_.indexOf(criteria);\n\n if (~index) {\n this.displayCriteria_.splice(index, 1);\n }\n };\n\n /**\n * @param {String} message\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.error = function (message, messageData) {\n return this.log_(message, levels.ERROR, messageData);\n };\n\n /**\n * @param {String} message\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.warn = function (message, messageData) {\n return this.log_(message, levels.WARN, messageData);\n };\n\n /**\n * @param {String} message\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.info = function (message, messageData) {\n return this.log_(message, levels.INFO, messageData);\n };\n\n /**\n * @param {String} message\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.debug = function (message, messageData) {\n return this.log_(message, levels.DEBUG, messageData);\n };\n\n /**\n * @protected\n * @param {String} message\n * @param {Number} level\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.log_ = function (message, level, messageData) {\n var entry = this.createEntry_(message, level, messageData);\n\n this.entries_.push(entry);\n\n if (this.matchesCriteria_(entry)) {\n this.processOutput_(entry);\n }\n\n return entry;\n };\n\n /**\n * @protected\n * @param {String} message\n * @param {Number} level\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n Logger.prototype.createEntry_ = function (message, level, messageData) {\n return this.entryFactory_.createEntry(message, level, messageData);\n };\n\n /**\n * Returns an array of log entries that have been added to the logger.\n *\n * @param {LogCriteria} [criteria] - Optional filter criteria.\n * @returns {Array<LogEntry>}\n */\n Logger.prototype.getEntries = function (criteria) {\n if (criteria) {\n return this.entries_.filter(criteria);\n }\n\n return this.entries_;\n };\n\n /**\n * @param {LogCriteria} [criteria]\n */\n Logger.prototype.dump = function (criteria) {\n var entries;\n\n if (!criteria) {\n criteria = this.matchesCriteria_;\n }\n\n entries = this.entries_.filter(criteria, this);\n\n this.outputHandlers_.forEach(function (handler) {\n handler.dump(entries);\n });\n };\n\n /**\n * @protected\n * @param {LogEntry} entry\n */\n Logger.prototype.processOutput_ = function (entry) {\n this.outputHandlers_.forEach(function (handler) {\n handler.show(entry);\n });\n };\n\n /**\n * @protected\n * @param {LogEntry} entry\n * @returns {Boolean}\n */\n Logger.prototype.matchesCriteria_ = function (entry) {\n return this.displayCriteria_.every(function (criteria) {\n return criteria.call(this, entry);\n }, this);\n };\n\n /**\n * Checks that the level of provided entry passes the \"displayLevel_\" threshold.\n *\n * @protected\n * @param {LogEntry} entry - Entry to be checked.\n * @returns {Boolean}\n */\n Logger.prototype.matchesLevel_ = function (entry) {\n return entry.level <= this.displayLevel_;\n };\n\n return Logger;\n});\n","Magento_Ui/js/lib/logger/entry-factory.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n './entry'\n], function (LogEntry) {\n 'use strict';\n\n return {\n /**\n * @param {String} message\n * @param {Number} level\n * @param {Object} [messageData]\n * @returns {LogEntry}\n */\n createEntry: function (message, level, messageData) {\n return new LogEntry(message, level, messageData);\n }\n };\n});\n","Magento_Ui/js/lib/core/events.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global WeakMap, Map*/\ndefine([\n 'ko',\n 'underscore',\n 'es6-collections'\n], function (ko, _) {\n 'use strict';\n\n var eventsMap = new WeakMap();\n\n /**\n * Returns events map or a specific event\n * data associated with a provided object.\n *\n * @param {Object} obj - Key in the events weakmap.\n * @param {String} [name] - Name of the event.\n * @returns {Map|Array|Boolean}\n */\n function getEvents(obj, name) {\n var events = eventsMap.get(obj);\n\n if (!events) {\n return false;\n }\n\n return name ? events.get(name) : events;\n }\n\n /**\n * Adds new event handler.\n *\n * @param {Object} obj - Key in the events weakmap.\n * @param {String} ns - Callback namespace.\n * @param {Function} callback - Event callback.\n * @param {String} name - Name of the event.\n */\n function addHandler(obj, ns, callback, name) {\n var events = getEvents(obj),\n observable,\n data;\n\n observable = !ko.isObservable(obj[name]) ?\n ko.getObservable(obj, name) :\n obj[name];\n\n if (observable) {\n observable.subscribe(callback);\n\n return;\n }\n\n if (!events) {\n events = new Map();\n\n eventsMap.set(obj, events);\n }\n\n data = {\n callback: callback,\n ns: ns\n };\n\n events.has(name) ?\n events.get(name).push(data) :\n events.set(name, [data]);\n }\n\n /**\n * Invokes provided callbacks with a specified arguments.\n *\n * @param {Array} handlers\n * @param {Array} args\n * @returns {Boolean}\n */\n function trigger(handlers, args) {\n var bubble = true,\n callback;\n\n handlers.forEach(function (handler) {\n callback = handler.callback;\n\n if (callback.apply(null, args) === false) {\n bubble = false;\n }\n });\n\n return bubble;\n }\n\n return {\n\n /**\n * Calls callback when name event is triggered.\n * @param {String} events\n * @param {Function} callback\n * @param {Function} ns\n * @return {Object} reference to this\n */\n on: function (events, callback, ns) {\n var iterator;\n\n if (arguments.length < 2) {\n ns = callback;\n }\n\n iterator = addHandler.bind(null, this, ns);\n\n _.isObject(events) ?\n _.each(events, iterator) :\n iterator(callback, events);\n\n return this;\n },\n\n /**\n * Removed callback from listening to target event\n * @param {String} ns\n * @return {Object} reference to this\n */\n off: function (ns) {\n var storage = getEvents(this);\n\n if (!storage) {\n return this;\n }\n\n storage.forEach(function (handlers, name) {\n handlers = handlers.filter(function (handler) {\n return !ns ? false : handler.ns !== ns;\n });\n\n handlers.length ?\n storage.set(name, handlers) :\n storage.delete(name);\n });\n\n return this;\n },\n\n /**\n * Triggers event and executes all attached callbacks.\n *\n * @param {String} name - Name of the event to be triggered.\n * @returns {Boolean}\n */\n trigger: function (name) {\n var handlers,\n args;\n\n handlers = getEvents(this, name),\n args = _.toArray(arguments).slice(1);\n\n if (!handlers || !name) {\n return true;\n }\n\n return trigger(handlers, args);\n }\n };\n});\n","Magento_Ui/js/lib/core/class.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'mage/utils/wrapper'\n], function (_, utils, wrapper) {\n 'use strict';\n\n var Class;\n\n /**\n * Returns property of an object if\n * it's his own property.\n *\n * @param {Object} obj - Object whose property should be retrieved.\n * @param {String} prop - Name of the property.\n * @returns {*} Value of the property or false.\n */\n function getOwn(obj, prop) {\n return _.isObject(obj) && obj.hasOwnProperty(prop) && obj[prop];\n }\n\n /**\n * Creates constructor function which allows\n * initialization without usage of a 'new' operator.\n *\n * @param {Object} protoProps - Prototypal properties of a new constructor.\n * @param {Function} constructor\n * @returns {Function} Created constructor.\n */\n function createConstructor(protoProps, constructor) {\n var UiClass = constructor;\n\n if (!UiClass) {\n\n /**\n * Default constructor function.\n */\n UiClass = function () {\n var obj = this;\n\n if (!_.isObject(obj) || Object.getPrototypeOf(obj) !== UiClass.prototype) {\n obj = Object.create(UiClass.prototype);\n }\n\n obj.initialize.apply(obj, arguments);\n\n return obj;\n };\n }\n\n UiClass.prototype = protoProps;\n UiClass.prototype.constructor = UiClass;\n\n return UiClass;\n }\n\n Class = createConstructor({\n\n /**\n * Entry point to the initialization of constructor's instance.\n *\n * @param {Object} [options={}]\n * @returns {Class} Chainable.\n */\n initialize: function (options) {\n this.initConfig(options);\n\n return this;\n },\n\n /**\n * Recursively extends data specified in constructors' 'defaults'\n * property with provided options object. Evaluates resulting\n * object using string templates (see: mage/utils/template.js).\n *\n * @param {Object} [options={}]\n * @returns {Class} Chainable.\n */\n initConfig: function (options) {\n var defaults = this.constructor.defaults,\n config = utils.extend({}, defaults, options || {}),\n ignored = config.ignoreTmpls || {},\n cached = utils.omit(config, ignored);\n\n config = utils.template(config, this, false, true);\n\n _.each(cached, function (value, key) {\n utils.nested(config, key, value);\n });\n\n return _.extend(this, config);\n }\n });\n\n _.extend(Class, {\n defaults: {\n ignoreTmpls: {\n templates: true\n }\n },\n\n /**\n * Creates new constructor based on a current prototype properties,\n * extending them with properties specified in 'exender' object.\n *\n * @param {Object} [extender={}]\n * @returns {Function} New constructor.\n */\n extend: function (extender) {\n var parent = this,\n parentProto = parent.prototype,\n childProto = Object.create(parentProto),\n child = createConstructor(childProto, getOwn(extender, 'constructor')),\n defaults;\n\n extender = extender || {};\n defaults = extender.defaults;\n\n delete extender.defaults;\n\n _.each(extender, function (method, name) {\n childProto[name] = wrapper.wrapSuper(parentProto[name], method);\n });\n\n child.defaults = utils.extend({}, parent.defaults || {});\n\n if (defaults) {\n utils.extend(child.defaults, defaults);\n extender.defaults = defaults;\n }\n\n return _.extend(child, {\n __super__: parentProto,\n extend: parent.extend\n });\n }\n });\n\n return Class;\n});\n","Magento_Ui/js/lib/core/collection.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n 'uiElement'\n], function (_, utils, registry, Element) {\n 'use strict';\n\n /**\n * Removes non plain object items from the specified array.\n *\n * @param {Array} container - Array whose value should be filtered.\n * @returns {Array}\n */\n function compact(container) {\n return container.filter(utils.isObject);\n }\n\n return Element.extend({\n defaults: {\n template: 'ui/collection',\n _elems: [],\n ignoreTmpls: {\n childDefaults: true\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Model} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe({\n elems: []\n });\n\n return this;\n },\n\n /**\n * Called when another element was added to current component.\n *\n * @param {Object} elem - Instance of an element that was added.\n * @returns {Collection} Chainable.\n */\n initElement: function (elem) {\n elem.initContainer(this);\n\n return this;\n },\n\n /**\n * Returns instance of a child found by provided index.\n *\n * @param {String} index - Index of a child.\n * @returns {Object}\n */\n getChild: function (index) {\n return _.findWhere(this.elems(), {\n index: index\n });\n },\n\n /**\n * Requests specified components to insert\n * them into 'elems' array starting from provided position.\n *\n * @param {(String|Array)} elems - Name of the component to insert.\n * @param {Number} [position=-1] - Position at which to insert elements.\n * @returns {Collection} Chainable.\n */\n insertChild: function (elems, position) {\n var container = this._elems,\n insert = this._insert.bind(this),\n update;\n\n if (!Array.isArray(elems)) {\n elems = [elems];\n }\n\n elems.map(function (item) {\n return item.elem ?\n utils.insert(item.elem, container, item.position) :\n utils.insert(item, container, position);\n }).forEach(function (item) {\n if (item === true) {\n update = true;\n } else if (_.isString(item)) {\n registry.get(item, insert);\n } else if (utils.isObject(item)) {\n insert(item);\n }\n });\n\n if (update) {\n this._updateCollection();\n }\n\n return this;\n },\n\n /**\n * Removes specified child from collection.\n *\n * @param {(Object|String)} elem - Child or index of a child to be removed.\n * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.\n *\n * @returns {Collection} Chainable.\n */\n removeChild: function (elem, skipUpdate) {\n if (_.isString(elem)) {\n elem = this.getChild(elem);\n }\n\n if (elem) {\n utils.remove(this._elems, elem);\n\n if (!skipUpdate) {\n this._updateCollection();\n }\n }\n\n return this;\n },\n\n /**\n * Destroys collection children with its' elements.\n */\n destroyChildren: function () {\n this.elems.each(function (elem) {\n elem.destroy(true);\n });\n\n this._updateCollection();\n },\n\n /**\n * Clear data. Call method \"clear\"\n * in child components\n *\n * @returns {Object} Chainable.\n */\n clear: function () {\n var elems = this.elems();\n\n _.each(elems, function (elem) {\n if (_.isFunction(elem.clear)) {\n elem.clear();\n }\n }, this);\n\n return this;\n },\n\n /**\n * Checks if specified child exists in collection.\n *\n * @param {String} index - Index of a child.\n * @returns {Boolean}\n */\n hasChild: function (index) {\n return !!this.getChild(index);\n },\n\n /**\n * Creates 'async' wrapper for the specified child\n * using uiRegistry 'async' method and caches it\n * in a '_requested' components object.\n *\n * @param {String} index - Index of a child.\n * @returns {Function} Async module wrapper.\n */\n requestChild: function (index) {\n var name = this.formChildName(index);\n\n return this.requestModule(name);\n },\n\n /**\n * Creates complete child name based on a provided index.\n *\n * @param {String} index - Index of a child.\n * @returns {String}\n */\n formChildName: function (index) {\n return this.name + '.' + index;\n },\n\n /**\n * Retrieves requested region.\n * Creates region if it was not created yet\n *\n * @returns {ObservableArray}\n */\n getRegion: function (name) {\n var regions = this.regions = this.regions || {};\n\n if (!regions[name]) {\n regions[name] = [];\n\n this.observe.call(regions, name);\n }\n\n return regions[name];\n },\n\n /**\n * Replaces specified regions' data with a provided one.\n * Creates region if it was not created yet.\n *\n * @param {Array} items - New regions' data.\n * @param {String} name - Name of the region.\n * @returns {Collection} Chainable.\n */\n updateRegion: function (items, name) {\n this.getRegion(name)(items);\n\n return this;\n },\n\n /**\n * Destroys collection along with its' elements.\n */\n destroy: function () {\n this._super();\n\n this.elems.each('destroy');\n },\n\n /**\n * Inserts provided component into 'elems' array at a specified position.\n * @private\n *\n * @param {Object} elem - Element to insert.\n */\n _insert: function (elem) {\n var index = this._elems.indexOf(elem.name);\n\n if (~index) {\n this._elems[index] = elem;\n }\n\n this._updateCollection()\n .initElement(elem);\n },\n\n /**\n * Synchronizes multiple elements arrays with a core '_elems' container.\n * Performs elemets grouping by theirs 'displayArea' property.\n * @private\n *\n * @returns {Collection} Chainable.\n */\n _updateCollection: function () {\n var _elems = compact(this._elems),\n grouped;\n\n grouped = _elems.filter(function (elem) {\n return elem.displayArea && _.isString(elem.displayArea);\n });\n grouped = _.groupBy(grouped, 'displayArea');\n\n _.each(grouped, this.updateRegion, this);\n\n _.each(this.regions, function (items) {\n var hasObsoleteComponents = items().length && !_.intersection(_elems, items()).length;\n\n if (hasObsoleteComponents) {\n items.removeAll();\n }\n });\n\n this.elems(_elems);\n\n return this;\n },\n\n /**\n * Tries to call specified method of a current component,\n * otherwise delegates attempt to its' children.\n *\n * @param {String} target - Name of the method.\n * @param {...*} parameters - Arguments that will be passed to method.\n * @returns {*} Result of the method calls.\n */\n delegate: function (target) {\n var args = _.toArray(arguments);\n\n target = this[target];\n\n if (_.isFunction(target)) {\n return target.apply(this, args.slice(1));\n }\n\n return this._delegate(args);\n },\n\n /**\n * Calls 'delegate' method of all of it's children components.\n * @private\n *\n * @param {Array} args - An array of arguments to pass to the next delegation call.\n * @returns {Array} An array of delegation results.\n */\n _delegate: function (args) {\n var result;\n\n result = this.elems.map(function (elem) {\n var target;\n\n if (!_.isFunction(elem.delegate)) {\n target = elem[args[0]];\n\n if (_.isFunction(target)) {\n return target.apply(elem, args.slice(1));\n }\n } else {\n return elem.delegate.apply(elem, args);\n }\n });\n\n return _.flatten(result);\n }\n });\n});\n","Magento_Ui/js/lib/core/element/links.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'ko',\n 'underscore',\n 'mageUtils',\n 'uiRegistry'\n], function (ko, _, utils, registry) {\n 'use strict';\n\n /**\n * Parse provided data.\n *\n * @param {String} placeholder\n * @param {String} data\n * @param {String} direction\n * @returns {Boolean|Object}\n */\n function parseData(placeholder, data, direction) {\n if (typeof data !== 'string') {\n return false;\n }\n\n data = data.split(':');\n\n if (!data[0]) {\n return false;\n }\n\n if (!data[1]) {\n data[1] = data[0];\n data[0] = placeholder;\n }\n\n return {\n target: data[0],\n property: data[1],\n direction: direction\n };\n }\n\n /**\n * Check if value not empty.\n *\n * @param {*} value\n * @returns {Boolean}\n */\n function notEmpty(value) {\n return typeof value !== 'undefined' && value != null;\n }\n\n /**\n * Update value for linked component.\n *\n * @param {Object} data\n * @param {Object} owner\n * @param {Object} target\n * @param {*} value\n */\n function updateValue(data, owner, target, value) {\n var component = target.component,\n property = target.property,\n linked = data.linked;\n\n if (data.mute) {\n return;\n }\n\n if (linked) {\n linked.mute = true;\n }\n\n if (owner.component !== target.component) {\n value = data.inversionValue ? !utils.copy(value) : utils.copy(value);\n }\n\n component.set(property, value, owner);\n\n if (linked) {\n linked.mute = false;\n }\n }\n\n /**\n * Get value form owner component property.\n *\n * @param {Object} owner\n * @returns {*}\n */\n function getValue(owner) {\n var component = owner.component,\n property = owner.property;\n\n return component.get(property);\n }\n\n /**\n * Format provided params to object.\n *\n * @param {String} ownerComponent\n * @param {String} targetComponent\n * @param {String} ownerProp\n * @param {String} targetProp\n * @param {String} direction\n * @returns {Object}\n */\n function form(ownerComponent, targetComponent, ownerProp, targetProp, direction) {\n var result,\n tmp;\n\n result = {\n owner: {\n component: ownerComponent,\n property: ownerProp\n },\n target: {\n component: targetComponent,\n property: targetProp\n }\n };\n\n if (direction === 'exports') {\n tmp = result.owner;\n result.owner = result.target;\n result.target = tmp;\n }\n\n return result;\n }\n\n /**\n * Set data to linked property.\n *\n * @param {Object} map\n * @param {Object} data\n */\n function setLinked(map, data) {\n var match;\n\n if (!map) {\n return;\n }\n\n match = _.findWhere(map, {\n linked: false,\n target: data.target,\n property: data.property\n });\n\n if (match) {\n match.linked = data;\n data.linked = match;\n }\n }\n\n /**\n * Set data by direction.\n *\n * @param {Object} maps\n * @param {String} property\n * @param {Object} data\n */\n function setData(maps, property, data) {\n var direction = data.direction,\n map = maps[direction];\n\n data.linked = false;\n\n (map[property] = map[property] || []).push(data);\n\n direction = direction === 'imports' ? 'exports' : 'imports';\n\n setLinked(maps[direction][property], data);\n }\n\n /**\n * Set links for components.\n *\n * @param {String} target\n * @param {String} owner\n * @param {Object} data\n * @param {String} property\n * @param {Boolean} immediate\n */\n function setLink(target, owner, data, property, immediate) {\n var direction = data.direction,\n formated = form(target, owner, data.property, property, direction),\n callback,\n value;\n\n owner = formated.owner;\n target = formated.target;\n\n callback = updateValue.bind(null, data, owner, target);\n\n owner.component.on(owner.property, callback, target.component.name);\n\n if (immediate) {\n value = getValue(owner);\n\n if (notEmpty(value)) {\n updateValue(data, owner, target, value);\n }\n }\n }\n\n /**\n * Transfer data between components.\n *\n * @param {Object} owner\n * @param {Object} data\n */\n function transfer(owner, data) {\n var args = _.toArray(arguments);\n\n if (data.target.substr(0, 1) === '!') {\n data.target = data.target.substr(1);\n data.inversionValue = true;\n }\n\n if (owner.name === data.target) {\n args.unshift(owner);\n\n setLink.apply(null, args);\n } else {\n registry.get(data.target, function (target) {\n args.unshift(target);\n\n setLink.apply(null, args);\n });\n }\n }\n\n return {\n /**\n * Assign listeners.\n *\n * @param {Object} listeners\n * @returns {Object} Chainable\n */\n setListeners: function (listeners) {\n var owner = this,\n data;\n\n _.each(listeners, function (callbacks, sources) {\n sources = sources.split(' ');\n callbacks = callbacks.split(' ');\n\n sources.forEach(function (target) {\n callbacks.forEach(function (callback) {//eslint-disable-line max-nested-callbacks\n data = parseData(owner.name, target, 'imports');\n\n if (data) {\n setData(owner.maps, callback, data);\n transfer(owner, data, callback);\n }\n });\n });\n });\n\n return this;\n },\n\n /**\n * Set links in provided direction.\n *\n * @param {Object} links\n * @param {String} direction\n * @returns {Object} Chainable\n */\n setLinks: function (links, direction) {\n var owner = this,\n property,\n data;\n\n for (property in links) {\n if (links.hasOwnProperty(property)) {\n data = parseData(owner.name, links[property], direction);\n\n if (data) {//eslint-disable-line max-depth\n setData(owner.maps, property, data);\n transfer(owner, data, property, true);\n }\n }\n }\n\n return this;\n }\n };\n});\n","Magento_Ui/js/lib/core/element/element.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n 'uiEvents',\n 'uiClass',\n './links',\n '../storage/local'\n], function (ko, _, utils, registry, Events, Class, links) {\n 'use strict';\n\n var Element;\n\n /**\n * Creates observable property using knockouts'\n * 'observableArray' or 'observable' methods,\n * depending on a type of 'value' parameter.\n *\n * @param {Object} obj - Object to whom property belongs.\n * @param {String} key - Key of the property.\n * @param {*} value - Initial value.\n */\n function observable(obj, key, value) {\n var method = Array.isArray(value) ? 'observableArray' : 'observable';\n\n if (_.isFunction(obj[key]) && !ko.isObservable(obj[key])) {\n return;\n }\n\n if (ko.isObservable(value)) {\n value = value();\n }\n\n ko.isObservable(obj[key]) ?\n obj[key](value) :\n obj[key] = ko[method](value);\n }\n\n /**\n * Creates observable property using 'track' method.\n *\n * @param {Object} obj - Object to whom property belongs.\n * @param {String} key - Key of the property.\n * @param {*} value - Initial value.\n */\n function accessor(obj, key, value) {\n if (_.isFunction(obj[key]) || ko.isObservable(obj[key])) {\n return;\n }\n\n obj[key] = value;\n\n if (!ko.es5.isTracked(obj, key)) {\n ko.track(obj, [key]);\n }\n }\n\n Element = _.extend({\n defaults: {\n _requested: {},\n containers: [],\n exports: {},\n imports: {},\n links: {},\n listens: {},\n name: '',\n ns: '${ $.name.split(\".\")[0] }',\n provider: '',\n registerNodes: true,\n source: null,\n statefull: {},\n template: '',\n tracks: {},\n storageConfig: {\n provider: 'localStorage',\n namespace: '${ $.name }',\n path: '${ $.storageConfig.provider }:${ $.storageConfig.namespace }'\n },\n maps: {\n imports: {},\n exports: {}\n },\n modules: {\n storage: '${ $.storageConfig.provider }'\n }\n },\n\n /**\n * Initializes model instance.\n *\n * @returns {Element} Chainable.\n */\n initialize: function () {\n this._super()\n .initObservable()\n .initModules()\n .initStatefull()\n .initLinks()\n .initUnique();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Element} Chainable.\n */\n initObservable: function () {\n _.each(this.tracks, function (enabled, key) {\n if (enabled) {\n this.track(key);\n }\n }, this);\n\n return this;\n },\n\n /**\n * Parses 'modules' object and creates\n * async wrappers for specified components.\n *\n * @returns {Element} Chainable.\n */\n initModules: function () {\n _.each(this.modules, function (name, property) {\n if (name) {\n this[property] = this.requestModule(name);\n }\n }, this);\n\n if (!_.isFunction(this.source)) {\n this.source = registry.get(this.provider);\n }\n\n return this;\n },\n\n /**\n * Called when current element was injected to another component.\n *\n * @param {Object} parent - Instance of a 'parent' component.\n * @returns {Collection} Chainable.\n */\n initContainer: function (parent) {\n this.containers.push(parent);\n\n return this;\n },\n\n /**\n * Initializes statefull properties\n * based on the keys of 'statefull' object.\n *\n * @returns {Element} Chainable.\n */\n initStatefull: function () {\n _.each(this.statefull, function (path, key) {\n if (path) {\n this.setStatefull(key, path);\n }\n }, this);\n\n return this;\n },\n\n /**\n * Initializes links between properties.\n *\n * @returns {Element} Chainbale.\n */\n initLinks: function () {\n return this.setListeners(this.listens)\n .setLinks(this.links, 'imports')\n .setLinks(this.links, 'exports')\n .setLinks(this.exports, 'exports')\n .setLinks(this.imports, 'imports');\n },\n\n /**\n * Initializes listeners of the unique property.\n *\n * @returns {Element} Chainable.\n */\n initUnique: function () {\n var update = this.onUniqueUpdate.bind(this),\n uniqueNs = this.uniqueNs;\n\n this.hasUnique = this.uniqueProp && uniqueNs;\n\n if (this.hasUnique) {\n this.source.on(uniqueNs, update, this.name);\n }\n\n return this;\n },\n\n /**\n * Makes specified property to be stored automatically.\n *\n * @param {String} key - Name of the property\n * that will be stored.\n * @param {String} [path=key] - Path to the property in storage.\n * @returns {Element} Chainable.\n */\n setStatefull: function (key, path) {\n var link = {};\n\n path = !_.isString(path) || !path ? key : path;\n link[key] = this.storageConfig.path + '.' + path;\n\n this.setLinks(link, 'imports')\n .setLinks(link, 'exports');\n\n return this;\n },\n\n /**\n * Updates property specified in uniqueNs\n * if elements' unique property is set to 'true'.\n *\n * @returns {Element} Chainable.\n */\n setUnique: function () {\n var property = this.uniqueProp;\n\n if (this[property]()) {\n this.source.set(this.uniqueNs, this.name);\n }\n\n return this;\n },\n\n /**\n * Creates 'async' wrapper for the specified component\n * using uiRegistry 'async' method and caches it\n * in a '_requested' components object.\n *\n * @param {String} name - Name of requested component.\n * @returns {Function} Async module wrapper.\n */\n requestModule: function (name) {\n var requested = this._requested;\n\n if (!requested[name]) {\n requested[name] = registry.async(name);\n }\n\n return requested[name];\n },\n\n /**\n * Returns path to elements' template.\n *\n * @returns {String}\n */\n getTemplate: function () {\n return this.template;\n },\n\n /**\n * Checks if template was specified for an element.\n *\n * @returns {Boolean}\n */\n hasTemplate: function () {\n return !!this.template;\n },\n\n /**\n * Returns value of the nested property.\n *\n * @param {String} path - Path to the property.\n * @returns {*} Value of the property.\n */\n get: function (path) {\n return utils.nested(this, path);\n },\n\n /**\n * Sets provided value as a value of the specified nested property.\n * Triggers changes notifications, if value has mutated.\n *\n * @param {String} path - Path to property.\n * @param {*} value - New value of the property.\n * @returns {Element} Chainable.\n */\n set: function (path, value) {\n var data = this.get(path),\n diffs;\n\n diffs = !_.isFunction(data) && !this.isTracked(path) ?\n utils.compare(data, value, path) :\n false;\n\n utils.nested(this, path, value);\n\n if (diffs) {\n this._notifyChanges(diffs);\n }\n\n return this;\n },\n\n /**\n * Removes nested property from the object.\n *\n * @param {String} path - Path to the property.\n * @returns {Element} Chainable.\n */\n remove: function (path) {\n var data = utils.nested(this, path),\n diffs;\n\n if (_.isUndefined(data) || _.isFunction(data)) {\n return this;\n }\n\n diffs = utils.compare(data, undefined, path);\n\n utils.nestedRemove(this, path);\n\n this._notifyChanges(diffs);\n\n return this;\n },\n\n /**\n * Creates observable properties for the current object.\n *\n * If 'useTrack' flag is set to 'true' then each property will be\n * created with a ES5 get/set accessor descriptors, instead of\n * making them an observable functions.\n * See 'knockout-es5' library for more information.\n *\n * @param {Boolean} [useAccessors=false] - Whether to create an\n * observable function or to use property accesessors.\n * @param {(Object|String|Array)} properties - List of observable properties.\n * @returns {Element} Chainable.\n *\n * @example Sample declaration and equivalent knockout methods.\n * this.key = 'value';\n * this.array = ['value'];\n *\n * this.observe(['key', 'array']);\n * =>\n * this.key = ko.observable('value');\n * this.array = ko.observableArray(['value']);\n *\n * @example Another syntaxes of the previous example.\n * this.observe({\n * key: 'value',\n * array: ['value']\n * });\n */\n observe: function (useAccessors, properties) {\n var model = this,\n trackMethod;\n\n if (typeof useAccessors !== 'boolean') {\n properties = useAccessors;\n useAccessors = false;\n }\n\n trackMethod = useAccessors ? accessor : observable;\n\n if (_.isString(properties)) {\n properties = properties.split(' ');\n }\n\n if (Array.isArray(properties)) {\n properties.forEach(function (key) {\n trackMethod(model, key, model[key]);\n });\n } else if (typeof properties === 'object') {\n _.each(properties, function (value, key) {\n trackMethod(model, key, value);\n });\n }\n\n return this;\n },\n\n /**\n * Delegates call to 'observe' method but\n * with a predefined 'useAccessors' flag.\n *\n * @param {(String|Array|Object)} properties - List of observable properties.\n * @returns {Element} Chainable.\n */\n track: function (properties) {\n this.observe(true, properties);\n\n return this;\n },\n\n /**\n * Checks if specified property is tracked.\n *\n * @param {String} property - Property to be checked.\n * @returns {Boolean}\n */\n isTracked: function (property) {\n return ko.es5.isTracked(this, property);\n },\n\n /**\n * Invokes subscribers for the provided changes.\n *\n * @param {Object} diffs - Object with changes descriptions.\n * @returns {Element} Chainable.\n */\n _notifyChanges: function (diffs) {\n diffs.changes.forEach(function (change) {\n this.trigger(change.path, change.value, change);\n }, this);\n\n _.each(diffs.containers, function (changes, name) {\n var value = utils.nested(this, name);\n\n this.trigger(name, value, changes);\n }, this);\n\n return this;\n },\n\n /**\n * Extracts all stored data and sets it to element.\n *\n * @returns {Element} Chainable.\n */\n restore: function () {\n var ns = this.storageConfig.namespace,\n storage = this.storage();\n\n if (storage) {\n utils.extend(this, storage.get(ns));\n }\n\n return this;\n },\n\n /**\n * Stores value of the specified property in components' storage module.\n *\n * @param {String} property\n * @param {*} [data=this[property]]\n * @returns {Element} Chainable.\n */\n store: function (property, data) {\n var ns = this.storageConfig.namespace,\n path = utils.fullPath(ns, property);\n\n if (arguments.length < 2) {\n data = this.get(property);\n }\n\n this.storage('set', path, data);\n\n return this;\n },\n\n /**\n * Extracts specified property from storage.\n *\n * @param {String} [property] - Name of the property\n * to be extracted. If not specified then all of the\n * stored will be returned.\n * @returns {*}\n */\n getStored: function (property) {\n var ns = this.storageConfig.namespace,\n path = utils.fullPath(ns, property),\n storage = this.storage(),\n data;\n\n if (storage) {\n data = storage.get(path);\n }\n\n return data;\n },\n\n /**\n * Removes stored property.\n *\n * @param {String} property - Property to be removed from storage.\n * @returns {Element} Chainable.\n */\n removeStored: function (property) {\n var ns = this.storageConfig.namespace,\n path = utils.fullPath(ns, property);\n\n this.storage('remove', path);\n\n return this;\n },\n\n /**\n * Destroys current instance along with all of its' children.\n * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.\n */\n destroy: function (skipUpdate) {\n this._dropHandlers()\n ._clearRefs(skipUpdate);\n },\n\n /**\n * Removes events listeners.\n * @private\n *\n * @returns {Element} Chainable.\n */\n _dropHandlers: function () {\n this.off();\n\n if (_.isFunction(this.source)) {\n this.source().off(this.name);\n } else if (this.source) {\n this.source.off(this.name);\n }\n\n return this;\n },\n\n /**\n * Removes all references to current instance and\n * calls 'destroy' method on all of its' children.\n * @private\n * @param {Boolean} skipUpdate - skip collection update when element to be destroyed.\n *\n * @returns {Element} Chainable.\n */\n _clearRefs: function (skipUpdate) {\n registry.remove(this.name);\n\n this.containers.forEach(function (parent) {\n parent.removeChild(this, skipUpdate);\n }, this);\n\n return this;\n },\n\n /**\n * Overrides 'EventsBus.trigger' method to implement events bubbling.\n *\n * @param {...*} arguments - Any number of arguments that should be passed to the events' handler.\n * @returns {Boolean} False if event bubbling was canceled.\n */\n bubble: function () {\n var args = _.toArray(arguments),\n bubble = this.trigger.apply(this, args),\n result;\n\n if (!bubble) {\n return false;\n }\n\n this.containers.forEach(function (parent) {\n result = parent.bubble.apply(parent, args);\n\n if (result === false) {\n bubble = false;\n }\n });\n\n return !!bubble;\n },\n\n /**\n * Callback which fires when property under uniqueNs has changed.\n */\n onUniqueUpdate: function (name) {\n var active = name === this.name,\n property = this.uniqueProp;\n\n this[property](active);\n },\n\n /**\n * Clean data form data source.\n *\n * @returns {Element}\n */\n cleanData: function () {\n if (this.source && this.source.componentType === 'dataSource') {\n if (this.elems) {\n _.each(this.elems(), function (val) {\n val.cleanData();\n });\n } else {\n this.source.remove(this.dataScope);\n }\n }\n\n return this;\n },\n\n /**\n * Fallback data.\n */\n cacheData: function () {\n this.cachedComponent = utils.copy(this);\n },\n\n /**\n * Update configuration in component.\n *\n * @param {*} oldValue\n * @param {*} newValue\n * @param {String} path - path to value.\n * @returns {Element}\n */\n updateConfig: function (oldValue, newValue, path) {\n var names = path.split('.'),\n index = _.lastIndexOf(names, 'config') + 1;\n\n names = names.splice(index, names.length - index).join('.');\n this.set(names, newValue);\n\n return this;\n }\n }, Events, links);\n\n return Class.extend(Element);\n});\n","Magento_Ui/js/lib/core/storage/local.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n 'mageUtils',\n 'uiEvents'\n], function (_, registry, utils, EventsBus) {\n 'use strict';\n\n var root = 'appData',\n localStorage = window.localStorage,\n hasSupport,\n storage;\n\n /**\n * Flag which indicates whether localStorage is supported.\n */\n hasSupport = (function () {\n var key = '_storageSupported';\n\n try {\n localStorage.setItem(key, 'true');\n\n if (localStorage.getItem(key) === 'true') {\n localStorage.removeItem(key);\n\n return true;\n }\n\n return false;\n } catch (e) {\n return false;\n }\n })();\n\n if (!hasSupport) {\n localStorage = {\n _data: {},\n\n /**\n * Sets value of the specified item.\n *\n * @param {String} key - Key of the property.\n * @param {*} value - Properties' value.\n */\n setItem: function (key, value) {\n this._data[key] = value + '';\n },\n\n /**\n * Retrieves specified item.\n *\n * @param {String} key - Key of the property to be retrieved.\n */\n getItem: function (key) {\n return this._data[key];\n },\n\n /**\n * Removes specified item.\n *\n * @param {String} key - Key of the property to be removed.\n */\n removeItem: function (key) {\n delete this._data[key];\n },\n\n /**\n * Removes all items.\n */\n clear: function () {\n this._data = {};\n }\n };\n }\n\n /**\n * Extracts and parses data stored in localStorage by the\n * key specified in 'root' varaible.\n *\n * @returns {Object}\n */\n function getRoot() {\n var data = localStorage.getItem(root),\n result = {};\n\n if (!_.isNull(data) && typeof data != 'undefined') {\n result = JSON.parse(data);\n }\n\n return result;\n }\n\n /**\n * Writes provided data to the localStorage.\n *\n * @param {*} data - Data to be stored.\n */\n function setRoot(data) {\n localStorage.setItem(root, JSON.stringify(data));\n }\n\n /**\n * Provides methods to work with a localStorage\n * as a single nested structure.\n */\n storage = _.extend({\n\n /**\n * Retrieves value of the specified property.\n *\n * @param {String} path - Path to the property.\n *\n * @example Retrieveing data.\n * localStoarge =>\n * 'appData' => '\n * \"one\": {\"two\": \"three\"}\n * '\n * storage.get('one.two')\n * => \"three\"\n *\n * storage.get('one')\n * => {\"two\": \"three\"}\n */\n get: function (path) {\n var data = getRoot();\n\n return utils.nested(data, path);\n },\n\n /**\n * Sets specified data to the localStorage.\n *\n * @param {String} path - Path of the property.\n * @param {*} value - Value of the property.\n *\n * @example Setting data.\n * storage.set('one.two', 'four');\n * => localStoarge =>\n * 'appData' => '\n * \"one\": {\"two\": \"four\"}\n * '\n */\n set: function (path, value) {\n var data = getRoot();\n\n utils.nested(data, path, value);\n\n setRoot(data);\n },\n\n /**\n * Removes specified data from the localStorage.\n *\n * @param {String} path - Path to the property that should be removed.\n *\n * @example Removing data.\n * storage.remove('one.two', 'four');\n * => localStoarge =>\n * 'appData' => '\n * \"one\": {}\n * '\n */\n remove: function (path) {\n var data = getRoot();\n\n utils.nestedRemove(data, path);\n\n setRoot(data);\n }\n }, EventsBus);\n\n registry.set('localStorage', storage);\n\n return storage;\n});\n","Magento_Ui/js/lib/registry/registry.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\n/* global WeakMap */\ndefine([\n 'jquery',\n 'underscore',\n 'es6-collections'\n], function ($, _) {\n 'use strict';\n\n var privateData = new WeakMap();\n\n /**\n * Extracts private item storage associated\n * with a provided registry instance.\n *\n * @param {Object} container\n * @returns {Object}\n */\n function getItems(container) {\n return privateData.get(container).items;\n }\n\n /**\n * Extracts private requests array associated\n * with a provided registry instance.\n *\n * @param {Object} container\n * @returns {Array}\n */\n function getRequests(container) {\n return privateData.get(container).requests;\n }\n\n /**\n * Wrapper function used for convenient access to the elements.\n * See 'async' method for examples of usage and comparison\n * with a regular 'get' method.\n *\n * @param {(String|Object|Function)} name - Key of the requested element.\n * @param {Registry} registry - Instance of a registry\n * where to search for the element.\n * @param {(Function|String)} [method] - Optional callback function\n * or a name of the elements' method which\n * will be invoked when element is available in registry.\n * @returns {*}\n */\n function async(name, registry, method) {\n var args = _.toArray(arguments).slice(3);\n\n if (_.isString(method)) {\n registry.get(name, function (component) {\n component[method].apply(component, args);\n });\n } else if (_.isFunction(method)) {\n registry.get(name, method);\n } else if (!args.length) {\n return registry.get(name);\n }\n }\n\n /**\n * Checks that every property of the query object\n * is present and equal to the corresponding\n * property in target object.\n * Note that non-strict comparison is used.\n *\n * @param {Object} query - Query object.\n * @param {Object} target - Target object.\n * @returns {Boolean}\n */\n function compare(query, target) {\n var matches = true,\n index,\n keys,\n key;\n\n if (!_.isObject(query) || !_.isObject(target)) {\n return false;\n }\n\n keys = Object.getOwnPropertyNames(query);\n index = keys.length;\n\n while (matches && index--) {\n key = keys[index];\n\n /* eslint-disable eqeqeq */\n if (target[key] != query[key]) {\n matches = false;\n }\n\n /* eslint-enable eqeqeq */\n }\n\n return matches;\n }\n\n /**\n * Explodes incoming string into object if\n * string is defined as a set of key = value pairs.\n *\n * @param {(String|*)} query - String to be processed.\n * @returns {Object|*} Either created object or an unmodified incoming\n * value if conversion was not possible.\n * @example Sample conversions.\n * 'key = value, key2 = value2'\n * => {key: 'value', key2: 'value2'}\n */\n function explode(query) {\n var result = {},\n index,\n data;\n\n if (typeof query !== 'string' || !~query.indexOf('=')) {\n return query;\n }\n\n query = query.split(',');\n index = query.length;\n\n while (index--) {\n data = query[index].split('=');\n\n result[data[0].trim()] = data[1].trim();\n }\n\n return result;\n }\n\n /**\n * Extracts items from the provided data object\n * which matches specified search criteria.\n *\n * @param {Object} data - Data object where to perform a lookup.\n * @param {(String|Object|Function)} query - Search criteria.\n * @param {Boolean} findAll - Flag that defines whether to\n * search for all applicable items or to stop on a first found entry.\n * @returns {Array|Object|*}\n */\n function find(data, query, findAll) {\n var iterator,\n item;\n\n query = explode(query);\n\n if (typeof query === 'string') {\n item = data[query];\n\n if (findAll) {\n return item ? [item] : [];\n }\n\n return item;\n }\n\n iterator = !_.isFunction(query) ?\n compare.bind(null, query) :\n query;\n\n return findAll ?\n _.filter(data, iterator) :\n _.find(data, iterator);\n }\n\n /**\n * @constructor\n */\n function Registry() {\n var data = {\n items: {},\n requests: []\n };\n\n this._updateRequests = _.debounce(this._updateRequests.bind(this), 10);\n privateData.set(this, data);\n }\n\n Registry.prototype = {\n constructor: Registry,\n\n /**\n * Retrieves item from registry which matches specified search criteria.\n *\n * @param {(Object|String|Function|Array)} query - Search condition (see examples).\n * @param {Function} [callback] - Callback that will be invoked when\n * all of the requested items are available.\n * @returns {*}\n *\n * @example Requesting item by it's name.\n * var obj = {index: 'test', sample: true};\n *\n * registry.set('first', obj);\n * registry.get('first') === obj;\n * => true\n *\n * @example Requesting item with a specific properties.\n * registry.get('sample = 1, index = test') === obj;\n * => true\n * registry.get('sample = 0, index = foo') === obj;\n * => false\n *\n * @example Declaring search criteria as an object.\n * registry.get({sample: true}) === obj;\n * => true;\n *\n * @example Providing custom search handler.\n * registry.get(function (item) { return item.sample === true; }) === obj;\n * => true\n *\n * @example Sample asynchronous request declaration.\n * registry.get('index = test', function (item) {});\n *\n * @example Requesting multiple elements.\n * registry.set('second', {index: 'test2'});\n * registry.get(['first', 'second'], function (first, second) {});\n */\n get: function (query, callback) {\n if (typeof callback !== 'function') {\n return find(getItems(this), query);\n }\n\n this._addRequest(query, callback);\n },\n\n /**\n * Sets provided item to the registry.\n *\n * @param {String} id - Item's identifier.\n * @param {*} item - Item's data.\n * returns {Registry} Chainable.\n */\n set: function (id, item) {\n getItems(this)[id] = item;\n\n this._updateRequests();\n\n return this;\n },\n\n /**\n * Removes specified item from registry.\n * Note that search query is not applicable.\n *\n * @param {String} id - Item's identifier.\n * @returns {Registry} Chainable.\n */\n remove: function (id) {\n delete getItems(this)[id];\n\n return this;\n },\n\n /**\n * Retrieves a collection of elements that match\n * provided search criteria.\n *\n * @param {(Object|String|Function)} query - Search query.\n * See 'get' method for the syntax examples.\n * @returns {Array} Found elements.\n */\n filter: function (query) {\n return find(getItems(this), query, true);\n },\n\n /**\n * Checks that at least one element in collection\n * matches provided search criteria.\n *\n * @param {(Object|String|Function)} query - Search query.\n * See 'get' method for the syntax examples.\n * @returns {Boolean}\n */\n has: function (query) {\n return !!this.get(query);\n },\n\n /**\n * Checks that registry contains a provided item.\n *\n * @param {*} item - Item to be checked.\n * @returns {Boolean}\n */\n contains: function (item) {\n return _.contains(getItems(this), item);\n },\n\n /**\n * Extracts identifier of an item if it's present in registry.\n *\n * @param {*} item - Item whose identifier will be extracted.\n * @returns {String|Undefined}\n */\n indexOf: function (item) {\n return _.findKey(getItems(this), function (elem) {\n return item === elem;\n });\n },\n\n /**\n * Same as a 'get' method except that it returns\n * a promise object instead of invoking provided callback.\n *\n * @param {(String|Function|Object|Array)} query - Search query.\n * See 'get' method for the syntax examples.\n * @returns {jQueryPromise}\n */\n promise: function (query) {\n var defer = $.Deferred(),\n callback = defer.resolve.bind(defer);\n\n this.get(query, callback);\n\n return defer.promise();\n },\n\n /**\n * Creates a wrapper function over the provided search query\n * in order to provide somehow more convenient access to the\n * registry's items.\n *\n * @param {(String|Object|Function)} query - Search criteria.\n * See 'get' method for the syntax examples.\n * @returns {Function}\n *\n * @example Comparison with a 'get' method on retrieving items.\n * var module = registry.async('name');\n *\n * module();\n * => registry.get('name');\n *\n * @example Asynchronous request.\n * module(function (component) {});\n * => registry.get('name', function (component) {});\n *\n * @example Requesting item and invoking it's method with specified parameters.\n * module('trigger', true);\n * => registry.get('name', function (component) {\n * component.trigger(true);\n * });\n */\n async: function (query) {\n return async.bind(null, query, this);\n },\n\n /**\n * Creates new instance of a Registry.\n *\n * @returns {Registry} New instance.\n */\n create: function () {\n return new Registry;\n },\n\n /**\n * Adds new request to the queue or resolves it immediately\n * if all of the required items are available.\n *\n * @private\n * @param {(Object|String|Function|Array)} queries - Search criteria.\n * See 'get' method for the syntax examples.\n * @param {Function} callback - Callback that will be invoked when\n * all of the requested items are available.\n * @returns {Registry}\n */\n _addRequest: function (queries, callback) {\n var request;\n\n if (!Array.isArray(queries)) {\n queries = queries ? [queries] : [];\n }\n\n request = {\n queries: queries.map(explode),\n callback: callback\n };\n\n this._canResolve(request) ?\n this._resolveRequest(request) :\n getRequests(this).push(request);\n\n return this;\n },\n\n /**\n * Updates requests list resolving applicable items.\n *\n * @private\n * @returns {Registry} Chainable.\n */\n _updateRequests: function () {\n getRequests(this)\n .filter(this._canResolve, this)\n .forEach(this._resolveRequest, this);\n\n return this;\n },\n\n /**\n * Resolves provided request invoking it's callback\n * with items specified in query parameters.\n *\n * @private\n * @param {Object} request - Request object.\n * @returns {Registry} Chainable.\n */\n _resolveRequest: function (request) {\n var requests = getRequests(this),\n items = request.queries.map(this.get, this),\n index = requests.indexOf(request);\n\n request.callback.apply(null, items);\n\n if (~index) {\n requests.splice(index, 1);\n }\n\n return this;\n },\n\n /**\n * Checks if provided request can be resolved.\n *\n * @private\n * @param {Object} request - Request object.\n * @returns {Boolean}\n */\n _canResolve: function (request) {\n var queries = request.queries;\n\n return queries.every(this.has, this);\n }\n };\n\n return new Registry;\n});\n","Magento_Ui/js/lib/view/utils/dom-observer.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'jquery',\n 'underscore',\n 'MutationObserver',\n 'domReady!'\n], function ($, _) {\n 'use strict';\n\n var counter = 1,\n watchers,\n globalObserver,\n disabledNodes = [];\n\n watchers = {\n selectors: {},\n nodes: {}\n };\n\n /**\n * Checks if node represents an element node (nodeType === 1).\n *\n * @param {HTMLElement} node\n * @returns {Boolean}\n */\n function isElementNode(node) {\n return node.nodeType === 1;\n }\n\n /**\n * Extracts all child descendant\n * elements of a specified node.\n *\n * @param {HTMLElement} node\n * @returns {Array}\n */\n function extractChildren(node) {\n var children = node.querySelectorAll('*');\n\n return _.toArray(children);\n }\n\n /**\n * Extracts node identifier. If ID is not specified,\n * then it will be created for the provided node.\n *\n * @param {HTMLElement} node\n * @returns {Number}\n */\n function getNodeId(node) {\n var id = node._observeId;\n\n if (!id) {\n id = node._observeId = counter++;\n }\n\n return id;\n }\n\n /**\n * Invokes callback passing node to it.\n *\n * @param {HTMLElement} node\n * @param {Object} data\n */\n function trigger(node, data) {\n var id = getNodeId(node),\n ids = data.invoked;\n\n if (_.contains(ids, id)) {\n return;\n }\n\n data.callback(node);\n data.invoked.push(id);\n }\n\n /**\n * Adds node to the observer list.\n *\n * @param {HTMLElement} node\n * @returns {Object}\n */\n function createNodeData(node) {\n var nodes = watchers.nodes,\n id = getNodeId(node);\n\n nodes[id] = nodes[id] || {};\n\n return nodes[id];\n }\n\n /**\n * Returns data associated with a specified node.\n *\n * @param {HTMLElement} node\n * @returns {Object|Undefined}\n */\n function getNodeData(node) {\n var nodeId = node._observeId;\n\n return watchers.nodes[nodeId];\n }\n\n /**\n * Removes data associated with a specified node.\n *\n * @param {HTMLElement} node\n */\n function removeNodeData(node) {\n var nodeId = node._observeId;\n\n delete watchers.nodes[nodeId];\n }\n\n /**\n * Adds removal listener for a specified node.\n *\n * @param {HTMLElement} node\n * @param {Object} data\n */\n function addRemovalListener(node, data) {\n var nodeData = createNodeData(node);\n\n (nodeData.remove = nodeData.remove || []).push(data);\n }\n\n /**\n * Adds listener for the nodes which matches specified selector.\n *\n * @param {String} selector - CSS selector.\n * @param {Object} data\n */\n function addSelectorListener(selector, data) {\n var storage = watchers.selectors;\n\n (storage[selector] = storage[selector] || []).push(data);\n }\n\n /**\n * Calls handlers assocoiated with an added node.\n * Adds listeners for the node removal.\n *\n * @param {HTMLElement} node - Added node.\n */\n function processAdded(node) {\n _.each(watchers.selectors, function (listeners, selector) {\n listeners.forEach(function (data) {\n if (!data.ctx.contains(node) || !$(node, data.ctx).is(selector)) {\n return;\n }\n\n if (data.type === 'add') {\n trigger(node, data);\n } else if (data.type === 'remove') {\n addRemovalListener(node, data);\n }\n });\n });\n }\n\n /**\n * Calls handlers assocoiated with a removed node.\n *\n * @param {HTMLElement} node - Removed node.\n */\n function processRemoved(node) {\n var nodeData = getNodeData(node),\n listeners = nodeData && nodeData.remove;\n\n if (!listeners) {\n return;\n }\n\n listeners.forEach(function (data) {\n trigger(node, data);\n });\n\n removeNodeData(node);\n }\n\n /**\n * Removes all non-element nodes from provided array\n * and appends to it descendant elements.\n *\n * @param {Array} nodes\n * @returns {Array}\n */\n function formNodesList(nodes) {\n var result = [],\n children;\n\n nodes = _.toArray(nodes).filter(isElementNode);\n\n nodes.forEach(function (node) {\n result.push(node);\n\n children = extractChildren(node);\n result = result.concat(children);\n });\n\n return result;\n }\n\n /**\n * Collects all removed and added nodes from\n * mutation records into separate arrays\n * while removing duplicates between both types of changes.\n *\n * @param {Array} mutations - An array of mutation records.\n * @returns {Object} Object with 'removed' and 'added' nodes arrays.\n */\n function formChangesLists(mutations) {\n var removed = [],\n added = [];\n\n mutations.forEach(function (record) {\n removed = removed.concat(_.toArray(record.removedNodes));\n added = added.concat(_.toArray(record.addedNodes));\n });\n\n removed = removed.filter(function (node) {\n var addIndex = added.indexOf(node),\n wasAdded = !!~addIndex;\n\n if (wasAdded) {\n added.splice(addIndex, 1);\n }\n\n return !wasAdded;\n });\n\n return {\n removed: formNodesList(removed),\n added: formNodesList(added)\n };\n }\n\n /**\n * Verify if the DOM node is a child of a defined disabled node, if so we shouldn't observe provided mutation.\n *\n * @param {Object} mutation - a single mutation\n * @returns {Boolean}\n */\n function shouldObserveMutation(mutation) {\n var isDisabled;\n\n if (disabledNodes.length > 0) {\n // Iterate through the disabled nodes and determine if this mutation is occurring inside one of them\n isDisabled = _.find(disabledNodes, function (node) {\n return node === mutation.target || $.contains(node, mutation.target);\n });\n\n // If we find a matching node we should not observe the mutation\n return !isDisabled;\n }\n\n return true;\n }\n\n /**\n * Should we observe these mutations? Check the first and last mutation to determine if this is a disabled mutation,\n * we check both the first and last in case one has been removed from the DOM during the process of the mutation.\n *\n * @param {Array} mutations - An array of mutation records.\n * @returns {Boolean}\n */\n function shouldObserveMutations(mutations) {\n var firstMutation,\n lastMutation;\n\n if (mutations.length > 0) {\n firstMutation = mutations[0];\n lastMutation = mutations[mutations.length - 1];\n\n return shouldObserveMutation(firstMutation) && shouldObserveMutation(lastMutation);\n }\n\n return true;\n }\n\n globalObserver = new MutationObserver(function (mutations) {\n var changes;\n\n if (shouldObserveMutations(mutations)) {\n changes = formChangesLists(mutations);\n\n changes.removed.forEach(processRemoved);\n changes.added.forEach(processAdded);\n }\n });\n\n globalObserver.observe(document.body, {\n subtree: true,\n childList: true\n });\n\n return {\n /**\n * Disable a node from being observed by the mutations, you may want to disable specific aspects of the\n * application which are heavy on DOM changes. The observer running on some actions could cause significant\n * delays and degrade the performance of that specific part of the application exponentially.\n *\n * @param {HTMLElement} node - a HTML node within the document\n */\n disableNode: function (node) {\n disabledNodes.push(node);\n },\n\n /**\n * Adds listener for the appearance of nodes that matches provided\n * selector and which are inside of the provided context. Callback will be\n * also invoked on elements which a currently present.\n *\n * @param {String} selector - CSS selector.\n * @param {Function} callback - Function that will invoked when node appears.\n * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.\n */\n get: function (selector, callback, ctx) {\n var data,\n nodes;\n\n data = {\n ctx: ctx || document.body,\n type: 'add',\n callback: callback,\n invoked: []\n };\n\n nodes = $(selector, data.ctx).toArray();\n\n nodes.forEach(function (node) {\n trigger(node, data);\n });\n\n addSelectorListener(selector, data);\n },\n\n /**\n * Adds listener for the nodes removal.\n *\n * @param {(jQueryObject|HTMLElement|Array|String)} selector\n * @param {Function} callback - Function that will invoked when node is removed.\n * @param {HTMLElement} [ctx=document.body] - Context inside of which to search for the node.\n */\n remove: function (selector, callback, ctx) {\n var nodes = [],\n data;\n\n data = {\n ctx: ctx || document.body,\n type: 'remove',\n callback: callback,\n invoked: []\n };\n\n if (typeof selector === 'object') {\n nodes = !_.isUndefined(selector.length) ?\n _.toArray(selector) :\n [selector];\n } else if (_.isString(selector)) {\n nodes = $(selector, ctx).toArray();\n\n addSelectorListener(selector, data);\n }\n\n nodes.forEach(function (node) {\n addRemovalListener(node, data);\n });\n },\n\n /**\n * Removes listeners.\n *\n * @param {String} selector\n * @param {Function} [fn]\n */\n off: function (selector, fn) {\n var selectors = watchers.selectors,\n listeners = selectors[selector];\n\n if (selector && !fn) {\n delete selectors[selector];\n } else if (listeners && fn) {\n selectors[selector] = listeners.filter(function (data) {\n return data.callback !== fn;\n });\n }\n }\n };\n});\n","Magento_Ui/js/lib/view/utils/bindings.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], function (ko, $, _) {\n 'use strict';\n\n /**\n * Checks if provided value is a dom element.\n *\n * @param {*} node - Value to be checked.\n * @returns {Boolean}\n */\n function isDomElement(node) {\n return typeof node === 'object' && node.tagName && node.nodeType;\n }\n\n /**\n * Removes from the provided array all non-root nodes located inside\n * of the comment element as long as the closing comment tags.\n *\n * @param {(Array|ArrayLike)} nodes - An array of nodes to be processed.\n * @returns {Array}\n */\n function normalize(nodes) {\n var result;\n\n nodes = _.toArray(nodes);\n result = nodes.slice();\n\n nodes.forEach(function (node) {\n if (node.nodeType === 8) {\n result = !ko.virtualElements.hasBindingValue(node) ?\n _.without(result, node) :\n _.difference(result, ko.virtualElements.childNodes(node));\n }\n });\n\n return result;\n }\n\n /**\n * Extends binding context of each item in the collection.\n *\n * @param {...Object} extenders - Multiple extender objects to be applied to the context.\n * @returns {jQueryCollection} Chainable.\n */\n $.fn.extendCtx = function () {\n var nodes = normalize(this),\n extenders = _.toArray(arguments);\n\n nodes.forEach(function (node) {\n var ctx = ko.contextFor(node),\n data = [ctx].concat(extenders);\n\n _.extend.apply(_, data);\n });\n\n return this;\n };\n\n /**\n * Evaluates bindings specified in each DOM element of collection.\n *\n * @param {(HTMLElement|Object)} [ctx] - Context to use for bindings evaluation.\n * If not specified then current context of a collections' item will be used.\n * @returns {jQueryCollection} Chainable.\n */\n $.fn.applyBindings = function (ctx) {\n var nodes = normalize(this),\n nodeCtx;\n\n if (isDomElement(ctx)) {\n ctx = ko.contextFor(ctx);\n }\n\n nodes.forEach(function (node) {\n nodeCtx = ctx || ko.contextFor(node);\n\n ko.applyBindings(nodeCtx, node);\n });\n\n return this;\n };\n\n /**\n * Adds specified bindings to each DOM element in\n * collection and evalutes them with provided context.\n *\n * @param {(Object|Function)} data - Either bindings object or a function\n * which returns bindings data for each element in collection.\n * @param {(HTMLElement|Object)} [ctx] - Context to use for bindings evaluation.\n * If not specified then current context of a collections' item will be used.\n * @returns {jQueryCollection} Chainable.\n */\n $.fn.bindings = function (data, ctx) {\n var nodes = normalize(this),\n bindings = data,\n nodeCtx;\n\n if (isDomElement(ctx)) {\n ctx = ko.contextFor(ctx);\n }\n\n nodes.forEach(function (node) {\n nodeCtx = ctx || ko.contextFor(node);\n\n if (_.isFunction(data)) {\n bindings = data(nodeCtx, node);\n }\n\n ko.applyBindingsToNode(node, bindings, nodeCtx);\n });\n\n return this;\n };\n});\n","Magento_Ui/js/lib/view/utils/raf.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global WeakMap */\ndefine([\n 'es6-collections'\n], function () {\n 'use strict';\n\n var processMap = new WeakMap(),\n origRaf,\n raf;\n\n origRaf = window.requestAnimationFrame ||\n window.webkitRequestAnimationFrame ||\n window.mozRequestAnimationFrame ||\n window.onRequestAnimationFrame ||\n window.msRequestAnimationFrame ||\n function (callback) {\n if (typeof callback != 'function') {\n throw new Error('raf argument \"callback\" must be of type function');\n }\n window.setTimeout(callback, 1000 / 60);\n };\n\n /**\n * Creates new process object or extracts the\n * the existing one.\n *\n * @param {*} id - Process identifier.\n * @param {Number} fps - Required FPS count.\n * @returns {Object}\n */\n function getProcess(id, fps) {\n var process = processMap.get(id);\n\n if (!process) {\n process = {};\n processMap.set(id, process);\n }\n\n if (process.fps !== fps) {\n process.fps = fps;\n process.interval = 1000 / fps;\n process.update = Date.now();\n }\n\n return process;\n }\n\n /**\n * Proxy method which delegates call to the 'requestAnimationFrame'\n * function and optionally can keep track of the FPS with which\n * provided function is called.\n *\n * @param {Function} callback - Callback function to be passed to 'requestAnimationFrame'.\n * @param {Number} [fps] - If specified, will update FPS counter for the provided function.\n * @returns {Number|Boolean} ID of request or a flag which indicates\n * whether callback fits specified FPS.\n */\n raf = function (callback, fps) {\n var rafId = origRaf(callback);\n\n return fps ? raf.tick(callback, fps) : rafId;\n };\n\n /**\n * Updates FPS counter for the specified process\n * and returns a flag which indicates whether\n * counter value is equal or greater than the required FPS.\n *\n * @param {*} id - Process identifier.\n * @param {Number} fps - Required FPS count.\n * @returns {Boolean}\n */\n raf.tick = function (id, fps) {\n var process = getProcess(id, fps),\n now = Date.now(),\n delta = now - process.update,\n interval = process.interval;\n\n if (fps >= 60 || delta >= interval) {\n process.update = now - delta % interval;\n\n return true;\n }\n\n return false;\n };\n\n return raf;\n});\n","Magento_Ui/js/lib/view/utils/async.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'jquery',\n 'underscore',\n 'uiRegistry',\n './dom-observer',\n 'Magento_Ui/js/lib/knockout/extender/bound-nodes',\n './bindings'\n], function (ko, $, _, registry, domObserver, boundedNodes) {\n 'use strict';\n\n /**\n * Checks if provided value is a dom element.\n *\n * @param {*} node - Value to be checked.\n * @returns {Boolean}\n */\n function isDomElement(node) {\n return typeof node === 'object' && node.tagName && node.nodeType;\n }\n\n /**\n * Parses provided string and extracts\n * component, context and selector data from it.\n *\n * @param {String} str - String to be processed.\n * @returns {Object} Data retrieved from string.\n *\n * @example Sample format.\n * '{{component}}:{{ctx}} -> {{selector}}'\n *\n * component - Name of component.\n * ctx - Selector of the root node upon which component is binded.\n * selector - Selector of DOM elements located\n * inside of a previously specified context.\n */\n function parseSelector(str) {\n var data = str.trim().split('->'),\n result = {},\n componentData;\n\n if (data.length === 1) {\n if (!~data[0].indexOf(':')) {\n result.selector = data[0];\n } else {\n componentData = data[0];\n }\n } else {\n componentData = data[0];\n result.selector = data[1];\n }\n\n if (componentData) {\n componentData = componentData.split(':');\n\n result.component = componentData[0];\n result.ctx = componentData[1];\n }\n\n _.each(result, function (value, key) {\n result[key] = value.trim();\n });\n\n return result;\n }\n\n /**\n * Internal method used to normalize argumnets passed\n * to 'async' module methods.\n *\n * @param {(String|Objetc)} selector\n * @param {(HTMLElement|Object|String)} [ctx]\n * @returns {Object}\n */\n function parseData(selector, ctx) {\n var data = {};\n\n if (arguments.length === 2) {\n data.selector = selector;\n\n if (isDomElement(ctx)) {\n data.ctx = ctx;\n } else {\n data.component = ctx;\n data.ctx = '*';\n }\n } else {\n data = _.isString(selector) ?\n parseSelector(selector) :\n selector;\n }\n\n return data;\n }\n\n /**\n * Creates promise that will be resolved\n * when requested component is registred.\n *\n * @param {String} name - Name of component.\n * @returns {jQueryPromise}\n */\n function waitComponent(name) {\n var deffer = $.Deferred();\n\n if (_.isString(name)) {\n registry.get(name, function (component) {\n deffer.resolve(component);\n });\n } else {\n deffer.resolve(name);\n }\n\n return deffer.promise();\n }\n\n /**\n * Creates listener for the nodes binded to provided component.\n *\n * @param {Object} data - Listener data.\n * @param {Object} component - Associated with nodes component.\n */\n function setRootListener(data, component) {\n boundedNodes.get(component, function (root) {\n if (!$(root).is(data.ctx || '*')) {\n return;\n }\n\n data.selector ?\n domObserver.get(data.selector, data.fn, root) :\n data.fn(root);\n });\n }\n\n /*eslint-disable no-unused-vars*/\n /**\n * Sets listener for the appearance of elements which\n * matches specified selector data.\n *\n * @param {(String|Object)} selector - Valid css selector or a string\n * in format acceptable by 'parseSelector' method or an object with\n * 'component', 'selector' and 'ctx' properties.\n * @param {(HTMLElement|Object|String)} [ctx] - Optional context parameter\n * which might be a DOM element, component instance or components' name.\n * @param {Function} fn - Callback that will be invoked\n * when required DOM element appears.\n *\n * @example\n * Creating listener of the 'span' nodes appearance,\n * located inside of 'div' nodes, which are binded to 'cms_page_listing' component:\n *\n * $.async('cms_page_listing:div -> span', function (node) {});\n *\n * @example Another syntaxes of the previous example.\n * $.async({\n * component: 'cms_page_listing',\n * ctx: 'div',\n * selector: 'span'\n * }, function (node) {});\n *\n * @example Listens for appearance of any child node inside of specified component.\n * $.async('> *', 'cms_page_lsiting', function (node) {});\n *\n * @example Listens for appearance of 'span' nodes inside of specific context.\n * $.async('span', document.getElementById('test'), function (node) {});\n */\n $.async = function (selector, ctx, fn) {\n var args = _.toArray(arguments),\n data = parseData.apply(null, _.initial(args));\n\n data.fn = _.last(args);\n\n if (data.component) {\n waitComponent(data.component)\n .then(setRootListener.bind(null, data));\n } else {\n domObserver.get(data.selector, data.fn, data.ctx);\n }\n };\n\n /*eslint-enable no-unused-vars*/\n\n _.extend($.async, {\n\n /*eslint-disable no-unused-vars*/\n /**\n * Returns collection of elements found by provided selector data.\n *\n * @param {(String|Object)} selector - See 'async' definition.\n * @param {(HTMLElement|Object|String)} [ctx] - See 'async' definition.\n * @returns {Array} An array of DOM elements.\n */\n get: function (selector, ctx) {\n var data = parseData.apply(null, arguments),\n component = data.component,\n nodes;\n\n if (!component) {\n return $(data.selector, data.ctx).toArray();\n } else if (_.isString(component)) {\n component = registry.get(component);\n }\n\n if (!component) {\n return [];\n }\n\n nodes = boundedNodes.get(component);\n nodes = $(nodes).filter(data.ctx).toArray();\n\n return data.selector ?\n $(data.selector, nodes).toArray() :\n nodes;\n },\n\n /*eslint-enable no-unused-vars*/\n\n /**\n * Sets removal listener of the specified nodes.\n *\n * @param {(HTMLElement|Array|ArrayLike)} nodes - Nodes whose removal to track.\n * @param {Function} fn - Callback that will be invoked when node is removed.\n */\n remove: function (nodes, fn) {\n domObserver.remove(nodes, fn);\n },\n\n parseSelector: parseSelector\n });\n\n return $;\n});\n","Magento_Ui/js/lib/validation/utils.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine(function () {\n 'use strict';\n\n var utils = {\n /**\n * Check if string is empty with trim.\n *\n * @param {String} value\n * @return {Boolean}\n */\n isEmpty: function (value) {\n return value === '' || value == null || value.length === 0 || /^\\s+$/.test(value);\n },\n\n /**\n * Check if string is empty no trim.\n *\n * @param {String} value\n * @return {Boolean}\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 *\n * @param {String} value\n * @param {String} from\n * @param {String} to\n * @return {Boolean}\n */\n isBetween: function (value, from, to) {\n return (from === null || from === '' || value >= utils.parseNumber(from)) &&\n (to === null || to === '' || value <= utils.parseNumber(to));\n },\n\n /**\n * Parse price string.\n *\n * @param {String} value\n * @return {Number}\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 return utils;\n});\n","Magento_Ui/js/lib/validation/rules.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n './utils',\n 'moment',\n 'tinycolor',\n 'jquery/validate',\n 'jquery/ui',\n 'mage/translate'\n], function ($, _, utils, moment, tinycolor) {\n 'use strict';\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 * Collection of validation rules including rules from additional-methods.js\n * @type {Object}\n */\n return _.mapObject({\n 'min_text_length': [\n function (value, params) {\n return _.isUndefined(value) || value.length === 0 || value.length >= +params;\n },\n $.mage.__('Please enter more or equal than {0} symbols.')\n ],\n 'max_text_length': [\n function (value, params) {\n return !_.isUndefined(value) && value.length <= +params;\n },\n $.mage.__('Please enter less or equal than {0} symbols.')\n ],\n 'max-words': [\n function (value, params) {\n return utils.isEmpty(value) || utils.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, params) {\n return utils.isEmpty(value) || utils.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, params) {\n var match = utils.stripHtml(value).match(/\\b\\w+\\b/g) || [];\n\n return utils.isEmpty(value) || match.length >= params[0] &&\n match.length <= params[1];\n },\n $.mage.__('Please enter between {0} and {1} words.')\n ],\n 'letters-with-basic-punc': [\n function (value) {\n return utils.isEmpty(value) || /^[a-z\\-.,()\\u0027\\u0022\\s]+$/i.test(value);\n },\n $.mage.__('Letters or punctuation only please')\n ],\n 'alphanumeric': [\n function (value) {\n return utils.isEmpty(value) || /^\\w+$/i.test(value);\n },\n $.mage.__('Letters, numbers, spaces or underscores only please')\n ],\n 'letters-only': [\n function (value) {\n return utils.isEmpty(value) || /^[a-z]+$/i.test(value);\n },\n $.mage.__('Letters only please')\n ],\n 'no-whitespace': [\n function (value) {\n return utils.isEmpty(value) || /^\\S+$/i.test(value);\n },\n $.mage.__('No white space please')\n ],\n 'no-marginal-whitespace': [\n function (value) {\n return !/^\\s+|\\s+$/i.test(value);\n },\n $.mage.__('No marginal white space please')\n ],\n 'zip-range': [\n function (value) {\n return utils.isEmpty(value) || /^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) {\n return utils.isEmpty(value) || /^-?\\d+$/.test(value);\n },\n $.mage.__('A positive or negative non-decimal number please')\n ],\n 'vinUS': [\n function (value) {\n if (utils.isEmpty(value)) {\n return true;\n }\n\n if (value.length !== 17) {\n return false;\n }\n var i, n, d, f, cd, cdv,//eslint-disable-line vars-on-top\n LL = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],//eslint-disable-line max-len\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 = value.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++) {//eslint-disable-line max-depth\n if (d.toUpperCase() === LL[n]) {//eslint-disable-line max-depth\n d = VL[n];\n d *= f;\n\n if (isNaN(cdv) && n === 8) {//eslint-disable-line max-depth\n cdv = LL[n];\n }\n break;\n }\n }\n }\n rs += d;\n }\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) {\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 check;\n },\n $.mage.__('Please enter a correct date')\n ],\n 'dateNL': [\n function (value) {\n return /^\\d\\d?[\\.\\/-]\\d\\d?[\\.\\/-]\\d\\d\\d?\\d?$/.test(value);\n },\n $.mage.__('Vul hier een geldige datum in.')\n ],\n 'time': [\n function (value) {\n return utils.isEmpty(value) || /^([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) {\n return utils.isEmpty(value) || /^((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 (value) {\n value = value.replace(/\\s+/g, '');\n\n return utils.isEmpty(value) || value.length > 9 &&\n value.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 (value) {\n return utils.isEmpty(value) || value.length > 9 &&\n value.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 (value) {\n return utils.isEmpty(value) || value.length > 9 && value.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, param) {\n return _.isUndefined(value) || value.length === 0 || utils.stripHtml(value).length >= param;\n },\n $.mage.__('Please enter at least {0} characters')\n ],\n 'email2': [\n function (value) {\n return utils.isEmpty(value) || /^((([a-z]|\\d|[!#\\$%&\\u0027\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&\\u0027\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\u0022)((((\\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)+)?(\\u0022)))@((([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);//eslint-disable-line max-len\n },\n $.validator.messages.email\n ],\n 'url2': [\n function (value) {\n return utils.isEmpty(value) || /^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&\\u0027\\(\\)\\*\\+,;=]|:)*@)?(((\\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})|[!\\$&\\u0027\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&\\u0027\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&\\u0027\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(\\#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&\\u0027\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i.test(value);//eslint-disable-line max-len\n },\n $.validator.messages.url\n ],\n 'credit-card-types': [\n function (value, param) {\n var validTypes;\n\n if (utils.isEmpty(value)) {\n return true;\n }\n\n if (/[^0-9-]+/.test(value)) {\n return false;\n }\n value = value.replace(/\\D/g, '');\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 'ipv4': [\n function (value) {\n return utils.isEmpty(value) || /^(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);//eslint-disable-line max-len\n },\n $.mage.__('Please enter a valid IP v4 address.')\n ],\n 'ipv6': [\n function (value) {\n return utils.isEmpty(value) || /^((([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);//eslint-disable-line max-len\n },\n $.mage.__('Please enter a valid IP v6 address.')\n ],\n 'pattern': [\n function (value, param) {\n return utils.isEmpty(value) || new RegExp(param).test(value);\n },\n $.mage.__('Invalid format.')\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 !utils.isEmpty(value);\n },\n $.mage.__('Empty Value.')\n ],\n 'validate-alphanum-with-spaces': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^[a-zA-Z0-9 ]+$/.test(value);\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 (value) {\n return utils.isEmptyNoTrim(value) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(value);\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 (value) {\n return utils.isEmptyNoTrim(value) ||\n /^[ \\w]{3,}([A-Za-z]\\.)?([ \\w]*\\#\\d+)?(\\r\\n| )[ \\w]{3,}/.test(value);\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 (value) {\n return utils.isEmptyNoTrim(value) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(value);\n },\n $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n ],\n 'validate-phoneLax': [\n function (value) {\n return utils.isEmptyNoTrim(value) ||\n /^((\\d[\\-. ]?)?((\\(\\d{3}\\))|\\d{3}))?[\\-. ]?\\d{3}[\\-. ]?\\d{4}$/.test(value);\n },\n $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n ],\n 'validate-fax': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(value);\n },\n $.mage.__('Please enter a valid fax number (Ex: 123-456-7890).')\n ],\n 'validate-email': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^([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(value);//eslint-disable-line max-len\n },\n $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')\n ],\n 'validate-emailSender': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^[\\S ]+$/.test(value);\n },\n $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')\n ],\n 'validate-password': [\n function (value) {\n var pass;\n\n if (value == null) {\n return false;\n }\n\n pass = $.trim(value);\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 (value) {\n var pass;\n\n if (value == null) {\n return false;\n }\n\n pass = $.trim(value);\n\n if (pass.length === 0) {\n return true;\n }\n\n if (!/[a-z]/i.test(value) || !/[0-9]/.test(value)) {\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 = $.trim(v),\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 (value) {\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n value = (value || '').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(value);//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 (value) {\n return utils.isEmptyNoTrim(value) || /^(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(value) || /^(www)((\\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\\d+))?\\/?/i.test(value);//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 (value) {\n return utils.isEmptyNoTrim(value) || /^[A-Z][A-Z0-9_\\/-]*$/i.test(value);\n\n },\n $.mage.__('Please enter a valid XML-identifier (Ex: something_1, block5, id-4).')\n ],\n 'validate-ssn': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^\\d{3}-?\\d{2}-?\\d{4}$/.test(value);\n\n },\n $.mage.__('Please enter a valid social security number (Ex: 123-45-6789).')\n ],\n 'validate-zip-us': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /(^\\d{5}$)|(^\\d{5}-\\d{4}$)/.test(value);\n\n },\n $.mage.__('Please enter a valid zip code (Ex: 90602 or 90602-1234).')\n ],\n 'validate-date-au': [\n function (value) {\n var regex = /^(\\d{2})\\/(\\d{2})\\/(\\d{4})$/,\n d;\n\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n\n if (utils.isEmpty(value) || !regex.test(value)) {\n return false;\n }\n d = new Date(value.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 (value) {\n return utils.isEmptyNoTrim(value) || /^\\$?\\-?([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(value);//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 (value) {\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n value = utils.parseNumber(value);\n\n return !isNaN(value) && value >= 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 (value) {\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n value = utils.parseNumber(value);\n\n return !isNaN(value) && value >= 0;\n },\n $.mage.__('Please enter a number 0 or greater in this field.')\n ],\n 'validate-greater-than-zero': [\n function (value) {\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n value = utils.parseNumber(value);\n\n return !isNaN(value) && value > 0;\n },\n $.mage.__('Please enter a number greater than 0 in this field.')\n ],\n 'validate-css-length': [\n function (value) {\n if (value !== '') {\n return (/^[0-9]*\\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(value);\n }\n\n return true;\n },\n $.mage.__('Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).')\n ],\n 'validate-number': [\n function (value) {\n return utils.isEmptyNoTrim(value) ||\n !isNaN(utils.parseNumber(value)) && /^\\s*-?\\d*(,\\d*)*(\\.\\d*)?\\s*$/.test(value);\n },\n $.mage.__('Please enter a valid number in this field.')\n ],\n 'validate-integer': [\n function (value) {\n return utils.isEmptyNoTrim(value) || !isNaN(utils.parseNumber(value)) && /^\\s*-?\\d*\\s*$/.test(value);\n },\n $.mage.__('Please enter a valid integer in this field.')\n ],\n 'validate-number-range': [\n function (value, param) {\n var numValue, dataAttrRange, result, range, m;\n\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n\n numValue = utils.parseNumber(value);\n\n if (isNaN(numValue)) {\n return false;\n }\n\n dataAttrRange = /^(-?[\\d.,]+)?-(-?[\\d.,]+)?$/;\n result = true;\n range = param;\n\n if (range) {\n m = dataAttrRange.exec(range);\n\n if (m) {\n result = result && utils.isBetween(numValue, m[1], m[2]);\n }\n }\n\n return result;\n },\n $.mage.__('The value is not within the specified range.')\n ],\n 'validate-positive-percent-decimal': [\n function (value) {\n var numValue;\n\n if (utils.isEmptyNoTrim(value) || !/^\\s*-?\\d*(\\.\\d*)?\\s*$/.test(value)) {\n return false;\n }\n\n numValue = utils.parseNumber(value);\n\n if (isNaN(numValue)) {\n return false;\n }\n\n return utils.isBetween(numValue, 0.01, 100);\n },\n $.mage.__('Please enter a valid percentage discount value greater than 0.')\n ],\n 'validate-digits': [\n function (value) {\n return utils.isEmptyNoTrim(value) || !/[^\\d]/.test(value);\n },\n $.mage.__('Please enter a valid number in this field.')\n ],\n 'validate-digits-range': [\n function (value, param) {\n var numValue, dataAttrRange, result, range, m;\n\n if (utils.isEmptyNoTrim(value)) {\n return true;\n }\n\n numValue = utils.parseNumber(value);\n\n if (isNaN(numValue)) {\n return false;\n }\n\n dataAttrRange = /^(-?\\d+)?-(-?\\d+)?$/;\n result = true;\n range = param;\n\n if (range) {\n m = dataAttrRange.exec(range);\n\n if (m) {\n result = result && utils.isBetween(numValue, m[1], m[2]);\n }\n }\n\n return result;\n },\n $.mage.__('The value is not within the specified range.')\n ],\n 'validate-range': [\n function (value) {\n var minValue, maxValue, ranges;\n\n if (utils.isEmptyNoTrim(value)) {\n return true;\n } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](value)) {\n minValue = maxValue = utils.parseNumber(value);\n } else {\n ranges = /^(-?\\d+)?-(-?\\d+)?$/.exec(value);\n\n if (ranges) {\n minValue = utils.parseNumber(ranges[1]);\n maxValue = utils.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 },\n $.mage.__('The value is not within the specified range.')\n ],\n 'validate-alpha': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^[a-zA-Z]+$/.test(value);\n },\n $.mage.__('Please use letters only (a-z or A-Z) in this field.')\n ],\n 'validate-code': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^[a-z]+[a-z0-9_]+$/.test(value);\n },\n $.mage.__('Please use only lowercase letters (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 (value) {\n return utils.isEmptyNoTrim(value) || /^[a-zA-Z0-9]+$/.test(value);\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 utils.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, additionalParams.dateFormat);\n\n return utils.isEmptyNoTrim(value) || test.isValid();\n },\n $.mage.__('Please enter a valid date.')\n ],\n 'validate-identifier': [\n function (value) {\n return utils.isEmptyNoTrim(value) || /^[a-z0-9][a-z0-9_\\/-]+(\\.[a-z0-9_-]+)?$/.test(value);\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) || /(^[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-state': [\n function (value) {\n return value !== 0 || value === '';\n },\n $.mage.__('Please select State/Province.')\n ],\n 'less-than-equals-to': [\n function (value, params) {\n if ($.isNumeric(params) && $.isNumeric(value)) {\n return parseFloat(value) <= parseFloat(params);\n }\n\n return true;\n },\n $.mage.__('Please enter a value less than or equal to {0}.')\n ],\n 'greater-than-equals-to': [\n function (value, params) {\n if ($.isNumeric(params) && $.isNumeric(value)) {\n return parseFloat(value) >= parseFloat(params);\n }\n\n return true;\n },\n $.mage.__('Please enter a value greater than or equal to {0}.')\n ],\n 'validate-emails': [\n function (value) {\n var validRegexp, emails, i;\n\n if (utils.isEmpty(value)) {\n return true;\n }\n validRegexp = /^[a-z0-9\\._-]{1,30}@([a-z0-9_-]{1,30}\\.){1,5}[a-z]{2,4}$/i;\n emails = value.split(/[\\s\\n\\,]+/g);\n\n for (i = 0; i < emails.length; i++) {\n if (!validRegexp.test(emails[i].strip())) {\n return false;\n }\n }\n\n return true;\n },\n $.mage.__('Please enter valid email addresses, separated by commas. For example, johndoe@domain.com, johnsmith@domain.com.')//eslint-disable-line max-len\n ],\n 'validate-cc-number': [\n\n /**\n * Validate credit card number based on mod 10.\n *\n * @param {String} value - credit card number\n * @return {Boolean}\n */\n function (value) {\n if (value) {\n return validateCreditCard(value);\n }\n\n return true;\n },\n $.mage.__('Please enter a valid credit card number.')\n ],\n 'validate-cc-ukss': [\n\n /**\n * Validate Switch/Solo/Maestro issue number and start date is filled.\n *\n * @param {String} 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 'required-entry': [\n function (value) {\n return !utils.isEmpty(value);\n },\n $.mage.__('This is a required field.')\n ],\n 'checked': [\n function (value) {\n return value;\n },\n $.mage.__('This is a required field.')\n ],\n 'not-negative-amount': [\n function (value) {\n if (value.length) {\n return (/^\\s*\\d+([,.]\\d+)*\\s*%?\\s*$/).test(value);\n }\n\n return true;\n },\n $.mage.__('Please enter positive number in this field.')\n ],\n 'validate-per-page-value-list': [\n function (value) {\n var isValid = utils.isEmpty(value),\n values = value.split(','),\n i;\n\n if (isValid) {\n return true;\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-new-password': [\n function (value) {\n if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](value)) {\n return false;\n }\n\n if (utils.isEmpty(value) && value !== '') {\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 'validate-item-quantity': [\n function (value, params) {\n var validator = this,\n result = false,\n // obtain values for validation\n qty = utils.parseNumber(value),\n isMinAllowedValid = typeof params.minAllowed === 'undefined' ||\n qty >= utils.parseNumber(params.minAllowed),\n isMaxAllowedValid = typeof params.maxAllowed === 'undefined' ||\n qty <= utils.parseNumber(params.maxAllowed),\n isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' ||\n qty % utils.parseNumber(params.qtyIncrements) === 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 'equalTo': [\n function (value, param) {\n return value === $(param).val();\n },\n $.validator.messages.equalTo\n ],\n 'validate-file-type': [\n function (name, types) {\n var extension = name.split('.').pop().toLowerCase();\n\n if (types && typeof types === 'string') {\n types = types.split(' ');\n }\n\n return !types || !types.length || ~types.indexOf(extension);\n },\n $.mage.__('We don\\'t recognize or support this file extension type.')\n ],\n 'validate-max-size': [\n function (size, maxSize) {\n return maxSize === false || size < maxSize;\n },\n $.mage.__('File you are trying to upload exceeds maximum file size limit.')\n ],\n 'validate-if-tag-script-exist': [\n function (value) {\n return !value || (/<script\\b[^>]*>([\\s\\S]*?)<\\/script>$/ig).test(value);\n },\n $.mage.__('Please use tag SCRIPT with SRC attribute or with proper content to include JavaScript to the document.')//eslint-disable-line max-len\n ],\n 'date_range_min': [\n function (value, minValue, params) {\n return moment.utc(value, params.dateFormat).unix() >= minValue;\n },\n $.mage.__('The date is not within the specified range.')\n ],\n 'date_range_max': [\n function (value, maxValue, params) {\n return moment.utc(value, params.dateFormat).unix() <= maxValue;\n },\n $.mage.__('The date is not within the specified range.')\n ],\n 'validate-color': [\n function (value) {\n if (value === '') {\n return true;\n }\n\n return tinycolor(value).isValid();\n },\n $.mage.__('Wrong color format. Please specify color in HEX, RGBa, HSVa, HSLa or use color name.')\n ],\n 'blacklist-url': [\n function (value, param) {\n return new RegExp(param).test(value);\n },\n $.mage.__('This link is not allowed.')\n ]\n }, function (data) {\n return {\n handler: data[0],\n message: data[1]\n };\n });\n});\n","Magento_Ui/js/lib/validation/validator.js":"/*\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n './rules'\n], function (_, rulesList) {\n 'use strict';\n\n /**\n * Validates provided value be the specified rule.\n *\n * @param {String} id - Rule identifier.\n * @param {*} value - Value to be checked.\n * @param {*} [params]\n * @param {*} additionalParams - additional validation params set by method caller\n * @returns {Object}\n */\n function validate(id, value, params, additionalParams) {\n var rule,\n message,\n valid,\n result = {\n rule: id,\n passed: true,\n message: ''\n };\n\n if (_.isObject(params)) {\n message = params.message || '';\n }\n\n if (!rulesList[id]) {\n return result;\n }\n\n rule = rulesList[id];\n message = message || rule.message;\n valid = rule.handler(value, params, additionalParams);\n\n if (!valid) {\n params = Array.isArray(params) ?\n params :\n [params];\n\n message = params.reduce(function (msg, param, idx) {\n return msg.replace(new RegExp('\\\\{' + idx + '\\\\}', 'g'), param);\n }, message);\n\n result.passed = false;\n result.message = message;\n }\n\n return result;\n }\n\n /**\n * Validates provied value by a specified set of rules.\n *\n * @param {(String|Object)} rules - One or many validation rules.\n * @param {*} value - Value to be checked.\n * @param {*} additionalParams - additional validation params set by method caller\n * @returns {Object}\n */\n function validator(rules, value, additionalParams) {\n var result;\n\n if (typeof rules === 'object') {\n result = {\n passed: true\n };\n\n _.every(rules, function (ruleParams, id) {\n if (ruleParams.validate || ruleParams !== false || additionalParams) {\n result = validate(id, value, ruleParams, additionalParams);\n\n return result.passed;\n }\n\n return true;\n });\n\n return result;\n }\n\n return validate.apply(null, arguments);\n }\n\n /**\n * Adds new validation rule.\n *\n * @param {String} id - Rule identifier.\n * @param {Function} handler - Validation function.\n * @param {String} message - Error message.\n */\n validator.addRule = function (id, handler, message) {\n rulesList[id] = {\n handler: handler,\n message: message\n };\n };\n\n /**\n * Returns rule object found by provided identifier.\n *\n * @param {String} id - Rule identifier.\n * @returns {Object}\n */\n validator.getRule = function (id) {\n return rulesList[id];\n };\n\n return validator;\n});\n","Magento_Ui/js/dynamic-rows/dynamic-rows-grid.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n './dynamic-rows'\n], function (_, dynamicRows) {\n 'use strict';\n\n return dynamicRows.extend({\n defaults: {\n dataProvider: '',\n insertData: [],\n map: null,\n cacheGridData: [],\n deleteProperty: false,\n positionProvider: 'position',\n dataLength: 0,\n identificationProperty: 'id',\n identificationDRProperty: 'id',\n listens: {\n 'insertData': 'processingInsertData',\n 'recordData': 'initElements setToInsertData'\n },\n mappingSettings: {\n enabled: true,\n distinct: true\n }\n },\n\n /**\n * Calls 'initObservable' of parent\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe([\n 'insertData'\n ]);\n\n return this;\n },\n\n /**\n * Set data from recordData to insertData\n */\n setToInsertData: function () {\n var insertData = [],\n obj;\n\n if (this.recordData().length && !this.update) {\n _.each(this.recordData(), function (recordData) {\n obj = {};\n obj[this.map[this.identificationProperty]] = recordData[this.identificationProperty];\n insertData.push(obj);\n }, this);\n\n if (insertData.length) {\n this.source.set(this.dataProvider, insertData);\n }\n }\n },\n\n /**\n * Initialize children\n *\n * @returns {Object} Chainable.\n */\n initChildren: function () {\n this.getChildItems().forEach(function (data, index) {\n this.processingAddChild(data, this.startIndex + index, data[this.identificationDRProperty]);\n }, this);\n\n return this;\n },\n\n /**\n * Initialize elements from grid\n *\n * @param {Array} data\n *\n * @returns {Object} Chainable.\n */\n initElements: function (data) {\n var newData = this.getNewData(data);\n\n this.parsePagesData(data);\n\n if (newData.length) {\n if (this.insertData().length) {\n this.processingAddChild(newData[0], data.length - 1, newData[0][this.identificationProperty]);\n }\n }\n\n return this;\n },\n\n /**\n * Delete record instance\n * update data provider dataScope\n *\n * @param {String|Number} index - record index\n * @param {String|Number} recordId\n */\n deleteRecord: function (index, recordId) {\n this.updateInsertData(recordId);\n this._super();\n },\n\n /**\n * Updates insertData when record is deleted\n *\n * @param {String|Number} recordId\n */\n updateInsertData: function (recordId) {\n var data = this.getElementData(this.insertData(), recordId),\n prop = this.map[this.identificationDRProperty];\n\n this.insertData(_.reject(this.source.get(this.dataProvider), function (recordData) {\n return recordData[prop].toString() === data[prop].toString();\n }, this));\n },\n\n /**\n * Find data object by index\n *\n * @param {Array} array - data collection\n * @param {Number} index - element index\n * @param {String} property - to find by property\n *\n * @returns {Object} data object\n */\n getElementData: function (array, index, property) {\n var obj = {},\n result;\n\n property ? obj[property] = index : obj[this.map[this.identificationDRProperty]] = index;\n result = _.findWhere(array, obj);\n\n if (!result) {\n property ?\n obj[property] = index.toString() :\n obj[this.map[this.identificationDRProperty]] = index.toString();\n }\n\n result = _.findWhere(array, obj);\n\n return result;\n },\n\n /**\n * Processing pages before addChild\n *\n * @param {Object} ctx - element context\n * @param {Number|String} index - element index\n * @param {Number|String} prop - additional property to element\n */\n processingAddChild: function (ctx, index, prop) {\n if (this._elems.length > this.pageSize) {\n return false;\n }\n\n this.showSpinner(true);\n this.addChild(ctx, index, prop);\n },\n\n /**\n * Contains old data with new\n *\n * @param {Array} data\n *\n * @returns {Array} changed data\n */\n getNewData: function (data) {\n var changes = [],\n tmpObj = {};\n\n if (data.length !== this.relatedData.length) {\n _.each(data, function (obj) {\n tmpObj[this.identificationDRProperty] = obj[this.identificationDRProperty];\n\n if (!_.findWhere(this.relatedData, tmpObj)) {\n changes.push(obj);\n }\n }, this);\n }\n\n return changes;\n },\n\n /**\n * Processing insert data\n *\n * @param {Object} data\n */\n processingInsertData: function (data) {\n var changes,\n obj = {};\n\n changes = this._checkGridData(data);\n this.cacheGridData = data;\n\n if (changes.length) {\n obj[this.identificationDRProperty] = changes[0][this.map[this.identificationProperty]];\n\n if (_.findWhere(this.recordData(), obj)) {\n return false;\n }\n\n changes.forEach(function (changedObject) {\n this.mappingValue(changedObject);\n }, this);\n }\n },\n\n /**\n * Mapping value from grid\n *\n * @param {Array} data\n */\n mappingValue: function (data) {\n var obj = {},\n tmpObj = {};\n\n if (this.mappingSettings.enabled) {\n _.each(this.map, function (prop, index) {\n obj[index] = !_.isUndefined(data[prop]) ? data[prop] : '';\n }, this);\n } else {\n obj = data;\n }\n\n if (this.mappingSettings.distinct) {\n tmpObj[this.identificationDRProperty] = obj[this.identificationDRProperty];\n\n if (_.findWhere(this.recordData(), tmpObj)) {\n return false;\n }\n }\n\n if (!obj.hasOwnProperty(this.positionProvider)) {\n this.setMaxPosition();\n obj[this.positionProvider] = this.maxPosition;\n }\n\n this.source.set(this.dataScope + '.' + this.index + '.' + this.recordData().length, obj);\n },\n\n /**\n * Check changed records\n *\n * @param {Array} data - array with records data\n * @returns {Array} Changed records\n */\n _checkGridData: function (data) {\n var cacheLength = this.cacheGridData.length,\n curData = data.length,\n max = cacheLength > curData ? this.cacheGridData : data,\n changes = [],\n obj = {};\n\n max.forEach(function (record, index) {\n obj[this.map[this.identificationDRProperty]] = record[this.map[this.identificationDRProperty]];\n\n if (!_.where(this.cacheGridData, obj).length) {\n changes.push(data[index]);\n }\n }, this);\n\n return changes;\n }\n });\n});\n","Magento_Ui/js/dynamic-rows/dnd.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'jquery',\n 'underscore',\n 'uiElement',\n 'Magento_Ui/js/lib/view/utils/async'\n], function (ko, $, _, Element) {\n 'use strict';\n\n var transformProp;\n\n /**\n * Get element context\n */\n function getContext(elem) {\n return ko.contextFor(elem);\n }\n\n /**\n * Defines supported css 'transform' property.\n *\n * @returns {String|Undefined}\n */\n transformProp = (function () {\n var style = document.createElement('div').style,\n base = 'Transform',\n vendors = ['webkit', 'moz', 'ms', 'o'],\n vi = vendors.length,\n property;\n\n if (typeof style.transform !== 'undefined') {\n return 'transform';\n }\n\n while (vi--) {\n property = vendors[vi] + base;\n\n if (typeof style[property] !== 'undefined') {\n return property;\n }\n }\n })();\n\n return Element.extend({\n defaults: {\n separatorsClass: {\n top: '_dragover-top',\n bottom: '_dragover-bottom'\n },\n step: 'auto',\n tableClass: 'table.admin__dynamic-rows',\n recordsCache: [],\n draggableElement: {},\n draggableElementClass: '_dragged',\n elemPositions: [],\n listens: {\n '${ $.recordsProvider }:elems': 'setCacheRecords'\n },\n modules: {\n parentComponent: '${ $.recordsProvider }'\n }\n },\n\n /**\n * Initialize component\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n _.bindAll(\n this,\n 'mousemoveHandler',\n 'mouseupHandler'\n );\n\n this._super()\n .body = $('body');\n\n return this;\n },\n\n /**\n * Calls 'initObservable' of parent, initializes 'options' and 'initialOptions'\n * properties, calls 'setOptions' passing options to it\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe([\n 'recordsCache'\n ]);\n\n return this;\n },\n\n /**\n * Init listens to start drag\n *\n * @param {Object} elem - DOM element\n * @param {Object} data - element data\n */\n initListeners: function (elem, data) {\n $(elem).on('mousedown touchstart', this.mousedownHandler.bind(this, data, elem));\n },\n\n /**\n * Mouse down handler\n *\n * @param {Object} data - element data\n * @param {Object} elem - element\n * @param {Object} event - key down event\n */\n mousedownHandler: function (data, elem, event) {\n var recordNode = this.getRecordNode(elem),\n originRecord = $(elem).parents('tr').eq(0),\n drEl = this.draggableElement,\n $table = $(elem).parents('table').eq(0),\n $tableWrapper = $table.parent();\n\n this.disableScroll();\n $(recordNode).addClass(this.draggableElementClass);\n $(originRecord).addClass(this.draggableElementClass);\n this.step = this.step === 'auto' ? originRecord.height() / 2 : this.step;\n drEl.originRow = originRecord;\n drEl.instance = recordNode = this.processingStyles(recordNode, elem);\n drEl.instanceCtx = this.getRecord(originRecord[0]);\n drEl.eventMousedownY = this.getPageY(event);\n drEl.minYpos =\n $table.offset().top - originRecord.offset().top + $table.children('thead').outerHeight();\n drEl.maxYpos = drEl.minYpos + $table.children('tbody').outerHeight() - originRecord.outerHeight();\n $tableWrapper.append(recordNode);\n this.body.bind('mousemove touchmove', this.mousemoveHandler);\n this.body.bind('mouseup touchend', this.mouseupHandler);\n },\n\n /**\n * Mouse move handler\n *\n * @param {Object} event - mouse move event\n */\n mousemoveHandler: function (event) {\n var depEl = this.draggableElement,\n pageY = this.getPageY(event),\n positionY = pageY - depEl.eventMousedownY,\n processingPositionY = positionY + 'px',\n processingMaxYpos = depEl.maxYpos + 'px',\n processingMinYpos = depEl.minYpos + 'px',\n depElement = this.getDepElement(depEl.instance, positionY, depEl.originRow);\n\n if (depElement) {\n depEl.depElement ? depEl.depElement.elem.removeClass(depEl.depElement.className) : false;\n depEl.depElement = depElement;\n depEl.depElement.insert !== 'none' ? depEl.depElement.elem.addClass(depElement.className) : false;\n } else if (depEl.depElement && depEl.depElement.insert !== 'none') {\n depEl.depElement.elem.removeClass(depEl.depElement.className);\n depEl.depElement.insert = 'none';\n }\n\n if (positionY > depEl.minYpos && positionY < depEl.maxYpos) {\n $(depEl.instance)[0].style[transformProp] = 'translateY(' + processingPositionY + ')';\n } else if (positionY < depEl.minYpos) {\n $(depEl.instance)[0].style[transformProp] = 'translateY(' + processingMinYpos + ')';\n } else if (positionY >= depEl.maxYpos) {\n $(depEl.instance)[0].style[transformProp] = 'translateY(' + processingMaxYpos + ')';\n }\n },\n\n /**\n * Mouse up handler\n */\n mouseupHandler: function (event) {\n var depElementCtx,\n drEl = this.draggableElement,\n pageY = this.getPageY(event),\n positionY = pageY - drEl.eventMousedownY;\n\n this.enableScroll();\n drEl.depElement = this.getDepElement(drEl.instance, positionY, this.draggableElement.originRow);\n\n drEl.instance.remove();\n\n if (drEl.depElement) {\n depElementCtx = this.getRecord(drEl.depElement.elem[0]);\n drEl.depElement.elem.removeClass(drEl.depElement.className);\n\n if (drEl.depElement.insert !== 'none') {\n this.setPosition(drEl.depElement.elem, depElementCtx, drEl);\n }\n }\n\n drEl.originRow.removeClass(this.draggableElementClass);\n\n this.body.unbind('mousemove touchmove', this.mousemoveHandler);\n this.body.unbind('mouseup touchend', this.mouseupHandler);\n\n this.draggableElement = {};\n },\n\n /**\n * Set position to element\n *\n * @param {Object} depElem - dep element\n * @param {Object} depElementCtx - dep element context\n * @param {Object} dragData - data draggable element\n */\n setPosition: function (depElem, depElementCtx, dragData) {\n var depElemPosition = ~~depElementCtx.position;\n\n if (dragData.depElement.insert === 'after') {\n dragData.instanceCtx.position = depElemPosition + 1;\n } else if (dragData.depElement.insert === 'before') {\n dragData.instanceCtx.position = depElemPosition;\n }\n },\n\n /**\n * Get dependency element\n *\n * @param {Object} curInstance - current element instance\n * @param {Number} position\n * @param {Object} row\n */\n getDepElement: function (curInstance, position, row) {\n var tableSelector = this.tableClass + ' tr',\n $table = $(row).parents('table').eq(0),\n $curInstance = $(curInstance),\n recordsCollection = $table.find('table').length ?\n $table.find('tbody > tr').filter(function (index, elem) {\n return !$(elem).parents(tableSelector).length;\n }) :\n $table.find('tbody > tr'),\n curInstancePositionTop = $curInstance.position().top,\n curInstancePositionBottom = curInstancePositionTop + $curInstance.height();\n\n if (position < 0) {\n return this._getDepElement(recordsCollection, 'before', curInstancePositionTop);\n } else if (position > 0) {\n return this._getDepElement(recordsCollection, 'after', curInstancePositionBottom);\n }\n },\n\n /**\n * Get dependency element private\n *\n * @param {Array} collection - record collection\n * @param {String} position - position to add\n * @param {Number} dragPosition - position drag element\n */\n _getDepElement: function (collection, position, dragPosition) {\n var rec,\n rangeEnd,\n rangeStart,\n result,\n className,\n i = 0,\n length = collection.length;\n\n for (i; i < length; i++) {\n rec = collection.eq(i);\n\n if (position === 'before') {\n rangeStart = collection.eq(i).position().top - this.step;\n rangeEnd = rangeStart + this.step * 2;\n className = this.separatorsClass.top;\n } else if (position === 'after') {\n rangeEnd = rec.position().top + rec.height() + this.step;\n rangeStart = rangeEnd - this.step * 2;\n className = this.separatorsClass.bottom;\n }\n\n if (dragPosition > rangeStart && dragPosition < rangeEnd) {\n result = {\n elem: rec,\n insert: rec[0] === this.draggableElement.originRow[0] ? 'none' : position,\n className: className\n };\n }\n }\n\n return result;\n },\n\n /**\n * Set default position of draggable element\n *\n * @param {Object} elem - current element instance\n * @param {Object} data - current element data\n */\n _setDefaultPosition: function (elem, data) {\n var originRecord = $(elem).parents('tr').eq(0),\n position = originRecord.position();\n\n ++position.top;\n $(data).css(position);\n },\n\n /**\n * Set records to cache\n *\n * @param {Object} records - record instance\n */\n setCacheRecords: function (records) {\n this.recordsCache(records);\n },\n\n /**\n * Set styles to draggable element\n *\n * @param {Object} data - data\n * @param {Object} elem - elem instance\n * @returns {Object} instance data.\n */\n processingStyles: function (data, elem) {\n var table = $(elem).parents('table').eq(0),\n columns = table.find('th'),\n recordColumns = $(data).find('td');\n\n this._setDefaultPosition(elem, $(data));\n this._setColumnsWidth(columns, recordColumns);\n this._setTableWidth(table, $(data));\n\n return data;\n },\n\n /**\n * Set table width.\n *\n * @param {Object} originalTable - original record instance\n * @param {Object} recordTable - draggable record instance\n */\n _setTableWidth: function (originalTable, recordTable) {\n recordTable.outerWidth(originalTable.outerWidth());\n },\n\n /**\n * Set columns width.\n *\n * @param {Object} originColumns - original record instance\n * @param {Object} recordColumns - draggable record instance\n */\n _setColumnsWidth: function (originColumns, recordColumns) {\n var i = 0,\n length = originColumns.length;\n\n for (i; i < length; i++) {\n recordColumns.eq(i).outerWidth(originColumns.eq(i).outerWidth());\n }\n },\n\n /**\n * Get copy original record\n *\n * @param {Object} record - original record instance\n * @returns {Object} draggable record instance\n */\n getRecordNode: function (record) {\n var $record = $(record),\n table = $record.parents('table')[0].cloneNode(true),\n $table = $(table);\n\n $table.find('tr').remove();\n $table.append($record.parents('tr')[0].cloneNode(true));\n\n return table;\n },\n\n /**\n * Get record context by element\n *\n * @param {Object} elem - original element\n * @returns {Object} draggable record context\n */\n getRecord: function (elem) {\n var ctx = getContext(elem),\n index = _.isFunction(ctx.$index) ? ctx.$index() : ctx.$index;\n\n return this.recordsCache()[index];\n },\n\n /**\n * Get correct page Y\n *\n * @param {Object} event - current event\n * @returns {integer}\n */\n getPageY: function (event) {\n var pageY;\n\n if (event.type.indexOf('touch') >= 0) {\n if (event.originalEvent.touches[0]) {\n pageY = event.originalEvent.touches[0].pageY;\n } else {\n pageY = event.originalEvent.changedTouches[0].pageY;\n }\n } else {\n pageY = event.pageY;\n }\n\n return pageY;\n },\n\n /**\n * Disable page scrolling\n */\n disableScroll: function () {\n document.body.addEventListener('touchmove', this.preventDefault, {\n passive: false\n });\n },\n\n /**\n * Enable page scrolling\n */\n enableScroll: function () {\n document.body.removeEventListener('touchmove', this.preventDefault, {\n passive: false\n });\n },\n\n /**\n * Prevent default function\n *\n * @param {Object} event - event object\n */\n preventDefault: function (event) {\n event.preventDefault();\n }\n\n });\n});\n","Magento_Ui/js/dynamic-rows/record.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiCollection',\n 'uiRegistry'\n], function (_, uiCollection, registry) {\n 'use strict';\n\n return uiCollection.extend({\n defaults: {\n visible: true,\n disabled: true,\n headerLabel: '',\n label: '',\n positionProvider: 'position',\n imports: {\n data: '${ $.provider }:${ $.dataScope }'\n },\n listens: {\n position: 'initPosition',\n elems: 'setColumnVisibleListener'\n },\n links: {\n position: '${ $.name }.${ $.positionProvider }:value'\n },\n exports: {\n recordId: '${ $.provider }:${ $.dataScope }.record_id'\n },\n modules: {\n parentComponent: '${ $.parentName }'\n }\n },\n\n /**\n * Extends instance with default config, calls initialize of parent\n * class, calls initChildren method, set observe variable.\n * Use parent \"track\" method - wrapper observe array\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n var self = this;\n\n this._super();\n\n registry.async(this.name + '.' + this.positionProvider)(function (component) {\n\n /**\n * Overwrite hasChanged method\n *\n * @returns {Boolean}\n */\n component.hasChanged = function () {\n\n /* eslint-disable eqeqeq */\n return this.value().toString() != this.initialValue.toString();\n\n /* eslint-enable eqeqeq */\n };\n\n if (!component.initialValue) {\n component.initialValue = self.parentComponent().maxPosition;\n component.bubble('update', component.hasChanged());\n }\n });\n\n return this;\n },\n\n /**\n * Init config\n *\n * @returns {Object} Chainable.\n */\n initConfig: function () {\n this._super();\n\n this.label = this.label || this.headerLabel;\n\n return this;\n },\n\n /**\n * Calls 'initObservable' of parent\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .track('position')\n .observe([\n 'visible',\n 'disabled',\n 'data',\n 'label'\n ]);\n\n return this;\n },\n\n /**\n * Init element position\n *\n * @param {Number} position - element position\n */\n initPosition: function (position) {\n var pos = parseInt(position, 10);\n\n this.parentComponent().setMaxPosition(pos, this);\n\n if (!pos && pos !== 0) {\n this.position = this.parentComponent().maxPosition;\n }\n },\n\n /**\n * Set column visibility listener\n */\n setColumnVisibleListener: function () {\n var elem = _.find(this.elems(), function (curElem) {\n return !curElem.hasOwnProperty('visibleListener');\n });\n\n if (!elem) {\n return;\n }\n\n this.childVisibleListener(elem);\n\n if (!elem.visibleListener) {\n elem.on('visible', this.childVisibleListener.bind(this, elem));\n }\n\n elem.visibleListener = true;\n },\n\n /**\n * Child visibility listener\n *\n * @param {Object} data\n */\n childVisibleListener: function (data) {\n this.setVisibilityColumn(data.index, data.visible());\n },\n\n /**\n * Reset data to initial value.\n * Call method reset on child elements.\n */\n reset: function () {\n var elems = this.elems(),\n nameIsEqual,\n dataScopeIsEqual;\n\n _.each(elems, function (elem) {\n nameIsEqual = this.name + '.' + this.positionProvider === elem.name;\n dataScopeIsEqual = this.dataScope === elem.dataScope;\n\n if (!(nameIsEqual || dataScopeIsEqual) && _.isFunction(elem.reset)) {\n elem.reset();\n }\n }, this);\n\n return this;\n },\n\n /**\n * Clear data\n *\n * @returns {Collection} Chainable.\n */\n clear: function () {\n var elems = this.elems(),\n nameIsEqual,\n dataScopeIsEqual;\n\n _.each(elems, function (elem) {\n nameIsEqual = this.name + '.' + this.positionProvider === elem.name;\n dataScopeIsEqual = this.dataScope === elem.dataScope;\n\n if (!(nameIsEqual || dataScopeIsEqual) && _.isFunction(elem.reset)) {\n elem.clear();\n }\n }, this);\n\n return this;\n },\n\n /**\n * Get label for collapsible header\n *\n * @param {String} label\n *\n * @returns {String}\n */\n getLabel: function (label) {\n if (_.isString(label)) {\n this.label(label);\n } else if (label && this.label()) {\n return this.label();\n } else {\n this.label(this.headerLabel);\n }\n\n return this.label();\n },\n\n /**\n * Set visibility to record child\n *\n * @param {Boolean} state\n */\n setVisible: function (state) {\n this.elems.each(function (cell) {\n cell.visible(state);\n });\n },\n\n /**\n * Set visibility to child by index\n *\n * @param {Number} index\n * @param {Boolean} state\n */\n setVisibilityColumn: function (index, state) {\n var elems = this.elems(),\n curElem = parseInt(index, 10),\n label;\n\n if (!this.parentComponent()) {\n return false;\n }\n\n if (_.isNaN(curElem)) {\n _.findWhere(elems, {\n index: index\n }).visible(state);\n label = _.findWhere(this.parentComponent().labels(), {\n name: index\n });\n label.visible() !== state ? label.visible(state) : false;\n } else {\n elems[curElem].visible(state);\n }\n },\n\n /**\n * Set disabled to child\n *\n * @param {Boolean} state\n */\n setDisabled: function (state) {\n this.elems.each(function (cell) {\n cell.disabled(state);\n });\n },\n\n /**\n * Set disabled to child by index\n *\n * @param {Number} index\n * @param {Boolean} state\n */\n setDisabledColumn: function (index, state) {\n index = ~~index;\n this.elems()[index].disabled(state);\n }\n });\n});\n","Magento_Ui/js/dynamic-rows/action-delete.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/form/element/abstract'\n], function (Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n links: {\n value: false\n }\n },\n\n /**\n * Delete record handler.\n *\n * @param {Number} index\n * @param {Number} id\n */\n deleteRecord: function (index, id) {\n this.bubble('deleteRecord', index, id);\n }\n });\n});\n","Magento_Ui/js/dynamic-rows/dynamic-rows.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'mageUtils',\n 'underscore',\n 'uiLayout',\n 'uiCollection',\n 'uiRegistry',\n 'mage/translate'\n], function (ko, utils, _, layout, uiCollection, registry, $t) {\n 'use strict';\n\n /**\n * Checks value type and cast to boolean if needed\n *\n * @param {*} value\n *\n * @returns {Boolean|*} casted or origin value\n */\n function castValue(value) {\n if (_.isUndefined(value) || value === '' || _.isNull(value)) {\n return false;\n }\n\n return value;\n }\n\n /**\n * Compares arrays.\n *\n * @param {Array} base - array as method bases its decision on first argument.\n * @param {Array} current - second array\n *\n * @returns {Boolean} result - is current array equal to base array\n */\n function compareArrays(base, current) {\n var index = 0,\n length = base.length;\n\n if (base.length !== current.length) {\n return false;\n }\n\n /*eslint-disable max-depth, eqeqeq, no-use-before-define */\n for (index; index < length; index++) {\n if (_.isArray(base[index]) && _.isArray(current[index])) {\n if (!compareArrays(base[index], current[index])) {\n return false;\n }\n } else if (typeof base[index] === 'object' && typeof current[index] === 'object') {\n if (!compareObjects(base[index], current[index])) {\n return false;\n }\n } else if (castValue(base[index]) != castValue(current[index])) {\n return false;\n }\n }/*eslint-enable max-depth, eqeqeq, no-use-before-define */\n\n return true;\n }\n\n /**\n * Compares objects. Compares only properties from origin object,\n * if current object has more properties - they are not considered\n *\n * @param {Object} base - first object\n * @param {Object} current - second object\n *\n * @returns {Boolean} result - is current object equal to base object\n */\n function compareObjects(base, current) {\n var prop;\n\n /*eslint-disable max-depth, eqeqeq*/\n for (prop in base) {\n if (_.isArray(base[prop]) && _.isArray(current[prop])) {\n if (!compareArrays(base[prop], current[prop])) {\n return false;\n }\n } else if (typeof base[prop] === 'object' && typeof current[prop] === 'object') {\n if (!compareObjects(base[prop], current[prop])) {\n return false;\n }\n } else if (castValue(base[prop]) != castValue(current[prop])) {\n return false;\n }\n }/*eslint-enable max-depth, eqeqeq */\n\n return true;\n }\n\n return uiCollection.extend({\n defaults: {\n defaultRecord: false,\n columnsHeader: true,\n columnsHeaderAfterRender: false,\n columnsHeaderClasses: '',\n labels: [],\n recordTemplate: 'record',\n collapsibleHeader: false,\n additionalClasses: {},\n visible: true,\n disabled: false,\n fit: false,\n addButton: true,\n addButtonLabel: $t('Add'),\n recordData: [],\n maxPosition: 0,\n deleteProperty: 'delete',\n identificationProperty: 'record_id',\n deleteValue: true,\n showSpinner: true,\n isDifferedFromDefault: false,\n defaultState: [],\n defaultPagesState: {},\n pagesChanged: {},\n hasInitialPagesState: {},\n changed: false,\n fallbackResetTpl: 'ui/form/element/helper/fallback-reset-link',\n dndConfig: {\n name: '${ $.name }_dnd',\n component: 'Magento_Ui/js/dynamic-rows/dnd',\n template: 'ui/dynamic-rows/cells/dnd',\n recordsProvider: '${ $.name }',\n enabled: true\n },\n templates: {\n record: {\n parent: '${ $.$data.collection.name }',\n name: '${ $.$data.index }',\n dataScope: '${ $.$data.collection.index }.${ $.name }',\n nodeTemplate: '${ $.parent }.${ $.$data.collection.recordTemplate }'\n }\n },\n links: {\n recordData: '${ $.provider }:${ $.dataScope }.${ $.index }'\n },\n listens: {\n visible: 'setVisible',\n disabled: 'setDisabled',\n childTemplate: 'initHeader',\n recordTemplate: 'onUpdateRecordTemplate',\n recordData: 'setDifferedFromDefault parsePagesData setRecordDataToCache',\n currentPage: 'changePage',\n elems: 'checkSpinner',\n changed: 'updateTrigger'\n },\n modules: {\n dnd: '${ $.dndConfig.name }'\n },\n pages: 1,\n pageSize: 20,\n relatedData: [],\n currentPage: 1,\n recordDataCache: [],\n startIndex: 0\n },\n\n /**\n * Sets record data to cache\n */\n setRecordDataToCache: function (data) {\n this.recordDataCache = data;\n },\n\n /**\n * Extends instance with default config, calls initialize of parent\n * class, calls initChildren method, set observe variable.\n * Use parent \"track\" method - wrapper observe array\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n _.bindAll(this,\n 'processingDeleteRecord',\n 'onChildrenUpdate',\n 'checkDefaultState',\n 'renderColumnsHeader',\n 'deleteHandler',\n 'setDefaultState'\n );\n\n this._super()\n .initChildren()\n .initDnd()\n .initDefaultRecord()\n .setInitialProperty()\n .setColumnsHeaderListener()\n .checkSpinner();\n\n this.on('recordData', this.checkDefaultState);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n bubble: function (event) {\n if (event === 'deleteRecord' || event === 'update') {\n return false;\n }\n\n return this._super();\n },\n\n /**\n * Inits DND module\n *\n * @returns {Object} Chainable.\n */\n initDnd: function () {\n if (this.dndConfig.enabled) {\n layout([this.dndConfig]);\n }\n\n return this;\n },\n\n /** @inheritdoc */\n destroy: function () {\n if (this.dnd()) {\n this.dnd().destroy();\n }\n this._super();\n },\n\n /**\n * Calls 'initObservable' of parent\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .track('childTemplate')\n .observe([\n 'pages',\n 'currentPage',\n 'recordData',\n 'columnsHeader',\n 'visible',\n 'disabled',\n 'labels',\n 'showSpinner',\n 'isDifferedFromDefault',\n 'changed'\n ]);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n initElement: function (elem) {\n this._super();\n elem.on({\n 'deleteRecord': this.deleteHandler,\n 'update': this.onChildrenUpdate,\n 'addChild': this.setDefaultState\n });\n\n return this;\n },\n\n /**\n * Handler for deleteRecord event\n *\n * @param {Number|String} index - element index\n * @param {Number|String} id\n */\n deleteHandler: function (index, id) {\n var defaultState;\n\n this.setDefaultState();\n defaultState = this.defaultPagesState[this.currentPage()];\n this.processingDeleteRecord(index, id);\n this.pagesChanged[this.currentPage()] =\n !compareArrays(defaultState, this.arrayFilter(this.getChildItems()));\n this.changed(_.some(this.pagesChanged));\n },\n\n /**\n * Set initial property to records data\n *\n * @returns {Object} Chainable.\n */\n setInitialProperty: function () {\n if (_.isArray(this.recordData())) {\n this.recordData.each(function (data, index) {\n this.source.set(this.dataScope + '.' + this.index + '.' + index + '.initialize', true);\n }, this);\n }\n\n return this;\n },\n\n /**\n * Handler for update event\n *\n * @param {Boolean} state\n */\n onChildrenUpdate: function (state) {\n var changed,\n dataScope,\n changedElemDataScope;\n\n if (state && !this.hasInitialPagesState[this.currentPage()]) {\n this.setDefaultState();\n changed = this.getChangedElems(this.elems());\n dataScope = this.elems()[0].dataScope.split('.');\n dataScope.splice(dataScope.length - 1, 1);\n changed.forEach(function (elem) {\n changedElemDataScope = elem.dataScope.split('.');\n changedElemDataScope.splice(0, dataScope.length);\n changedElemDataScope[0] =\n (parseInt(changedElemDataScope[0], 10) - this.pageSize * (this.currentPage() - 1)).toString();\n this.setValueByPath(\n this.defaultPagesState[this.currentPage()],\n changedElemDataScope, elem.initialValue\n );\n }, this);\n }\n\n if (this.defaultPagesState[this.currentPage()]) {\n this.setChangedForCurrentPage();\n }\n },\n\n /**\n * Set default dynamic-rows state or state before changing data\n *\n * @param {Array} data - defaultState data\n */\n setDefaultState: function (data) {\n var componentData,\n childItems;\n\n if (!this.hasInitialPagesState[this.currentPage()]) {\n childItems = this.getChildItems();\n componentData = childItems.length ?\n utils.copy(childItems) :\n utils.copy(this.getChildItems(this.recordDataCache));\n componentData.forEach(function (dataObj) {\n if (dataObj.hasOwnProperty('initialize')) {\n delete dataObj.initialize;\n }\n });\n\n this.hasInitialPagesState[this.currentPage()] = true;\n this.defaultPagesState[this.currentPage()] = data ? data : this.arrayFilter(componentData);\n }\n },\n\n /**\n * Sets value to object by string path\n *\n * @param {Object} obj\n * @param {Array|String} path\n * @param {*} value\n */\n setValueByPath: function (obj, path, value) {\n var prop;\n\n if (_.isString(path)) {\n path = path.split('.');\n }\n\n if (path.length - 1) {\n prop = obj[path[0]];\n path.splice(0, 1);\n this.setValueByPath(prop, path, value);\n } else if (path.length && obj) {\n obj[path[0]] = value;\n }\n },\n\n /**\n * Returns elements which changed self state\n *\n * @param {Array} array - data array\n * @param {Array} changed - array with changed elements\n * @returns {Array} changed - array with changed elements\n */\n getChangedElems: function (array, changed) {\n changed = changed || [];\n\n array.forEach(function (elem) {\n if (_.isFunction(elem.elems)) {\n this.getChangedElems(elem.elems(), changed);\n } else if (_.isFunction(elem.hasChanged) && elem.hasChanged()) {\n changed.push(elem);\n }\n }, this);\n\n return changed;\n },\n\n /**\n * Checks columnsHeaderAfterRender property,\n * and set listener on elems if needed\n *\n * @returns {Object} Chainable.\n */\n setColumnsHeaderListener: function () {\n if (this.columnsHeaderAfterRender) {\n this.on('recordData', this.renderColumnsHeader);\n\n if (_.isArray(this.recordData()) && this.recordData().length) {\n this.renderColumnsHeader();\n }\n }\n\n return this;\n },\n\n /**\n * Checks whether component's state is default or not\n */\n checkDefaultState: function () {\n var isRecordDataArray = _.isArray(this.recordData()),\n initialize,\n hasNotDefaultRecords = isRecordDataArray ? !!this.recordData().filter(function (data) {\n return !data.initialize;\n }).length : false;\n\n if (!this.hasInitialPagesState[this.currentPage()] && isRecordDataArray && hasNotDefaultRecords) {\n this.hasInitialPagesState[this.currentPage()] = true;\n this.defaultPagesState[this.currentPage()] = utils.copy(this.getChildItems().filter(function (data) {\n initialize = data.initialize;\n delete data.initialize;\n\n return initialize;\n }));\n\n this.setChangedForCurrentPage();\n } else if (this.hasInitialPagesState[this.currentPage()]) {\n this.setChangedForCurrentPage();\n }\n },\n\n /**\n * Filters out deleted items from array\n *\n * @param {Array} data\n *\n * @returns {Array} filtered array\n */\n arrayFilter: function (data) {\n var prop;\n\n /*eslint-disable no-loop-func*/\n data.forEach(function (elem) {\n for (prop in elem) {\n if (_.isArray(elem[prop])) {\n elem[prop] = _.filter(elem[prop], function (elemProp) {\n return elemProp[this.deleteProperty] !== this.deleteValue;\n }, this);\n\n elem[prop].forEach(function (elemProp) {\n if (_.isArray(elemProp)) {\n elem[prop] = this.arrayFilter(elemProp);\n }\n }, this);\n }\n }\n }, this);\n\n /*eslint-enable no-loop-func*/\n\n return data;\n },\n\n /**\n * Triggers update event\n *\n * @param {Boolean} val\n */\n updateTrigger: function (val) {\n this.trigger('update', val);\n },\n\n /**\n * Returns component state\n */\n hasChanged: function () {\n return this.changed();\n },\n\n /**\n * Render column header\n */\n renderColumnsHeader: function () {\n this.recordData().length ? this.columnsHeader(true) : this.columnsHeader(false);\n },\n\n /**\n * Init default record\n *\n * @returns Chainable.\n */\n initDefaultRecord: function () {\n if (this.defaultRecord && !this.recordData().length) {\n this.addChild();\n }\n\n return this;\n },\n\n /**\n * Create header template\n *\n * @param {Object} prop - instance obj\n *\n * @returns {Object} Chainable.\n */\n createHeaderTemplate: function (prop) {\n var visible = prop.visible !== false,\n disabled = _.isUndefined(prop.disabled) ? this.disabled() : prop.disabled;\n\n return {\n visible: ko.observable(visible),\n disabled: ko.observable(disabled)\n };\n },\n\n /**\n * Init header elements\n */\n initHeader: function () {\n var labels = [],\n data;\n\n if (!this.labels().length) {\n _.each(this.childTemplate.children, function (cell) {\n data = this.createHeaderTemplate(cell.config);\n cell.config.labelVisible = false;\n _.extend(data, {\n label: cell.config.label,\n name: cell.name,\n required: !!cell.config.validation,\n columnsHeaderClasses: cell.config.columnsHeaderClasses,\n sortOrder: cell.config.sortOrder\n });\n labels.push(data);\n }, this);\n this.labels(_.sortBy(labels, 'sortOrder'));\n }\n },\n\n /**\n * Set max element position\n *\n * @param {Number} position - element position\n * @param {Object} elem - instance\n */\n setMaxPosition: function (position, elem) {\n if (position || position === 0) {\n this.checkMaxPosition(position);\n this.sort(position, elem);\n } else {\n this.maxPosition += 1;\n }\n },\n\n /**\n * Sort element by position\n *\n * @param {Number} position - element position\n * @param {Object} elem - instance\n */\n sort: function (position, elem) {\n var that = this,\n sorted,\n updatedCollection;\n\n if (this.elems().filter(function (el) {\n return el.position || el.position === 0;\n }).length !== this.getChildItems().length) {\n\n return false;\n }\n\n if (!elem.containers.length) {\n registry.get(elem.name, function () {\n that.sort(position, elem);\n });\n\n return false;\n }\n\n sorted = this.elems().sort(function (propOne, propTwo) {\n return ~~propOne.position - ~~propTwo.position;\n });\n\n updatedCollection = this.updatePosition(sorted, position, elem.name);\n this.elems(updatedCollection);\n },\n\n /**\n * Checking loader visibility\n *\n * @param {Array} elems\n */\n checkSpinner: function (elems) {\n this.showSpinner(!(!this.recordData().length || elems && elems.length === this.getChildItems().length));\n },\n\n /**\n * Filtering data and calculates the quantity of pages\n *\n * @param {Array} data\n */\n parsePagesData: function (data) {\n var pages;\n\n this.relatedData = this.deleteProperty ?\n _.filter(data, function (elem) {\n return elem && elem[this.deleteProperty] !== this.deleteValue;\n }, this) : data;\n\n pages = Math.ceil(this.relatedData.length / this.pageSize) || 1;\n this.pages(pages);\n },\n\n /**\n * Reinit record data in order to remove deleted values\n *\n * @return void\n */\n reinitRecordData: function () {\n this.recordData(\n _.filter(this.recordData(), function (elem) {\n return elem && elem[this.deleteProperty] !== this.deleteValue;\n }, this)\n );\n },\n\n /**\n * Get items to rendering on current page\n *\n * @returns {Array} data\n */\n getChildItems: function (data, page) {\n var dataRecord = data || this.relatedData,\n startIndex;\n\n this.startIndex = (~~this.currentPage() - 1) * this.pageSize;\n\n startIndex = page || this.startIndex;\n\n return dataRecord.slice(startIndex, this.startIndex + this.pageSize);\n },\n\n /**\n * Get record count with filtered delete property.\n *\n * @returns {Number} count\n */\n getRecordCount: function () {\n return _.filter(this.recordData(), function (record) {\n return record && record[this.deleteProperty] !== this.deleteValue;\n }, this).length;\n },\n\n /**\n * Get number of columns\n *\n * @returns {Number} columns\n */\n getColumnsCount: function () {\n return this.labels().length + (this.dndConfig.enabled ? 1 : 0);\n },\n\n /**\n * Processing pages before addChild\n *\n * @param {Object} ctx - element context\n * @param {Number|String} index - element index\n * @param {Number|String} prop - additional property to element\n */\n processingAddChild: function (ctx, index, prop) {\n this.bubble('addChild', false);\n\n if (this.relatedData.length && this.relatedData.length % this.pageSize === 0) {\n this.pages(this.pages() + 1);\n this.nextPage();\n } else if (~~this.currentPage() !== this.pages()) {\n this.currentPage(this.pages());\n }\n\n this.addChild(ctx, index, prop);\n },\n\n /**\n * Processing pages before deleteRecord\n *\n * @param {Number|String} index - element index\n * @param {Number|String} recordId\n */\n processingDeleteRecord: function (index, recordId) {\n this.deleteRecord(index, recordId);\n },\n\n /**\n * Change page\n *\n * @param {Number} page - current page\n */\n changePage: function (page) {\n this.clear();\n\n if (page === 1 && !this.recordData().length) {\n return false;\n }\n\n if (~~page > this.pages()) {\n this.currentPage(this.pages());\n\n return false;\n } else if (~~page < 1) {\n this.currentPage(1);\n\n return false;\n }\n\n this.initChildren();\n\n return true;\n },\n\n /**\n * Check page\n *\n * @returns {Boolean} is page first or not\n */\n isFirst: function () {\n return this.currentPage() === 1;\n },\n\n /**\n * Check page\n *\n * @returns {Boolean} is page last or not\n */\n isLast: function () {\n return this.currentPage() === this.pages();\n },\n\n /**\n * Change page to next\n */\n nextPage: function () {\n this.currentPage(this.currentPage() + 1);\n },\n\n /**\n * Change page to previous\n */\n previousPage: function () {\n this.currentPage(this.currentPage() - 1);\n },\n\n /**\n * Check dependency and set position to elements\n *\n * @param {Array} collection - elems\n * @param {Number} position - current position\n * @param {String} elemName - element name\n *\n * @returns {Array} collection\n */\n updatePosition: function (collection, position, elemName) {\n var curPos,\n parsePosition = ~~position,\n result = _.filter(collection, function (record) {\n return ~~record.position === parsePosition;\n });\n\n if (result[1]) {\n curPos = parsePosition + 1;\n result[0].name === elemName ? result[1].position = curPos : result[0].position = curPos;\n this.updatePosition(collection, curPos);\n }\n\n return collection;\n },\n\n /**\n * Check max elements position and set if max\n *\n * @param {Number} position - current position\n */\n checkMaxPosition: function (position) {\n var max = 0,\n pos;\n\n this.elems.each(function (record) {\n pos = ~~record.position;\n pos > max ? max = pos : false;\n });\n\n max < position ? max = position : false;\n this.maxPosition = max;\n },\n\n /**\n * Remove and set new max position\n */\n removeMaxPosition: function () {\n this.maxPosition = 0;\n this.elems.each(function (record) {\n this.maxPosition < record.position ? this.maxPosition = ~~record.position : false;\n }, this);\n },\n\n /**\n * Update record template and rerender elems\n *\n * @param {String} recordName - record name\n */\n onUpdateRecordTemplate: function (recordName) {\n if (recordName) {\n this.recordTemplate = recordName;\n this.reload();\n }\n },\n\n /**\n * Delete record\n *\n * @param {Number} index - row index\n *\n */\n deleteRecord: function (index, recordId) {\n var recordInstance,\n lastRecord,\n recordsData,\n lastRecordIndex;\n\n if (this.deleteProperty) {\n recordsData = this.recordData();\n recordInstance = _.find(this.elems(), function (elem) {\n return elem.index === index;\n });\n recordInstance.destroy();\n this.elems([]);\n this._updateCollection();\n this.removeMaxPosition();\n recordsData[recordInstance.index][this.deleteProperty] = this.deleteValue;\n this.recordData(recordsData);\n this.reinitRecordData();\n this.reload();\n } else {\n this.update = true;\n\n if (~~this.currentPage() === this.pages()) {\n lastRecordIndex = this.startIndex + this.getChildItems().length - 1;\n lastRecord =\n _.findWhere(this.elems(), {\n index: lastRecordIndex\n }) ||\n _.findWhere(this.elems(), {\n index: lastRecordIndex.toString()\n });\n\n lastRecord.destroy();\n }\n\n this.removeMaxPosition();\n recordsData = this._getDataByProp(recordId);\n this._updateData(recordsData);\n this.update = false;\n }\n\n this._reducePages();\n this._sort();\n },\n\n /**\n * Reduce the number of pages\n *\n * @private\n * @return void\n */\n _reducePages: function () {\n if (this.pages() < ~~this.currentPage()) {\n this.currentPage(this.pages());\n }\n },\n\n /**\n * Get data object by some property\n *\n * @param {Number} id - element id\n * @param {String} prop - property\n */\n _getDataByProp: function (id, prop) {\n prop = prop || this.identificationProperty;\n\n return _.reject(this.getChildItems(), function (recordData) {\n return recordData[prop].toString() === id.toString();\n }, this);\n },\n\n /**\n * Sort elems by position property\n */\n _sort: function () {\n this.elems(this.elems().sort(function (propOne, propTwo) {\n return ~~propOne.position - ~~propTwo.position;\n }));\n },\n\n /**\n * Set new data to dataSource,\n * delete element\n *\n * @param {Array} data - record data\n */\n _updateData: function (data) {\n var elems = _.clone(this.elems()),\n path,\n dataArr;\n\n dataArr = this.recordData.splice(this.startIndex, this.recordData().length - this.startIndex);\n dataArr.splice(0, this.pageSize);\n elems = _.sortBy(this.elems(), function (elem) {\n return ~~elem.index;\n });\n\n data.concat(dataArr).forEach(function (rec, idx) {\n if (elems[idx]) {\n elems[idx].recordId = rec[this.identificationProperty];\n }\n\n if (!rec.position) {\n rec.position = this.maxPosition;\n this.setMaxPosition();\n }\n\n path = this.dataScope + '.' + this.index + '.' + (this.startIndex + idx);\n this.source.set(path, rec);\n }, this);\n\n this.elems(elems);\n },\n\n /**\n * Rerender dynamic-rows elems\n */\n reload: function () {\n this.clear();\n this.initChildren(false, true);\n },\n\n /**\n * Destroy all dynamic-rows elems\n *\n * @returns {Object} Chainable.\n */\n clear: function () {\n this.destroyChildren();\n\n return this;\n },\n\n /**\n * Reset data to initial value.\n * Call method reset on child elements.\n */\n reset: function () {\n var elems = this.elems();\n\n _.each(elems, function (elem) {\n if (_.isFunction(elem.reset)) {\n elem.reset();\n }\n });\n },\n\n /**\n * Set classes\n *\n * @param {Object} data\n *\n * @returns {Object} Classes\n */\n setClasses: function (data) {\n var additional;\n\n if (_.isString(data.additionalClasses)) {\n additional = data.additionalClasses.split(' ');\n data.additionalClasses = {};\n\n additional.forEach(function (name) {\n data.additionalClasses[name] = true;\n });\n }\n\n if (!data.additionalClasses) {\n data.additionalClasses = {};\n }\n\n _.extend(data.additionalClasses, {\n '_fit': data.fit,\n '_required': data.required,\n '_error': data.error,\n '_empty': !this.elems().length,\n '_no-header': this.columnsHeaderAfterRender || this.collapsibleHeader\n });\n\n return data.additionalClasses;\n },\n\n /**\n * Initialize children\n *\n * @returns {Object} Chainable.\n */\n initChildren: function () {\n this.showSpinner(true);\n this.getChildItems().forEach(function (data, index) {\n this.addChild(data, this.startIndex + index);\n }, this);\n\n return this;\n },\n\n /**\n * Set visibility to dynamic-rows child\n *\n * @param {Boolean} state\n */\n setVisible: function (state) {\n this.elems.each(function (record) {\n record.setVisible(state);\n }, this);\n },\n\n /**\n * Set disabled property to dynamic-rows child\n *\n * @param {Boolean} state\n */\n setDisabled: function (state) {\n this.elems.each(function (record) {\n record.setDisabled(state);\n }, this);\n },\n\n /**\n * Set visibility to column\n *\n * @param {Number} index - column index\n * @param {Boolean} state\n */\n setVisibilityColumn: function (index, state) {\n this.elems.each(function (record) {\n record.setVisibilityColumn(index, state);\n }, this);\n },\n\n /**\n * Set disabled property to column\n *\n * @param {Number} index - column index\n * @param {Boolean} state\n */\n setDisabledColumn: function (index, state) {\n this.elems.each(function (record) {\n record.setDisabledColumn(index, state);\n }, this);\n },\n\n /**\n * Add child components\n *\n * @param {Object} data - component data\n * @param {Number} index - record(row) index\n * @param {Number|String} prop - custom identify property\n *\n * @returns {Object} Chainable.\n */\n addChild: function (data, index, prop) {\n var template = this.templates.record,\n child;\n\n index = index || _.isNumber(index) ? index : this.recordData().length;\n prop = prop || _.isNumber(prop) ? prop : index;\n\n _.extend(this.templates.record, {\n recordId: prop\n });\n\n child = utils.template(template, {\n collection: this,\n index: index\n });\n\n layout([child]);\n\n return this;\n },\n\n /**\n * Restore value to default\n */\n restoreToDefault: function () {\n this.recordData(utils.copy(this.default));\n this.reload();\n },\n\n /**\n * Update whether value differs from default value\n */\n setDifferedFromDefault: function () {\n var recordData = utils.copy(this.recordData());\n\n Array.isArray(recordData) && recordData.forEach(function (item) {\n delete item['record_id'];\n });\n\n this.isDifferedFromDefault(!_.isEqual(recordData, this.default));\n },\n\n /**\n * Set the changed property if the current page is different\n * than the default state\n *\n * @return void\n */\n setChangedForCurrentPage: function () {\n this.pagesChanged[this.currentPage()] =\n !compareArrays(this.defaultPagesState[this.currentPage()], this.arrayFilter(this.getChildItems()));\n this.changed(_.some(this.pagesChanged));\n }\n });\n});\n","Magento_Ui/js/timeline/timeline.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'moment',\n 'uiLayout',\n 'Magento_Ui/js/grid/listing'\n], function (_, moment, layout, Listing) {\n 'use strict';\n\n var ONE_DAY = 86400000;\n\n return Listing.extend({\n defaults: {\n recordTmpl: 'ui/timeline/record',\n dateFormat: 'YYYY-MM-DD HH:mm:ss',\n headerFormat: 'ddd MM/DD',\n detailsFormat: 'DD/MM/YYYY HH:mm:ss',\n scale: 7,\n scaleStep: 1,\n minScale: 7,\n maxScale: 28,\n minDays: 28,\n displayMode: 'timeline',\n displayModes: {\n timeline: {\n label: 'Timeline',\n value: 'timeline',\n template: 'ui/timeline/timeline'\n }\n },\n viewConfig: {\n component: 'Magento_Ui/js/timeline/timeline-view',\n name: '${ $.name }_view',\n model: '${ $.name }'\n },\n tracks: {\n scale: true\n },\n statefull: {\n scale: true\n },\n range: {}\n },\n\n /**\n * Initializes Timeline component.\n *\n * @returns {Timeline} Chainable.\n */\n initialize: function () {\n this._super()\n .initView()\n .updateRange();\n\n return this;\n },\n\n /**\n * Initializes components configuration.\n *\n * @returns {Timeline} Chainable.\n */\n initConfig: function () {\n this._super();\n\n this.maxScale = Math.min(this.minDays, this.maxScale);\n this.minScale = Math.min(this.maxScale, this.minScale);\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Timeline} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe.call(this.range, true, 'hasToday');\n\n return this;\n },\n\n /**\n * Initializes TimelineView component.\n *\n * @returns {Timeline} Chainable.\n */\n initView: function () {\n layout([this.viewConfig]);\n\n return this;\n },\n\n /**\n * Checks if provided event record is active,\n * i.e. it has already started.\n *\n * @param {Object} record\n * @returns {Boolean}\n */\n isActive: function (record) {\n return Number(record.status) === 1;\n },\n\n /**\n * Checks if provided event record is upcoming,\n * i.e. it will start later on.\n *\n * @param {Object} record\n * @returns {Boolean}\n */\n isUpcoming: function (record) {\n return Number(record.status) === 2;\n },\n\n /**\n * Checks if provided event record is permanent,\n * i.e. it has no ending time.\n *\n * @param {Object} record\n * @returns {Boolean}\n */\n isPermanent: function (record) {\n return !this.getEndDate(record);\n },\n\n /**\n * Checks if provided date indicates current day.\n *\n * @param {(Number|Moment)} date\n * @returns {Boolenan}\n */\n isToday: function (date) {\n return moment().isSame(date, 'day');\n },\n\n /**\n * Checks if range object contains todays date.\n *\n * @returns {Boolean}\n */\n hasToday: function () {\n return this.range.hasToday;\n },\n\n /**\n * Returns start date of provided record.\n *\n * @param {Object} record\n * @returns {String}\n */\n getStartDate: function (record) {\n return record['start_time'];\n },\n\n /**\n * Returns end date of provided record.\n *\n * @param {Object} record\n * @returns {String}\n */\n getEndDate: function (record) {\n return record['end_time'];\n },\n\n /**\n * Returns difference in days between records' start date\n * and a first day of a range.\n *\n * @param {Object} record\n * @returns {Number}\n */\n getStartDelta: function (record) {\n var start = this.createDate(this.getStartDate(record)),\n firstDay = this.range.firstDay;\n\n return start.diff(firstDay, 'days', true);\n },\n\n /**\n * Calculates the amount of days that provided event lasts.\n *\n * @param {Object} record\n * @returns {Number}\n */\n getDaysLength: function (record) {\n var start = this.createDate(this.getStartDate(record)),\n end = this.createDate(this.getEndDate(record));\n\n if (!end.isValid()) {\n end = this.range.lastDay.endOf('day');\n }\n\n return end.diff(start, 'days', true);\n },\n\n /**\n * Creates new date object based on provided date string value.\n *\n * @param {String} dateStr\n * @returns {Moment}\n */\n createDate: function (dateStr) {\n return moment(dateStr, this.dateFormat);\n },\n\n /**\n * Converts days to weeks.\n *\n * @param {Number} days\n * @returns {Number}\n */\n daysToWeeks: function (days) {\n var weeks = days / 7;\n\n if (weeks % 1) {\n weeks = weeks.toFixed(1);\n }\n\n return weeks;\n },\n\n /**\n * Updates data of a range object,\n * e.g. total days, first day and last day, etc.\n *\n * @returns {Object} Range instance.\n */\n updateRange: function () {\n var firstDay = this._getFirstDay(),\n lastDay = this._getLastDay(),\n totalDays = lastDay.diff(firstDay, 'days'),\n days = [],\n i = -1;\n\n if (totalDays < this.minDays) {\n totalDays += this.minDays - totalDays - 1;\n }\n\n while (++i <= totalDays) {\n days.push(+firstDay + ONE_DAY * i);\n }\n\n return _.extend(this.range, {\n days: days,\n totalDays: totalDays,\n firstDay: firstDay,\n lastDay: moment(_.last(days)),\n hasToday: this.isToday(firstDay)\n });\n },\n\n /**\n *\n * @private\n * @param {String} key\n * @returns {Array<Moment>}\n */\n _getDates: function (key) {\n var dates = [];\n\n this.rows.forEach(function (record) {\n if (record[key]) {\n dates.push(this.createDate(record[key]));\n }\n }, this);\n\n return dates;\n },\n\n /**\n * Returns date which is closest to the current day.\n *\n * @private\n * @returns {Moment}\n */\n _getFirstDay: function () {\n var dates = this._getDates('start_time'),\n first = moment.min(dates).subtract(1, 'day'),\n today = moment();\n\n if (!first.isValid() || first < today) {\n first = today;\n }\n\n return first.startOf('day');\n },\n\n /**\n * Returns the most distant date\n * specified in available records.\n *\n * @private\n * @returns {Moment}\n */\n _getLastDay: function () {\n var startDates = this._getDates('start_time'),\n endDates = this._getDates('end_time'),\n last = moment.max(startDates.concat(endDates));\n\n return last.add(1, 'day').startOf('day');\n },\n\n /**\n * TODO: remove after integration with date binding.\n *\n * @param {Number} timestamp\n * @returns {String}\n */\n formatHeader: function (timestamp) {\n return moment(timestamp).format(this.headerFormat);\n },\n\n /**\n * TODO: remove after integration with date binding.\n *\n * @param {String} date\n * @returns {String}\n */\n formatDetails: function (date) {\n return moment(date).format(this.detailsFormat);\n }\n });\n});\n","Magento_Ui/js/timeline/timeline-view.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'Magento_Ui/js/lib/view/utils/async',\n 'underscore',\n 'Magento_Ui/js/lib/view/utils/raf',\n 'uiRegistry',\n 'uiClass'\n], function (ko, $, _, raf, registry, Class) {\n 'use strict';\n\n var hasClassList = (function () {\n var list = document.createElement('_').classList;\n\n return !!list && !list.toggle('_test', false);\n })();\n\n /**\n * Polyfill of the 'classList.toggle' method.\n *\n * @param {HTMLElement} elem\n */\n function toggleClass(elem) {\n var classList = elem.classList,\n args = Array.prototype.slice.call(arguments, 1),\n $elem;\n\n if (hasClassList) {\n classList.toggle.apply(classList, args);\n } else {\n $elem = $(elem);\n $elem.toggleClass.apply($elem, args);\n }\n }\n\n return Class.extend({\n defaults: {\n selectors: {\n content: '.timeline-content',\n timeUnit: '.timeline-unit',\n item: '.timeline-item:not([data-role=no-data-msg])',\n event: '.timeline-event'\n }\n },\n\n /**\n * Initializes TimelineView component.\n *\n * @returns {TimelineView} Chainable.\n */\n initialize: function () {\n _.bindAll(\n this,\n 'refresh',\n 'initContent',\n 'initItem',\n 'initTimeUnit',\n 'getItemBindings',\n 'updateItemsPosition',\n 'onScaleChange',\n 'onEventElementRender',\n 'onWindowResize',\n 'onContentScroll',\n 'onDataReloaded',\n 'onToStartClick',\n 'onToEndClick'\n );\n\n this._super()\n .initModel()\n .waitContent();\n\n return this;\n },\n\n /**\n * Applies listeners for the model properties changes.\n *\n * @returns {TimelineView} Chainable.\n */\n initModel: function () {\n var model = registry.get(this.model);\n\n model.on('scale', this.onScaleChange);\n model.source.on('reloaded', this.onDataReloaded);\n\n this.model = model;\n\n return this;\n },\n\n /**\n * Applies DOM watcher for the\n * content element rendering.\n *\n * @returns {TimelineView} Chainable.\n */\n waitContent: function () {\n $.async({\n selector: this.selectors.content,\n component: this.model\n }, this.initContent);\n\n return this;\n },\n\n /**\n * Initializes timelines' content element.\n *\n * @param {HTMLElement} content\n * @returns {TimelineView} Chainable.\n */\n initContent: function (content) {\n this.$content = content;\n\n $(content).on('scroll', this.onContentScroll);\n $(window).on('resize', this.onWindowResize);\n\n $.async(this.selectors.item, content, this.initItem);\n $.async(this.selectors.event, content, this.onEventElementRender);\n $.async(this.selectors.timeUnit, content, this.initTimeUnit);\n\n this.refresh();\n\n return this;\n },\n\n /**\n * Initializes timeline item element,\n * e.g. establishes event listeners and applies data bindings.\n *\n * @param {HTMLElement} elem\n * @returns {TimelineView} Chainable.\n */\n initItem: function (elem) {\n $(elem)\n .bindings(this.getItemBindings)\n .on('click', '._toend', this.onToEndClick)\n .on('click', '._tostart', this.onToStartClick);\n\n return this;\n },\n\n /**\n * Initializes timeline unit element.\n *\n * @param {HTMLElement} elem\n * @returns {TimelineView} Chainable.\n */\n initTimeUnit: function (elem) {\n $(elem).bindings(this.getTimeUnitBindings());\n\n return this;\n },\n\n /**\n * Updates items positions in a\n * loop if state of a view has changed.\n */\n refresh: function () {\n raf(this.refresh);\n\n if (this._update) {\n this._update = false;\n\n this.updateItemsPosition();\n }\n },\n\n /**\n * Returns object width additional bindings\n * for a timeline unit element.\n *\n * @returns {Object}\n */\n getTimeUnitBindings: function () {\n return {\n style: {\n width: ko.computed(function () {\n return this.getTimeUnitWidth() + '%';\n }.bind(this))\n }\n };\n },\n\n /**\n * Returns object with additional\n * bindings for a timeline item element.\n *\n * @param {Object} ctx\n * @returns {Object}\n */\n getItemBindings: function (ctx) {\n return {\n style: {\n width: ko.computed(function () {\n return this.getItemWidth(ctx.$row()) + '%';\n }.bind(this)),\n\n 'margin-left': ko.computed(function () {\n return this.getItemMargin(ctx.$row()) + '%';\n }.bind(this))\n }\n };\n },\n\n /**\n * Calculates width in percents of a timeline unit element.\n *\n * @returns {Number}\n */\n getTimeUnitWidth: function () {\n return 100 / this.model.scale;\n },\n\n /**\n * Calculates width of a record in percents.\n *\n * @param {Object} record\n * @returns {String}\n */\n getItemWidth: function (record) {\n var days = 0;\n\n if (record) {\n days = this.model.getDaysLength(record);\n }\n\n return this.getTimeUnitWidth() * days;\n },\n\n /**\n * Calculates left margin value for provided record.\n *\n * @param {Object} record\n * @returns {String}\n */\n getItemMargin: function (record) {\n var offset = 0;\n\n if (record) {\n offset = this.model.getStartDelta(record);\n }\n\n return this.getTimeUnitWidth() * offset;\n },\n\n /**\n * Returns collection of currently available\n * timeline item elements.\n *\n * @returns {Array<HTMLElement>}\n */\n getItems: function () {\n var items = this.$content.querySelectorAll(this.selectors.item);\n\n return _.toArray(items);\n },\n\n /**\n * Updates positions of timeline elements.\n *\n * @returns {TimelineView} Chainable.\n */\n updateItemsPosition: function () {\n this.getItems()\n .forEach(this.updatePositionFor, this);\n\n return this;\n },\n\n /**\n * Updates position of provided timeline element.\n *\n * @param {HTMLElement} $elem\n * @returns {TimelineView} Chainable.\n */\n updatePositionFor: function ($elem) {\n var $event = $elem.querySelector(this.selectors.event),\n leftEdge = this.getLeftEdgeFor($elem),\n rightEdge = this.getRightEdgeFor($elem);\n\n if ($event) {\n $event.style.left = Math.max(-leftEdge, 0) + 'px';\n $event.style.right = Math.max(rightEdge, 0) + 'px';\n }\n\n toggleClass($elem, '_scroll-start', leftEdge < 0);\n toggleClass($elem, '_scroll-end', rightEdge > 0);\n\n return this;\n },\n\n /**\n * Scrolls content area to the start of provided element.\n *\n * @param {HTMLElement} elem\n * @returns {TimelineView}\n */\n toStartOf: function (elem) {\n var leftEdge = this.getLeftEdgeFor(elem);\n\n this.$content.scrollLeft += leftEdge;\n\n return this;\n },\n\n /**\n * Scrolls content area to the end of provided element.\n *\n * @param {HTMLElement} elem\n * @returns {TimelineView}\n */\n toEndOf: function (elem) {\n var rightEdge = this.getRightEdgeFor(elem);\n\n this.$content.scrollLeft += rightEdge + 1;\n\n return this;\n },\n\n /**\n * Calculates location of the left edge of an element\n * relative to the contents' left edge.\n *\n * @param {HTMLElement} elem\n * @returns {Number}\n */\n getLeftEdgeFor: function (elem) {\n var leftOffset = elem.getBoundingClientRect().left;\n\n return leftOffset - this.$content.getBoundingClientRect().left;\n },\n\n /**\n * Calculates location of the right edge of an element\n * relative to the contents' right edge.\n *\n * @param {HTMLElement} elem\n * @returns {Number}\n */\n getRightEdgeFor: function (elem) {\n var elemWidth = elem.offsetWidth,\n leftEdge = this.getLeftEdgeFor(elem);\n\n return leftEdge + elemWidth - this.$content.offsetWidth;\n },\n\n /**\n * 'To Start' button 'click' event handler.\n *\n * @param {jQueryEvent} event\n */\n onToStartClick: function (event) {\n var elem = event.originalEvent.currentTarget;\n\n event.stopPropagation();\n\n this.toStartOf(elem);\n },\n\n /**\n * 'To End' button 'click' event handler.\n *\n * @param {jQueryEvent} event\n */\n onToEndClick: function (event) {\n var elem = event.originalEvent.currentTarget;\n\n event.stopPropagation();\n\n this.toEndOf(elem);\n },\n\n /**\n * Handler of the scale value 'change' event.\n */\n onScaleChange: function () {\n this._update = true;\n },\n\n /**\n * Callback function which is invoked\n * when event element was rendered.\n */\n onEventElementRender: function () {\n this._update = true;\n },\n\n /**\n * Window 'resize' event handler.\n */\n onWindowResize: function () {\n this._update = true;\n },\n\n /**\n * Content container 'scroll' event handler.\n */\n onContentScroll: function () {\n this._update = true;\n },\n\n /**\n * Data 'reload' event handler.\n */\n onDataReloaded: function () {\n this._update = true;\n }\n });\n});\n","Magento_Ui/js/form/client.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'uiClass'\n], function ($, _, utils, Class) {\n 'use strict';\n\n /**\n * Before save validate request.\n *\n * @param {Object} data\n * @param {String} url\n * @param {String} selectorPrefix\n * @param {String} messagesClass\n * @returns {*}\n */\n function beforeSave(data, url, selectorPrefix, messagesClass) {\n var save = $.Deferred();\n\n data = utils.serialize(utils.filterFormData(data));\n data['form_key'] = window.FORM_KEY;\n\n if (!url || url === 'undefined') {\n return save.resolve();\n }\n\n $('body').trigger('processStart');\n\n $.ajax({\n url: url,\n data: data,\n\n /**\n * Success callback.\n * @param {Object} resp\n * @returns {Boolean}\n */\n success: function (resp) {\n if (!resp.error) {\n save.resolve();\n\n return true;\n }\n\n $('body').notification('clear');\n $.each(resp.messages || [resp.message] || [], function (key, message) {\n $('body').notification('add', {\n error: resp.error,\n message: message,\n\n /**\n * Insert method.\n *\n * @param {String} msg\n */\n insertMethod: function (msg) {\n var $wrapper = $('<div/>').addClass(messagesClass).html(msg);\n\n $('.page-main-actions', selectorPrefix).after($wrapper);\n }\n });\n });\n },\n\n /**\n * Complete callback.\n */\n complete: function () {\n $('body').trigger('processStop');\n }\n });\n\n return save.promise();\n }\n\n return Class.extend({\n\n /**\n * Assembles data and submits it using 'utils.submit' method\n */\n save: function (data, options) {\n var url = this.urls.beforeSave,\n save = this._save.bind(this, data, options);\n\n beforeSave(data, url, this.selectorPrefix, this.messagesClass).then(save);\n\n return this;\n },\n\n /**\n * Save data.\n *\n * @param {Object} data\n * @param {Object} options\n * @returns {Object}\n * @private\n */\n _save: function (data, options) {\n var url = this.urls.save;\n\n $('body').trigger('processStart');\n options = options || {};\n\n if (!options.redirect) {\n url += 'back/edit';\n }\n\n if (options.ajaxSave) {\n utils.ajaxSubmit({\n url: url,\n data: data\n }, options);\n\n $('body').trigger('processStop');\n\n return this;\n }\n\n utils.submit({\n url: url,\n data: data\n }, options.attributes);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/adapter.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'Magento_Ui/js/form/adapter/buttons'\n], function ($, _, buttons) {\n 'use strict';\n\n var selectorPrefix = '',\n eventPrefix;\n\n /**\n * Initialize listener.\n *\n * @param {Function} callback\n * @param {String} action\n */\n function initListener(callback, action) {\n var selector = selectorPrefix ? selectorPrefix + ' ' + buttons[action] : buttons[action],\n elem = $(selector)[0];\n\n if (!elem) {\n return;\n }\n\n if (elem.onclick) {\n elem.onclick = null;\n }\n\n $(elem).on('click' + eventPrefix, callback);\n }\n\n /**\n * Destroy listener.\n *\n * @param {String} action\n */\n function destroyListener(action) {\n var selector = selectorPrefix ? selectorPrefix + ' ' + buttons[action] : buttons[action],\n elem = $(selector)[0];\n\n if (!elem) {\n return;\n }\n\n if (elem.onclick) {\n elem.onclick = null;\n }\n\n $(elem).off('click' + eventPrefix);\n }\n\n return {\n\n /**\n * Attaches events handlers.\n *\n * @param {Object} handlers\n * @param {String} selectorPref\n * @param {String} eventPref\n */\n on: function (handlers, selectorPref, eventPref) {\n selectorPrefix = selectorPrefix || selectorPref;\n eventPrefix = eventPref;\n _.each(handlers, initListener);\n selectorPrefix = '';\n },\n\n /**\n * Removes events handlers.\n *\n * @param {Object} handlers\n * @param {String} eventPref\n */\n off: function (handlers, eventPref) {\n eventPrefix = eventPref;\n _.each(handlers, destroyListener);\n }\n };\n});\n","Magento_Ui/js/form/provider.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiElement',\n './client',\n 'mageUtils'\n], function (_, Element, Client, utils) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n clientConfig: {\n urls: {\n save: '${ $.submit_url }',\n beforeSave: '${ $.validate_url }'\n }\n },\n ignoreTmpls: {\n data: true\n }\n },\n\n /**\n * Initializes provider component.\n *\n * @returns {Provider} Chainable.\n */\n initialize: function () {\n this._super()\n .initClient();\n\n return this;\n },\n\n /**\n * Initializes client component.\n *\n * @returns {Provider} Chainable.\n */\n initClient: function () {\n this.client = new Client(this.clientConfig);\n\n return this;\n },\n\n /**\n * Saves currently available data.\n *\n * @param {Object} [options] - Addtitional request options.\n * @returns {Provider} Chainable.\n */\n save: function (options) {\n var data = this.get('data');\n\n this.client.save(data, options);\n\n return this;\n },\n\n /**\n * Update data that stored in provider.\n *\n * @param {Boolean} isProvider\n * @param {Object} newData\n * @param {Object} oldData\n *\n * @returns {Provider}\n */\n updateConfig: function (isProvider, newData, oldData) {\n if (isProvider === true) {\n this.setData(oldData, newData, this);\n }\n\n return this;\n },\n\n /**\n * Set data to provider based on current data.\n *\n * @param {Object} oldData\n * @param {Object} newData\n * @param {Provider} current\n * @param {String} parentPath\n */\n setData: function (oldData, newData, current, parentPath) {\n _.each(newData, function (val, key) {\n if (_.isObject(val) || _.isArray(val)) {\n this.setData(oldData[key], val, current[key], utils.fullPath(parentPath, key));\n } else if (val != oldData[key] && oldData[key] == current[key]) {//eslint-disable-line eqeqeq\n this.set(utils.fullPath(parentPath, key), val);\n }\n }, this);\n }\n });\n});\n","Magento_Ui/js/form/form.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'Magento_Ui/js/lib/spinner',\n 'rjsResolver',\n './adapter',\n 'uiCollection',\n 'mageUtils',\n 'jquery',\n 'Magento_Ui/js/core/app',\n 'mage/validation'\n], function (_, loader, resolver, adapter, Collection, utils, $, app) {\n 'use strict';\n\n /**\n * Format params\n *\n * @param {Object} params\n * @returns {Array}\n */\n function prepareParams(params) {\n var result = '?';\n\n _.each(params, function (value, key) {\n result += key + '=' + value + '&';\n });\n\n return result.slice(0, -1);\n }\n\n /**\n * Collect form data.\n *\n * @param {Array} items\n * @returns {Object}\n */\n function collectData(items) {\n var result = {},\n name;\n\n items = Array.prototype.slice.call(items);\n\n items.forEach(function (item) {\n switch (item.type) {\n case 'checkbox':\n result[item.name] = +!!item.checked;\n break;\n\n case 'radio':\n if (item.checked) {\n result[item.name] = item.value;\n }\n break;\n\n case 'select-multiple':\n name = item.name.substring(0, item.name.length - 2); //remove [] from the name ending\n result[name] = _.pluck(item.selectedOptions, 'value');\n break;\n\n default:\n result[item.name] = item.value;\n }\n });\n\n return result;\n }\n\n /**\n * Makes ajax request\n *\n * @param {Object} params\n * @param {Object} data\n * @param {String} url\n * @returns {*}\n */\n function makeRequest(params, data, url) {\n var save = $.Deferred();\n\n data = utils.serialize(data);\n data['form_key'] = window.FORM_KEY;\n\n if (!url) {\n save.resolve();\n }\n\n $('body').trigger('processStart');\n\n $.ajax({\n url: url + prepareParams(params),\n data: data,\n dataType: 'json',\n\n /**\n * Success callback.\n * @param {Object} resp\n * @returns {Boolean}\n */\n success: function (resp) {\n if (resp.ajaxExpired) {\n window.location.href = resp.ajaxRedirect;\n }\n\n if (!resp.error) {\n save.resolve(resp);\n\n return true;\n }\n\n $('body').notification('clear');\n $.each(resp.messages, function (key, message) {\n $('body').notification('add', {\n error: resp.error,\n message: message,\n\n /**\n * Inserts message on page\n * @param {String} msg\n */\n insertMethod: function (msg) {\n $('.page-main-actions').after(msg);\n }\n });\n });\n },\n\n /**\n * Complete callback.\n */\n complete: function () {\n $('body').trigger('processStop');\n }\n });\n\n return save.promise();\n }\n\n /**\n * Check if fields is valid.\n *\n * @param {Array}items\n * @returns {Boolean}\n */\n function isValidFields(items) {\n var result = true;\n\n _.each(items, function (item) {\n if (!$.validator.validateSingleElement(item)) {\n result = false;\n }\n });\n\n return result;\n }\n\n return Collection.extend({\n defaults: {\n additionalFields: [],\n additionalInvalid: false,\n selectorPrefix: '.page-content',\n messagesClass: 'messages',\n errorClass: '.admin__field._error',\n eventPrefix: '.${ $.index }',\n ajaxSave: false,\n ajaxSaveType: 'default',\n imports: {\n reloadUrl: '${ $.provider}:reloadUrl'\n },\n listens: {\n selectorPrefix: 'destroyAdapter initAdapter',\n '${ $.name }.${ $.reloadItem }': 'params.set reload'\n },\n exports: {\n selectorPrefix: '${ $.provider }:client.selectorPrefix',\n messagesClass: '${ $.provider }:client.messagesClass'\n }\n },\n\n /** @inheritdoc */\n initialize: function () {\n this._super()\n .initAdapter();\n\n resolver(this.hideLoader, this);\n\n return this;\n },\n\n /** @inheritdoc */\n initObservable: function () {\n return this._super()\n .observe([\n 'responseData',\n 'responseStatus'\n ]);\n },\n\n /** @inheritdoc */\n initConfig: function () {\n this._super();\n\n this.selector = '[data-form-part=' + this.namespace + ']';\n\n return this;\n },\n\n /**\n * Initialize adapter handlers.\n *\n * @returns {Object}\n */\n initAdapter: function () {\n adapter.on({\n 'reset': this.reset.bind(this),\n 'save': this.save.bind(this, true, {}),\n 'saveAndContinue': this.save.bind(this, false, {})\n }, this.selectorPrefix, this.eventPrefix);\n\n return this;\n },\n\n /**\n * Destroy adapter handlers.\n *\n * @returns {Object}\n */\n destroyAdapter: function () {\n adapter.off([\n 'reset',\n 'save',\n 'saveAndContinue'\n ], this.eventPrefix);\n\n return this;\n },\n\n /**\n * Hide loader.\n *\n * @returns {Object}\n */\n hideLoader: function () {\n loader.get(this.name).hide();\n\n return this;\n },\n\n /**\n * Validate and save form.\n *\n * @param {String} redirect\n * @param {Object} data\n */\n save: function (redirect, data) {\n this.validate();\n\n if (!this.additionalInvalid && !this.source.get('params.invalid')) {\n this.setAdditionalData(data)\n .submit(redirect);\n } else {\n this.focusInvalid();\n }\n },\n\n /**\n * Tries to set focus on first invalid form field.\n *\n * @returns {Object}\n */\n focusInvalid: function () {\n var invalidField = _.find(this.delegate('checkInvalid'));\n\n if (!_.isUndefined(invalidField) && _.isFunction(invalidField.focused)) {\n invalidField.focused(true);\n }\n\n return this;\n },\n\n /**\n * Set additional data to source before form submit and after validation.\n *\n * @param {Object} data\n * @returns {Object}\n */\n setAdditionalData: function (data) {\n _.each(data, function (value, name) {\n this.source.set('data.' + name, value);\n }, this);\n\n return this;\n },\n\n /**\n * Submits form\n *\n * @param {String} redirect\n */\n submit: function (redirect) {\n var additional = collectData(this.additionalFields),\n source = this.source;\n\n _.each(additional, function (value, name) {\n source.set('data.' + name, value);\n });\n\n source.save({\n redirect: redirect,\n ajaxSave: this.ajaxSave,\n ajaxSaveType: this.ajaxSaveType,\n response: {\n data: this.responseData,\n status: this.responseStatus\n },\n attributes: {\n id: this.namespace\n }\n });\n },\n\n /**\n * Validates each element and returns true, if all elements are valid.\n */\n validate: function () {\n this.additionalFields = document.querySelectorAll(this.selector);\n this.source.set('params.invalid', false);\n this.source.trigger('data.validate');\n this.set('additionalInvalid', !isValidFields(this.additionalFields));\n },\n\n /**\n * Trigger reset form data.\n */\n reset: function () {\n this.source.trigger('data.reset');\n },\n\n /**\n * Trigger overload form data.\n */\n overload: function () {\n this.source.trigger('data.overload');\n },\n\n /**\n * Updates data from server.\n */\n reload: function () {\n makeRequest(this.params, this.data, this.reloadUrl).then(function (data) {\n app(data, true);\n });\n }\n });\n});\n","Magento_Ui/js/form/switcher.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n 'uiClass'\n], function (_, registry, Class) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n rules: []\n },\n\n /**\n * Initializes instance of a DataSwitcher.\n *\n * @returns {DataSwitcher} Chainable.\n */\n initialize: function () {\n this._super()\n .initRules();\n\n return this;\n },\n\n /**\n *\n * @returns {DataSwitcher} Chainable.\n */\n initRules: function () {\n this.rules.forEach(this.initRule, this);\n\n return this;\n },\n\n /**\n *\n * @param {Object} rule - Rule definition.\n * @returns {DataSwitcher} Chainable.\n */\n initRule: function (rule) {\n var handler = this.onValueChange.bind(this, rule);\n\n if (!rule.target) {\n rule.target = this.target;\n }\n\n if (!rule.property) {\n rule.property = this.property;\n }\n\n registry.get(rule.target, function (target) {\n this.applyRule(rule, target.get(rule.property));\n target.on(rule.property, handler);\n }.bind(this));\n\n return this;\n },\n\n /**\n *\n * @param {Object} rule - Rule definition.\n * @returns {DataSwitcher} Chainable.\n */\n addRule: function (rule) {\n this.rules.push(rule);\n this.initRule(rule);\n\n return this;\n },\n\n /**\n *\n * @param {Object} rule - Rule object.\n * @param {*} value - Current value associated with a rule.\n */\n applyRule: function (rule, value) {\n var actions = rule.actions;\n\n //TODO Refactor this logic in scope of MAGETWO-48585\n /* eslint-disable eqeqeq */\n if (rule.value != value) {\n return;\n } else if (rule.strict) {\n return;\n }\n\n /* eslint-enable eqeqeq */\n actions.forEach(this.applyAction, this);\n },\n\n /**\n *\n * @param {Object} action - Action object.\n */\n applyAction: function (action) {\n registry.get(action.target, function (target) {\n var callback = target[action.callback];\n\n callback.apply(target, action.params || []);\n });\n },\n\n /**\n *\n * @param {Object} rule - Rules object.\n * @param {*} value - Current value associated with a rule.\n */\n onValueChange: function (rule, value) {\n this.applyRule(rule, value);\n }\n });\n});\n","Magento_Ui/js/form/button-adapter.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'uiClass',\n 'jquery',\n 'underscore',\n 'uiRegistry'\n], function (Class, $, _, registry) {\n 'use strict';\n\n return Class.extend({\n\n /**\n * Initialize actions and adapter.\n *\n * @param {Object} config\n * @param {Element} elem\n * @returns {Object}\n */\n initialize: function (config, elem) {\n return this._super()\n .initActions()\n .initAdapter(elem);\n },\n\n /**\n * Creates callback from declared actions.\n *\n * @returns {Object}\n */\n initActions: function () {\n var callbacks = [];\n\n _.each(this.actions, function (action) {\n callbacks.push({\n action: registry.async(action.targetName),\n args: _.union([action.actionName], action.params)\n });\n });\n\n /**\n * Callback function.\n */\n this.callback = function () {\n _.each(callbacks, function (callback) {\n callback.action.apply(callback.action, callback.args);\n });\n };\n\n return this;\n },\n\n /**\n * Attach callback handler on button.\n *\n * @param {Element} elem\n */\n initAdapter: function (elem) {\n $(elem).on('click', this.callback);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/components/insert-form.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n './insert',\n 'mageUtils',\n 'jquery'\n], function (Insert, utils, $) {\n 'use strict';\n\n /**\n * Get page actions element.\n *\n * @param {String} elem\n * @param {String} actionsClass\n * @returns {String}\n */\n function getPageActions(elem, actionsClass) {\n var el = document.createElement('div');\n\n el.innerHTML = elem;\n\n return el.getElementsByClassName(actionsClass)[0];\n }\n\n /**\n * Return element without page actions toolbar\n *\n * @param {String} elem\n * @param {String} actionsClass\n * @returns {String}\n */\n function removePageActions(elem, actionsClass) {\n var el = document.createElement('div'),\n actions;\n\n el.innerHTML = elem;\n actions = el.getElementsByClassName(actionsClass)[0];\n\n if (actions) {\n el.removeChild(actions);\n }\n\n return el.innerHTML;\n }\n\n return Insert.extend({\n defaults: {\n externalFormName: '${ $.ns }.${ $.ns }',\n pageActionsClass: 'page-actions',\n actionsContainerClass: 'page-main-actions',\n exports: {\n prefix: '${ $.externalFormName }:selectorPrefix'\n },\n imports: {\n toolbarSection: '${ $.toolbarContainer }:toolbarSection',\n prefix: '${ $.toolbarContainer }:rootSelector',\n messagesClass: '${ $.externalFormName }:messagesClass'\n },\n settings: {\n ajax: {\n ajaxSave: true,\n exports: {\n ajaxSave: '${ $.externalFormName }:ajaxSave'\n },\n imports: {\n responseStatus: '${ $.externalFormName }:responseStatus',\n responseData: '${ $.externalFormName }:responseData'\n }\n }\n },\n modules: {\n externalForm: '${ $.externalFormName }'\n }\n },\n\n /** @inheritdoc */\n initObservable: function () {\n return this._super()\n .observe('responseStatus');\n },\n\n /** @inheritdoc */\n initConfig: function (config) {\n var defaults = this.constructor.defaults;\n\n utils.extend(defaults, defaults.settings[config.formSubmitType] || {});\n\n return this._super();\n },\n\n /** @inheritdoc*/\n destroyInserted: function () {\n if (this.isRendered && this.externalForm()) {\n this.externalForm().delegate('destroy');\n this.removeActions();\n this.responseStatus(undefined);\n this.responseData = {};\n }\n\n return this._super();\n },\n\n /** @inheritdoc */\n onRender: function (data) {\n var actions = getPageActions(data, this.pageActionsClass);\n\n if (!data.length) {\n return this;\n }\n data = removePageActions(data, this.pageActionsClass);\n this.renderActions(actions);\n this._super(data);\n },\n\n /**\n * Insert actions in toolbar.\n *\n * @param {String} actions\n */\n renderActions: function (actions) {\n var $container = $('<div/>');\n\n $container\n .addClass(this.actionsContainerClass)\n .append(actions);\n\n this.formHeader = $container;\n\n $(this.toolbarSection).append(this.formHeader);\n },\n\n /**\n * Remove actions toolbar.\n */\n removeActions: function () {\n $(this.formHeader).siblings('.' + this.messagesClass).remove();\n $(this.formHeader).remove();\n this.formHeader = $();\n },\n\n /**\n * Reset external form data.\n */\n resetForm: function () {\n if (this.externalSource()) {\n this.externalSource().trigger('data.reset');\n this.responseStatus(undefined);\n }\n }\n });\n});\n","Magento_Ui/js/form/components/insert-listing.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n './insert',\n 'mageUtils',\n 'underscore'\n], function ($, Insert, utils, _) {\n 'use strict';\n\n return Insert.extend({\n defaults: {\n externalListingName: '${ $.ns }.${ $.ns }',\n behaviourType: 'simple',\n externalFilterMode: false,\n requestConfig: {\n method: 'POST'\n },\n externalCondition: 'nin',\n settings: {\n edit: {\n imports: {\n 'onChangeRecord': '${ $.editorProvider }:changed'\n }\n },\n filter: {\n exports: {\n 'requestConfig': '${ $.externalProvider }:requestConfig'\n }\n }\n },\n imports: {\n onSelectedChange: '${ $.selectionsProvider }:selected',\n 'update_url': '${ $.externalProvider }:update_url',\n 'indexField': '${ $.selectionsProvider }:indexField'\n },\n exports: {\n externalFiltersModifier: '${ $.externalProvider }:params.filters_modifier'\n },\n listens: {\n externalValue: 'updateExternalFiltersModifier updateSelections',\n indexField: 'initialUpdateListing'\n },\n modules: {\n selections: '${ $.selectionsProvider }',\n externalListing: '${ $.externalListingName }'\n }\n },\n\n /**\n * Invokes initialize method of parent class,\n * contains initialization logic\n */\n initialize: function () {\n this._super();\n _.bindAll(this, 'updateValue', 'updateExternalValueByEditableData');\n\n return this;\n },\n\n /** @inheritdoc */\n initConfig: function (config) {\n var defaults = this.constructor.defaults;\n\n if (config.behaviourType === 'edit') {\n defaults.editableData = {};\n _.map(defaults.settings.edit.imports, function (value, key) {\n this.imports[key] = value;\n }, defaults);\n }\n\n if (config.externalFilterMode === true) {\n _.map(defaults.settings.filter.exports, function (value, key) {\n this.exports[key] = value;\n }, defaults);\n }\n\n return this._super();\n },\n\n /** @inheritdoc */\n initObservable: function () {\n return this._super()\n .observe([\n 'externalValue'\n ]);\n },\n\n /** @inheritdoc */\n destroyInserted: function () {\n if (this.isRendered && this.externalListing()) {\n this.externalListing().source.storage().clearRequests();\n this.externalListing().delegate('destroy');\n }\n\n return this._super();\n },\n\n /**\n * Store data from edited record\n *\n * @param {Object} record\n */\n onChangeRecord: function (record) {\n this.updateEditableData(record);\n\n if (!this.dataLinks.imports) {\n return;\n }\n\n this.updateExternalValueByEditableData();\n },\n\n /**\n * Updates externalValue every time row is selected,\n * if it is configured by 'dataLinks.imports'\n * Also suppress dataLinks so import/export of selections will not activate each other in circle\n *\n */\n onSelectedChange: function () {\n if (!this.dataLinks.imports ||\n this.suppressDataLinks ||\n _.isBoolean(this.initialExportDone) && !this.initialExportDone\n ) {\n this.suppressDataLinks = false;\n\n return;\n }\n\n this.suppressDataLinks = true;\n this.updateExternalValue();\n },\n\n /**\n * Stores data from editor in editableData\n * @param {Object} record\n *\n */\n updateEditableData: function (record) {\n var id = _.keys(record[0])[0];\n\n this.editableData[id] = record[0][id];\n },\n\n /**\n * Updates externalValue by data from editor (already stored in editableData)\n *\n */\n updateExternalValueByEditableData: function () {\n var updatedExtValue;\n\n if (!this.behaviourType === 'edit' || _.isEmpty(this.editableData) || _.isEmpty(this.externalValue())) {\n return;\n }\n\n updatedExtValue = this.externalValue();\n updatedExtValue.map(function (item) {\n _.extend(item, this.editableData[item[this.indexField]]);\n }, this);\n this.setExternalValue(updatedExtValue);\n },\n\n /**\n * Updates externalValue, from selectionsProvider data (if it is enough)\n * or ajax request to server\n *\n * @returns {Object} result - deferred that will be resolved when value is updated\n */\n updateExternalValue: function () {\n var result = $.Deferred(),\n provider = this.selections(),\n selections,\n totalSelected,\n itemsType,\n rows;\n\n if (!provider) {\n return result;\n }\n\n selections = provider && provider.getSelections();\n totalSelected = provider.totalSelected();\n itemsType = selections && selections.excludeMode ? 'excluded' : 'selected';\n rows = provider && provider.rows();\n\n if (this.canUpdateFromClientData(totalSelected, selections.selected, rows)) {\n this.updateFromClientData(selections.selected, rows);\n this.updateExternalValueByEditableData();\n result.resolve();\n } else {\n this.updateFromServerData(selections, itemsType).done(function () {\n this.updateExternalValueByEditableData();\n result.resolve();\n }.bind(this));\n }\n\n return result;\n },\n\n /**\n * Check if the selected rows data can be taken from selectionsProvider data\n * (which only stores data of the current page rows)\n * + from already saved data\n *\n * @param {Boolean} totalSelected - total rows selected (include rows that were filtered out)\n * @param {Array} selected - ids of selected rows\n * @param {Object} rows\n */\n canUpdateFromClientData: function (totalSelected, selected, rows) {\n var alreadySavedSelectionsIds = _.pluck(this.externalValue(), this.indexField),\n rowsOnCurrentPageIds = _.pluck(rows, this.indexField);\n\n return totalSelected === selected.length &&\n _.intersection(_.union(alreadySavedSelectionsIds, rowsOnCurrentPageIds), selected).length ===\n selected.length;\n },\n\n /**\n * Updates externalValue, from selectionsProvider data\n * (which only stores data of the current page rows)\n * + from already saved data\n * so we can avoid request to server\n *\n * @param {Array} selected - ids of selected rows\n * @param {Object} rows\n */\n updateFromClientData: function (selected, rows) {\n var value,\n rowIds,\n valueIds;\n\n if (!selected || !selected.length) {\n this.setExternalValue([]);\n\n return;\n }\n\n value = this.externalValue();\n rowIds = _.pluck(rows, this.indexField);\n valueIds = _.pluck(value, this.indexField);\n\n value = _.map(selected, function (item) {\n if (_.contains(rowIds, item)) {\n return _.find(rows, function (row) {\n return row[this.indexField] === item;\n }, this);\n } else if (_.contains(valueIds, item)) {\n return _.find(value, function (row) {\n return row[this.indexField] === item;\n }, this);\n }\n }, this);\n\n this.setExternalValue(value);\n },\n\n /**\n * Updates externalValue, from ajax request to grab selected rows data\n *\n * @param {Object} selections\n * @param {String} itemsType\n *\n * @returns {Object} request - deferred that will be resolved when ajax is done\n */\n updateFromServerData: function (selections, itemsType) {\n var filterType = selections && selections.excludeMode ? 'nin' : 'in',\n selectionsData = {},\n request;\n\n _.extend(selectionsData, this.params || {}, selections.params);\n\n if (selections[itemsType] && selections[itemsType].length) {\n selectionsData.filters = {};\n selectionsData['filters_modifier'] = {};\n selectionsData['filters_modifier'][this.indexField] = {\n 'condition_type': filterType,\n value: selections[itemsType]\n };\n }\n\n selectionsData.paging = {\n notLimits: 1\n };\n\n request = this.requestData(selectionsData, {\n method: this.requestConfig.method\n });\n request\n .done(function (data) {\n this.setExternalValue(data.items || data);\n this.loading(false);\n }.bind(this))\n .fail(this.onError);\n\n return request;\n },\n\n /**\n * Set listing rows data to the externalValue,\n * or if externalData is configured with the names of particular columns,\n * filter rows data to have only these columns, and then set to the externalValue\n *\n * @param {Object} newValue - rows data\n *\n */\n setExternalValue: function (newValue) {\n var keys = this.externalData,\n value = this.externalValue(),\n selectedIds = _.pluck(newValue, this.indexField);\n\n if (_.isArray(keys) && !_.isEmpty(keys)) {\n newValue = _.map(newValue, function (item) {\n return _.pick(item, keys);\n }, this);\n } else if (keys && _.isString(keys) && !_.isEmpty(newValue)) {\n newValue = newValue[0][keys];\n }\n\n if (this.externalFilterMode) {\n newValue = _.union(newValue, _.filter(value,\n function (item) {\n return !_.contains(selectedIds, item[this.indexField]);\n }, this));\n }\n\n this.set('externalValue', newValue);\n },\n\n /**\n * Updates external filter (if externalFilterMode is on)\n * every time, when value is updated,\n * so grid is re-filtered to exclude or include selected rows only\n *\n * @param {Object} items\n */\n updateExternalFiltersModifier: function (items) {\n var provider,\n filter = {};\n\n if (!this.externalFilterMode) {\n return;\n }\n\n provider = this.selections();\n\n if (!provider) {\n this.needInitialListingUpdate = true;\n\n return;\n }\n\n filter[this.indexField] = {\n 'condition_type': this.externalCondition,\n value: _.pluck(items, this.indexField)\n };\n this.set('externalFiltersModifier', filter);\n },\n\n /**\n * Updates grid selections\n * every time, when extenalValue is updated,\n * so grid is re-selected according to externalValue updated\n * Also suppress dataLinks so import/export of selections will not activate each other in circle\n *\n * @param {Object} items\n */\n updateSelections: function (items) {\n var provider,\n ids;\n\n if (!this.dataLinks.exports || this.suppressDataLinks) {\n this.suppressDataLinks = false;\n this.initialExportDone = true;\n\n return;\n }\n\n provider = this.selections();\n\n if (!provider) {\n this.needInitialListingUpdate = true;\n\n return;\n }\n\n this.suppressDataLinks = true;\n provider.deselectAll();\n\n if (_.isString(items)) {\n provider.selected([items] || []);\n } else {\n ids = _.pluck(items || [], this.indexField)\n .map(function (item) {\n return item.toString();\n });\n\n provider.selected(ids || []);\n }\n this.initialExportDone = true;\n },\n\n /**\n * initial update of the listing\n * with rows that must be checked/filtered\n * by the indexes\n */\n initialUpdateListing: function () {\n var items = this.externalValue();\n\n if (this.needInitialListingUpdate && items) {\n this.updateExternalFiltersModifier(items);\n this.updateSelections(items);\n this.needInitialListingUpdate = false;\n }\n },\n\n /**\n * Reload source\n */\n reload: function () {\n this.externalSource().set('params.t', new Date().getTime());\n },\n\n /**\n * Updates value from external value\n *\n */\n updateValue: function () {\n this.set('value', this.externalValue());\n },\n\n /**\n * Updates external value, then updates value from external value\n *\n */\n save: function () {\n this.updateExternalValue().done(\n function () {\n if (!this.realTimeLink) {\n this.updateValue();\n }\n }.bind(this)\n );\n }\n });\n});\n","Magento_Ui/js/form/components/html.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'uiComponent'\n], function ($, _, Component) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n content: '',\n showSpinner: false,\n loading: false,\n visible: true,\n template: 'ui/content/content',\n additionalClasses: {},\n ignoreTmpls: {\n content: true\n }\n },\n\n /**\n * Extends instance with default config, calls 'initialize' method of\n * parent, calls 'initAjaxConfig'\n */\n initialize: function () {\n _.bindAll(this, 'onContainerToggle', 'onDataLoaded');\n\n this._super()\n ._setClasses()\n .initAjaxConfig();\n\n return this;\n },\n\n /**\n * Calls 'initObservable' method of parent, initializes observable\n * properties of instance\n *\n * @return {Object} - reference to instance\n */\n initObservable: function () {\n this._super()\n .observe('content loading visible');\n\n return this;\n },\n\n /**\n * Extends 'additionalClasses' object.\n *\n * @returns {Group} Chainable.\n */\n _setClasses: function () {\n var additional = this.additionalClasses,\n classes;\n\n if (_.isString(additional)) {\n additional = this.additionalClasses.split(' ');\n classes = this.additionalClasses = {};\n\n additional.forEach(function (name) {\n classes[name] = true;\n }, this);\n }\n\n _.extend(this.additionalClasses, {\n 'admin__scope-old': !!additional\n });\n\n return this;\n },\n\n /** @inheritdoc */\n initContainer: function (parent) {\n this._super();\n\n parent.on('active', this.onContainerToggle);\n\n return this;\n },\n\n /**\n * Initializes default ajax config on instance\n *\n * @return {Object} - reference to instance\n */\n initAjaxConfig: function () {\n this.ajaxConfig = {\n url: this.url,\n data: {\n FORM_KEY: window.FORM_KEY\n },\n success: this.onDataLoaded\n };\n\n return this;\n },\n\n /**\n * Calls 'loadData' if both 'active' variable and 'shouldLoad'\n * property are truthy\n *\n * @param {Boolean} active\n */\n onContainerToggle: function (active) {\n if (active && this.shouldLoad()) {\n this.loadData();\n }\n },\n\n /**\n * Defines if instance has 'content' property defined.\n *\n * @return {Boolean} [description]\n */\n hasData: function () {\n return !!this.content();\n },\n\n /**\n * Defines if instance should load external data\n *\n * @return {Boolean}\n */\n shouldLoad: function () {\n return this.url && !this.hasData() && !this.loading();\n },\n\n /**\n * Sets loading property to true, makes ajax call\n *\n * @return {Object} - reference to instance\n */\n loadData: function () {\n this.loading(true);\n\n $.ajax(this.ajaxConfig);\n\n return this;\n },\n\n /**\n * Ajax's request success handler. Calls 'updateContent' passing 'data'\n * to it, then sets 'loading' property to false.\n *\n * @param {String} data\n */\n onDataLoaded: function (data) {\n this.updateContent(data)\n .loading(false);\n },\n\n /**\n * Sets incoming data 'content' property's value\n *\n * @param {String} content\n * @return {Object} - reference to instance\n */\n updateContent: function (content) {\n this.content(content);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/components/fieldset.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/lib/collapsible',\n 'underscore'\n], function (Collapsible, _) {\n 'use strict';\n\n return Collapsible.extend({\n defaults: {\n template: 'ui/form/fieldset',\n collapsible: false,\n changed: false,\n loading: false,\n error: false,\n opened: false,\n level: 0,\n visible: true,\n initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */\n disabled: false,\n listens: {\n 'opened': 'onVisibilityChange'\n },\n additionalClasses: {}\n },\n\n /**\n * Extends instance with defaults. Invokes parent initialize method.\n * Calls initListeners and pushParams methods.\n */\n initialize: function () {\n _.bindAll(this, 'onChildrenUpdate', 'onChildrenError', 'onContentLoading');\n\n return this._super()\n ._setClasses();\n },\n\n /**\n * Initializes components' configuration.\n *\n * @returns {Fieldset} Chainable.\n */\n initConfig: function () {\n this._super();\n this._wasOpened = this.opened || !this.collapsible;\n\n return this;\n },\n\n /**\n * Calls initObservable of parent class.\n * Defines observable properties of instance.\n *\n * @returns {Object} Reference to instance\n */\n initObservable: function () {\n this._super()\n .observe('changed loading error visible');\n\n return this;\n },\n\n /**\n * Calls parent's initElement method.\n * Assigns callbacks on various events of incoming element.\n *\n * @param {Object} elem\n * @return {Object} - reference to instance\n */\n initElement: function (elem) {\n elem.initContainer(this);\n\n elem.on({\n 'update': this.onChildrenUpdate,\n 'loading': this.onContentLoading,\n 'error': this.onChildrenError\n });\n\n if (this.disabled) {\n try {\n elem.disabled(true);\n }\n catch (e) {\n\n }\n }\n\n return this;\n },\n\n /**\n * Is being invoked on children update.\n * Sets changed property to one incoming.\n *\n * @param {Boolean} hasChanged\n */\n onChildrenUpdate: function (hasChanged) {\n if (!hasChanged) {\n hasChanged = _.some(this.delegate('hasChanged'));\n }\n\n this.bubble('update', hasChanged);\n this.changed(hasChanged);\n },\n\n /**\n * Extends 'additionalClasses' object.\n *\n * @returns {Group} Chainable.\n */\n _setClasses: function () {\n var additional = this.additionalClasses,\n classes;\n\n if (_.isString(additional)) {\n additional = this.additionalClasses.split(' ');\n classes = this.additionalClasses = {};\n\n additional.forEach(function (name) {\n classes[name] = true;\n }, this);\n }\n\n _.extend(this.additionalClasses, {\n 'admin__collapsible-block-wrapper': this.collapsible,\n _show: this.opened,\n _hide: !this.opened,\n _disabled: this.disabled\n });\n\n return this;\n },\n\n /**\n * Handler of the \"opened\" property changes.\n *\n * @param {Boolean} isOpened\n */\n onVisibilityChange: function (isOpened) {\n if (!this._wasOpened) {\n this._wasOpened = isOpened;\n }\n },\n\n /**\n * Is being invoked on children validation error.\n * Sets error property to one incoming.\n *\n * @param {String} message - error message.\n */\n onChildrenError: function (message) {\n var hasErrors = false;\n\n if (!message) {\n hasErrors = this._isChildrenHasErrors(hasErrors, this);\n }\n\n this.error(hasErrors || message);\n\n if (hasErrors || message) {\n this.open();\n }\n },\n\n /**\n * Returns errors of children if exist\n *\n * @param {Boolean} hasErrors\n * @param {*} container\n * @return {Boolean}\n * @private\n */\n _isChildrenHasErrors: function (hasErrors, container) {\n var self = this;\n\n if (hasErrors === false && container.hasOwnProperty('elems')) {\n hasErrors = container.elems.some('error');\n\n if (hasErrors === false && container.hasOwnProperty('_elems')) {\n container._elems.forEach(function (child) {\n\n if (hasErrors === false) {\n hasErrors = self._isChildrenHasErrors(hasErrors, child);\n }\n });\n }\n }\n\n return hasErrors;\n },\n\n /**\n * Callback that sets loading property to true.\n */\n onContentLoading: function (isLoading) {\n this.loading(isLoading);\n }\n });\n});\n","Magento_Ui/js/form/components/area.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n './tab'\n], function (_, Tab) {\n 'use strict';\n\n return Tab.extend({\n defaults: {\n uniqueNs: 'params.activeArea',\n template: 'ui/area',\n changed: false,\n loading: false\n },\n\n /**\n * Extends instance with defaults. Invokes parent initialize method.\n * Calls initListeners and pushParams methods.\n */\n initialize: function () {\n _.bindAll(this, 'onChildrenUpdate', 'onContentLoading');\n\n return this._super();\n },\n\n /**\n * Calls initObservable of parent class.\n * Defines observable properties of instance.\n * @return {Object} - reference to instance\n */\n initObservable: function () {\n this._super()\n .observe('changed loading');\n\n return this;\n },\n\n /**\n * Calls parent's initElement method.\n * Assigns callbacks on various events of incoming element.\n * @param {Object} elem\n * @return {Object} - reference to instance\n */\n initElement: function (elem) {\n this._super();\n\n elem.on({\n 'update': this.onChildrenUpdate,\n 'loading': this.onContentLoading\n });\n\n return this;\n },\n\n /**\n * Is being invoked on children update.\n * Sets changed property to one incoming.\n * Invokes setActive method if settings\n * contain makeVisible property set to true.\n *\n * @param {Boolean} hasChanged\n */\n onChildrenUpdate: function (hasChanged) {\n if (!hasChanged) {\n hasChanged = _.some(this.delegate('hasChanged'));\n }\n\n this.changed(hasChanged);\n },\n\n /**\n * Callback that sets loading property to true.\n */\n onContentLoading: function (isLoading) {\n this.loading(isLoading);\n }\n });\n});\n","Magento_Ui/js/form/components/collection.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n 'uiComponent',\n 'uiLayout',\n 'Magento_Ui/js/modal/confirm'\n], function (_, utils, registry, Component, layout, confirm) {\n 'use strict';\n\n var childTemplate = {\n parent: '${ $.$data.name }',\n name: '${ $.$data.childIndex }',\n dataScope: '${ $.name }',\n nodeTemplate: '${ $.$data.name }.${ $.$data.itemTemplate }'\n };\n\n return Component.extend({\n defaults: {\n lastIndex: 0,\n template: 'ui/form/components/collection'\n },\n\n /**\n * Extends instance with default config, calls initialize of parent\n * class, calls initChildren method.\n */\n initialize: function () {\n this._super()\n .initChildren();\n\n return this;\n },\n\n /**\n * Activates the incoming child and triggers the update event.\n *\n * @param {Object} elem - Incoming child.\n */\n initElement: function (elem) {\n this._super();\n\n elem.activate();\n\n this.bubble('update');\n\n return this;\n },\n\n /**\n * Loops over corresponding data in data storage,\n * creates child for each and pushes it's identifier to initialItems array.\n *\n * @returns {Collection} Chainable.\n */\n initChildren: function () {\n var children = this.source.get(this.dataScope),\n initial = this.initialItems = [];\n\n _.each(children, function (item, index) {\n initial.push(index);\n this.addChild(index);\n }, this);\n\n return this;\n },\n\n /**\n * Creates new item of collection, based on incoming 'index'.\n * If not passed creates one with 'new_' prefix.\n *\n * @param {String|Object} [index] - Index of a child.\n * @returns {Collection} Chainable.\n */\n addChild: function (index) {\n this.childIndex = !_.isString(index) ?\n 'new_' + this.lastIndex++ :\n index;\n\n layout([utils.template(childTemplate, this)]);\n\n return this;\n },\n\n /**\n * Returns true if current set of items differ from initial one,\n * or if some child has been changed.\n *\n * @returns {Boolean}\n */\n hasChanged: function () {\n var initial = this.initialItems,\n current = this.elems.pluck('index'),\n changed = !utils.equalArrays(initial, current);\n\n return changed || this.elems.some(function (elem) {\n return _.some(elem.delegate('hasChanged'));\n });\n },\n\n /**\n * Initiates validation of its' children components.\n *\n * @returns {Array} An array of validation results.\n */\n validate: function () {\n var elems;\n\n this.allValid = true;\n\n elems = this.elems.sortBy(function (elem) {\n return !elem.active();\n });\n\n elems = elems.map(this._validate, this);\n\n return _.flatten(elems);\n },\n\n /**\n * Iterator function for components validation.\n * Activates first invalid child component.\n *\n * @param {Object} elem - Element to run validation on.\n * @returns {Array} An array of validation results.\n */\n _validate: function (elem) {\n var result = elem.delegate('validate'),\n invalid;\n\n invalid = _.some(result, function (item) {\n return !item.valid;\n });\n\n if (this.allValid && invalid) {\n this.allValid = false;\n\n elem.activate();\n }\n\n return result;\n },\n\n /**\n * Creates function that removes element\n * from collection using '_removeChild' method.\n * @param {Object} elem - Element that should be removed.\n * @deprecated Not used anymore\n */\n removeAddress: function (elem) {\n var self = this;\n\n confirm({\n content: this.removeMessage,\n actions: {\n /** @inheritdoc */\n confirm: function () {\n self._removeAddress(elem);\n }\n }\n });\n },\n\n /**\n * Removes element from both collection and data storage,\n * activates first element if removed one was active,\n * triggers 'update' event.\n *\n * @param {Object} elem - Element to remove.\n */\n _removeAddress: function (elem) {\n var isActive = elem.active(),\n first;\n\n elem.destroy();\n\n first = this.elems.first();\n\n if (first && isActive) {\n first.activate();\n }\n\n this.bubble('update');\n }\n });\n});\n","Magento_Ui/js/form/components/insert.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'uiElement',\n 'jquery',\n 'mage/translate',\n 'mageUtils',\n 'underscore',\n 'Magento_Ui/js/modal/alert',\n 'Magento_Ui/js/lib/view/utils/bindings',\n 'Magento_Ui/js/lib/view/utils/async'\n], function (Element, $, $t, utils, _, alert) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n content: '',\n template: 'ui/form/insert',\n showSpinner: true,\n loading: false,\n autoRender: true,\n visible: true,\n contentSelector: '${$.name}',\n externalData: [],\n params: {\n namespace: '${ $.ns }'\n },\n renderSettings: {\n url: '${ $.render_url }',\n dataType: 'html'\n },\n updateSettings: {\n url: '${ $.update_url }',\n dataType: 'json'\n },\n imports: {},\n exports: {},\n listens: {},\n links: {\n value: '${ $.provider }:${ $.dataScope}'\n },\n modules: {\n externalSource: '${ $.externalProvider }'\n }\n },\n\n /** @inheritdoc */\n initialize: function () {\n this._super();\n _.bindAll(this, 'onRender', 'onUpdate');\n\n if (this.autoRender) {\n this.render();\n }\n\n return this;\n },\n\n /** @inheritdoc */\n initObservable: function () {\n return this._super()\n .observe([\n 'visible',\n 'content',\n 'value',\n 'loading'\n ]);\n },\n\n /** @inheritdoc */\n initConfig: function (config) {\n this.initDataLink(config)._super();\n this.contentSelector = this.contentSelector.replace(/\\./g, '_');\n\n return this;\n },\n\n /**\n * Sync data with external provider.\n *\n * @param {Object} config\n * @returns {Object}\n */\n initDataLink: function (config) {\n var key, value;\n\n if (config.dataLinks) {\n _.each(config.externalData, function (val) {\n value = val;\n key = 'externalValue.' + val.replace('data.', '');\n\n if (config.dataLinks.imports) {\n this.imports[key] = '${ $.externalProvider }:' + value;\n }\n\n if (config.dataLinks.exports) {\n this.exports[key] = '${ $.externalProvider }:' + value;\n }\n this.links[key] = '${ $.externalProvider }:' + value;\n }, this.constructor.defaults);\n }\n\n if (config.realTimeLink) {\n this.constructor.defaults.links.externalValue = 'value';\n }\n\n return this;\n },\n\n /**\n * Request for render content.\n *\n * @returns {Object}\n */\n render: function (params) {\n var self = this,\n request;\n\n if (this.isRendered) {\n return this;\n }\n\n self.previousParams = params || {};\n\n $.async({\n component: this.name,\n ctx: '.' + this.contentSelector\n }, function (el) {\n self.contentEl = $(el);\n self.startRender = true;\n params = _.extend({}, self.params, params || {});\n request = self.requestData(params, self.renderSettings);\n request\n .done(self.onRender)\n .fail(self.onError);\n });\n\n return this;\n },\n\n /** @inheritdoc */\n destroy: function () {\n this.destroyInserted()\n ._super();\n },\n\n /**\n * Destroy inserted components.\n *\n * @returns {Object}\n */\n destroyInserted: function () {\n if (this.isRendered) {\n this.isRendered = false;\n this.content('');\n\n if (this.externalSource()) {\n this.externalSource().destroy();\n }\n this.initExternalLinks();\n }\n\n return this;\n },\n\n /**\n * Initialize links on external components.\n *\n * @returns {Object}\n */\n initExternalLinks: function () {\n var imports = this.filterExternalLinks(this.imports, this.ns),\n exports = this.filterExternalLinks(this.exports, this.ns),\n links = this.filterExternalLinks(this.links, this.ns);\n\n this.setLinks(links, 'imports')\n .setLinks(links, 'exports');\n\n _.each({\n exports: exports,\n imports: imports\n }, this.setLinks, this);\n\n return this;\n },\n\n /**\n * Filter external links.\n *\n * @param {Object} data\n * @param {String }ns\n * @returns {Object}\n */\n filterExternalLinks: function (data, ns) {\n var links = {};\n\n _.each(data, function (value, key) {\n if (value.split('.')[0] === ns) {\n links[key] = value;\n }\n });\n\n return links;\n },\n\n /**\n * Request with configurable params and settings.\n *\n * @param {Object} params\n * @param {Object} ajaxSettings\n * @returns {Object}\n */\n requestData: function (params, ajaxSettings) {\n var query = utils.copy(params);\n\n ajaxSettings = _.extend({\n url: this['update_url'],\n method: 'GET',\n data: query,\n dataType: 'json'\n }, ajaxSettings);\n\n this.loading(true);\n\n return $.ajax(ajaxSettings);\n },\n\n /**\n * Callback that render content.\n *\n * @param {*} data\n */\n onRender: function (data) {\n var resp;\n\n this.loading(false);\n\n try {\n resp = JSON.parse(data);\n\n if (resp.ajaxExpired) {\n window.location.href = resp.ajaxRedirect;\n }\n } catch (e) {\n this.set('content', data);\n this.isRendered = true;\n this.startRender = false;\n }\n },\n\n /**\n * Error callback.\n *\n * @param {Object} xhr\n */\n onError: function (xhr) {\n if (xhr.statusText === 'abort') {\n return;\n }\n\n alert({\n content: $t('Something went wrong.')\n });\n },\n\n /**\n * Getter for external data.\n *\n * @returns {Object}\n */\n getExternalData: function () {\n var data = {};\n\n _.each(this.externalData, function (path) {\n utils.nested(data, path.replace('data.', ''), this.externalSource().get(path));\n }, this);\n\n return data;\n },\n\n /**\n * Request for update data.\n *\n * @returns {*|Object}\n */\n updateData: function (params) {\n var request;\n\n params = _.extend(params || {}, this.params);\n\n if (!this.startRender && !this.isRendered) {\n return this.render(params);\n }\n\n request = this.requestData(params, this.updateSettings);\n request\n .done(this.onUpdate)\n .fail(this.onError);\n\n return request;\n },\n\n /**\n * Set data to external provider, clear changes.\n *\n * @param {*} data\n */\n onUpdate: function (data) {\n this.externalSource().set('data', data);\n this.externalSource().trigger('data.overload');\n this.loading(false);\n }\n });\n});\n","Magento_Ui/js/form/components/group.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiCollection'\n], function (_, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n visible: true,\n label: '',\n showLabel: true,\n required: false,\n template: 'ui/group/group',\n fieldTemplate: 'ui/form/field',\n breakLine: true,\n validateWholeGroup: false,\n additionalClasses: {}\n },\n\n /**\n * Extends this with defaults and config.\n * Then calls initObservable, iniListenes and extractData methods.\n */\n initialize: function () {\n this._super()\n ._setClasses();\n\n return this;\n },\n\n /**\n * Calls initObservable of parent class.\n * Defines observable properties of instance.\n *\n * @return {Object} - reference to instance\n */\n initObservable: function () {\n this._super()\n .observe('visible')\n .observe({\n required: !!+this.required\n });\n\n return this;\n },\n\n /**\n * Extends 'additionalClasses' object.\n *\n * @returns {Group} Chainable.\n */\n _setClasses: function () {\n var additional = this.additionalClasses,\n classes;\n\n if (_.isString(additional)) {\n additional = this.additionalClasses.split(' ');\n classes = this.additionalClasses = {};\n\n additional.forEach(function (name) {\n classes[name] = true;\n }, this);\n }\n\n _.extend(this.additionalClasses, {\n 'admin__control-grouped': !this.breakLine,\n 'admin__control-fields': this.breakLine,\n required: this.required,\n _error: this.error,\n _disabled: this.disabled\n });\n\n return this;\n },\n\n /**\n * Defines if group has only one element.\n * @return {Boolean}\n */\n isSingle: function () {\n return this.elems.getLength() === 1;\n },\n\n /**\n * Defines if group has multiple elements.\n * @return {Boolean}\n */\n isMultiple: function () {\n return this.elems.getLength() > 1;\n },\n\n /**\n * Returns an array of child components previews.\n *\n * @returns {Array}\n */\n getPreview: function () {\n return this.elems.map('getPreview');\n }\n });\n});\n","Magento_Ui/js/form/components/tab_group.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'Magento_Ui/js/lib/collapsible'\n], function (_, Collapsible) {\n 'use strict';\n\n return Collapsible.extend({\n defaults: {\n listens: {\n '${ $.provider }:data.validate': 'onValidate'\n },\n collapsible: false,\n opened: true\n },\n\n /**\n * Invokes initElement method of parent class, calls 'initActivation' method\n * passing element to it.\n * @param {Object} elem\n * @returns {Object} - reference to instance\n */\n initElement: function (elem) {\n this._super()\n .initActivation(elem);\n\n return this;\n },\n\n /**\n * Activates element if one is first or if one has 'active' propert\n * set to true.\n *\n * @param {Object} elem\n * @returns {Object} - reference to instance\n */\n initActivation: function (elem) {\n var elems = this.elems(),\n isFirst = !elems.indexOf(elem);\n\n if (isFirst || elem.active()) {\n elem.activate();\n }\n\n return this;\n },\n\n /**\n * Delegates 'validate' method on element, then reads 'invalid' property\n * of params storage, and if defined, activates element, sets\n * 'allValid' property of instance to false and sets invalid's\n * 'focused' property to true.\n *\n * @param {Object} elem\n */\n validate: function (elem) {\n var result = elem.delegate('validate'),\n invalid;\n\n invalid = _.find(result, function (item) {\n return typeof item !== 'undefined' && !item.valid;\n });\n\n if (invalid) {\n elem.activate();\n invalid.target.focused(true);\n }\n\n return invalid;\n },\n\n /**\n * Sets 'allValid' property of instance to true, then calls 'validate' method\n * of instance for each element.\n */\n onValidate: function () {\n this.elems.sortBy(function (elem) {\n return !elem.active();\n }).some(this.validate, this);\n }\n });\n});\n","Magento_Ui/js/form/components/tab.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'uiCollection'\n], function (Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n uniqueProp: 'active',\n active: false,\n wasActivated: false\n },\n\n /**\n * Extends instance with defaults. Invokes parent initialize method.\n * Calls initListeners and pushParams methods.\n */\n initialize: function () {\n this._super()\n .setUnique();\n },\n\n /**\n * Calls initObservable of parent class.\n * Defines observable properties of instance.\n * @return {Object} - reference to instance\n */\n initObservable: function () {\n this._super()\n .observe('active wasActivated');\n\n return this;\n },\n\n /**\n * Sets active property to true, then invokes pushParams method.\n */\n activate: function () {\n this.active(true);\n this.wasActivated(true);\n\n this.setUnique();\n\n return true;\n }\n });\n});\n","Magento_Ui/js/form/components/button.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'uiElement',\n 'uiRegistry',\n 'uiLayout',\n 'mageUtils',\n 'underscore'\n], function (Element, registry, layout, utils, _) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n buttonClasses: {},\n additionalClasses: {},\n displayArea: 'outsideGroup',\n displayAsLink: false,\n elementTmpl: 'ui/form/element/button',\n template: 'ui/form/components/button/simple',\n visible: true,\n disabled: false,\n title: ''\n },\n\n /**\n * Initializes component.\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n return this._super()\n ._setClasses()\n ._setButtonClasses();\n },\n\n /** @inheritdoc */\n initObservable: function () {\n return this._super()\n .observe([\n 'visible',\n 'disabled',\n 'title'\n ]);\n },\n\n /**\n * Performs configured actions\n */\n action: function () {\n this.actions.forEach(this.applyAction, this);\n },\n\n /**\n * Apply action on target component,\n * but previously create this component from template if it is not existed\n *\n * @param {Object} action - action configuration\n */\n applyAction: function (action) {\n var targetName = action.targetName,\n params = utils.copy(action.params) || [],\n actionName = action.actionName,\n target;\n\n if (!registry.has(targetName)) {\n this.getFromTemplate(targetName);\n }\n target = registry.async(targetName);\n\n if (target && typeof target === 'function' && actionName) {\n params.unshift(actionName);\n target.apply(target, params);\n }\n },\n\n /**\n * Create target component from template\n *\n * @param {Object} targetName - name of component,\n * that supposed to be a template and need to be initialized\n */\n getFromTemplate: function (targetName) {\n var parentName = targetName.split('.'),\n index = parentName.pop(),\n child;\n\n parentName = parentName.join('.');\n child = utils.template({\n parent: parentName,\n name: index,\n nodeTemplate: targetName\n });\n layout([child]);\n },\n\n /**\n * Extends 'additionalClasses' object.\n *\n * @returns {Object} Chainable.\n */\n _setClasses: function () {\n if (typeof this.additionalClasses === 'string') {\n if (this.additionalClasses === '') {\n this.additionalClasses = {};\n\n return this;\n }\n\n this.additionalClasses = this.additionalClasses\n .trim()\n .split(' ')\n .reduce(function (classes, name) {\n classes[name] = true;\n\n return classes;\n }, {}\n );\n }\n\n return this;\n },\n\n /**\n * Extends 'buttonClasses' object.\n *\n * @returns {Object} Chainable.\n */\n _setButtonClasses: function () {\n var additional = this.buttonClasses;\n\n if (_.isString(additional)) {\n this.buttonClasses = {};\n\n if (additional.trim().length) {\n additional = additional.trim().split(' ');\n\n additional.forEach(function (name) {\n if (name.length) {\n this.buttonClasses[name] = true;\n }\n }, this);\n }\n }\n\n _.extend(this.buttonClasses, {\n 'action-basic': !this.displayAsLink,\n 'action-additional': this.displayAsLink\n });\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/components/collection/item.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n '../tab'\n], function (_, utils, Tab) {\n 'use strict';\n\n var previewConfig = {\n separator: ' ',\n prefix: ''\n };\n\n /**\n * Parses incoming data and returns result merged with default preview config\n *\n * @param {Object|String} data\n * @return {Object}\n */\n function parsePreview(data) {\n if (typeof data == 'string') {\n data = {\n items: data\n };\n }\n\n data.items = utils.stringToArray(data.items);\n\n return _.defaults(data, previewConfig);\n }\n\n return Tab.extend({\n defaults: {\n label: '',\n uniqueNs: 'activeCollectionItem',\n previewTpl: 'ui/form/components/collection/preview'\n },\n\n /**\n * Extends instance with default config, calls initializes of parent class\n */\n initialize: function () {\n _.bindAll(this, 'buildPreview', 'hasPreview');\n\n return this._super();\n },\n\n /**\n * Calls initProperties of parent class, initializes properties\n * of instance.\n *\n * @return {Object} - reference to instance\n */\n initConfig: function () {\n this._super();\n\n this.displayed = [];\n\n return this;\n },\n\n /**\n * Calls initObservable of parent class, initializes observable\n * properties of instance.\n *\n * @return {Object} - reference to instance\n */\n initObservable: function () {\n this._super()\n .observe({\n noPreview: true,\n indexed: {}\n });\n\n return this;\n },\n\n /**\n * Is being called when child element has been initialized,\n * calls initElement of parent class, binds to element's update event,\n * calls insertToArea and insertToIndexed methods passing element to it\n *\n * @param {Object} elem\n */\n initElement: function (elem) {\n this._super()\n .insertToIndexed(elem);\n\n return this;\n },\n\n /**\n * Adds element to observable indexed object of instance\n *\n * @param {Object} elem\n * @return {Object} - reference to instance\n */\n insertToIndexed: function (elem) {\n var indexed = this.indexed();\n\n indexed[elem.index] = elem;\n\n this.indexed(indexed);\n\n return this;\n },\n\n /**\n * Destroys current instance along with all of its' children.\n * Overrides base method to clear data when this method is called.\n */\n destroy: function () {\n this._super();\n this._clearData();\n },\n\n /**\n * Clears all data associated with component.\n * @private\n *\n * @returns {Item} Chainable.\n */\n _clearData: function () {\n this.source.remove(this.dataScope);\n\n return this;\n },\n\n /**\n * Formats incoming previews array via parsePreview function.\n *\n * @param {Array} previews\n * @return {Array} - formatted previews\n */\n formatPreviews: function (previews) {\n return previews.map(parsePreview);\n },\n\n /**\n * Creates string view of previews\n *\n * @param {Object} data\n * @return {Strict} - formatted preview string\n */\n buildPreview: function (data) {\n var preview = this.getPreview(data.items),\n prefix = data.prefix;\n\n return prefix + preview.join(data.separator);\n },\n\n /**\n * Defines if instance has preview for incoming data\n *\n * @param {Object} data\n * @return {Boolean}\n */\n hasPreview: function (data) {\n return !!this.getPreview(data.items).length;\n },\n\n /**\n * Creates an array of previews for elements specified in incoming\n * items array, calls updatePreview afterwards.\n *\n * @param {Array} items - An array of element's indexes.\n * @returns {Array} An array of previews.\n */\n getPreview: function (items) {\n var elems = this.indexed(),\n displayed = this.displayed,\n preview;\n\n items = items.map(function (index) {\n var elem = elems[index];\n\n preview = elem && elem.visible() ? elem.getPreview() : '';\n\n preview = Array.isArray(preview) ?\n _.compact(preview).join(', ') :\n preview;\n\n utils.toggle(displayed, index, !!preview);\n\n return preview;\n });\n\n this.noPreview(!displayed.length);\n\n return _.compact(items);\n }\n });\n});\n","Magento_Ui/js/form/element/ui-select.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n './abstract',\n 'Magento_Ui/js/lib/key-codes',\n 'mage/translate',\n 'ko',\n 'jquery',\n 'Magento_Ui/js/lib/view/utils/async'\n], function (_, Abstract, keyCodes, $t, ko, $) {\n 'use strict';\n\n var isTouchDevice = typeof document.ontouchstart !== 'undefined';\n\n /**\n * Processing options list\n *\n * @param {Array} array - Property array\n * @param {String} separator - Level separator\n * @param {Array} created - list to add new options\n *\n * @return {Array} Plain options list\n */\n function flattenCollection(array, separator, created) {\n var i = 0,\n length,\n childCollection;\n\n array = _.compact(array);\n length = array.length;\n created = created || [];\n\n for (i; i < length; i++) {\n created.push(array[i]);\n\n if (array[i].hasOwnProperty(separator)) {\n childCollection = array[i][separator];\n delete array[i][separator];\n flattenCollection.call(this, childCollection, separator, created);\n }\n }\n\n return created;\n }\n\n /**\n * Set levels to options list\n *\n * @param {Array} array - Property array\n * @param {String} separator - Level separator\n * @param {Number} level - Starting level\n * @param {String} path - path to root\n *\n * @returns {Array} Array with levels\n */\n function setProperty(array, separator, level, path) {\n var i = 0,\n length,\n nextLevel,\n nextPath;\n\n array = _.compact(array);\n length = array.length;\n level = level || 0;\n path = path || '';\n\n for (i; i < length; i++) {\n if (array[i]) {\n _.extend(array[i], {\n level: level,\n path: path\n });\n }\n\n if (array[i].hasOwnProperty(separator)) {\n nextLevel = level + 1;\n nextPath = path ? path + '.' + array[i].label : array[i].label;\n setProperty.call(this, array[i][separator], separator, nextLevel, nextPath);\n }\n }\n\n return array;\n }\n\n /**\n * Preprocessing options list\n *\n * @param {Array} nodes - Options list\n *\n * @return {Object} Object with property - options(options list)\n * and cache options with plain and tree list\n */\n function parseOptions(nodes) {\n var caption,\n value,\n cacheNodes,\n copyNodes;\n\n nodes = setProperty(nodes, 'optgroup');\n copyNodes = JSON.parse(JSON.stringify(nodes));\n cacheNodes = flattenCollection(copyNodes, 'optgroup');\n\n nodes = _.map(nodes, function (node) {\n value = node.value;\n\n if (value == null || value === '') {\n if (_.isUndefined(caption)) {\n caption = node.label;\n }\n } else {\n return node;\n }\n });\n\n return {\n options: _.compact(nodes),\n cacheOptions: {\n plain: _.compact(cacheNodes),\n tree: _.compact(nodes)\n }\n };\n }\n\n return Abstract.extend({\n defaults: {\n options: [],\n listVisible: false,\n value: [],\n filterOptions: false,\n chipsEnabled: true,\n itemsQuantity: '',\n filterInputValue: '',\n filterOptionsFocus: false,\n multiselectFocus: false,\n multiple: true,\n selectType: 'tree',\n lastSelectable: false,\n showFilteredQuantity: true,\n showCheckbox: true,\n levelsVisibility: true,\n openLevelsAction: true,\n showOpenLevelsActionIcon: true,\n optgroupLabels: false,\n closeBtn: true,\n showPath: true,\n labelsDecoration: false,\n disableLabel: false,\n filterRateLimit: 500,\n closeBtnLabel: $t('Done'),\n optgroupTmpl: 'ui/grid/filters/elements/ui-select-optgroup',\n quantityPlaceholder: $t('options'),\n hoverClass: '_hover',\n rootListSelector: 'ul.admin__action-multiselect-menu-inner._root',\n visibleOptionSelector: 'li.admin__action-multiselect-menu-inner-item:visible',\n actionTargetSelector: '.action-menu-item',\n selectedPlaceholders: {\n defaultPlaceholder: $t('Select...'),\n lotPlaceholders: $t('Selected')\n },\n separator: 'optgroup',\n searchOptions: false,\n loading: false,\n searchUrl: false,\n lastSearchKey: '',\n lastSearchPage: 1,\n filterPlaceholder: '',\n emptyOptionsHtml: '',\n cachedSearchResults: {},\n pageLimit: 50,\n deviation: 30,\n validationLoading: false,\n isRemoveSelectedIcon: false,\n debounce: 300,\n missingValuePlaceholder: $t('Entity with ID: %s doesn\\'t exist'),\n isDisplayMissingValuePlaceholder: false,\n listens: {\n listVisible: 'cleanHoveredElement',\n filterInputValue: 'filterOptionsList',\n options: 'checkOptionsList'\n },\n presets: {\n single: {\n showCheckbox: false,\n chipsEnabled: false,\n closeBtn: false\n },\n optgroup: {\n showCheckbox: false,\n lastSelectable: true,\n optgroupLabels: true,\n openLevelsAction: false,\n labelsDecoration: true,\n showOpenLevelsActionIcon: false\n }\n }\n },\n\n /**\n * Initializes UISelect component.\n *\n * @returns {UISelect} Chainable.\n */\n initialize: function () {\n this._super();\n\n $.async(\n this.rootListSelector,\n this,\n this.onRootListRender.bind(this)\n );\n\n return this;\n },\n\n /**\n * Parses options and merges the result with instance\n * Set defaults according to mode and levels configuration\n *\n * @param {Object} config\n * @returns {Object} Chainable.\n */\n initConfig: function (config) {\n var result = parseOptions(config.options),\n defaults = this.constructor.defaults,\n multiple = _.isBoolean(config.multiple) ? config.multiple : defaults.multiple,\n type = config.selectType || defaults.selectType,\n showOpenLevelsActionIcon = _.isBoolean(config.showOpenLevelsActionIcon) ?\n config.showOpenLevelsActionIcon :\n defaults.showOpenLevelsActionIcon,\n openLevelsAction = _.isBoolean(config.openLevelsAction) ?\n config.openLevelsAction :\n defaults.openLevelsAction;\n\n multiple = !multiple ? 'single' : false;\n config.showOpenLevelsActionIcon = showOpenLevelsActionIcon && openLevelsAction;\n _.extend(config, result, defaults.presets[multiple], defaults.presets[type]);\n this._super();\n\n return this;\n },\n\n /**\n * Check child optgroup\n */\n hasChildList: function () {\n return _.find(this.options(), function (option) {\n return !!option[this.separator];\n }, this);\n },\n\n /**\n * Check tree mode\n */\n isTree: function () {\n return this.hasChildList() && this.selectType !== 'optgroup';\n },\n\n /**\n * Add option to lastOptions array\n *\n * @param {Object} data\n * @returns {Boolean}\n */\n addLastElement: function (data) {\n if (!data.hasOwnProperty(this.separator)) {\n !this.cacheOptions.lastOptions ? this.cacheOptions.lastOptions = [] : false;\n\n if (!_.findWhere(\n this.cacheOptions.lastOptions,\n {\n value: data.value\n }\n )\n ) {\n this.cacheOptions.lastOptions.push(data);\n }\n\n return true;\n }\n\n return false;\n },\n\n /**\n * Check options length and set to cache\n * if some options is added\n *\n * @param {Array} options - ui select options\n */\n checkOptionsList: function (options) {\n if (options.length > this.cacheOptions.plain.length) {\n this.cacheOptions.plain = options;\n this.setCaption();\n }\n },\n\n /**\n * Check label decoration\n */\n isLabelDecoration: function (data) {\n return data.hasOwnProperty(this.separator) && this.labelsDecoration;\n },\n\n /**\n * Calls 'initObservable' of parent, initializes 'options' and 'initialOptions'\n * properties, calls 'setOptions' passing options to it\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super();\n this.observe([\n 'listVisible',\n 'placeholder',\n 'multiselectFocus',\n 'options',\n 'itemsQuantity',\n 'filterInputValue',\n 'filterOptionsFocus',\n 'loading',\n 'validationLoading',\n 'isDisplayMissingValuePlaceholder'\n ]);\n\n this.filterInputValue.extend({\n rateLimit: this.filterRateLimit\n });\n\n return this;\n },\n\n /**\n * object with key - keyname and value - handler function for this key\n *\n * @returns {Object} Object with handlers function name.\n */\n keyDownHandlers: function () {\n return {\n enterKey: this.enterKeyHandler,\n escapeKey: this.escapeKeyHandler,\n spaceKey: this.enterKeyHandler,\n pageUpKey: this.pageUpKeyHandler,\n pageDownKey: this.pageDownKeyHandler\n };\n },\n\n /**\n * Processing level visibility for levels\n *\n * @param {Object} data - element data\n *\n * @returns {Boolean} level visibility.\n */\n showLevels: function (data) {\n var curLevel = ++data.level,\n isVisible;\n\n if (data.visible) {\n isVisible = data.visible();\n } else {\n isVisible = !!data.hasOwnProperty(this.separator) &&\n _.isBoolean(this.levelsVisibility) &&\n this.levelsVisibility ||\n data.hasOwnProperty(this.separator) && parseInt(this.levelsVisibility, 10) >= curLevel;\n\n data.visible = ko.observable(isVisible);\n data.isVisited = isVisible;\n }\n\n return isVisible;\n },\n\n /**\n * Processing level visibility for levels\n *\n * @param {Object} data - element data\n *\n * @returns {Boolean} level visibility.\n */\n getLevelVisibility: function (data) {\n if (data.visible) {\n return data.visible();\n }\n\n return this.showLevels(data);\n },\n\n /**\n * Set option to options array.\n *\n * @param {Object} option\n * @param {Array} options\n */\n setOption: function (option, options) {\n var copyOptionsTree;\n\n options = options || this.cacheOptions.tree;\n\n _.each(options, function (opt) {\n if (opt.value == option.parent) { //eslint-disable-line eqeqeq\n delete option.parent;\n opt[this.separator] ? opt[this.separator].push(option) : opt[this.separator] = [option];\n copyOptionsTree = JSON.parse(JSON.stringify(this.cacheOptions.tree));\n this.cacheOptions.plain = flattenCollection(copyOptionsTree, this.separator);\n this.options(this.cacheOptions.tree);\n } else if (opt[this.separator]) {\n this.setOption(option, opt[this.separator]);\n }\n }, this);\n },\n\n /**\n * Handler outerClick event. Closed options list\n */\n outerClick: function () {\n this.listVisible() ? this.listVisible(false) : false;\n\n if (isTouchDevice) {\n this.multiselectFocus(false);\n }\n },\n\n /**\n * Handler keydown event to filter options input\n *\n * @returns {Boolean} Returned true for emersion events\n */\n filterOptionsKeydown: function (data, event) {\n var key = keyCodes[event.keyCode];\n\n !this.isTabKey(event) ? event.stopPropagation() : false;\n\n if (key === 'pageDownKey' || key === 'pageUpKey') {\n event.preventDefault();\n this.filterOptionsFocus(false);\n this.cacheUiSelect.focus();\n }\n\n this.keydownSwitcher(data, event);\n\n return true;\n },\n\n /**\n * Filtered options list by value from filter options list\n */\n filterOptionsList: function () {\n var value = this.filterInputValue().trim().toLowerCase(),\n array = [];\n\n if (value && value.length < 2) {\n return false;\n }\n\n if (this.searchOptions) {\n return _.debounce(this.loadOptions.bind(this, value), this.debounce)();\n }\n\n this.cleanHoveredElement();\n\n if (!value) {\n this.renderPath = false;\n this.options(this.cacheOptions.tree);\n this._setItemsQuantity(false);\n\n return false;\n }\n\n this.showPath ? this.renderPath = true : false;\n\n if (this.filterInputValue()) {\n\n array = this.selectType === 'optgroup' ?\n this._getFilteredArray(this.cacheOptions.lastOptions, value) :\n this._getFilteredArray(this.cacheOptions.plain, value);\n\n if (!value.length) {\n this.options(this.cacheOptions.plain);\n this._setItemsQuantity(this.cacheOptions.plain.length);\n } else {\n this.options(array);\n this._setItemsQuantity(array.length);\n }\n\n return false;\n }\n\n this.options(this.cacheOptions.plain);\n },\n\n /**\n * Filtered options list by value from filter options list\n *\n * @param {Array} list - option list\n * @param {String} value\n *\n * @returns {Array} filters result\n */\n _getFilteredArray: function (list, value) {\n var i = 0,\n array = [],\n curOption;\n\n for (i; i < list.length; i++) {\n curOption = list[i].label.toLowerCase();\n\n if (curOption.indexOf(value) > -1) {\n array.push(list[i]); /*eslint max-depth: [2, 4]*/\n }\n }\n\n return array;\n },\n\n /**\n * Get path to current option\n *\n * @param {Object} data - option data\n * @returns {String} path\n */\n getPath: function (data) {\n var pathParts,\n createdPath = '';\n\n if (this.renderPath) {\n pathParts = data.path.split('.');\n _.each(pathParts, function (curData) {\n createdPath = createdPath ? createdPath + ' / ' + curData : curData;\n });\n\n return createdPath;\n }\n },\n\n /**\n * Set filtered items quantity\n *\n * @param {Object} data - option data\n */\n _setItemsQuantity: function (data) {\n if (this.showFilteredQuantity) {\n data || parseInt(data, 10) === 0 ?\n this.itemsQuantity(data + ' ' + this.quantityPlaceholder) :\n this.itemsQuantity('');\n }\n },\n\n /**\n * Remove element from selected array\n */\n removeSelected: function (value, data, event) {\n event ? event.stopPropagation() : false;\n this.value.remove(value);\n },\n\n /**\n * Checked key name\n *\n * @returns {Boolean}\n */\n isTabKey: function (event) {\n return keyCodes[event.keyCode] === 'tabKey';\n },\n\n /**\n * Clean hoveredElement variable\n *\n * @returns {Object} Chainable\n */\n cleanHoveredElement: function () {\n if (this.hoveredElement) {\n $(this.hoveredElement)\n .children(this.actionTargetSelector)\n .removeClass(this.hoverClass);\n\n this.hoveredElement = null;\n }\n\n return this;\n },\n\n /**\n * Check selected option\n *\n * @param {String} value - option value\n * @return {Boolean}\n */\n isSelected: function (value) {\n return this.multiple ? _.contains(this.value(), value) : this.value() === value;\n },\n\n /**\n * Check selected option\n *\n * @param {Object} option - option value\n * @return {Boolean}\n */\n isSelectedValue: function (option) {\n if (_.isUndefined(option)) {\n return false;\n }\n\n return this.isSelected(option.value);\n },\n\n /**\n * Check optgroup label\n *\n * @param {Object} data - element data\n * @return {Boolean}\n */\n isOptgroupLabels: function (data) {\n return data.hasOwnProperty(this.separator) && this.optgroupLabels;\n },\n\n /**\n * Check hovered option\n *\n * @param {Object} data - element data\n * @return {Boolean}\n */\n isHovered: function (data) {\n var element = this.hoveredElement,\n elementData;\n\n if (!element) {\n return false;\n }\n\n elementData = ko.dataFor(this.hoveredElement);\n\n if (_.isUndefined(elementData)) {\n return false;\n }\n\n return data.value === elementData.value;\n },\n\n /**\n * Toggle list visibility\n *\n * @returns {Object} Chainable\n */\n toggleListVisible: function () {\n this.listVisible(!this.listVisible());\n\n return this;\n },\n\n /**\n * Get selected element labels\n *\n * @returns {Array} array labels\n */\n getSelected: function () {\n var selected = this.value();\n\n return this.cacheOptions.plain.filter(function (opt) {\n return _.isArray(selected) ?\n _.contains(selected, opt.value) :\n selected == opt.value;//eslint-disable-line eqeqeq\n });\n },\n\n /**\n * Toggle activity list element\n *\n * @param {Object} data - selected option data\n * @returns {Object} Chainable\n */\n toggleOptionSelected: function (data) {\n var isSelected = this.isSelected(data.value);\n\n if (this.lastSelectable && data.hasOwnProperty(this.separator)) {\n return this;\n }\n\n if (!this.multiple) {\n if (!isSelected) {\n this.value(data.value);\n }\n this.listVisible(false);\n } else {\n if (!isSelected) { /*eslint no-lonely-if: 0*/\n this.value.push(data.value);\n } else {\n this.value(_.without(this.value(), data.value));\n }\n }\n\n return this;\n },\n\n /**\n * Change visibility to child level\n *\n * @param {Object} data - element data\n */\n openChildLevel: function (data) {\n var contextElement = data,\n isVisible;\n\n if (\n this.openLevelsAction &&\n data.hasOwnProperty(this.separator) && _.isBoolean(this.levelsVisibility) ||\n this.openLevelsAction &&\n data.hasOwnProperty(this.separator) && parseInt(this.levelsVisibility, 10) <= data.level\n ) {\n isVisible = !contextElement.visible();\n\n if (isVisible && !contextElement.isVisited) {\n contextElement.isVisited = true;\n }\n\n contextElement.visible(isVisible);\n }\n },\n\n /**\n * Check selected elements\n *\n * @returns {Boolean}\n */\n hasData: function () {\n if (!this.value()) {\n this.value([]);\n }\n\n return this.value() ? !!this.value().length : false;\n },\n\n /**\n * @deprecated\n */\n onMousemove: function () {},\n\n /**\n * Handles hover on list items.\n *\n * @param {Object} event - mousemove event\n */\n onDelegatedMouseMouve: function (event) {\n var target = $(event.currentTarget).closest(this.visibleOptionSelector)[0];\n\n if (this.isCursorPositionChange(event) || this.hoveredElement === target) {\n return;\n }\n\n this._hoverTo(target);\n this.setCursorPosition(event);\n },\n\n /**\n * Get option index\n *\n * @param {Object} data - object with data about this element\n *\n * @returns {Number}\n */\n getOptionIndex: function (data) {\n var index;\n\n _.each(this.cacheOptions.plain, function (opt, id) {\n if (data.value === opt.value) {\n index = id;\n }\n });\n\n return index;\n },\n\n /**\n * Set X and Y cursor position\n *\n * @param {Object} event - mousemove event\n */\n setCursorPosition: function (event) {\n this.cursorPosition = {\n x: event.pageX,\n y: event.pageY\n };\n },\n\n /**\n * Check previous and current cursor position\n *\n * @param {Object} event - mousemove event\n * @returns {Boolean}\n */\n isCursorPositionChange: function (event) {\n return this.cursorPosition &&\n this.cursorPosition.x === event.pageX &&\n this.cursorPosition.y === event.pageY;\n },\n\n /**\n * Set true to observable variable multiselectFocus\n * @param {Object} ctx\n * @param {Object} event - focus event\n */\n onFocusIn: function (ctx, event) {\n !this.cacheUiSelect ? this.cacheUiSelect = event.target : false;\n this.multiselectFocus(true);\n },\n\n /**\n * Set false to observable variable multiselectFocus\n * and close list\n */\n onFocusOut: function () {\n this.multiselectFocus(false);\n },\n\n /**\n * Handler enter key, if select list is closed - open select,\n * if select list is open toggle selected current option\n */\n enterKeyHandler: function () {\n\n if (this.filterOptionsFocus()) {\n return false;\n }\n\n if (this.listVisible()) {\n if (this.hoveredElement) {\n this.toggleOptionSelected(ko.dataFor(this.hoveredElement));\n }\n } else {\n this.setListVisible(true);\n }\n },\n\n /**\n * Handler escape key, if select list is open - closes it,\n */\n escapeKeyHandler: function () {\n this.listVisible() ? this.setListVisible(false) : false;\n },\n\n /**\n * Handler pageDown key, selected next option in list, if current option is last\n * selected first option in list\n */\n pageDownKeyHandler: function () {\n this._setHoverToElement(1);\n },\n\n /**\n * Get jQuery element by option data\n *\n * @param {Object} data - option data\n *\n * @returns {Object} jQuery element\n */\n _getElemByData: function (data) {\n var i = 0,\n list = $(this.cacheUiSelect).find('li'),\n length = this.options().length,\n result;\n\n for (i; i < length; i++) {\n if (this.options()[i].value === data.value) {\n result = $(list[i]);\n }\n }\n\n return result;\n },\n\n /**\n * Set hover to visible element\n *\n * @param {Number} direction - iterator\n */\n _setHoverToElement: function (direction) {\n var element;\n\n if (direction === 1) {\n element = this._getNextElement();\n } else if (direction === -1) {\n element = this._getPreviousElement();\n }\n\n if (element) {\n this._hoverTo(element);\n this._scrollTo(element);\n }\n },\n\n /**\n * Find current hovered element\n * and change scroll position\n *\n * @param {Number} element - element index\n */\n _scrollTo: function (element) {\n var curEl = $(element).children(this.actionTargetSelector),\n wrapper = $(this.rootList),\n curElPos = {},\n wrapperPos = {};\n\n curElPos.start = curEl.offset().top;\n curElPos.end = curElPos.start + curEl.outerHeight();\n\n wrapperPos.start = wrapper.offset().top;\n wrapperPos.end = wrapperPos.start + wrapper.height();\n\n if (curElPos.start < wrapperPos.start) {\n wrapper.scrollTop(wrapper.scrollTop() - (wrapperPos.start - curElPos.start));\n } else if (curElPos.end > wrapperPos.end) {\n wrapper.scrollTop(wrapper.scrollTop() + curElPos.end - wrapperPos.end);\n }\n },\n\n /**\n * Handler pageUp key, selected previous option in list, if current option is first -\n * selected last option in list\n */\n pageUpKeyHandler: function () {\n this._setHoverToElement(-1);\n },\n\n /**\n * Switcher to parse keydown event and delegate event to needful method\n *\n * @param {Object} data - element data\n * @param {Object} event - keydown event\n * @returns {Boolean} if handler for this event doesn't found return true\n */\n keydownSwitcher: function (data, event) {\n var keyName = keyCodes[event.keyCode];\n\n if (this.isTabKey(event)) {\n if (!this.filterOptionsFocus() && this.listVisible() && this.filterOptions) {\n this.cacheUiSelect.blur();\n this.filterOptionsFocus(true);\n this.cleanHoveredElement();\n\n return false;\n }\n this.listVisible(false);\n\n return true;\n }\n\n if (this.keyDownHandlers().hasOwnProperty(keyName)) {\n this.keyDownHandlers()[keyName].apply(this, arguments);\n } else {\n return true;\n }\n },\n\n /**\n * Set caption\n */\n setCaption: function () {\n var length, caption = '';\n\n if (!_.isArray(this.value()) && this.value()) {\n length = 1;\n } else if (this.value()) {\n length = this.value().length;\n } else {\n this.value([]);\n length = 0;\n }\n this.warn(caption);\n\n //check if option was removed\n if (this.isDisplayMissingValuePlaceholder && length && !this.getSelected().length) {\n caption = this.missingValuePlaceholder.replace('%s', this.value());\n this.placeholder(caption);\n this.warn(caption);\n\n return this.placeholder();\n }\n\n if (length > 1) {\n this.placeholder(length + ' ' + this.selectedPlaceholders.lotPlaceholders);\n } else if (length && this.getSelected().length) {\n this.placeholder(this.getSelected()[0].label);\n } else {\n this.placeholder(this.selectedPlaceholders.defaultPlaceholder);\n }\n\n return this.placeholder();\n },\n\n /**\n * Set list status, open or close\n *\n * @param {Boolean} value - variable for set list visible status\n */\n setListVisible: function (value) {\n this.listVisible(value);\n },\n\n /**\n * Processes preview for option by it's value, and sets the result\n * to 'preview' observable\n *\n * @returns {String}\n */\n getPreview: function () {\n var selected = this.getSelected();\n\n return selected.map(function (option) {\n return option.label;\n }).join(', ');\n },\n\n /**\n * Defines previous option element to\n * the one that is currently hovered.\n *\n * @returns {Element}\n */\n _getPreviousElement: function () {\n var currentElement = this.hoveredElement,\n lastElement = this._getLastIn(this.rootList),\n previousElement;\n\n if (!currentElement) {\n return lastElement;\n }\n\n previousElement = $(currentElement).prev()[0];\n\n return this._getLastIn(previousElement) ||\n previousElement ||\n this._getFirstParentOf(currentElement) ||\n lastElement;\n },\n\n /**\n * Defines next option element to\n * the one that is currently hovered.\n *\n * @returns {Element}\n */\n _getNextElement: function () {\n var currentElement = this.hoveredElement,\n firstElement = this._getFirstIn(this.rootList);\n\n if (!currentElement) {\n return firstElement;\n }\n\n return this._getFirstIn(currentElement) ||\n $(currentElement).next()[0] ||\n this._getParentsOf(currentElement).next()[0] ||\n firstElement;\n },\n\n /**\n * Returns first option element in provided scope.\n *\n * @param {Element} scope\n * @returns {Element}\n */\n _getFirstIn: function (scope) {\n return $(scope).find(this.visibleOptionSelector)[0];\n },\n\n /**\n * Returns last descendant option element in provided scope.\n *\n * @param {Element} scope\n * @returns {Element}\n */\n _getLastIn: function (scope) {\n return $(scope).find(this.visibleOptionSelector).last()[0];\n },\n\n /**\n * Returns a collection of parent option elements.\n *\n * @param {Element} scope\n * @returns {jQueryCollection}\n */\n _getParentsOf: function (scope) {\n return $(scope).parents(this.visibleOptionSelector);\n },\n\n /**\n * Returns first parent option element.\n *\n * @param {Element} scope\n * @returns {Element}\n */\n _getFirstParentOf: function (scope) {\n return this._getParentsOf(scope)[0];\n },\n\n /**\n * Sets hover class to provided option element.\n *\n * @param {Element} element\n */\n _hoverTo: function (element) {\n if (this.hoveredElement) {\n $(this.hoveredElement)\n .children(this.actionTargetSelector)\n .removeClass(this.hoverClass);\n }\n\n $(element)\n .children(this.actionTargetSelector)\n .addClass(this.hoverClass);\n\n this.hoveredElement = element;\n },\n\n /**\n * Callback which fires when root list element is rendered.\n *\n * @param {Element} element\n */\n onRootListRender: function (element) {\n var targetSelector = 'li > ' + this.actionTargetSelector;\n\n this.rootList = element;\n\n $(this.rootList).on(\n 'mousemove',\n targetSelector,\n this.onDelegatedMouseMouve.bind(this)\n );\n },\n\n /**\n * Returns options from cache or send request\n *\n * @param {String} searchKey\n */\n loadOptions: function (searchKey) {\n var currentPage = searchKey === this.lastSearchKey ? this.lastSearchPage + 1 : 1,\n cachedSearchResult;\n\n this.renderPath = !!this.showPath;\n\n if (this.isSearchKeyCached(searchKey)) {\n cachedSearchResult = this.getCachedSearchResults(searchKey);\n this.options(cachedSearchResult.options);\n this.afterLoadOptions(searchKey, cachedSearchResult.lastPage, cachedSearchResult.total);\n\n return;\n }\n\n if (searchKey !== this.lastSearchKey) {\n this.options([]);\n }\n this.processRequest(searchKey, currentPage);\n },\n\n /**\n * Load more options on scroll down\n * @param {Object} data\n * @param {Event} event\n */\n onScrollDown: function (data, event) {\n var clientHight = event.target.scrollTop + event.target.clientHeight,\n scrollHeight = event.target.scrollHeight;\n\n if (!this.searchOptions) {\n return;\n }\n\n if (clientHight > scrollHeight - this.deviation && !this.isSearchKeyCached(data.filterInputValue())) {\n this.loadOptions(data.filterInputValue());\n }\n },\n\n /**\n * Returns cached search result by search key\n *\n * @param {String} searchKey\n * @return {Object}\n */\n getCachedSearchResults: function (searchKey) {\n if (this.cachedSearchResults.hasOwnProperty(searchKey)) {\n return this.cachedSearchResults[searchKey];\n }\n\n return {\n options: [],\n lastPage: 1,\n total: 0\n };\n },\n\n /**\n * Cache loaded data\n *\n * @param {String} searchKey\n * @param {Array} optionsArray\n * @param {Number} page\n * @param {Number} total\n */\n setCachedSearchResults: function (searchKey, optionsArray, page, total) {\n var cachedData = {};\n\n cachedData.options = optionsArray;\n cachedData.lastPage = page;\n cachedData.total = total;\n this.cachedSearchResults[searchKey] = cachedData;\n },\n\n /**\n * Check if search key cached\n *\n * @param {String} searchKey\n * @return {Boolean}\n */\n isSearchKeyCached: function (searchKey) {\n var totalCached = this.cachedSearchResults.hasOwnProperty(searchKey) ?\n this.deviation * this.cachedSearchResults[searchKey].lastPage :\n 0;\n\n return totalCached > 0 && totalCached >= this.cachedSearchResults[searchKey].total;\n },\n\n /**\n * Submit request to load data\n *\n * @param {String} searchKey\n * @param {Number} page\n */\n processRequest: function (searchKey, page) {\n var total = 0,\n existingOptions = this.options();\n\n this.loading(true);\n $.ajax({\n url: this.searchUrl,\n type: 'post',\n dataType: 'json',\n context: this,\n data: {\n searchKey: searchKey,\n page: page,\n limit: this.pageLimit\n },\n\n /** @param {Object} response */\n success: function (response) {\n _.each(response.options, function (opt) {\n existingOptions.push(opt);\n });\n total = response.total;\n this.options(existingOptions);\n },\n\n /** set empty array if error occurs */\n error: function () {\n this.options([]);\n },\n\n /** cache options and stop loading*/\n complete: function () {\n this.setCachedSearchResults(searchKey, this.options(), page, total);\n this.afterLoadOptions(searchKey, page, total);\n }\n });\n },\n\n /**\n * Stop loading and update data after options were updated\n *\n * @param {String} searchKey\n * @param {Number} page\n * @param {Number} total\n */\n afterLoadOptions: function (searchKey, page, total) {\n this._setItemsQuantity(total);\n this.lastSearchPage = page;\n this.lastSearchKey = searchKey;\n this.loading(false);\n }\n });\n});\n","Magento_Ui/js/form/element/wysiwyg.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'wysiwygAdapter',\n 'Magento_Ui/js/lib/view/utils/async',\n 'underscore',\n 'ko',\n './abstract',\n 'mage/adminhtml/events',\n 'Magento_Variable/variables'\n], function (wysiwyg, $, _, ko, Abstract, varienGlobalEvents) {\n 'use strict';\n\n return Abstract.extend({\n currentWysiwyg: undefined,\n defaults: {\n elementSelector: 'textarea',\n suffixRegExpPattern: '${ $.wysiwygUniqueSuffix }',\n $wysiwygEditorButton: '',\n links: {\n value: '${ $.provider }:${ $.dataScope }'\n },\n template: 'ui/form/field',\n elementTmpl: 'ui/form/element/wysiwyg',\n content: '',\n showSpinner: false,\n loading: false,\n listens: {\n disabled: 'setDisabled'\n }\n },\n\n /**\n *\n * @returns {} Chainable.\n */\n initialize: function () {\n this._super()\n .initNodeListener();\n\n $.async({\n component: this,\n selector: 'button'\n }, function (element) {\n this.$wysiwygEditorButton = this.$wysiwygEditorButton ?\n this.$wysiwygEditorButton.add($(element)) : $(element);\n }.bind(this));\n\n // disable editor completely after initialization is field is disabled\n varienGlobalEvents.attachEventHandler('wysiwygEditorInitialized', function () {\n if (!_.isUndefined(window.tinyMceEditors)) {\n this.currentWysiwyg = window.tinyMceEditors[this.wysiwygId];\n }\n\n if (this.disabled()) {\n this.setDisabled(true);\n }\n }.bind(this));\n\n return this;\n },\n\n /** @inheritdoc */\n initConfig: function (config) {\n var pattern = config.suffixRegExpPattern || this.constructor.defaults.suffixRegExpPattern;\n\n pattern = pattern.replace(/\\$/g, '\\\\$&');\n config.content = config.content.replace(new RegExp(pattern, 'g'), this.getUniqueSuffix(config));\n this._super();\n\n return this;\n },\n\n /**\n * Build unique id based on name, underscore separated.\n *\n * @param {Object} config\n */\n getUniqueSuffix: function (config) {\n return config.name.replace(/(\\.|-)/g, '_');\n },\n\n /**\n * @inheritdoc\n */\n destroy: function () {\n this._super();\n wysiwyg.removeEvents(this.wysiwygId);\n },\n\n /**\n *\n * @returns {exports}\n */\n initObservable: function () {\n this._super()\n .observe('value');\n\n return this;\n },\n\n /**\n *\n * @returns {} Chainable.\n */\n initNodeListener: function () {\n $.async({\n component: this,\n selector: this.elementSelector\n }, this.setElementNode.bind(this));\n\n return this;\n },\n\n /**\n *\n * @param {HTMLElement} node\n */\n setElementNode: function (node) {\n $(node).bindings({\n value: this.value\n });\n },\n\n /**\n * Set disabled property to wysiwyg component\n *\n * @param {Boolean} disabled\n */\n setDisabled: function (disabled) {\n if (this.$wysiwygEditorButton && disabled) {\n this.$wysiwygEditorButton.prop('disabled', 'disabled');\n } else if (this.$wysiwygEditorButton) {\n this.$wysiwygEditorButton.removeProp('disabled');\n }\n\n /* eslint-disable no-undef */\n if (!_.isUndefined(this.currentWysiwyg) && this.currentWysiwyg.activeEditor()) {\n this.currentWysiwyg.setEnabledStatus(!disabled);\n this.currentWysiwyg.getPluginButtons().prop('disabled', disabled);\n }\n }\n });\n});\n","Magento_Ui/js/form/element/abstract.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiLayout',\n 'uiElement',\n 'Magento_Ui/js/lib/validation/validator'\n], function (_, utils, layout, Element, validator) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n visible: true,\n preview: '',\n focused: false,\n required: false,\n disabled: false,\n valueChangedByUser: false,\n elementTmpl: 'ui/form/element/input',\n tooltipTpl: 'ui/form/element/helper/tooltip',\n fallbackResetTpl: 'ui/form/element/helper/fallback-reset',\n 'input_type': 'input',\n placeholder: false,\n description: '',\n labelVisible: true,\n label: '',\n error: '',\n warn: '',\n notice: '',\n customScope: '',\n default: '',\n isDifferedFromDefault: false,\n showFallbackReset: false,\n additionalClasses: {},\n isUseDefault: '',\n serviceDisabled: false,\n valueUpdate: false, // ko binding valueUpdate\n\n switcherConfig: {\n component: 'Magento_Ui/js/form/switcher',\n name: '${ $.name }_switcher',\n target: '${ $.name }',\n property: 'value'\n },\n listens: {\n visible: 'setPreview',\n value: 'setDifferedFromDefault',\n '${ $.provider }:data.reset': 'reset',\n '${ $.provider }:data.overload': 'overload',\n '${ $.provider }:${ $.customScope ? $.customScope + \".\" : \"\"}data.validate': 'validate',\n 'isUseDefault': 'toggleUseDefault'\n },\n ignoreTmpls: {\n value: true\n },\n\n links: {\n value: '${ $.provider }:${ $.dataScope }'\n }\n },\n\n /**\n * Invokes initialize method of parent class,\n * contains initialization logic\n */\n initialize: function () {\n _.bindAll(this, 'reset');\n\n this._super()\n .setInitialValue()\n ._setClasses()\n .initSwitcher();\n\n return this;\n },\n\n /**\n * Checks if component has error.\n *\n * @returns {Object}\n */\n checkInvalid: function () {\n return this.error() && this.error().length ? this : null;\n },\n\n /**\n * Initializes observable properties of instance\n *\n * @returns {Abstract} Chainable.\n */\n initObservable: function () {\n var rules = this.validation = this.validation || {};\n\n this._super();\n\n this.observe('error disabled focused preview visible value warn notice isDifferedFromDefault')\n .observe('isUseDefault serviceDisabled')\n .observe({\n 'required': !!rules['required-entry']\n });\n\n return this;\n },\n\n /**\n * Initializes regular properties of instance.\n *\n * @returns {Abstract} Chainable.\n */\n initConfig: function () {\n var uid = utils.uniqueid(),\n name,\n valueUpdate,\n scope;\n\n this._super();\n\n scope = this.dataScope.split('.');\n name = scope.length > 1 ? scope.slice(1) : scope;\n\n valueUpdate = this.showFallbackReset ? 'afterkeydown' : this.valueUpdate;\n\n _.extend(this, {\n uid: uid,\n noticeId: 'notice-' + uid,\n errorId: 'error-' + uid,\n inputName: utils.serializeName(name.join('.')),\n valueUpdate: valueUpdate\n });\n\n return this;\n },\n\n /**\n * Initializes switcher element instance.\n *\n * @returns {Abstract} Chainable.\n */\n initSwitcher: function () {\n if (this.switcherConfig.enabled) {\n layout([this.switcherConfig]);\n }\n\n return this;\n },\n\n /**\n * Sets initial value of the element and subscribes to it's changes.\n *\n * @returns {Abstract} Chainable.\n */\n setInitialValue: function () {\n this.initialValue = this.getInitialValue();\n\n if (this.value.peek() !== this.initialValue) {\n this.value(this.initialValue);\n }\n\n this.on('value', this.onUpdate.bind(this));\n this.isUseDefault(this.disabled());\n\n return this;\n },\n\n /**\n * Extends 'additionalClasses' object.\n *\n * @returns {Abstract} Chainable.\n */\n _setClasses: function () {\n var additional = this.additionalClasses;\n\n if (_.isString(additional)) {\n this.additionalClasses = {};\n\n if (additional.trim().length) {\n additional = additional.trim().split(' ');\n\n additional.forEach(function (name) {\n if (name.length) {\n this.additionalClasses[name] = true;\n }\n }, this);\n }\n }\n\n _.extend(this.additionalClasses, {\n _required: this.required,\n _error: this.error,\n _warn: this.warn,\n _disabled: this.disabled\n });\n\n return this;\n },\n\n /**\n * Gets initial value of element\n *\n * @returns {*} Elements' value.\n */\n getInitialValue: function () {\n var values = [this.value(), this.default],\n value;\n\n values.some(function (v) {\n if (v !== null && v !== undefined) {\n value = v;\n\n return true;\n }\n\n return false;\n });\n\n return this.normalizeData(value);\n },\n\n /**\n * Sets 'value' as 'hidden' property's value, triggers 'toggle' event,\n * sets instance's hidden identifier in params storage based on\n * 'value'.\n *\n * @returns {Abstract} Chainable.\n */\n setVisible: function (isVisible) {\n this.visible(isVisible);\n\n return this;\n },\n\n /**\n * Show element.\n *\n * @returns {Abstract} Chainable.\n */\n show: function () {\n this.visible(true);\n\n return this;\n },\n\n /**\n * Hide element.\n *\n * @returns {Abstract} Chainable.\n */\n hide: function () {\n this.visible(false);\n\n return this;\n },\n\n /**\n * Disable element.\n *\n * @returns {Abstract} Chainable.\n */\n disable: function () {\n this.disabled(true);\n\n return this;\n },\n\n /**\n * Enable element.\n *\n * @returns {Abstract} Chainable.\n */\n enable: function () {\n this.disabled(false);\n\n return this;\n },\n\n /**\n *\n * @param {(String|Object)} rule\n * @param {(Object|Boolean)} [options]\n * @returns {Abstract} Chainable.\n */\n setValidation: function (rule, options) {\n var rules = utils.copy(this.validation),\n changed;\n\n if (_.isObject(rule)) {\n _.extend(this.validation, rule);\n } else {\n this.validation[rule] = options;\n }\n\n changed = utils.compare(rules, this.validation).equal;\n\n if (changed) {\n this.required(!!rules['required-entry']);\n this.validate();\n }\n\n return this;\n },\n\n /**\n * Returns unwrapped preview observable.\n *\n * @returns {String} Value of the preview observable.\n */\n getPreview: function () {\n return this.value();\n },\n\n /**\n * Checks if element has addons\n *\n * @returns {Boolean}\n */\n hasAddons: function () {\n return this.addbefore || this.addafter;\n },\n\n /**\n * Checks if element has service setting\n *\n * @returns {Boolean}\n */\n hasService: function () {\n return this.service && this.service.template;\n },\n\n /**\n * Defines if value has changed.\n *\n * @returns {Boolean}\n */\n hasChanged: function () {\n var notEqual = this.value() !== this.initialValue;\n\n return !this.visible() ? false : notEqual;\n },\n\n /**\n * Checks if 'value' is not empty.\n *\n * @returns {Boolean}\n */\n hasData: function () {\n return !utils.isEmpty(this.value());\n },\n\n /**\n * Sets value observable to initialValue property.\n *\n * @returns {Abstract} Chainable.\n */\n reset: function () {\n this.value(this.initialValue);\n this.error(false);\n\n return this;\n },\n\n /**\n * Sets current state as initial.\n */\n overload: function () {\n this.setInitialValue();\n this.bubble('update', this.hasChanged());\n },\n\n /**\n * Clears 'value' property.\n *\n * @returns {Abstract} Chainable.\n */\n clear: function () {\n this.value('');\n\n return this;\n },\n\n /**\n * Converts values like 'null' or 'undefined' to an empty string.\n *\n * @param {*} value - Value to be processed.\n * @returns {*}\n */\n normalizeData: function (value) {\n return utils.isEmpty(value) ? '' : value;\n },\n\n /**\n * Validates itself by it's validation rules using validator object.\n * If validation of a rule did not pass, writes it's message to\n * 'error' observable property.\n *\n * @returns {Object} Validate information.\n */\n validate: function () {\n var value = this.value(),\n result = validator(this.validation, value, this.validationParams),\n message = !this.disabled() && this.visible() ? result.message : '',\n isValid = this.disabled() || !this.visible() || result.passed;\n\n this.error(message);\n this.error.valueHasMutated();\n this.bubble('error', message);\n\n //TODO: Implement proper result propagation for form\n if (this.source && !isValid) {\n this.source.set('params.invalid', true);\n }\n\n return {\n valid: isValid,\n target: this\n };\n },\n\n /**\n * Callback that fires when 'value' property is updated.\n */\n onUpdate: function () {\n this.bubble('update', this.hasChanged());\n\n this.validate();\n },\n\n /**\n * Restore value to default\n */\n restoreToDefault: function () {\n this.value(this.default);\n this.focused(true);\n },\n\n /**\n * Update whether value differs from default value\n */\n setDifferedFromDefault: function () {\n var value = typeof this.value() != 'undefined' && this.value() !== null ? this.value() : '',\n defaultValue = typeof this.default != 'undefined' && this.default !== null ? this.default : '';\n\n this.isDifferedFromDefault(value !== defaultValue);\n },\n\n /**\n * @param {Boolean} state\n */\n toggleUseDefault: function (state) {\n this.disabled(state);\n\n if (this.source && this.hasService()) {\n this.source.set('data.use_default.' + this.index, Number(state));\n }\n },\n\n /**\n * Callback when value is changed by user\n */\n userChanges: function () {\n this.valueChangedByUser = true;\n },\n\n /**\n * Returns correct id for 'aria-describedby' accessibility attribute\n *\n * @returns {Boolean|String}\n */\n getDescriptionId: function () {\n var id = false;\n\n if (this.error()) {\n id = this.errorId;\n } else if (this.notice()) {\n id = this.noticeId;\n }\n\n return id;\n }\n });\n});\n","Magento_Ui/js/form/element/multiselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n './select'\n], function (_, utils, Select) {\n 'use strict';\n\n return Select.extend({\n defaults: {\n size: 5,\n elementTmpl: 'ui/form/element/multiselect',\n listens: {\n value: 'setDifferedFromDefault setPrepareToSendData'\n }\n },\n\n /**\n * @inheritdoc\n */\n setInitialValue: function () {\n this._super();\n\n this.initialValue = utils.copy(this.initialValue);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n normalizeData: function (value) {\n if (utils.isEmpty(value)) {\n value = [];\n }\n\n return _.isString(value) ? value.split(',') : value;\n },\n\n /**\n * Sets the prepared data to dataSource\n * by path, where key is component link to dataSource with\n * suffix \"-prepared-for-send\"\n *\n * @param {Array} data - current component value\n */\n setPrepareToSendData: function (data) {\n if (_.isUndefined(data) || !data.length) {\n data = '';\n }\n\n this.source.set(this.dataScope + '-prepared-for-send', data);\n },\n\n /**\n * @inheritdoc\n */\n getInitialValue: function () {\n var values = [\n this.normalizeData(this.source.get(this.dataScope)),\n this.normalizeData(this.default)\n ],\n value;\n\n values.some(function (v) {\n return _.isArray(v) && (value = utils.copy(v)) && !_.isEmpty(v);\n });\n\n return value;\n },\n\n /**\n * @inheritdoc\n */\n hasChanged: function () {\n var value = this.value(),\n initial = this.initialValue;\n\n return !utils.equalArrays(value, initial);\n },\n\n /**\n * @inheritdoc\n */\n reset: function () {\n this.value(utils.copy(this.initialValue));\n this.error(false);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n clear: function () {\n this.value([]);\n this.error(false);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/color-picker-palette.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/**\n * @api\n */\ndefine([], function () {\n 'use strict';\n\n return [\n [\n 'rgb(0,0,0)', 'rgb(52,52,52)', 'rgb(83,83,83)', 'rgb(135,135,135)', 'rgb(193,193,193)',\n 'rgb(234,234,234)', 'rgb(240,240,240)', 'rgb(255,255,255)'\n ],\n [\n 'rgb(252,0,9)', 'rgb(253,135,10)', 'rgb(255,255,13)', 'rgb(35,255,9)', 'rgb(33,255,255)',\n 'rgb(0,0,254)', 'rgb(132,0,254)', 'rgb(251,0,255)'\n ],\n [\n 'rgb(240,192,194)', 'rgb(251,223,194)', 'rgb(255,241,193)', 'rgb(210,230,201)',\n 'rgb(199,217,220)', 'rgb(197,219,240)', 'rgb(208,200,227)', 'rgb(229,199,212)'\n ],\n [\n 'rgb(228,133,135)', 'rgb(246,193,139)', 'rgb(254,225,136)', 'rgb(168,208,152)',\n 'rgb(146,184,190)', 'rgb(143,184,227)', 'rgb(165,148,204)', 'rgb(202,147,175)'\n ],\n [\n 'rgb(214,78,83)', 'rgb(243,163,88)', 'rgb(254,211,83)', 'rgb(130,187,106)',\n 'rgb(99,149,159)', 'rgb(93,150,211)', 'rgb(123,100,182)', 'rgb(180,100,142)'\n ],\n [\n 'rgb(190,0,5)', 'rgb(222,126,44)', 'rgb(236,183,39)', 'rgb(89,155,61)', 'rgb(55,110,123)',\n 'rgb(49,112,185)', 'rgb(83,55,150)', 'rgb(147,55,101)'\n ],\n [\n 'rgb(133,0,3)', 'rgb(163,74,10)', 'rgb(177,127,7)', 'rgb(45,101,23)', 'rgb(18,62,74)',\n 'rgb(14,62,129)', 'rgb(40,15,97)', 'rgb(95,16,55)'\n ],\n [\n 'rgb(81,0,1)', 'rgb(100,48,7)', 'rgb(107,78,3)', 'rgb(31,63,16)',\n 'rgb(13,39,46)', 'rgb(10,40,79)', 'rgb(24,12,59)', 'rgb(59,10,36)'\n ]\n ];\n});\n","Magento_Ui/js/form/element/website.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n './select'\n], function (_, registry, Select) {\n 'use strict';\n\n return Select.extend({\n defaults: {\n customerId: null,\n isGlobalScope: 0\n },\n\n /**\n * Website component constructor.\n * @returns {exports}\n */\n initialize: function () {\n this._super();\n\n if (this.customerId || this.isGlobalScope) {\n this.disable(true);\n }\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/color-picker.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'mage/translate',\n 'Magento_Ui/js/form/element/abstract',\n 'Magento_Ui/js/form/element/color-picker-palette'\n], function ($t, Abstract, palette) {\n 'use strict';\n\n return Abstract.extend({\n\n defaults: {\n colorPickerConfig: {\n chooseText: $t('Apply'),\n cancelText: $t('Cancel'),\n maxSelectionSize: 8,\n clickoutFiresChange: true,\n allowEmpty: true,\n localStorageKey: 'magento.spectrum',\n palette: palette\n }\n },\n\n /**\n * Invokes initialize method of parent class,\n * contains initialization logic\n */\n initialize: function () {\n this._super();\n\n this.colorPickerConfig.value = this.value;\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/single-checkbox.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/form/element/abstract',\n 'underscore',\n 'mage/translate'\n], function (AbstractField, _, $t) {\n 'use strict';\n\n return AbstractField.extend({\n defaults: {\n template: 'ui/form/components/single/field',\n checked: false,\n initialChecked: false,\n multiple: false,\n prefer: 'checkbox', // 'radio' | 'checkbox' | 'toggle'\n valueMap: {},\n\n templates: {\n radio: 'ui/form/components/single/radio',\n checkbox: 'ui/form/components/single/checkbox',\n toggle: 'ui/form/components/single/switcher'\n },\n\n listens: {\n 'checked': 'onCheckedChanged',\n 'value': 'onExtendedValueChanged'\n }\n },\n\n /**\n * @inheritdoc\n */\n initConfig: function (config) {\n this._super();\n\n if (!config.elementTmpl) {\n if (!this.prefer && !this.multiple) {\n this.elementTmpl = this.templates.radio;\n } else if (this.prefer === 'radio') {\n this.elementTmpl = this.templates.radio;\n } else if (this.prefer === 'checkbox') {\n this.elementTmpl = this.templates.checkbox;\n } else if (this.prefer === 'toggle') {\n this.elementTmpl = this.templates.toggle;\n } else {\n this.elementTmpl = this.templates.checkbox;\n }\n }\n\n if (this.prefer === 'toggle' && _.isEmpty(this.toggleLabels)) {\n this.toggleLabels = {\n 'on': $t('Yes'),\n 'off': $t('No')\n };\n }\n\n if (typeof this.default === 'undefined' || this.default === null) {\n this.default = '';\n }\n\n if (typeof this.value === 'undefined' || this.value === null) {\n this.value = _.isEmpty(this.valueMap) || this.default !== '' ? this.default : this.valueMap.false;\n this.initialValue = this.value;\n } else {\n this.initialValue = this.value;\n }\n\n if (this.multiple && !_.isArray(this.value)) {\n this.value = []; // needed for correct observable assignment\n }\n\n this.initialChecked = this.checked;\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n initObservable: function () {\n return this\n ._super()\n .observe('checked');\n },\n\n /**\n * Get true/false key from valueMap by value.\n *\n * @param {*} value\n * @returns {Boolean|undefined}\n */\n getReverseValueMap: function getReverseValueMap(value) {\n var bool = false;\n\n _.some(this.valueMap, function (iValue, iBool) {\n if (iValue === value) {\n bool = iBool === 'true';\n\n return true;\n }\n });\n\n return bool;\n },\n\n /**\n * @inheritdoc\n */\n setInitialValue: function () {\n if (_.isEmpty(this.valueMap)) {\n this.on('value', this.onUpdate.bind(this));\n } else {\n this._super();\n this.checked(this.getReverseValueMap(this.value()));\n }\n\n return this;\n },\n\n /**\n * Handle dataScope changes for checkbox / radio button.\n *\n * @param {*} newExportedValue\n */\n onExtendedValueChanged: function (newExportedValue) {\n var isMappedUsed = !_.isEmpty(this.valueMap),\n oldChecked = this.checked.peek(),\n oldValue = this.initialValue,\n newChecked;\n\n if (this.multiple) {\n newChecked = newExportedValue.indexOf(oldValue) !== -1;\n } else if (isMappedUsed) {\n newChecked = this.getReverseValueMap(newExportedValue);\n } else if (typeof newExportedValue === 'boolean') {\n newChecked = newExportedValue;\n } else {\n newChecked = newExportedValue === oldValue;\n }\n\n if (newChecked !== oldChecked) {\n this.checked(newChecked);\n }\n },\n\n /**\n * Handle checked state changes for checkbox / radio button.\n *\n * @param {Boolean} newChecked\n */\n onCheckedChanged: function (newChecked) {\n var isMappedUsed = !_.isEmpty(this.valueMap),\n oldValue = this.initialValue,\n newValue;\n\n if (isMappedUsed) {\n newValue = this.valueMap[newChecked];\n } else {\n newValue = oldValue;\n }\n\n if (!this.multiple && newChecked) {\n this.value(newValue);\n } else if (!this.multiple && !newChecked) {\n if (typeof newValue === 'boolean') {\n this.value(newChecked);\n } else if (newValue === this.value.peek()) {\n this.value('');\n }\n\n if (isMappedUsed) {\n this.value(newValue);\n }\n } else if (this.multiple && newChecked && this.value.indexOf(newValue) === -1) {\n this.value.push(newValue);\n } else if (this.multiple && !newChecked && this.value.indexOf(newValue) !== -1) {\n this.value.splice(this.value.indexOf(newValue), 1);\n }\n },\n\n /**\n * @inheritdoc\n */\n onUpdate: function () {\n if (this.hasUnique) {\n this.setUnique();\n }\n\n return this._super();\n },\n\n /**\n * @inheritdoc\n */\n reset: function () {\n if (this.multiple && this.initialChecked) {\n this.value.push(this.initialValue);\n } else if (this.multiple && !this.initialChecked) {\n this.value.splice(this.value.indexOf(this.initialValue), 1);\n } else {\n this.value(this.initialValue);\n }\n\n this.error(false);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n clear: function () {\n if (this.multiple) {\n this.value([]);\n } else {\n this.value('');\n }\n\n this.error(false);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/boolean.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n './abstract'\n], function (Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n checked: false,\n links: {\n checked: 'value'\n }\n },\n\n /**\n * @returns {*|void|Element}\n */\n initObservable: function () {\n return this._super()\n .observe('checked');\n },\n\n /**\n * Converts provided value to boolean.\n *\n * @returns {Boolean}\n */\n normalizeData: function () {\n return !!+this._super();\n },\n\n /**\n * Calls 'onUpdate' method of parent, if value is defined and instance's\n * 'unique' property set to true, calls 'setUnique' method\n *\n * @return {Object} - reference to instance\n */\n onUpdate: function () {\n if (this.hasUnique) {\n this.setUnique();\n }\n\n return this._super();\n }\n });\n});\n","Magento_Ui/js/form/element/country.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n './select'\n], function (_, registry, Select) {\n 'use strict';\n\n return Select.extend({\n defaults: {\n imports: {\n update: '${ $.parentName }.website_id:value'\n }\n },\n\n /**\n * Filters 'initialOptions' property by 'field' and 'value' passed,\n * calls 'setOptions' passing the result to it\n *\n * @param {*} value\n * @param {String} field\n */\n filter: function (value, field) {\n var result, defaultCountry, defaultValue;\n\n if (!field) { //validate field, if we are on update\n field = this.filterBy.field;\n }\n\n this._super(value, field);\n result = _.filter(this.initialOptions, function (item) {\n\n if (item[field]) {\n return ~item[field].indexOf(value);\n }\n\n return false;\n });\n\n this.setOptions(result);\n this.reset();\n\n if (!this.value()) {\n defaultCountry = _.filter(result, function (item) {\n return item['is_default'] && _.contains(item['is_default'], value);\n });\n\n if (defaultCountry.length) {\n defaultValue = defaultCountry.shift();\n this.value(defaultValue.value);\n }\n }\n }\n });\n});\n\n","Magento_Ui/js/form/element/region.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n './select',\n 'Magento_Checkout/js/model/default-post-code-resolver'\n], function (_, registry, Select, defaultPostCodeResolver) {\n 'use strict';\n\n return Select.extend({\n defaults: {\n skipValidation: false,\n imports: {\n update: '${ $.parentName }.country_id:value'\n }\n },\n\n /**\n * @param {String} value\n */\n update: function (value) {\n var country = registry.get(this.parentName + '.' + 'country_id'),\n options = country.indexedOptions,\n isRegionRequired,\n option;\n\n if (!value) {\n return;\n }\n option = options[value];\n\n if (typeof option === 'undefined') {\n return;\n }\n\n defaultPostCodeResolver.setUseDefaultPostCode(!option['is_zipcode_optional']);\n\n if (this.skipValidation) {\n this.validation['required-entry'] = false;\n this.required(false);\n } else {\n if (option && !option['is_region_required']) {\n this.error(false);\n this.validation = _.omit(this.validation, 'required-entry');\n registry.get(this.customName, function (input) {\n input.validation['required-entry'] = false;\n input.required(false);\n });\n } else {\n this.validation['required-entry'] = true;\n }\n\n if (option && !this.options().length) {\n registry.get(this.customName, function (input) {\n isRegionRequired = !!option['is_region_required'];\n input.validation['required-entry'] = isRegionRequired;\n input.validation['validate-not-number-first'] = true;\n input.required(isRegionRequired);\n });\n }\n\n this.required(!!option['is_region_required']);\n }\n },\n\n /**\n * Filters 'initialOptions' property by 'field' and 'value' passed,\n * calls 'setOptions' passing the result to it\n *\n * @param {*} value\n * @param {String} field\n */\n filter: function (value, field) {\n var superFn = this._super;\n\n registry.get(this.parentName + '.' + 'country_id', function (country) {\n var option = country.indexedOptions[value];\n\n superFn.call(this, value, field);\n\n if (option && option['is_region_visible'] === false) {\n // hide select and corresponding text input field if region must not be shown for selected country\n this.setVisible(false);\n\n if (this.customEntry) {// eslint-disable-line max-depth\n this.toggleInput(false);\n }\n }\n }.bind(this));\n }\n });\n});\n\n","Magento_Ui/js/form/element/url-input.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiLayout',\n 'mage/translate',\n 'Magento_Ui/js/form/element/abstract'\n], function (_, layout, $t, Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n linkedElement: {},\n settingTemplate: 'ui/form/element/urlInput/setting',\n typeSelectorTemplate: 'ui/form/element/urlInput/typeSelector',\n options: [],\n linkedElementInstances: {},\n //checkbox\n isDisplayAdditionalSettings: true,\n settingValue: false,\n settingLabel: $t('Open in new tab'),\n tracks: {\n linkedElement: true\n },\n baseLinkSetting: {\n namePrefix: '${$.name}.',\n dataScopePrefix: '${$.dataScope}.',\n provider: '${$.provider}'\n },\n urlTypes: {},\n listens: {\n settingValue: 'checked',\n disabled: 'hideLinkedElement',\n linkType: 'createChildUrlInputComponent'\n },\n links: {\n linkType: '${$.provider}:${$.dataScope}.type',\n settingValue: '${$.provider}:${$.dataScope}.setting'\n }\n },\n\n /** @inheritdoc */\n initConfig: function (config) {\n var processedLinkTypes = {},\n baseLinkType = this.constructor.defaults.baseLinkSetting;\n\n _.each(config.urlTypes, function (linkSettingsArray, linkName) {\n //add link name by link type\n linkSettingsArray.name = baseLinkType.namePrefix + linkName;\n linkSettingsArray.dataScope = baseLinkType.dataScopePrefix + linkName;\n linkSettingsArray.type = linkName;\n linkSettingsArray.disabled = config.disabled;\n linkSettingsArray.visible = config.visible;\n processedLinkTypes[linkName] = {};\n _.extend(processedLinkTypes[linkName], baseLinkType, linkSettingsArray);\n });\n _.extend(this.constructor.defaults.urlTypes, processedLinkTypes);\n\n this._super();\n },\n\n /**\n * Initializes observable properties of instance\n *\n * @returns {Abstract} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe('componentTemplate options value linkType settingValue checked isDisplayAdditionalSettings')\n .setOptions();\n\n return this;\n },\n\n /**\n * Set options to select based on link types configuration\n *\n * @return {Object}\n */\n setOptions: function () {\n var result = [];\n\n _.each(this.urlTypes, function (option, key) {\n result.push({\n value: key,\n label: option.label,\n sortOrder: option.sortOrder || 0\n });\n });\n\n //sort options by sortOrder\n result.sort(function (a, b) {\n return a.sortOrder > b.sortOrder ? 1 : -1;\n });\n\n this.options(result);\n\n return this;\n },\n\n /** @inheritdoc */\n setPreview: function (visible) {\n this.linkedElement().visible(visible);\n },\n\n /**\n * Initializes observable properties of instance\n *\n * @param {Boolean} disabled\n */\n hideLinkedElement: function (disabled) {\n this.linkedElement().disabled(disabled);\n },\n\n /** @inheritdoc */\n destroy: function () {\n _.each(this.linkedElementInstances, function (value) {\n value().destroy();\n });\n this._super();\n },\n\n /**\n * Create child component by value\n *\n * @param {String} value\n * @return void\n */\n createChildUrlInputComponent: function (value) {\n var elementConfig;\n\n if (!_.isEmpty(value) && _.isUndefined(this.linkedElementInstances[value])) {\n elementConfig = this.urlTypes[value];\n layout([elementConfig]);\n this.linkedElementInstances[value] = this.requestModule(elementConfig.name);\n }\n this.linkedElement = this.linkedElementInstances[value];\n\n },\n\n /**\n * Returns linked element to display related field in template\n * @return String\n */\n getLinkedElementName: function () {\n return this.linkedElement;\n },\n\n /**\n * Add ability to choose check box by clicking on label\n */\n checkboxClick: function () {\n if (!this.disabled()) {\n this.settingValue(!this.settingValue());\n }\n }\n });\n});\n","Magento_Ui/js/form/element/textarea.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n './abstract'\n], function (Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n cols: 15,\n rows: 2,\n elementTmpl: 'ui/form/element/textarea'\n }\n });\n});\n","Magento_Ui/js/form/element/media.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'mageUtils',\n './abstract'\n], function (utils, Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n links: {\n value: ''\n }\n },\n\n /**\n * Initializes file component.\n *\n * @returns {Media} Chainable.\n */\n initialize: function () {\n this._super()\n .initFormId();\n\n return this;\n },\n\n /**\n * Defines form ID with which file input will be associated.\n *\n * @returns {Media} Chainable.\n */\n initFormId: function () {\n var namespace;\n\n if (this.formId) {\n return this;\n }\n\n namespace = this.name.split('.');\n this.formId = namespace[0];\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/date.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'moment',\n 'mageUtils',\n './abstract',\n 'moment-timezone-with-data'\n], function (moment, utils, Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n options: {},\n\n storeTimeZone: 'UTC',\n\n validationParams: {\n dateFormat: '${ $.outputDateFormat }'\n },\n\n /**\n * Format of date that comes from the\n * server (ICU Date Format).\n *\n * Used only in date picker mode\n * (this.options.showsTime == false).\n *\n * @type {String}\n */\n inputDateFormat: 'y-MM-dd',\n\n /**\n * Format of date that should be sent to the\n * server (ICU Date Format).\n *\n * Used only in date picker mode\n * (this.options.showsTime == false).\n *\n * @type {String}\n */\n outputDateFormat: 'MM/dd/y',\n\n /**\n * Date/time format that is used to display date in\n * the input field.\n *\n * @type {String}\n */\n pickerDateTimeFormat: '',\n\n pickerDefaultDateFormat: 'MM/dd/y', // ICU Date Format\n pickerDefaultTimeFormat: 'h:mm a', // ICU Time Format\n\n elementTmpl: 'ui/form/element/date',\n\n /**\n * Format needed by moment timezone for conversion\n */\n timezoneFormat: 'YYYY-MM-DD HH:mm',\n\n listens: {\n 'value': 'onValueChange',\n 'shiftedValue': 'onShiftedValueChange'\n },\n\n /**\n * Date/time value shifted to corresponding timezone\n * according to this.storeTimeZone property. This value\n * will be sent to the server.\n *\n * @type {String}\n */\n shiftedValue: ''\n },\n\n /**\n * Initializes regular properties of instance.\n *\n * @returns {Object} Chainable.\n */\n initConfig: function () {\n this._super();\n\n if (!this.options.dateFormat) {\n this.options.dateFormat = this.pickerDefaultDateFormat;\n }\n\n if (!this.options.timeFormat) {\n this.options.timeFormat = this.pickerDefaultTimeFormat;\n }\n\n this.prepareDateTimeFormats();\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n initObservable: function () {\n return this._super().observe(['shiftedValue']);\n },\n\n /**\n * Prepares and sets date/time value that will be displayed\n * in the input field.\n *\n * @param {String} value\n */\n onValueChange: function (value) {\n var dateFormat,\n shiftedValue;\n\n if (value) {\n if (this.options.showsTime) {\n shiftedValue = moment.tz(value, 'UTC').tz(this.storeTimeZone);\n } else {\n dateFormat = this.shiftedValue() ? this.outputDateFormat : this.inputDateFormat;\n\n shiftedValue = moment(value, dateFormat);\n }\n\n shiftedValue = shiftedValue.format(this.pickerDateTimeFormat);\n } else {\n shiftedValue = '';\n }\n\n if (shiftedValue !== this.shiftedValue()) {\n this.shiftedValue(shiftedValue);\n }\n },\n\n /**\n * Prepares and sets date/time value that will be sent\n * to the server.\n *\n * @param {String} shiftedValue\n */\n onShiftedValueChange: function (shiftedValue) {\n var value,\n formattedValue,\n momentValue;\n\n if (shiftedValue) {\n momentValue = moment(shiftedValue, this.pickerDateTimeFormat);\n\n if (this.options.showsTime) {\n formattedValue = moment(momentValue).format(this.timezoneFormat);\n value = moment.tz(formattedValue, this.storeTimeZone).tz('UTC').toISOString();\n } else {\n value = momentValue.format(this.outputDateFormat);\n }\n } else {\n value = '';\n }\n\n if (value !== this.value()) {\n this.value(value);\n }\n },\n\n /**\n * Prepares and converts all date/time formats to be compatible\n * with moment.js library.\n */\n prepareDateTimeFormats: function () {\n this.pickerDateTimeFormat = this.options.dateFormat;\n\n if (this.options.showsTime) {\n this.pickerDateTimeFormat += ' ' + this.options.timeFormat;\n }\n\n this.pickerDateTimeFormat = utils.convertToMomentFormat(this.pickerDateTimeFormat);\n\n if (this.options.dateFormat) {\n this.outputDateFormat = this.options.dateFormat;\n }\n\n this.inputDateFormat = utils.convertToMomentFormat(this.inputDateFormat);\n this.outputDateFormat = utils.convertToMomentFormat(this.outputDateFormat);\n\n this.validationParams.dateFormat = this.outputDateFormat;\n }\n });\n});\n","Magento_Ui/js/form/element/image-uploader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global Base64 */\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'Magento_Ui/js/modal/alert',\n 'Magento_Ui/js/lib/validation/validator',\n 'Magento_Ui/js/form/element/file-uploader',\n 'mage/adminhtml/browser',\n 'mage/adminhtml/tools'\n], function ($, _, utils, uiAlert, validator, Element, browser) {\n 'use strict';\n\n return Element.extend({\n /**\n * {@inheritDoc}\n */\n initialize: function () {\n this._super();\n\n // Listen for file deletions from the media browser\n $(window).on('fileDeleted.mediabrowser', this.onDeleteFile.bind(this));\n },\n\n /**\n * Assign uid for media gallery\n *\n * @return {ImageUploader} Chainable.\n */\n initConfig: function () {\n var mediaGalleryUid = utils.uniqueid();\n\n this._super();\n\n _.extend(this, {\n mediaGalleryUid: mediaGalleryUid\n });\n\n return this;\n },\n\n /**\n * Add file event callback triggered from media gallery\n *\n * @param {ImageUploader} imageUploader - UI Class\n * @param {Event} e\n */\n addFileFromMediaGallery: function (imageUploader, e) {\n var $buttonEl = $(e.target),\n fileSize = $buttonEl.data('size'),\n fileMimeType = $buttonEl.data('mime-type'),\n filePathname = $buttonEl.val(),\n fileBasename = filePathname.split('/').pop();\n\n this.addFile({\n type: fileMimeType,\n name: fileBasename,\n size: fileSize,\n url: filePathname\n });\n },\n\n /**\n * Open the media browser dialog\n *\n * @param {ImageUploader} imageUploader - UI Class\n * @param {Event} e\n */\n openMediaBrowserDialog: function (imageUploader, e) {\n var $buttonEl = $(e.target),\n openDialogUrl = this.mediaGallery.openDialogUrl +\n 'target_element_id/' + $buttonEl.attr('id') +\n '/store/' + this.mediaGallery.storeId +\n '/type/image/?isAjax=true';\n\n if (this.mediaGallery.initialOpenSubpath) {\n openDialogUrl += '¤t_tree_path=' + Base64.mageEncode(this.mediaGallery.initialOpenSubpath);\n }\n\n browser.openDialog(openDialogUrl, null, null, this.mediaGallery.openDialogTitle);\n },\n\n /**\n * @param {jQuery.event} e\n * @param {Object} data\n * @returns {Object} Chainables\n */\n onDeleteFile: function (e, data) {\n var fileId = this.getFileId(),\n deletedFileIds = data.ids;\n\n if (fileId && $.inArray(fileId, deletedFileIds) > -1) {\n this.clear();\n }\n\n return this;\n },\n\n /**\n * {@inheritDoc}\n */\n clear: function () {\n this.value([]);\n\n return this;\n },\n\n /**\n * Gets the ID of the file used if set\n *\n * @return {String|Null} ID\n */\n getFileId: function () {\n return this.hasData() ? this.value()[0].id : null;\n },\n\n /**\n * Trigger native browser file upload UI via clicking on 'Upload' button\n *\n * @param {ImageUploader} imageUploader - UI Class\n * @param {Event} e\n */\n triggerImageUpload: function (imageUploader, e) {\n $(e.target).closest('.file-uploader').find('input[type=\"file\"]').click();\n },\n\n /**\n * Get list of file extensions allowed in comma delimited format\n *\n * @return {String}\n */\n getAllowedFileExtensionsInCommaDelimitedFormat: function () {\n var allowedExtensions = this.allowedExtensions.toUpperCase().split(' ');\n\n // if jpg and jpeg in allowed extensions, remove jpeg from list\n if (allowedExtensions.indexOf('JPG') !== -1 && allowedExtensions.indexOf('JPEG') !== -1) {\n allowedExtensions.splice(allowedExtensions.indexOf('JPEG'), 1);\n }\n\n return allowedExtensions.join(', ');\n }\n });\n});\n","Magento_Ui/js/form/element/file-uploader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\n/* global Base64 */\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'Magento_Ui/js/modal/alert',\n 'Magento_Ui/js/lib/validation/validator',\n 'Magento_Ui/js/form/element/abstract',\n 'mage/backend/notification',\n 'mage/translate',\n 'jquery/file-uploader',\n 'mage/adminhtml/tools'\n], function ($, _, utils, uiAlert, validator, Element, notification, $t) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n value: [],\n aggregatedErrors: [],\n maxFileSize: false,\n isMultipleFiles: false,\n placeholderType: 'document', // 'image', 'video'\n allowedExtensions: false,\n previewTmpl: 'ui/form/element/uploader/preview',\n dropZone: '[data-role=drop-zone]',\n isLoading: false,\n uploaderConfig: {\n dataType: 'json',\n sequentialUploads: true,\n formData: {\n 'form_key': window.FORM_KEY\n }\n },\n tracks: {\n isLoading: true\n }\n },\n\n /**\n * Initializes file uploader plugin on provided input element.\n *\n * @param {HTMLInputElement} fileInput\n * @returns {FileUploader} Chainable.\n */\n initUploader: function (fileInput) {\n this.$fileInput = fileInput;\n\n _.extend(this.uploaderConfig, {\n dropZone: $(fileInput).closest(this.dropZone),\n change: this.onFilesChoosed.bind(this),\n drop: this.onFilesChoosed.bind(this),\n add: this.onBeforeFileUpload.bind(this),\n done: this.onFileUploaded.bind(this),\n start: this.onLoadingStart.bind(this),\n stop: this.onLoadingStop.bind(this)\n });\n\n $(fileInput).fileupload(this.uploaderConfig);\n\n return this;\n },\n\n /**\n * Defines initial value of the instance.\n *\n * @returns {FileUploader} Chainable.\n */\n setInitialValue: function () {\n var value = this.getInitialValue();\n\n value = value.map(this.processFile, this);\n\n this.initialValue = value.slice();\n\n this.value(value);\n this.on('value', this.onUpdate.bind(this));\n this.isUseDefault(this.disabled());\n\n return this;\n },\n\n /**\n * Empties files list.\n *\n * @returns {FileUploader} Chainable.\n */\n clear: function () {\n this.value.removeAll();\n\n return this;\n },\n\n /**\n * Checks if files list contains any items.\n *\n * @returns {Boolean}\n */\n hasData: function () {\n return !!this.value().length;\n },\n\n /**\n * Resets files list to its' initial value.\n *\n * @returns {FileUploader}\n */\n reset: function () {\n var value = this.initialValue.slice();\n\n this.value(value);\n\n return this;\n },\n\n /**\n * Adds provided file to the files list.\n *\n * @param {Object} file\n * @returns {FileUploader} Chainable.\n */\n addFile: function (file) {\n file = this.processFile(file);\n\n this.isMultipleFiles ?\n this.value.push(file) :\n this.value([file]);\n\n return this;\n },\n\n /**\n * Retrieves from the list file which matches\n * search criteria implemented in itertor function.\n *\n * @param {Function} fn - Function that will be invoked\n * for each file in the list.\n * @returns {Object}\n */\n getFile: function (fn) {\n return _.find(this.value(), fn);\n },\n\n /**\n * Removes provided file from thes files list.\n *\n * @param {Object} file\n * @returns {FileUploader} Chainable.\n */\n removeFile: function (file) {\n this.value.remove(file);\n\n return this;\n },\n\n /**\n * May perform modifications on the provided\n * file object before adding it to the files list.\n *\n * @param {Object} file\n * @returns {Object} Modified file object.\n */\n processFile: function (file) {\n file.previewType = this.getFilePreviewType(file);\n\n if (!file.id && file.name) {\n file.id = Base64.mageEncode(file.name);\n }\n\n this.observe.call(file, true, [\n 'previewWidth',\n 'previewHeight'\n ]);\n\n return file;\n },\n\n /**\n * Formats incoming bytes value to a readable format.\n *\n * @param {Number} bytes\n * @returns {String}\n */\n formatSize: function (bytes) {\n var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'],\n i;\n\n if (bytes === 0) {\n return '0 Byte';\n }\n\n i = window.parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));\n\n return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];\n },\n\n /**\n * Returns path to the files' preview image.\n *\n * @param {Object} file\n * @returns {String}\n */\n getFilePreview: function (file) {\n return file.url;\n },\n\n /**\n * Returns path to the file's preview template.\n *\n * @returns {String}\n */\n getPreviewTmpl: function () {\n return this.previewTmpl;\n },\n\n /**\n * Checks if provided file is allowed to be uploaded.\n *\n * @param {Object} file\n * @returns {Object} Validation result.\n */\n isFileAllowed: function (file) {\n var result;\n\n _.every([\n this.isExtensionAllowed(file),\n this.isSizeExceeded(file)\n ], function (value) {\n result = value;\n\n return value.passed;\n });\n\n return result;\n },\n\n /**\n * Checks if extension of provided file is allowed.\n *\n * @param {Object} file - File to be checked.\n * @returns {Boolean}\n */\n isExtensionAllowed: function (file) {\n return validator('validate-file-type', file.name, this.allowedExtensions);\n },\n\n /**\n * Get simplified file type.\n *\n * @param {Object} file - File to be checked.\n * @returns {String}\n */\n getFilePreviewType: function (file) {\n var type;\n\n if (!file.type) {\n return 'document';\n }\n\n type = file.type.split('/')[0];\n\n return type !== 'image' && type !== 'video' ? 'document' : type;\n },\n\n /**\n * Checks if size of provided file exceeds\n * defined in configuration size limits.\n *\n * @param {Object} file - File to be checked.\n * @returns {Boolean}\n */\n isSizeExceeded: function (file) {\n return validator('validate-max-size', file.size, this.maxFileSize);\n },\n\n /**\n * Displays provided error message.\n *\n * @param {String} msg\n * @returns {FileUploader} Chainable.\n */\n notifyError: function (msg) {\n var data = {\n content: msg\n };\n\n if (this.isMultipleFiles) {\n data.modalClass = '_image-box';\n }\n\n uiAlert(data);\n\n return this;\n },\n\n /**\n * Performs data type conversions.\n *\n * @param {*} value\n * @returns {Array}\n */\n normalizeData: function (value) {\n return utils.isEmpty(value) ? [] : value;\n },\n\n /**\n * Checks if files list is different\n * from its' initial value.\n *\n * @returns {Boolean}\n */\n hasChanged: function () {\n var value = this.value(),\n initial = this.initialValue;\n\n return !utils.equalArrays(value, initial);\n },\n\n /**\n * Handler which is invoked when files are choosed for upload.\n * May be used for implementation of additional validation rules,\n * e.g. total files and a total size rules.\n *\n * @param {Event} e - Event object.\n * @param {Object} data - File data that will be uploaded.\n */\n onFilesChoosed: function (e, data) {\n // no option exists in fileuploader for restricting upload chains to single files; this enforces that policy\n if (!this.isMultipleFiles) {\n data.files.splice(1);\n }\n },\n\n /**\n * Handler which is invoked prior to the start of a file upload.\n *\n * @param {Event} e - Event object.\n * @param {Object} data - File data that will be uploaded.\n */\n onBeforeFileUpload: function (e, data) {\n var file = data.files[0],\n allowed = this.isFileAllowed(file),\n target = $(e.target);\n\n if (allowed.passed) {\n target.on('fileuploadsend', function (event, postData) {\n postData.data.append('param_name', this.paramName);\n }.bind(data));\n\n target.fileupload('process', data).done(function () {\n data.submit();\n });\n } else {\n this.aggregateError(file.name, allowed.message);\n\n // if all files in upload chain are invalid, stop callback is never called; this resolves promise\n if (this.aggregatedErrors.length === data.originalFiles.length) {\n this.uploaderConfig.stop();\n }\n }\n },\n\n /**\n * Add error message associated with filename for display when upload chain is complete\n *\n * @param {String} filename\n * @param {String} message\n */\n aggregateError: function (filename, message) {\n this.aggregatedErrors.push({\n filename: filename,\n message: message\n });\n },\n\n /**\n * Handler of the file upload complete event.\n *\n * @param {Event} e\n * @param {Object} data\n */\n onFileUploaded: function (e, data) {\n var uploadedFilename = data.files[0].name,\n file = data.result,\n error = file.error;\n\n error ?\n this.aggregateError(uploadedFilename, error) :\n this.addFile(file);\n },\n\n /**\n * Load start event handler.\n */\n onLoadingStart: function () {\n this.isLoading = true;\n },\n\n /**\n * Load stop event handler.\n */\n onLoadingStop: function () {\n var aggregatedErrorMessages = [];\n\n this.isLoading = false;\n\n if (!this.aggregatedErrors.length) {\n return;\n }\n\n if (!this.isMultipleFiles) { // only single file upload occurred; use first file's error message\n aggregatedErrorMessages.push(this.aggregatedErrors[0].message);\n } else { // construct message from all aggregatedErrors\n _.each(this.aggregatedErrors, function (error) {\n notification().add({\n error: true,\n message: '%s' + error.message, // %s to be used as placeholder for html injection\n\n /**\n * Adds constructed error notification to aggregatedErrorMessages\n *\n * @param {String} constructedMessage\n */\n insertMethod: function (constructedMessage) {\n var errorMsgBodyHtml = '<strong>%s</strong> %s.<br>'\n .replace('%s', error.filename)\n .replace('%s', $t('was not uploaded'));\n\n // html is escaped in message body for notification widget; prepend unescaped html here\n constructedMessage = constructedMessage.replace('%s', errorMsgBodyHtml);\n\n aggregatedErrorMessages.push(constructedMessage);\n }\n });\n });\n }\n\n this.notifyError(aggregatedErrorMessages.join(''));\n\n // clear out aggregatedErrors array for this completed upload chain\n this.aggregatedErrors = [];\n },\n\n /**\n * Handler function which is supposed to be invoked when\n * file input element has been rendered.\n *\n * @param {HTMLInputElement} fileInput\n */\n onElementRender: function (fileInput) {\n this.initUploader(fileInput);\n },\n\n /**\n * Handler of the preview image load event.\n *\n * @param {Object} file - File associated with an image.\n * @param {Event} e\n */\n onPreviewLoad: function (file, e) {\n var img = e.currentTarget;\n\n file.previewWidth = img.naturalWidth;\n file.previewHeight = img.naturalHeight;\n },\n\n /**\n * Restore value to default\n */\n restoreToDefault: function () {\n var defaultValue = utils.copy(this.default);\n\n defaultValue.map(this.processFile, this);\n this.value(defaultValue);\n },\n\n /**\n * Update whether value differs from default value\n */\n setDifferedFromDefault: function () {\n var value = utils.copy(this.value());\n\n this.isDifferedFromDefault(!_.isEqual(value, this.default));\n }\n });\n});\n","Magento_Ui/js/form/element/single-checkbox-use-config.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/form/element/single-checkbox'\n], function (Component) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n isUseDefault: false,\n isUseConfig: false,\n listens: {\n 'isUseConfig': 'toggleElement',\n 'isUseDefault': 'toggleElement'\n }\n },\n\n /**\n * @inheritdoc\n */\n initObservable: function () {\n\n return this\n ._super()\n .observe('isUseConfig');\n },\n\n /**\n * Toggle element\n */\n toggleElement: function () {\n this.disabled(this.isUseDefault() || this.isUseConfig());\n\n if (this.source) {\n this.source.set('data.use_default.' + this.index, Number(this.isUseDefault()));\n }\n }\n });\n});\n","Magento_Ui/js/form/element/text.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'uiElement',\n 'mageUtils'\n], function (Element, utils) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n visible: true,\n label: '',\n error: '',\n uid: utils.uniqueid(),\n disabled: false,\n links: {\n value: '${ $.provider }:${ $.dataScope }'\n }\n },\n\n /**\n * Has service\n *\n * @returns {Boolean} false.\n */\n hasService: function () {\n return false;\n },\n\n /**\n * Has addons\n *\n * @returns {Boolean} false.\n */\n hasAddons: function () {\n return false;\n },\n\n /**\n * Calls 'initObservable' of parent\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe('disabled visible value');\n\n return this;\n }\n });\n});\n","Magento_Ui/js/form/element/checkbox-set.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n './abstract'\n], function (_, utils, Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n template: 'ui/form/element/checkbox-set',\n multiple: false,\n multipleScopeValue: null\n },\n\n /**\n * @inheritdoc\n */\n initConfig: function () {\n this._super();\n\n this.value = this.normalizeData(this.value);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n initLinks: function () {\n var scope = this.source.get(this.dataScope);\n\n this.multipleScopeValue = this.multiple && _.isArray(scope) ? utils.copy(scope) : undefined;\n\n return this._super();\n },\n\n /**\n * @inheritdoc\n */\n reset: function () {\n this.value(utils.copy(this.initialValue));\n this.error(false);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n clear: function () {\n var value = this.multiple ? [] : '';\n\n this.value(value);\n this.error(false);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n normalizeData: function (value) {\n if (!this.multiple) {\n return this._super();\n }\n\n return _.isArray(value) ? utils.copy(value) : [];\n },\n\n /**\n * @inheritdoc\n */\n setInitialValue: function () {\n this._super();\n\n this.initialValue = utils.copy(this.initialValue);\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n getInitialValue: function () {\n var values = [this.multipleScopeValue, this.default, this.value.peek(), []],\n value;\n\n if (!this.multiple) {\n return this._super();\n }\n\n values.some(function (v) {\n return _.isArray(v) && (value = utils.copy(v));\n });\n\n return value;\n },\n\n /**\n * Returns labels which matches current value.\n *\n * @returns {String|Array}\n */\n getPreview: function () {\n var option;\n\n if (!this.multiple) {\n option = this.getOption(this.value());\n\n return option ? option.label : '';\n }\n\n return this.value.map(function (value) {\n return this.getOption(value).label;\n }, this);\n },\n\n /**\n * Returns option object associated with provided value.\n *\n * @param {String} value\n * @returns {Object}\n */\n getOption: function (value) {\n return _.findWhere(this.options, {\n value: value\n });\n },\n\n /**\n * @inheritdoc\n */\n hasChanged: function () {\n var value = this.value(),\n initial = this.initialValue;\n\n return this.multiple ?\n !utils.equalArrays(value, initial) :\n this._super();\n }\n });\n});\n","Magento_Ui/js/form/element/single-checkbox-toggle-notice.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/form/element/single-checkbox'\n], function (SingleCheckbox) {\n 'use strict';\n\n return SingleCheckbox.extend({\n defaults: {\n notices: [],\n tracks: {\n notice: true\n }\n },\n\n /**\n * Choose notice on initialization\n *\n * @returns {*|void|Element}\n */\n initialize: function () {\n this._super()\n .chooseNotice();\n\n return this;\n },\n\n /**\n * Choose notice function\n *\n * @returns void\n */\n chooseNotice: function () {\n var checkedNoticeNumber = Number(this.checked());\n\n this.notice = this.notices[checkedNoticeNumber];\n },\n\n /**\n * Choose notice on update\n *\n * @returns void\n */\n onUpdate: function () {\n this._super();\n this.chooseNotice();\n }\n });\n});\n","Magento_Ui/js/form/element/post-code.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n './abstract'\n], function (_, registry, Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n imports: {\n update: '${ $.parentName }.country_id:value'\n }\n },\n\n /**\n * @param {String} value\n */\n update: function (value) {\n var country = registry.get(this.parentName + '.' + 'country_id'),\n options = country.indexedOptions,\n option = null;\n\n if (!value) {\n return;\n }\n\n option = options[value];\n\n if (!option) {\n return;\n }\n\n if (option['is_zipcode_optional']) {\n this.error(false);\n this.validation = _.omit(this.validation, 'required-entry');\n } else {\n this.validation['required-entry'] = true;\n }\n\n this.required(!option['is_zipcode_optional']);\n }\n });\n});\n","Magento_Ui/js/form/element/select.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n './abstract',\n 'uiLayout'\n], function (_, utils, registry, Abstract, layout) {\n 'use strict';\n\n var inputNode = {\n parent: '${ $.$data.parentName }',\n component: 'Magento_Ui/js/form/element/abstract',\n template: '${ $.$data.template }',\n provider: '${ $.$data.provider }',\n name: '${ $.$data.index }_input',\n dataScope: '${ $.$data.customEntry }',\n customScope: '${ $.$data.customScope }',\n sortOrder: {\n after: '${ $.$data.name }'\n },\n displayArea: 'body',\n label: '${ $.$data.label }'\n };\n\n /**\n * Parses incoming options, considers options with undefined value property\n * as caption\n *\n * @param {Array} nodes\n * @return {Object}\n */\n function parseOptions(nodes, captionValue) {\n var caption,\n value;\n\n nodes = _.map(nodes, function (node) {\n value = node.value;\n\n if (value === null || value === captionValue) {\n if (_.isUndefined(caption)) {\n caption = node.label;\n }\n } else {\n return node;\n }\n });\n\n return {\n options: _.compact(nodes),\n caption: _.isString(caption) ? caption : false\n };\n }\n\n /**\n * Recursively loops over data to find non-undefined, non-array value\n *\n * @param {Array} data\n * @return {*} - first non-undefined value in array\n */\n function findFirst(data) {\n var value;\n\n data.some(function (node) {\n value = node.value;\n\n if (Array.isArray(value)) {\n value = findFirst(value);\n }\n\n return !_.isUndefined(value);\n });\n\n return value;\n }\n\n /**\n * Recursively set to object item like value and item.value like key.\n *\n * @param {Array} data\n * @param {Object} result\n * @returns {Object}\n */\n function indexOptions(data, result) {\n var value;\n\n result = result || {};\n\n data.forEach(function (item) {\n value = item.value;\n\n if (Array.isArray(value)) {\n indexOptions(value, result);\n } else {\n result[value] = item;\n }\n });\n\n return result;\n }\n\n return Abstract.extend({\n defaults: {\n customName: '${ $.parentName }.${ $.index }_input',\n elementTmpl: 'ui/form/element/select',\n caption: '',\n options: []\n },\n\n /**\n * Extends instance with defaults, extends config with formatted values\n * and options, and invokes initialize method of AbstractElement class.\n * If instance's 'customEntry' property is set to true, calls 'initInput'\n */\n initialize: function () {\n this._super();\n\n if (this.customEntry) {\n registry.get(this.name, this.initInput.bind(this));\n }\n\n if (this.filterBy) {\n this.initFilter();\n }\n\n return this;\n },\n\n /**\n * Calls 'initObservable' of parent, initializes 'options' and 'initialOptions'\n * properties, calls 'setOptions' passing options to it\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super();\n\n this.initialOptions = this.options;\n\n this.observe('options caption')\n .setOptions(this.options());\n\n return this;\n },\n\n /**\n * Set link for filter.\n *\n * @returns {Object} Chainable\n */\n initFilter: function () {\n var filter = this.filterBy;\n\n this.filter(this.default, filter.field);\n this.setLinks({\n filter: filter.target\n }, 'imports');\n\n return this;\n },\n\n /**\n * Creates input from template, renders it via renderer.\n *\n * @returns {Object} Chainable.\n */\n initInput: function () {\n layout([utils.template(inputNode, this)]);\n\n return this;\n },\n\n /**\n * Matches specified value with existing options\n * or, if value is not specified, returns value of the first option.\n *\n * @returns {*}\n */\n normalizeData: function () {\n var value = this._super(),\n option;\n\n if (value !== '') {\n option = this.getOption(value);\n\n return option && option.value;\n }\n\n if (!this.caption()) {\n return findFirst(this.options);\n }\n },\n\n /**\n * Filters 'initialOptions' property by 'field' and 'value' passed,\n * calls 'setOptions' passing the result to it\n *\n * @param {*} value\n * @param {String} field\n */\n filter: function (value, field) {\n var source = this.initialOptions,\n result;\n\n field = field || this.filterBy.field;\n\n result = _.filter(source, function (item) {\n return item[field] === value || item.value === '';\n });\n\n this.setOptions(result);\n },\n\n /**\n * Change visibility for input.\n *\n * @param {Boolean} isVisible\n */\n toggleInput: function (isVisible) {\n registry.get(this.customName, function (input) {\n input.setVisible(isVisible);\n });\n },\n\n /**\n * Sets 'data' to 'options' observable array, if instance has\n * 'customEntry' property set to true, calls 'setHidden' method\n * passing !options.length as a parameter\n *\n * @param {Array} data\n * @returns {Object} Chainable\n */\n setOptions: function (data) {\n var captionValue = this.captionValue || '',\n result = parseOptions(data, captionValue),\n isVisible;\n\n this.indexedOptions = indexOptions(result.options);\n\n this.options(result.options);\n\n if (!this.caption()) {\n this.caption(result.caption);\n }\n\n if (this.customEntry) {\n isVisible = !!result.options.length;\n\n this.setVisible(isVisible);\n this.toggleInput(!isVisible);\n }\n\n return this;\n },\n\n /**\n * Processes preview for option by it's value, and sets the result\n * to 'preview' observable\n *\n * @returns {Object} Chainable.\n */\n getPreview: function () {\n var value = this.value(),\n option = this.indexedOptions[value],\n preview = option ? option.label : '';\n\n this.preview(preview);\n\n return preview;\n },\n\n /**\n * Get option from indexedOptions list.\n *\n * @param {Number} value\n * @returns {Object} Chainable\n */\n getOption: function (value) {\n return this.indexedOptions[value];\n },\n\n /**\n * Select first available option\n *\n * @returns {Object} Chainable.\n */\n clear: function () {\n var value = this.caption() ? '' : findFirst(this.options);\n\n this.value(value);\n\n return this;\n },\n\n /**\n * Initializes observable properties of instance\n *\n * @returns {Object} Chainable.\n */\n setInitialValue: function () {\n if (_.isUndefined(this.value()) && !this.default) {\n this.clear();\n }\n\n return this._super();\n }\n });\n});\n","Magento_Ui/js/form/adapter/buttons.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine(function () {\n 'use strict';\n\n return {\n 'reset': '#reset',\n 'save': '#save',\n 'saveAndContinue': '#save_and_continue'\n };\n});\n","Magento_Ui/js/core/app.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n './renderer/types',\n './renderer/layout',\n '../lib/knockout/bootstrap'\n], function (types, layout) {\n 'use strict';\n\n return function (data, merge) {\n types.set(data.types);\n layout(data.components, undefined, true, merge);\n };\n});\n","Magento_Ui/js/core/renderer/layout.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'jquery',\n 'mageUtils',\n 'uiRegistry',\n './types',\n '../../lib/logger/console-logger'\n], function (_, $, utils, registry, types, consoleLogger) {\n 'use strict';\n\n var templates = registry.create(),\n layout = {},\n cachedConfig = {};\n\n /**\n * Build name from parent name and node name\n *\n * @param {Object} parent\n * @param {Object} node\n * @param {String} [name]\n * @returns {String}\n */\n function getNodeName(parent, node, name) {\n var parentName = parent && parent.name;\n\n if (typeof name !== 'string') {\n name = node.name || name;\n }\n\n return utils.fullPath(parentName, name);\n }\n\n /**\n * Get node type from node or parent.\n *\n * @param {Object} parent\n * @param {Object} node\n * @returns {String}\n */\n function getNodeType(parent, node) {\n return node.type || parent && parent.childType;\n }\n\n /**\n * Get data scope based on parent data scope and node data scope.\n *\n * @param {Object} parent\n * @param {Object} node\n * @returns {String}\n */\n function getDataScope(parent, node) {\n var dataScope = node.dataScope,\n parentScope = parent && parent.dataScope;\n\n return !utils.isEmpty(parentScope) ?\n !utils.isEmpty(dataScope) ?\n parentScope + '.' + dataScope :\n parentScope :\n dataScope || '';\n }\n\n /**\n * Load node dependencies on other instances.\n *\n * @param {Object} node\n * @returns {jQueryPromise}\n */\n function loadDeps(node) {\n var loaded = $.Deferred(),\n loggerUtils = consoleLogger.utils;\n\n if (node.deps) {\n consoleLogger.utils.asyncLog(\n loaded,\n {\n data: {\n component: node.name,\n deps: node.deps\n },\n messages: loggerUtils.createMessages(\n 'depsStartRequesting',\n 'depsFinishRequesting',\n 'depsLoadingFail'\n )\n }\n );\n }\n\n registry.get(node.deps, function (deps) {\n node.provider = node.extendProvider ? deps && deps.name : node.provider;\n loaded.resolve(node);\n });\n\n return loaded.promise();\n }\n\n /**\n * Load node component file via requirejs.\n *\n * @param {Object} node\n * @returns {jQueryPromise}\n */\n function loadSource(node) {\n var loaded = $.Deferred(),\n source = node.component;\n\n consoleLogger.info('componentStartLoading', {\n component: node.component\n });\n\n require([source], function (constr) {\n consoleLogger.info('componentFinishLoading', {\n component: node.component\n });\n loaded.resolve(node, constr);\n }, function () {\n consoleLogger.error('componentLoadingFail', {\n component: node.component\n });\n });\n\n return loaded.promise();\n }\n\n /**\n * Create a new component instance and set it to the registry.\n *\n * @param {Object} node\n * @param {Function} Constr\n */\n function initComponent(node, Constr) {\n var component = new Constr(_.omit(node, 'children'));\n\n consoleLogger.info('componentStartInitialization', {\n component: node.component,\n componentName: node.name\n });\n\n registry.set(node.name, component);\n }\n\n /**\n * Application entry point.\n *\n * @param {Object} nodes\n * @param {Object} parent\n * @param {Boolean} cached\n * @param {Boolean} merge\n * @returns {Boolean|undefined}\n */\n function run(nodes, parent, cached, merge) {\n if (_.isBoolean(merge) && merge) {\n layout.merge(nodes);\n\n return false;\n }\n\n if (cached) {\n cachedConfig[_.keys(nodes)[0]] = JSON.parse(JSON.stringify(nodes));\n }\n\n _.each(nodes || [], layout.iterator.bind(layout, parent));\n }\n\n _.extend(layout, {\n /**\n * Determines if node ready to be added or process it.\n *\n * @param {Object} parent\n * @param {Object|String} node\n */\n iterator: function (parent, node) {\n var action = _.isString(node) ?\n this.addChild :\n this.process;\n\n action.apply(this, arguments);\n },\n\n /**\n * Prepare component.\n *\n * @param {Object} parent\n * @param {Object} node\n * @param {String} name\n * @returns {Object}\n */\n process: function (parent, node, name) {\n if (!parent && node.parent) {\n return this.waitParent(node, name);\n }\n\n if (node.nodeTemplate) {\n return this.waitTemplate.apply(this, arguments);\n }\n\n node = this.build.apply(this, arguments);\n\n if (!registry.has(node.name)) {\n this.addChild(parent, node)\n .manipulate(node)\n .initComponent(node);\n }\n\n if (node) {\n run(node.children, node);\n }\n\n return this;\n },\n\n /**\n * Detailed processing of component config.\n *\n * @param {Object} parent\n * @param {Object} node\n * @param {String} name\n * @returns {Boolean|Object}\n */\n build: function (parent, node, name) {\n var defaults = parent && parent.childDefaults || {},\n children = this.filterDisabledChildren(node.children),\n type = getNodeType(parent, node),\n dataScope = getDataScope(parent, node),\n component,\n extendDeps = true,\n nodeName;\n\n node.children = false;\n node.extendProvider = true;\n\n if (node.config && node.config.provider || node.provider) {\n node.extendProvider = false;\n }\n\n if (node.config && node.config.deps || node.deps) {\n extendDeps = false;\n }\n\n node = utils.extend({\n }, types.get(type), defaults, node);\n\n nodeName = getNodeName(parent, node, name);\n\n if (registry.has(nodeName)) {\n component = registry.get(nodeName);\n component.children = children;\n\n return component;\n }\n\n if (extendDeps && parent && parent.deps && type) {\n node.deps = parent.deps;\n }\n\n _.extend(node, node.config || {}, {\n index: node.name || name,\n name: nodeName,\n dataScope: dataScope,\n parentName: utils.getPart(nodeName, -2),\n parentScope: utils.getPart(dataScope, -2)\n });\n\n node.children = children;\n node.componentType = node.type;\n\n delete node.type;\n delete node.config;\n\n if (children) {\n node.initChildCount = _.size(children);\n }\n\n if (node.isTemplate) {\n node.isTemplate = false;\n\n templates.set(node.name, node);\n registry.get(node.parentName, function (parentComp) {\n parentComp.childTemplate = node;\n });\n\n return false;\n }\n\n if (node.componentDisabled === true) {\n return false;\n }\n\n return node;\n },\n\n /**\n * Filter out all disabled components.\n *\n * @param {Object} children\n * @returns {*}\n */\n filterDisabledChildren: function (children) {\n var cIds;\n\n //cleanup children config.componentDisabled = true\n if (children && typeof children === 'object') {\n cIds = Object.keys(children);\n\n if (cIds) {\n _.each(cIds, function (cId) {\n if (typeof children[cId] === 'object' &&\n children[cId].hasOwnProperty('config') &&\n typeof children[cId].config === 'object' &&\n children[cId].config.hasOwnProperty('componentDisabled') &&\n children[cId].config.componentDisabled === true) {\n delete children[cId];\n }\n });\n }\n }\n\n return children;\n },\n\n /**\n * Init component.\n *\n * @param {Object} node\n * @returns {Object}\n */\n initComponent: function (node) {\n if (!node.component) {\n return this;\n }\n\n loadDeps(node)\n .then(loadSource)\n .done(initComponent);\n\n return this;\n }\n });\n\n _.extend(layout, {\n /**\n * Loading component marked as isTemplate.\n *\n * @param {Object} parent\n * @param {Object} node\n * @returns {Object}\n */\n waitTemplate: function (parent, node) {\n var args = _.toArray(arguments);\n\n templates.get(node.nodeTemplate, function () {\n this.applyTemplate.apply(this, args);\n }.bind(this));\n\n return this;\n },\n\n /**\n * Waiting for parent component and process provided component.\n *\n * @param {Object} node\n * @param {String} name\n * @returns {Object}\n */\n waitParent: function (node, name) {\n var process = this.process.bind(this);\n\n registry.get(node.parent, function (parent) {\n process(parent, node, name);\n });\n\n return this;\n },\n\n /**\n * Processing component marked as isTemplate.\n *\n * @param {Object} parent\n * @param {Object} node\n * @param {String} name\n */\n applyTemplate: function (parent, node, name) {\n var template = templates.get(node.nodeTemplate);\n\n node = utils.extend({}, template, node);\n\n delete node.nodeTemplate;\n\n this.process(parent, node, name);\n }\n });\n\n _.extend(layout, {\n /**\n * Determines inserting strategy.\n *\n * @param {Object} node\n * @returns {Object}\n */\n manipulate: function (node) {\n var name = node.name;\n\n if (node.appendTo) {\n this.insert(name, node.appendTo, -1);\n }\n\n if (node.prependTo) {\n this.insert(name, node.prependTo, 0);\n }\n\n if (node.insertTo) {\n this.insertTo(name, node.insertTo);\n }\n\n return this;\n },\n\n /**\n * Insert component to provide target and position.\n *\n * @param {Object|String} item\n * @param {Object} target\n * @param {Number} position\n * @returns {Object}\n */\n insert: function (item, target, position) {\n registry.get(target, function (container) {\n container.insertChild(item, position);\n });\n\n return this;\n },\n\n /**\n * Insert component into multiple targets.\n *\n * @param {Object} item\n * @param {Array} targets\n * @returns {Object}\n */\n insertTo: function (item, targets) {\n _.each(targets, function (info, target) {\n this.insert(item, target, info.position);\n }, this);\n\n return this;\n },\n\n /**\n * Add provided child to parent.\n *\n * @param {Object} parent\n * @param {Object|String} child\n * @returns {Object}\n */\n addChild: function (parent, child) {\n var name;\n\n if (parent && parent.component) {\n name = child.name || child;\n\n this.insert(name, parent.name, child.sortOrder);\n }\n\n return this;\n },\n\n /**\n * Merge components configuration with cached configuration.\n *\n * @param {Array} components\n */\n merge: function (components) {\n var cachedKey = _.keys(components)[0],\n compared = utils.compare(cachedConfig[cachedKey], components),\n remove = this.filterComponents(this.getByProperty(compared.changes, 'type', 'remove'), true),\n update = this.getByProperty(compared.changes, 'type', 'update'),\n dataSources = this.getDataSources(components),\n names, index, name, component;\n\n _.each(dataSources, function (val, key) {\n name = key.replace(/\\.children|\\.config/g, '');\n component = registry.get(name);\n\n component.cacheData();\n component.updateConfig(\n true,\n this.getFullConfig(key, components),\n this.getFullConfig(key, cachedConfig[cachedKey])\n );\n }, this);\n\n _.each(remove, function (val) {\n component = registry.get(val.path);\n\n if (component) {\n component.cleanData().destroy();\n }\n });\n\n update = _.compact(_.filter(update, function (val) {\n return !_.isEqual(val.oldValue, val.value);\n }));\n\n _.each(update, function (val) {\n names = val.path.split('.');\n index = Math.max(_.lastIndexOf(names, 'config'), _.lastIndexOf(names, 'children') + 2);\n name = _.without(names.splice(0, index), 'children', 'config').join('.');\n component = registry.get(name);\n\n if (val.name === 'sortOrder' && component) {\n registry.get(component.parentName).insertChild(component, val.value);\n } else if (component) {\n component.updateConfig(\n val.oldValue,\n val.value,\n val.path\n );\n }\n }, this);\n\n run(components, undefined, true);\n },\n\n /**\n * Recursive dataSource assignment.\n *\n * @param {Object} config\n * @param {String} parentPath\n * @returns {Object}\n */\n getDataSources: function (config, parentPath) {\n var dataSources = {},\n key, obj;\n\n /* eslint-disable no-loop-func, max-depth */\n for (key in config) {\n if (config.hasOwnProperty(key)) {\n if (\n key === 'type' &&\n config[key] === 'dataSource' &&\n config.hasOwnProperty('config')\n ) {\n dataSources[parentPath + '.config'] = config.config;\n } else if (_.isObject(config[key])) {\n obj = this.getDataSources(config[key], utils.fullPath(parentPath, key));\n\n _.each(obj, function (value, path) {\n dataSources[path] = value;\n });\n }\n }\n }\n\n /* eslint-enable no-loop-func, max-depth */\n\n return dataSources;\n },\n\n /**\n * Configuration getter.\n *\n * @param {String} path\n * @param {Object} config\n * @returns {Boolean|Object}\n */\n getFullConfig: function (path, config) {\n var index;\n\n path = path.split('.');\n index = _.lastIndexOf(path, 'config');\n\n if (!~index) {\n return false;\n }\n path = path.splice(0, index);\n\n _.each(path, function (val) {\n config = config[val];\n });\n\n return config.config;\n },\n\n /**\n * Filter data by property and value.\n *\n * @param {Object} data\n * @param {String} prop\n * @param {*} propValue\n */\n getByProperty: function (data, prop, propValue) {\n return _.filter(data, function (value) {\n return value[prop] === propValue;\n });\n },\n\n /**\n * Filter components.\n *\n * @param {Array} data\n * @param {Boolean} splitPath\n * @param {Number} index\n * @param {String} separator\n * @param {String} keyName\n * @returns {Array}\n */\n filterComponents: function (data, splitPath, index, separator, keyName) {\n var result = [],\n names, length;\n\n index = -2;\n separator = '.' || separator;\n keyName = 'children' || keyName;\n\n _.each(data, function (val) {\n names = val.path.split(separator);\n length = names.length;\n\n if (names[length + index] === keyName) {\n val.path = splitPath ? _.without(names, keyName).join(separator) : val.path;\n result.push(val);\n }\n });\n\n return result;\n }\n });\n\n return run;\n});\n","Magento_Ui/js/core/renderer/types.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'mageUtils'\n], function (_, utils) {\n 'use strict';\n\n var store = {};\n\n /**\n * Flatten a nested data.\n *\n * @param {Object} data\n * @returns {Object}\n */\n function flatten(data) {\n var extender = data.extends || [],\n result = {};\n\n extender = utils.stringToArray(extender);\n\n extender.push(data);\n\n extender.forEach(function (item) {\n if (_.isString(item)) {\n item = store[item] || {};\n }\n\n utils.extend(result, item);\n });\n\n delete result.extends;\n\n return result;\n }\n\n return {\n /**\n * Set types to store object.\n *\n * @param {Object} types\n */\n set: function (types) {\n types = types || {};\n\n utils.extend(store, types);\n\n _.each(types, function (data, type) {\n store[type] = flatten(data);\n });\n },\n\n /**\n * Get type from store object.\n *\n * @param {String} type\n * @returns {*|{}}\n */\n get: function (type) {\n return store[type] || {};\n }\n };\n});\n","Magento_Ui/js/grid/tree-massactions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'underscore',\n 'Magento_Ui/js/grid/massactions'\n], function (ko, _, Massactions) {\n 'use strict';\n\n return Massactions.extend({\n defaults: {\n template: 'ui/grid/tree-massactions',\n submenuTemplate: 'ui/grid/submenu',\n listens: {\n opened: 'hideSubmenus'\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Massactions} Chainable.\n */\n initObservable: function () {\n this._super()\n .recursiveObserveActions(this.actions());\n\n return this;\n },\n\n /**\n * Recursive initializes observable actions.\n *\n * @param {Array} actions - Action objects.\n * @param {String} [prefix] - An optional string that will be prepended\n * to the \"type\" field of all child actions.\n * @returns {Massactions} Chainable.\n */\n recursiveObserveActions: function (actions, prefix) {\n _.each(actions, function (action) {\n if (prefix) {\n action.type = prefix + '.' + action.type;\n }\n\n if (action.actions) {\n action.visible = ko.observable(false);\n action.parent = actions;\n this.recursiveObserveActions(action.actions, action.type);\n }\n }, this);\n\n return this;\n },\n\n /**\n * Applies specified action.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @returns {Massactions} Chainable.\n */\n applyAction: function (actionIndex) {\n var action = this.getAction(actionIndex),\n visibility;\n\n if (action.visible) {\n visibility = action.visible();\n\n this.hideSubmenus(action.parent);\n action.visible(!visibility);\n\n return this;\n }\n\n return this._super(actionIndex);\n },\n\n /**\n * Retrieves action object associated with a specified index.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @param {Array} actions - Action objects.\n * @returns {Object} Action object.\n */\n getAction: function (actionIndex, actions) {\n var currentActions = actions || this.actions(),\n result = false;\n\n _.find(currentActions, function (action) {\n if (action.type === actionIndex) {\n result = action;\n\n return true;\n }\n\n if (action.actions) {\n result = this.getAction(actionIndex, action.actions);\n\n return result;\n }\n }, this);\n\n return result;\n },\n\n /**\n * Recursive hide all sub folders in given array.\n *\n * @param {Array} actions - Action objects.\n * @returns {Massactions} Chainable.\n */\n hideSubmenus: function (actions) {\n var currentActions = actions || this.actions();\n\n _.each(currentActions, function (action) {\n if (action.visible && action.visible()) {\n action.visible(false);\n }\n\n if (action.actions) {\n this.hideSubmenus(action.actions);\n }\n }, this);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/grid/toolbar.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'Magento_Ui/js/lib/view/utils/async',\n 'Magento_Ui/js/lib/view/utils/raf',\n 'rjsResolver',\n 'uiCollection'\n], function (_, $, raf, resolver, Collection) {\n 'use strict';\n\n var transformProp;\n\n /**\n * Defines supported css 'transform' property.\n *\n * @returns {String|Undefined}\n */\n transformProp = (function () {\n var style = document.documentElement.style,\n base = 'Transform',\n vendors = ['webkit', 'moz', 'ms', 'o'],\n vi = vendors.length,\n property;\n\n if (typeof style.transform != 'undefined') {\n return 'transform';\n }\n\n while (vi--) {\n property = vendors[vi] + base;\n\n if (typeof style[property] != 'undefined') {\n return property;\n }\n }\n })();\n\n /**\n * Moves specified DOM element to the x and y coordinates.\n *\n * @param {HTMLElement} elem - Element to be relocated.\n * @param {Number} x - X coordinate.\n * @param {Number} y - Y coordinate.\n */\n function locate(elem, x, y) {\n var value = 'translate(' + x + 'px,' + y + 'px)';\n\n elem.style[transformProp] = value;\n }\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/toolbar',\n stickyTmpl: 'ui/grid/sticky/sticky',\n tableSelector: 'table',\n columnsProvider: 'ns = ${ $.ns }, componentType = columns',\n refreshFPS: 15,\n sticky: false,\n visible: false,\n _resized: true,\n _scrolled: true,\n _tableScrolled: true,\n _requiredNodes: {\n '$stickyToolbar': true,\n '$stickyTable': true,\n '$table': true,\n '$sticky': true\n },\n stickyClass: {\n 'sticky-header': true\n }\n },\n\n /**\n * Initializes sticky toolbar component.\n *\n * @returns {Sticky} Chainable.\n */\n initialize: function () {\n this._super();\n\n if (this.sticky) {\n this.waitDOMElements()\n .then(this.run.bind(this));\n }\n\n return this;\n },\n\n /**\n * Establishes DOM elements wait process.\n *\n * @returns {jQueryPromise} Promise which will be resolved\n * when all of the required DOM elements are defined.\n */\n waitDOMElements: function () {\n var _domPromise = $.Deferred();\n\n _.bindAll(this, 'setStickyTable', 'setTableNode');\n\n $.async({\n ctx: ':not([data-role=\"sticky-el-root\"])',\n component: this.columnsProvider,\n selector: this.tableSelector\n }, this.setTableNode);\n\n $.async({\n ctx: '[data-role=\"sticky-el-root\"]',\n component: this.columnsProvider,\n selector: this.tableSelector\n }, this.setStickyTable);\n\n this._domPromise = _domPromise;\n\n return _domPromise.promise();\n },\n\n /**\n * Defines left caption element.\n *\n * @param {HTMLElement} node\n */\n setLeftCap: function (node) {\n this.$leftCap = node;\n },\n\n /**\n * Defines right caption element.\n *\n * @param {HTMLElement} node\n */\n setRightCap: function (node) {\n this.$rightCap = node;\n },\n\n /**\n * Defines original table element.\n *\n * @param {HTMLTableElement} node\n */\n setTableNode: function (node) {\n this.$cols = node.tHead.children[0].cells;\n this.$tableContainer = node.parentNode;\n\n this.setNode('$table', node);\n },\n\n /**\n * Defines sticky table element.\n *\n * @param {HTMLTableElement} node\n */\n setStickyTable: function (node) {\n this.$stickyCols = node.tHead.children[0].cells;\n\n this.setNode('$stickyTable', node);\n },\n\n /**\n * Defines sticky toolbar node.\n *\n * @param {HTMLElement} node\n */\n setStickyToolbarNode: function (node) {\n this.setNode('$stickyToolbar', node);\n },\n\n /**\n * Defines sticky element container.\n *\n * @param {HTMLElement} node\n */\n setStickyNode: function (node) {\n this.setNode('$sticky', node);\n },\n\n /**\n * Defines toolbar element container.\n *\n * @param {HTMLElement} node\n */\n setToolbarNode: function (node) {\n this.$toolbar = node;\n },\n\n /**\n * Sets provided node as a value of 'key' property and\n * performs check for required DOM elements.\n *\n * @param {String} key - Properties key.\n * @param {HTMLElement} node - DOM element.\n */\n setNode: function (key, node) {\n var nodes = this._requiredNodes,\n promise = this._domPromise,\n defined;\n\n this[key] = node;\n\n defined = _.every(nodes, function (enabled, name) {\n return enabled ? this[name] : true;\n }, this);\n\n if (defined) {\n resolver(promise.resolve, promise);\n }\n },\n\n /**\n * Starts refresh process of the sticky element\n * and assigns DOM elements events handlers.\n */\n run: function () {\n _.bindAll(\n this,\n 'refresh',\n '_onWindowResize',\n '_onWindowScroll',\n '_onTableScroll'\n );\n\n $(window).on({\n scroll: this._onWindowScroll,\n resize: this._onWindowResize\n });\n\n $(this.$tableContainer).on('scroll', this._onTableScroll);\n\n this.refresh();\n this.checkTableWidth();\n },\n\n /**\n * Refreshes state of the sticky element and\n * invokes DOM elements events handlers\n * if corresponding event has been triggered.\n */\n refresh: function () {\n if (!raf(this.refresh, this.refreshFPS)) {\n return;\n }\n\n if (this._scrolled) {\n this.onWindowScroll();\n }\n\n if (this._tableScrolled) {\n this.onTableScroll();\n }\n\n if (this._resized) {\n this.onWindowResize();\n }\n\n if (this.visible) {\n this.checkTableWidth();\n }\n },\n\n /**\n * Shows sticky toolbar.\n *\n * @returns {Sticky} Chainable.\n */\n show: function () {\n this.visible = true;\n\n this.$sticky.style.display = '';\n this.$toolbar.style.visibility = 'hidden';\n\n return this;\n },\n\n /**\n * Hides sticky toolbar.\n *\n * @returns {Sticky} Chainable.\n */\n hide: function () {\n this.visible = false;\n\n this.$sticky.style.display = 'none';\n this.$toolbar.style.visibility = '';\n\n return this;\n },\n\n /**\n * Checks if sticky toolbar covers original elements.\n *\n * @returns {Boolean}\n */\n isCovered: function () {\n var stickyTop = this._stickyTableTop + this._wScrollTop;\n\n return stickyTop > this._tableTop;\n },\n\n /**\n * Updates offset of the sticky table element.\n *\n * @returns {Sticky} Chainable.\n */\n updateStickyTableOffset: function () {\n var style,\n top;\n\n if (this.visible) {\n top = this.$stickyTable.getBoundingClientRect().top;\n } else {\n style = this.$sticky.style;\n\n style.visibility = 'hidden';\n style.display = '';\n\n top = this.$stickyTable.getBoundingClientRect().top;\n\n style.display = 'none';\n style.visibility = '';\n }\n\n this._stickyTableTop = top;\n\n return this;\n },\n\n /**\n * Updates offset of the original table element.\n *\n * @returns {Sticky} Chainable.\n */\n updateTableOffset: function () {\n var box = this.$table.getBoundingClientRect(),\n top = box.top + this._wScrollTop;\n\n if (this._tableTop !== top) {\n this._tableTop = top;\n\n this.onTableTopChange(top);\n }\n\n return this;\n },\n\n /**\n * Checks if width of the table or it's columns has changed.\n *\n * @returns {Sticky} Chainable.\n */\n checkTableWidth: function () {\n var cols = this.$cols,\n total = cols.length,\n rightBorder = cols[total - 2].offsetLeft,\n tableWidth = this.$table.offsetWidth;\n\n if (this._tableWidth !== tableWidth) {\n this._tableWidth = tableWidth;\n\n this.onTableWidthChange(tableWidth);\n }\n\n if (this._rightBorder !== rightBorder) {\n this._rightBorder = rightBorder;\n\n this.onColumnsWidthChange();\n }\n\n return this;\n },\n\n /**\n * Updates width of the sticky table.\n *\n * @returns {Sticky} Chainable.\n */\n updateTableWidth: function () {\n this.$stickyTable.style.width = this._tableWidth + 'px';\n\n if (this._tableWidth < this._toolbarWidth) {\n this.checkToolbarSize();\n }\n\n return this;\n },\n\n /**\n * Updates width of the sticky columns.\n *\n * @returns {Sticky} Chainable.\n */\n updateColumnsWidth: function () {\n var cols = this.$cols,\n index = cols.length,\n stickyCols = this.$stickyCols;\n\n while (index--) {\n stickyCols[index].width = cols[index].offsetWidth;\n }\n\n return this;\n },\n\n /**\n * Upadates size of the sticky toolbar element\n * and invokes corresponding 'change' event handlers.\n *\n * @returns {Sticky} Chainable.\n */\n checkToolbarSize: function () {\n var width = this.$tableContainer.offsetWidth;\n\n if (this._toolbarWidth !== width) {\n this._toolbarWidth = width;\n\n this.onToolbarWidthChange(width);\n }\n\n return this;\n },\n\n /**\n * Toggles sticky toolbar visibility if it's necessary.\n *\n * @returns {Sticky} Chainable.\n */\n updateVisibility: function () {\n if (this.visible !== this.isCovered()) {\n this.visible ? this.hide() : this.show();\n }\n\n return this;\n },\n\n /**\n * Updates position of the left cover area.\n *\n * @returns {Sticky} Chainable.\n */\n updateLeftCap: function () {\n locate(this.$leftCap, -this._wScrollLeft, 0);\n\n return this;\n },\n\n /**\n * Updates position of the right cover area.\n *\n * @returns {Sticky} Chainable.\n */\n updateRightCap: function () {\n var left = this._toolbarWidth - this._wScrollLeft;\n\n locate(this.$rightCap, left, 0);\n\n return this;\n },\n\n /**\n * Updates position of the sticky table.\n *\n * @returns {Sticky} Chainable.\n */\n updateTableScroll: function () {\n var container = this.$tableContainer,\n left = container.scrollLeft + this._wScrollLeft;\n\n locate(this.$stickyTable, -left, 0);\n\n return this;\n },\n\n /**\n * Updates width of the toolbar element.\n *\n * @returns {Sticky} Chainable.\n */\n updateToolbarWidth: function () {\n this.$stickyToolbar.style.width = this._toolbarWidth + 'px';\n\n return this;\n },\n\n /**\n * Handles changes of the toolbar element's width.\n */\n onToolbarWidthChange: function () {\n this.updateToolbarWidth()\n .updateRightCap();\n },\n\n /**\n * Handles changes of the table top position.\n */\n onTableTopChange: function () {\n this.updateStickyTableOffset();\n },\n\n /**\n * Handles change of the table width.\n */\n onTableWidthChange: function () {\n this.updateTableWidth();\n },\n\n /**\n * Handles change of the table columns width.\n */\n onColumnsWidthChange: function () {\n this.updateColumnsWidth();\n },\n\n /**\n * Handles changes of the window's size.\n */\n onWindowResize: function () {\n this.checkToolbarSize();\n\n this._resized = false;\n },\n\n /**\n * Handles changes of the original table scroll position.\n */\n onTableScroll: function () {\n this.updateTableScroll();\n\n this._tableScrolled = false;\n },\n\n /**\n * Handles changes of window's scroll position.\n */\n onWindowScroll: function () {\n var scrollTop = window.pageYOffset,\n scrollLeft = window.pageXOffset;\n\n if (this._wScrollTop !== scrollTop) {\n this._wScrollTop = scrollTop;\n\n this.onWindowScrollTop(scrollTop);\n }\n\n if (this._wScrollLeft !== scrollLeft) {\n this._wScrollLeft = scrollLeft;\n\n this.onWindowScrollLeft(scrollLeft);\n }\n\n this._scrolled = false;\n },\n\n /**\n * Handles changes of windows' top scroll position.\n */\n onWindowScrollTop: function () {\n this.updateTableOffset()\n .updateVisibility();\n },\n\n /**\n * Handles changes of windows' left scroll position.\n */\n onWindowScrollLeft: function () {\n this.updateRightCap()\n .updateLeftCap()\n .updateTableScroll();\n },\n\n /**\n * Original window 'scroll' event handler.\n * Sets 'scrolled' flag to 'true'.\n *\n * @private\n */\n _onWindowScroll: function () {\n this._scrolled = true;\n },\n\n /**\n * Original window 'resize' event handler.\n * Sets 'resized' flag to 'true'.\n *\n * @private\n */\n _onWindowResize: function () {\n this._resized = true;\n },\n\n /**\n * Original table 'scroll' event handler.\n * Sets '_tableScrolled' flag to 'true'.\n *\n * @private\n */\n _onTableScroll: function () {\n this._tableScrolled = true;\n }\n });\n});\n","Magento_Ui/js/grid/export.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'uiElement'\n], function ($, _, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n template: 'ui/grid/exportButton',\n selectProvider: 'ns = ${ $.ns }, index = ids',\n checked: '',\n additionalParams: [],\n modules: {\n selections: '${ $.selectProvider }'\n }\n },\n\n /** @inheritdoc */\n initialize: function () {\n this._super()\n .initChecked();\n },\n\n /** @inheritdoc */\n initConfig: function () {\n this._super();\n\n _.each(this.additionalParams, function (value, key) {\n key = 'additionalParams.' + key;\n this.imports[key] = value;\n }, this);\n\n return this;\n },\n\n /** @inheritdoc */\n initObservable: function () {\n this._super()\n .observe('checked');\n\n return this;\n },\n\n /**\n * Checks first option if checked not defined.\n *\n * @returns {Object}\n */\n initChecked: function () {\n if (!this.checked()) {\n this.checked(\n this.options[0].value\n );\n }\n\n return this;\n },\n\n /**\n * Compose params object that will be added to request.\n *\n * @returns {Object}\n */\n getParams: function () {\n var selections = this.selections(),\n data = selections ? selections.getSelections() : null,\n itemsType,\n result = {};\n\n if (data) {\n itemsType = data.excludeMode ? 'excluded' : 'selected';\n result.filters = data.params.filters;\n result.search = data.params.search;\n result.namespace = data.params.namespace;\n result[itemsType] = data[itemsType];\n _.each(this.additionalParams, function (param, key) {\n result[key] = param;\n });\n\n if (!result[itemsType].length) {\n result[itemsType] = false;\n }\n }\n\n return result;\n },\n\n /**\n * Find checked option.\n *\n * @returns {Object}\n */\n getActiveOption: function () {\n return _.findWhere(this.options, {\n value: this.checked()\n });\n },\n\n /**\n * Build option url.\n *\n * @param {Object} option\n * @returns {String}\n */\n buildOptionUrl: function (option) {\n var params = this.getParams();\n\n if (!params) {\n return 'javascript:void(0);';\n }\n\n return option.url + '?' + $.param(params);\n //TODO: MAGETWO-40250\n },\n\n /**\n * Redirect to built option url.\n */\n applyOption: function () {\n var option = this.getActiveOption(),\n url = this.buildOptionUrl(option);\n\n location.href = url;\n\n }\n });\n});\n","Magento_Ui/js/grid/massactions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n 'mageUtils',\n 'Magento_Ui/js/lib/collapsible',\n 'Magento_Ui/js/modal/confirm',\n 'Magento_Ui/js/modal/alert',\n 'mage/translate'\n], function (_, registry, utils, Collapsible, confirm, alert, $t) {\n 'use strict';\n\n return Collapsible.extend({\n defaults: {\n template: 'ui/grid/actions',\n stickyTmpl: 'ui/grid/sticky/actions',\n selectProvider: 'ns = ${ $.ns }, index = ids',\n actions: [],\n noItemsMsg: $t('You haven\\'t selected any items!'),\n modules: {\n selections: '${ $.selectProvider }'\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Massactions} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe('actions');\n\n return this;\n },\n\n /**\n * Applies specified action.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @returns {Massactions} Chainable.\n */\n applyAction: function (actionIndex) {\n var data = this.getSelections(),\n action,\n callback;\n\n if (!data.total) {\n alert({\n content: this.noItemsMsg\n });\n\n return this;\n }\n\n action = this.getAction(actionIndex);\n callback = this._getCallback(action, data);\n\n action.confirm ?\n this._confirm(action, callback) :\n callback();\n\n return this;\n },\n\n /**\n * Retrieves selections data from the selections provider.\n *\n * @returns {Object|Undefined}\n */\n getSelections: function () {\n var provider = this.selections(),\n selections = provider && provider.getSelections();\n\n return selections;\n },\n\n /**\n * Retrieves action object associated with a specified index.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @returns {Object} Action object.\n */\n getAction: function (actionIndex) {\n return _.findWhere(this.actions(), {\n type: actionIndex\n });\n },\n\n /**\n * Adds new action. If action with a specified identifier\n * already exists, than the original one will be overrided.\n *\n * @param {Object} action - Action object.\n * @returns {Massactions} Chainable.\n */\n addAction: function (action) {\n var actions = this.actions(),\n index = _.findIndex(actions, {\n type: action.type\n });\n\n ~index ?\n actions[index] = action :\n actions.push(action);\n\n this.actions(actions);\n\n return this;\n },\n\n /**\n * Creates action callback based on its' data. If action doesn't spicify\n * a callback function than the default one will be used.\n *\n * @private\n * @param {Object} action - Actions' object.\n * @param {Object} selections - Selections data.\n * @returns {Function} Callback function.\n */\n _getCallback: function (action, selections) {\n var callback = action.callback,\n args = [action, selections];\n\n if (utils.isObject(callback)) {\n args.unshift(callback.target);\n\n callback = registry.async(callback.provider);\n } else if (typeof callback != 'function') {\n callback = this.defaultCallback.bind(this);\n }\n\n return function () {\n callback.apply(null, args);\n };\n },\n\n /**\n * Default action callback. Sends selections data\n * via POST request.\n *\n * @param {Object} action - Action data.\n * @param {Object} data - Selections data.\n */\n defaultCallback: function (action, data) {\n var itemsType = data.excludeMode ? 'excluded' : 'selected',\n selections = {};\n\n if (itemsType === 'excluded' && data.selected && data.selected.length) {\n itemsType = 'selected';\n data[itemsType] = _.difference(data.selected, data.excluded);\n }\n\n selections[itemsType] = data[itemsType];\n\n if (!selections[itemsType].length) {\n selections[itemsType] = false;\n }\n\n _.extend(selections, data.params || {});\n\n utils.submit({\n url: action.url,\n data: selections\n });\n },\n\n /**\n * Shows actions' confirmation window.\n *\n * @param {Object} action - Actions' data.\n * @param {Function} callback - Callback that will be\n * invoked if action is confirmed.\n */\n _confirm: function (action, callback) {\n var confirmData = action.confirm,\n data = this.getSelections(),\n total = data.total ? data.total : 0,\n confirmMessage = confirmData.message + ' (' + total + ' record' + (total > 1 ? 's' : '') + ')';\n\n confirm({\n title: confirmData.title,\n content: confirmMessage,\n actions: {\n confirm: callback\n }\n });\n }\n });\n});\n","Magento_Ui/js/grid/dnd.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'Magento_Ui/js/lib/view/utils/async',\n 'underscore',\n 'uiRegistry',\n 'uiClass'\n], function (ko, $, _, registry, Class) {\n 'use strict';\n\n var isTouchDevice = typeof document.ontouchstart !== 'undefined',\n transformProp;\n\n /**\n * Defines supported css 'transform' property.\n *\n * @returns {String|Undefined}\n */\n transformProp = (function () {\n var style = document.body.style,\n base = 'Transform',\n vendors = ['webkit', 'moz', 'ms', 'o'],\n vi = vendors.length,\n property;\n\n if (typeof style.transform != 'undefined') {\n return 'transform';\n }\n\n while (vi--) {\n property = vendors[vi] + base;\n\n if (typeof style[property] != 'undefined') {\n return property;\n }\n }\n })();\n\n /**\n * Returns first touch data if it's available.\n *\n * @param {(MouseEvent|TouchEvent)} e - Event object.\n * @returns {Object}\n */\n function getTouch(e) {\n return e.touches ? e.touches[0] : e;\n }\n\n /**\n * Moves specified DOM element to the x and y coordinates.\n *\n * @param {HTMLElement} elem - Element to be relocated.\n * @param {Number} x - X coordinate.\n * @param {Number} y - Y coordinate.\n */\n function locate(elem, x, y) {\n var value = 'translate(' + x + 'px,' + y + 'px)';\n\n elem.style[transformProp] = value;\n }\n\n /*eslint-disable no-extra-parens*/\n /**\n * Checks if specified coordinate is inside of the provided area.\n *\n * @param {Number} x - X coordinate.\n * @param {Number} y - Y coordinate.\n * @param {Object} area - Object which represents area.\n * @returns {Boolean}\n */\n function isInside(x, y, area) {\n return (\n area &&\n x >= area.left && x <= area.right &&\n y >= area.top && y <= area.bottom\n );\n }\n\n /*eslint-enable no-extra-parens*/\n\n /**\n * Calculates distance between two points.\n *\n * @param {Number} x1 - X coordinate of a first point.\n * @param {Number} y1 - Y coordinate of a first point.\n * @param {Number} x2 - X coordinate of a second point.\n * @param {Number} y2 - Y coordinate of a second point.\n * @returns {Number} Distance between points.\n */\n function distance(x1, y1, x2, y2) {\n var dx = x2 - x1,\n dy = y2 - y1;\n\n dx *= dx;\n dy *= dy;\n\n return Math.sqrt(dx + dy);\n }\n\n /**\n * Returns viewModel associated with a provided DOM element.\n *\n * @param {HTMLElement} elem\n * @returns {Object|Array}\n */\n function getModel(elem) {\n return ko.dataFor(elem);\n }\n\n /**\n * Checks whether cols are identical\n *\n * @param {HTMLElement} c1\n * @param {HTMLElement} c2\n * @returns {Boolean}\n */\n function compareCols(c1, c2) {\n return c1.cellIndex === c2.cellIndex;\n }\n\n return Class.extend({\n defaults: {\n rootSelector: '${ $.columnsProvider }:.admin__data-grid-wrap',\n tableSelector: '${ $.rootSelector } -> table.data-grid',\n mainTableSelector: '[data-role=\"grid\"]',\n columnSelector: '${ $.tableSelector } thead tr th',\n noSelectClass: '_no-select',\n hiddenClass: '_hidden',\n fixedX: false,\n fixedY: true,\n minDistance: 2,\n columns: []\n },\n\n /**\n * Initializes Dnd component.\n *\n * @returns {Dnd} Chainable.\n */\n initialize: function () {\n _.bindAll(\n this,\n 'initTable',\n 'initColumn',\n 'removeColumn',\n 'onMouseMove',\n 'onMouseUp',\n 'onMouseDown'\n );\n\n this.$body = $('body');\n\n this._super()\n .initListeners();\n\n $.async(this.tableSelector, this.initTable);\n $.async(this.columnSelector, this.initColumn);\n\n return this;\n },\n\n /**\n * Binds necessary events listeners.\n *\n * @returns {Dnd} Chainbale.\n */\n initListeners: function () {\n if (isTouchDevice) {\n $(document).on({\n touchmove: this.onMouseMove,\n touchend: this.onMouseUp,\n touchleave: this.onMouseUp\n });\n } else {\n $(document).on({\n mousemove: this.onMouseMove,\n mouseup: this.onMouseUp\n });\n }\n\n return this;\n },\n\n /**\n * Defines specified table element as a main container.\n *\n * @param {HTMLTableElement} table\n * @returns {Dnd} Chainable.\n */\n initTable: function (table) {\n this.table = $(table).is(this.mainTableSelector) ? table : this.table;\n\n $(table).addClass('data-grid-draggable');\n\n return this;\n },\n\n /**\n * Sets specified column as a draggable element.\n *\n * @param {HTMLTableHeaderCellElement} column - Columns header element.\n * @returns {Dnd} Chainable.\n */\n initColumn: function (column) {\n var model = getModel(column),\n eventName;\n\n if (!model || !model.draggable) {\n return this;\n }\n\n if (!ko.es5.isTracked(model, 'dragover')) {\n model.track('dragover');\n }\n\n this.columns.push(column);\n\n $(column).bindings({\n css: {\n '_dragover-left': ko.computed(function () {\n return model.dragover === 'right';\n }),\n '_dragover-right': ko.computed(function () {\n return model.dragover === 'left';\n })\n }\n });\n\n eventName = isTouchDevice ?\n 'touchstart' :\n 'mousedown';\n\n $(column).on(eventName, this.onMouseDown);\n $.async.remove(column, this.removeColumn);\n\n return this;\n },\n\n /**\n * Removes specified column element from the columns array.\n *\n * @param {HTMLTableHeaderCellElement} column - Columns header element.\n * @returns {Dnd} Chainable.\n */\n removeColumn: function (column) {\n var columns = this.columns,\n index = columns.indexOf(column);\n\n if (~index) {\n columns.splice(index, 1);\n }\n\n return this;\n },\n\n /**\n * Returns index of column.\n *\n * @param {HTMLTableHeaderCellElement} elem\n * @returns {Number}\n */\n _getColumnIndex: function (elem) {\n return _.toArray(elem.parentNode.cells).indexOf(elem);\n },\n\n /**\n * Calculates coordinates of draggable elements.\n *\n * @returns {Dnd} Chainbale.\n */\n _cacheCoords: function () {\n var container = this.table.getBoundingClientRect(),\n bodyRect = document.body.getBoundingClientRect(),\n grabbed = this.grabbed,\n dragElem = grabbed.elem,\n cells = _.toArray(dragElem.parentNode.cells),\n rect;\n\n this.coords = this.columns.map(function (column) {\n var data,\n colIndex = _.findIndex(cells, function (cell) {\n return compareCols(cell, column);\n });\n\n rect = column.getBoundingClientRect();\n\n data = {\n index: colIndex,\n target: column,\n orig: rect,\n left: rect.left - bodyRect.left,\n right: rect.right - bodyRect.left,\n top: rect.top - bodyRect.top,\n bottom: container.bottom - bodyRect.top\n };\n\n if (column === dragElem) {\n this.dragArea = data;\n\n grabbed.shiftX = rect.left - grabbed.x;\n grabbed.shiftY = rect.top - grabbed.y;\n }\n\n return data;\n }, this);\n\n return this;\n },\n\n /**\n * Creates clone of a target table with only specified column visible.\n *\n * @param {HTMLTableHeaderCellElement} elem - Dragging column.\n * @returns {Dnd} Chainbale.\n */\n _cloneTable: function (elem) {\n var clone = this.table.cloneNode(true),\n columnIndex = this._getColumnIndex(elem),\n headRow = clone.tHead.firstElementChild,\n headCells = _.toArray(headRow.cells),\n tableBody = clone.tBodies[0],\n bodyRows = _.toArray(tableBody.children),\n origTrs = this.table.tBodies[0].children;\n\n clone.style.width = elem.offsetWidth + 'px';\n\n headCells.forEach(function (th, index) {\n if (index !== columnIndex) {\n headRow.removeChild(th);\n }\n });\n\n headRow.cells[0].style.height = elem.offsetHeight + 'px';\n\n bodyRows.forEach(function (row, rowIndex) {\n var cells = row.cells,\n cell;\n\n if (cells.length !== headCells.length) {\n tableBody.removeChild(row);\n\n return;\n }\n\n cell = row.cells[columnIndex].cloneNode(true);\n\n while (row.firstElementChild) {\n row.removeChild(row.firstElementChild);\n }\n\n cell.style.height = origTrs[rowIndex].cells[columnIndex].offsetHeight + 'px';\n\n row.appendChild(cell);\n });\n\n this.dragTable = clone;\n\n $(clone)\n .addClass('_dragging-copy')\n .appendTo('body');\n\n return this;\n },\n\n /**\n * Matches provided coordinates to available areas.\n *\n * @param {Number} x - X coordinate of a mouse pointer.\n * @param {Number} y - Y coordinate of a mouse pointer.\n * @returns {Object|Undefined} Matched area.\n */\n _getDropArea: function (x, y) {\n return _.find(this.coords, function (area) {\n return isInside(x, y, area);\n });\n },\n\n /**\n * Updates state of hovered areas.\n *\n * @param {Number} x - X coordinate of a mouse pointer.\n * @param {Number} y - Y coordinate of a mouse pointer.\n */\n _updateAreas: function (x, y) {\n var leavedArea = this.dropArea,\n area = this.dropArea = this._getDropArea(x, y);\n\n if (leavedArea) {\n this.dragleave(leavedArea);\n }\n\n if (area && !compareCols(area.target, this.dragArea.target)) {\n this.dragenter(area);\n }\n },\n\n /**\n * Grab action handler.\n *\n * @param {Number} x - X coordinate of a grabbed point.\n * @param {Number} y - Y coordinate of a grabbed point.\n * @param {HTMLElement} elem - Grabbed element.\n */\n grab: function (x, y, elem) {\n this.initDrag = true;\n\n this.grabbed = {\n x: x,\n y: y,\n elem: elem\n };\n\n this.$body.addClass(this.noSelectClass);\n },\n\n /**\n * Dragstart action handler.\n *\n * @param {HTMLTableHeaderCellElement} elem - Element which is dragging.\n */\n dragstart: function (elem) {\n this.initDrag = false;\n this.dropArea = false;\n this.dragging = true;\n\n getModel(elem).dragging(true);\n\n this._cacheCoords()\n ._cloneTable(elem);\n },\n\n /**\n * Drag action handler. Locates draggable\n * grid at a specified coordinates.\n *\n * @param {Number} x - X coordinate.\n * @param {Number} y - Y coordinate.\n */\n drag: function (x, y) {\n var grabbed = this.grabbed,\n dragArea = this.dragArea,\n posX = x + grabbed.shiftX,\n posY = y + grabbed.shiftY;\n\n if (this.fixedX) {\n x = dragArea.left;\n posX = dragArea.orig.left;\n }\n\n if (this.fixedY) {\n y = dragArea.top;\n posY = dragArea.orig.top;\n }\n\n locate(this.dragTable, posX, posY);\n\n if (!isInside(x, y, this.dropArea)) {\n this._updateAreas(x, y);\n }\n },\n\n /**\n * Dragenter action handler.\n *\n * @param {Object} dropArea\n */\n dragenter: function (dropArea) {\n var direction = this.dragArea.index < dropArea.index ?\n 'left' :\n 'right';\n\n getModel(dropArea.target).dragover = direction;\n },\n\n /**\n * Dragleave action handler.\n *\n * @param {Object} dropArea\n */\n dragleave: function (dropArea) {\n getModel(dropArea.target).dragover = false;\n },\n\n /**\n * Dragend action handler.\n *\n * @param {Object} dragArea\n */\n dragend: function (dragArea) {\n var dropArea = this.dropArea,\n dragElem = dragArea.target;\n\n this.dragging = false;\n\n document.body.removeChild(this.dragTable);\n\n getModel(dragElem).dragging(false);\n\n if (dropArea && !compareCols(dropArea.target, dragElem)) {\n this.drop(dropArea, dragArea);\n }\n },\n\n /**\n * Drop action handler.\n *\n * @param {Object} dropArea\n * @param {Object} dragArea\n */\n drop: function (dropArea, dragArea) {\n var dropModel = getModel(dropArea.target),\n dragModel = getModel(dragArea.target);\n\n getModel(this.table).insertChild(dragModel, dropModel);\n dropModel.dragover = false;\n },\n\n /**\n * Documents' 'mousemove' event handler.\n *\n * @param {(MouseEvent|TouchEvent)} e - Event object.\n */\n onMouseMove: function (e) {\n var grab = this.grabbed,\n touch = getTouch(e),\n x = touch.pageX,\n y = touch.pageY;\n\n if (this.initDrag || this.dragging) {\n e.preventDefault();\n }\n\n if (this.initDrag && distance(x, y, grab.x, grab.y) >= this.minDistance) {\n this.dragstart(grab.elem);\n }\n\n if (this.dragging) {\n this.drag(x, y);\n }\n },\n\n /**\n * Documents' 'mouseup' event handler.\n */\n onMouseUp: function () {\n if (this.initDrag || this.dragging) {\n this.initDrag = false;\n this.$body.removeClass(this.noSelectClass);\n }\n\n if (this.dragging) {\n this.dragend(this.dragArea);\n }\n },\n\n /**\n * Columns' 'mousedown' event handler.\n *\n * @param {(MouseEvent|TouchEvent)} e - Event object.\n */\n onMouseDown: function (e) {\n var touch = getTouch(e);\n\n this.grab(touch.pageX, touch.pageY, e.currentTarget);\n }\n });\n});\n","Magento_Ui/js/grid/provider.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'rjsResolver',\n 'uiLayout',\n 'Magento_Ui/js/modal/alert',\n 'mage/translate',\n 'uiElement'\n], function ($, _, utils, resolver, layout, alert, $t, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n firstLoad: true,\n lastError: false,\n storageConfig: {\n component: 'Magento_Ui/js/grid/data-storage',\n provider: '${ $.storageConfig.name }',\n name: '${ $.name }_storage',\n updateUrl: '${ $.update_url }'\n },\n listens: {\n params: 'onParamsChange',\n requestConfig: 'updateRequestConfig'\n },\n ignoreTmpls: {\n data: true\n }\n },\n\n /**\n * Initializes provider component.\n *\n * @returns {Provider} Chainable.\n */\n initialize: function () {\n utils.limit(this, 'onParamsChange', 5);\n _.bindAll(this, 'onReload');\n\n this._super()\n .initStorage()\n .clearData();\n\n // Load data when there will\n // be no more pending assets.\n resolver(this.reload, this);\n\n return this;\n },\n\n /**\n * Initializes storage component.\n *\n * @returns {Provider} Chainable.\n */\n initStorage: function () {\n layout([this.storageConfig]);\n\n return this;\n },\n\n /**\n * Clears provider's data properties.\n *\n * @returns {Provider} Chainable.\n */\n clearData: function () {\n this.setData({\n items: [],\n totalRecords: 0\n });\n\n return this;\n },\n\n /**\n * Overrides current data with a provided one.\n *\n * @param {Object} data - New data object.\n * @returns {Provider} Chainable.\n */\n setData: function (data) {\n data = this.processData(data);\n\n this.set('data', data);\n\n return this;\n },\n\n /**\n * Processes data before applying it.\n *\n * @param {Object} data - Data to be processed.\n * @returns {Object}\n */\n processData: function (data) {\n var items = data.items;\n\n _.each(items, function (record, index) {\n record._rowIndex = index;\n });\n\n return data;\n },\n\n /**\n * Reloads data with current parameters.\n *\n * @returns {Promise} Reload promise object.\n */\n reload: function (options) {\n var request = this.storage().getData(this.params, options);\n\n this.trigger('reload');\n\n request\n .done(this.onReload)\n .fail(this.onError.bind(this));\n\n return request;\n },\n\n /**\n * Handles changes of 'params' object.\n */\n onParamsChange: function () {\n // It's necessary to make a reload only\n // after the initial loading has been made.\n if (!this.firstLoad) {\n this.reload();\n }\n },\n\n /**\n * Handles reload error.\n */\n onError: function (xhr) {\n if (xhr.statusText === 'abort') {\n return;\n }\n\n this.set('lastError', true);\n\n this.firstLoad = false;\n\n alert({\n content: $t('Something went wrong.')\n });\n },\n\n /**\n * Handles successful data reload.\n *\n * @param {Object} data - Retrieved data object.\n */\n onReload: function (data) {\n this.firstLoad = false;\n\n this.set('lastError', false);\n\n this.setData(data)\n .trigger('reloaded');\n },\n\n /**\n * Updates storage's request configuration\n *\n * @param {Object} requestConfig\n */\n updateRequestConfig: function (requestConfig) {\n if (this.storage()) {\n _.extend(this.storage().requestConfig, requestConfig);\n }\n }\n });\n});\n","Magento_Ui/js/grid/resize.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/lib/view/utils/async',\n 'ko',\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n 'Magento_Ui/js/lib/knockout/extender/bound-nodes',\n 'uiElement'\n], function ($, ko, _, utils, registry, boundedNodes, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n rootSelector: '${ $.columnsProvider }:.admin__data-grid-wrap',\n tableSelector: '${ $.rootSelector } -> table.data-grid',\n mainTableSelector: '[data-role=\"grid\"]',\n columnSelector: '${ $.tableSelector } thead tr th',\n fieldSelector: '${ $.tableSelector } tbody tr td',\n\n imports: {\n storageColumnsData: '${ $.storageConfig.path }.storageColumnsData'\n },\n storageColumnsData: {},\n columnsElements: {},\n tableWidth: 0,\n sumColumnsWidth: 0,\n showLines: 4,\n resizableElementClass: 'shadow-div',\n resizingColumnClass: '_resizing',\n fixedLayoutClass: '_layout-fixed',\n inResizeClass: '_in-resize',\n visibleClass: '_resize-visible',\n cellContentElement: 'div.data-grid-cell-content',\n minColumnWidth: 40,\n layoutFixedPolyfillIterator: 0,\n windowResize: false,\n resizable: false,\n resizeConfig: {\n maxRowsHeight: [],\n curResizeElem: {},\n depResizeElem: {},\n previousWidth: null\n }\n },\n\n /**\n * Initialize application -\n * binding functions context,\n * set handlers for table elements\n *\n * @returns {Object} Chainable\n */\n initialize: function () {\n _.bindAll(\n this,\n 'initTable',\n 'initColumn',\n 'mousedownHandler',\n 'mousemoveHandler',\n 'mouseupHandler',\n 'refreshLastColumn',\n 'refreshMaxRowHeight',\n 'preprocessingWidth',\n '_eventProxy',\n 'checkAfterResize'\n );\n\n this._super();\n this.observe(['maxRowsHeight']);\n this.maxRowsHeight([]);\n\n $.async(this.tableSelector, this.initTable);\n $.async(this.columnSelector, this.initColumn);\n\n return this;\n },\n\n /**\n * Set table element and adds handler to mousedown on headers\n *\n * @returns {Object} Chainable\n */\n initTable: function (table) {\n if ($(table).is(this.mainTableSelector)) {\n this.table = table;\n this.tableWidth = $(table).outerWidth();\n $(window).resize(this.checkAfterResize);\n }\n\n //TODO - Must be deleted when Firefox fixed problem with table-layout: fixed\n //ticket to Firefox: https://bugs.webkit.org/show_bug.cgi?id=90068\n if (navigator.userAgent.search(/Firefox/) > -1) {\n this._layoutFixedPolyfill();\n }\n\n $(table).addClass(this.fixedLayoutClass);\n\n return this;\n },\n\n /**\n * Window resize handler,\n * check changes on table width and\n * set new width to variable\n * after window resize start preprocessingWidth method\n */\n checkAfterResize: function () {\n var tableWidth,\n self = this;\n\n setTimeout(function () {\n tableWidth = $(self.table).outerWidth();\n\n if (self.tableWidth !== tableWidth) {\n self.tableWidth = tableWidth;\n } else {\n self.preprocessingWidth();\n }\n }, 300);\n },\n\n /**\n * Check conditions to set minimal width\n */\n checkSumColumnsWidth: function () {\n var table = $(this.table),\n elems = table.find('th:not([style*=\"width: auto\"]):visible'),\n elemsWidthMin = table.find('th[style*=\"width: ' + (this.minColumnWidth - 1) + 'px\"]:visible'),\n elemsWidthAuto = table.find('th[style*=\"width: auto\"]:visible'),\n model;\n\n this.sumColumnsWidth = 0;\n _.each(elems, function (elem) {\n model = ko.dataFor(elem);\n model.width && model.width !== 'auto' ? this.sumColumnsWidth += model.width : false;\n }, this);\n\n if (\n this.sumColumnsWidth + elemsWidthAuto.length *\n this.minColumnWidth + elemsWidthMin.length *\n this.minColumnWidth > this.tableWidth\n ) {\n return true;\n }\n\n return false;\n },\n\n /**\n * Set minimal width to element with \"auto\" width\n */\n setWidthToColumnsWidthAuto: function () {\n var elemsWidthAuto = $(this.table).find('th[style*=\"width: auto\"]:visible');\n\n _.each(elemsWidthAuto, function (elem) {\n $(elem).outerWidth(this.minColumnWidth - 1);\n }, this);\n },\n\n /**\n * Check conditions to set auto width\n */\n hasMinimal: function () {\n var table = $(this.table),\n elemsWidthMin = table.find('th[style*=\"width: ' + (this.minColumnWidth - 1) + 'px\"]:visible'),\n elemsWidthAuto = table.find('th[style*=\"width: auto\"]:visible');\n\n if (\n elemsWidthAuto && this.sumColumnsWidth + elemsWidthAuto.length *\n this.minColumnWidth + elemsWidthMin.length * this.minColumnWidth + 5 < this.tableWidth\n ) {\n return true;\n }\n\n return false;\n },\n\n /**\n * Set \"auto\" width to element with minimal width\n */\n setAuto: function () {\n var elemsWidthAuto = $(this.table).find('th[style*=\"width: ' + (this.minColumnWidth - 1) + 'px\"]:visible');\n\n _.each(elemsWidthAuto, function (elem) {\n $(elem).outerWidth('auto');\n }, this);\n },\n\n /**\n * Check columns width and preprocessing\n */\n preprocessingWidth: function () {\n if (this.checkSumColumnsWidth()) {\n this.setWidthToColumnsWidthAuto();\n } else if (this.hasMinimal()) {\n this.setAuto();\n }\n },\n\n /**\n * Init columns elements,\n * set width to current column element,\n * add resizable element to columns header,\n * check and add no-resize class to last column,\n * stop parents events,\n * add handler to visibility column\n *\n * @param {Object} column - columns header element (th)\n */\n initColumn: function (column) {\n var model = ko.dataFor(column),\n ctxIndex = this.getCtxIndex(ko.contextFor(column));\n\n model.width = this.getDefaultWidth(column);\n\n if (!this.hasColumn(model, ctxIndex, false)) {\n this.columnsElements[model.index] = this.columnsElements[model.index] || {};\n this.columnsElements[model.index][ctxIndex] = column;\n this.initResizableElement(column);\n this.setStopPropagationHandler(column);\n $(column).outerWidth(model.width);\n }\n\n this.refreshLastColumn(column);\n this.preprocessingWidth();\n\n model.on('visible', this.refreshLastColumn.bind(this, column));\n model.on('visible', this.preprocessingWidth.bind(this));\n },\n\n /**\n * Hack for mozilla firefox\n */\n _layoutFixedPolyfill: function () {\n var self = this;\n\n setTimeout(function () {\n if (self.layoutFixedPolyfillIterator < 20) {\n $(window).resize();\n self.layoutFixedPolyfillIterator++;\n self._layoutFixedPolyfill();\n } else {\n return false;\n }\n }, 500);\n },\n\n /**\n * Check element is resizable or not\n * and append resizable element to DOM\n *\n * @param {Object} column - columns header element (th)\n * @returns {Boolean}\n */\n initResizableElement: function (column) {\n var model = ko.dataFor(column),\n templateDragElement = '<div class=\"' + this.resizableElementClass + '\"></div>';\n\n if (_.isUndefined(model.resizeEnabled) || model.resizeEnabled) {\n $(column).append(templateDragElement);\n\n return true;\n }\n\n return false;\n },\n\n /**\n * Check event target and if need stop parents event,\n *\n * @param {Object} column - columns header element (th)\n * @returns {Boolean}\n */\n setStopPropagationHandler: function (column) {\n var events,\n click,\n mousedown;\n\n $(column).on('click', this._eventProxy);\n $(column).on('mousedown', this._eventProxy);\n\n events = $._data(column, 'events');\n\n click = events.click;\n mousedown = events.mousedown;\n click.unshift(click.pop());\n mousedown.unshift(mousedown.pop());\n\n return this;\n },\n\n /**\n * Check event target and stop event if need\n *\n * @param {Object} event\n */\n _eventProxy: function (event) {\n if ($(event.target).is('.' + this.resizableElementClass)) {\n\n if (event.type === 'click') {\n event.stopImmediatePropagation();\n } else if (event.type === 'mousedown') {\n this.mousedownHandler(event);\n }\n }\n },\n\n /**\n * Check visible columns and set disable class to resizable elements,\n *\n * @param {Object} column - columns header element (th)\n */\n refreshLastColumn: function (column) {\n var i = 0,\n columns = $(column).parent().children().not(':hidden'),\n length = columns.length;\n\n $('.' + this.visibleClass).removeClass(this.visibleClass);\n\n $(column).parent().children().not(':hidden').last().addClass(this.visibleClass);\n\n for (i; i < length; i++) {\n\n if (!columns.eq(i).find('.' + this.resizableElementClass).length && i) {\n columns.eq(i - 1).addClass(this.visibleClass);\n }\n }\n\n },\n\n /**\n * Refresh max height to row elements,\n *\n * @param {Object} elem - (td)\n */\n refreshMaxRowHeight: function (elem) {\n var rowsH = this.maxRowsHeight(),\n curEL = $(elem).find('div'),\n height,\n obj = this.hasRow($(elem).parent()[0], true);\n\n curEL.css('white-space', 'nowrap');\n height = curEL.height() * this.showLines;\n curEL.css('white-space', 'normal');\n\n if (obj) {\n if (obj.maxHeight < height) {\n rowsH[_.indexOf(rowsH, obj)].maxHeight = height;\n } else {\n return false;\n }\n } else {\n rowsH.push({\n elem: $(elem).parent()[0],\n maxHeight: height\n });\n }\n\n $(elem).parent().children().find(this.cellContentElement).css('max-height', height + 'px');\n this.maxRowsHeight(rowsH);\n },\n\n /**\n * Set resize class to elements when resizable\n */\n _setResizeClass: function () {\n var rowElements = $(this.table).find('tr');\n\n rowElements\n .find('td:eq(' + this.resizeConfig.curResizeElem.ctx.$index() + ')')\n .addClass(this.resizingColumnClass);\n rowElements\n .find('td:eq(' + this.resizeConfig.depResizeElem.ctx.$index() + ')')\n .addClass(this.resizingColumnClass);\n },\n\n /**\n * Remove resize class to elements when resizable\n */\n _removeResizeClass: function () {\n var rowElements = $(this.table).find('tr');\n\n rowElements\n .find('td:eq(' + this.resizeConfig.curResizeElem.ctx.$index() + ')')\n .removeClass(this.resizingColumnClass);\n rowElements\n .find('td:eq(' + this.resizeConfig.depResizeElem.ctx.$index() + ')')\n .removeClass(this.resizingColumnClass);\n },\n\n /**\n * Check conditions to resize\n *\n * @returns {Boolean}\n */\n _canResize: function (column) {\n if (\n $(column).hasClass(this.visibleClass) ||\n !$(this.resizeConfig.depResizeElem.elems[0]).find('.' + this.resizableElementClass).length\n ) {\n return false;\n }\n\n return true;\n },\n\n /**\n * Mouse down event handler,\n * find current and dep column to resize\n *\n * @param {Object} event\n */\n mousedownHandler: function (event) {\n var target = event.target,\n column = $(target).parent()[0],\n cfg = this.resizeConfig,\n body = $('body');\n\n event.stopImmediatePropagation();\n cfg.curResizeElem.model = ko.dataFor(column);\n cfg.curResizeElem.ctx = ko.contextFor(column);\n cfg.curResizeElem.elems = this.hasColumn(cfg.curResizeElem.model, false, true);\n cfg.curResizeElem.position = event.pageX;\n cfg.depResizeElem.elems = this.getNextElements(cfg.curResizeElem.elems[0]);\n cfg.depResizeElem.model = ko.dataFor(cfg.depResizeElem.elems[0]);\n cfg.depResizeElem.ctx = ko.contextFor(cfg.depResizeElem.elems[0]);\n\n this._setResizeClass();\n\n if (!this._canResize(column)) {\n return false;\n }\n\n event.stopPropagation();\n this.resizable = true;\n cfg.curResizeElem.model.width = $(cfg.curResizeElem.elems[0]).outerWidth();\n cfg.depResizeElem.model.width = $(cfg.depResizeElem.elems[0]).outerWidth();\n body.addClass(this.inResizeClass);\n body.bind('mousemove', this.mousemoveHandler);\n $(window).bind('mouseup', this.mouseupHandler);\n },\n\n /**\n * Mouse move event handler,\n * change columns width\n *\n * @param {Object} event\n */\n mousemoveHandler: function (event) {\n var cfg = this.resizeConfig,\n width = event.pageX - cfg.curResizeElem.position,\n self = this;\n\n event.stopPropagation();\n event.preventDefault();\n\n if (\n this.resizable &&\n this.minColumnWidth < cfg.curResizeElem.model.width + width &&\n this.minColumnWidth < cfg.depResizeElem.model.width - width &&\n cfg.previousWidth !== width\n ) {\n cfg.curResizeElem.model.width += width;\n cfg.depResizeElem.model.width -= width;\n\n cfg.curResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(cfg.curResizeElem.model.width);\n });\n cfg.depResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(cfg.depResizeElem.model.width);\n });\n\n cfg.previousWidth = width;\n cfg.curResizeElem.position = event.pageX;\n } else if (width <= -(cfg.curResizeElem.model.width - this.minColumnWidth)) {\n\n cfg.curResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(self.minColumnWidth);\n });\n cfg.depResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(\n cfg.depResizeElem.model.width +\n cfg.curResizeElem.model.width -\n self.minColumnWidth);\n });\n\n } else if (width >= cfg.depResizeElem.model.width - this.minColumnWidth) {\n\n cfg.depResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(self.minColumnWidth);\n });\n cfg.curResizeElem.elems.forEach(function (el) {\n $(el).outerWidth(\n cfg.curResizeElem.model.width +\n cfg.depResizeElem.model.width -\n self.minColumnWidth\n );\n });\n }\n },\n\n /**\n * Mouse up event handler,\n * change columns width\n *\n * @param {Object} event\n */\n mouseupHandler: function (event) {\n var cfg = this.resizeConfig,\n body = $('body');\n\n event.stopPropagation();\n event.preventDefault();\n\n this._removeResizeClass();\n this.storageColumnsData[cfg.curResizeElem.model.index] = cfg.curResizeElem.model.width;\n this.storageColumnsData[cfg.depResizeElem.model.index] = cfg.depResizeElem.model.width;\n this.resizable = false;\n\n this.store('storageColumnsData');\n\n body.removeClass(this.inResizeClass);\n body.unbind('mousemove', this.mousemoveHandler);\n $(window).unbind('mouseup', this.mouseupHandler);\n },\n\n /**\n * Find dependency element\n *\n * @param {Object} element - current element\n * @returns {Object} next element data\n */\n getNextElements: function (element) {\n var nextElem = $(element).next()[0],\n nextElemModel = ko.dataFor(nextElem),\n nextElemData = this.hasColumn(nextElemModel, false, true);\n\n if (nextElemData) {\n if (nextElemModel.visible) {\n return nextElemData;\n }\n\n return this.getNextElements(nextElem);\n }\n },\n\n /**\n * Get default width\n *\n * @param {Object} column - (th) element\n * @return {String} width for current column\n */\n getDefaultWidth: function (column) {\n var model = ko.dataFor(column);\n\n if (this.storageColumnsData[model.index]) {\n return this.storageColumnsData[model.index];\n }\n\n if (model.resizeDefaultWidth) {\n return parseInt(model.resizeDefaultWidth, 10);\n }\n\n return 'auto';\n },\n\n /**\n * Check column is render or not\n *\n * @param {Object} model - cur column model\n * @param {String|Boolean} ctxIndex - index of context, or false, if want to get cols from all ctx\n * @param {Boolean} returned - need return column object or not\n * @return {Boolean} if returned param is false, returned boolean value, else return current object data\n */\n hasColumn: function (model, ctxIndex, returned) {\n var colElem = this.columnsElements[model.index] || {},\n getFromAllCtx = ctxIndex === false;\n\n if (colElem && (getFromAllCtx || colElem.hasOwnProperty(ctxIndex))) {\n\n if (returned) {\n return getFromAllCtx ?\n _.values(colElem) :\n colElem[ctxIndex];\n }\n\n return true;\n }\n\n return false;\n },\n\n /**\n * Check row is render or not\n *\n * @param {Object} elem - cur column element\n * @param {Boolean} returned - need return column object or not\n * @return {Boolean|Object} if returned param is false, returned boolean value, else return current object data\n */\n hasRow: function (elem, returned) {\n var i = 0,\n el = this.maxRowsHeight(),\n length = el.length;\n\n for (i; i < length; i++) {\n if (this.maxRowsHeight()[i].elem === elem) {\n if (returned) {//eslint-disable-line max-depth\n return this.maxRowsHeight()[i];\n }\n\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Generate index that will identify context\n *\n * @param {Object} ctx\n * @return {String}\n */\n getCtxIndex: function (ctx) {\n return ctx ? ctx.$parents.reduce(function (pv, cv) {\n return (pv.index || pv) + (cv || {}).index;\n }) : ctx;\n }\n });\n});\n","Magento_Ui/js/grid/data-storage.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'uiClass'\n], function ($, _, utils, Class) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n cacheRequests: true,\n cachedRequestDelay: 50,\n indexField: 'entity_id',\n requestConfig: {\n url: '${ $.updateUrl }',\n method: 'GET',\n dataType: 'json'\n },\n dataScope: '',\n data: {}\n },\n\n /**\n * Initializes dataStorage configuration.\n *\n * @returns {DataStorage} Chainable.\n */\n initConfig: function () {\n var scope;\n\n this._super();\n\n scope = this.dataScope;\n\n if (typeof scope === 'string') {\n this.dataScope = scope ? [scope] : [];\n }\n\n this._requests = [];\n\n return this;\n },\n\n /**\n * Extracts data which matches specified set of identifiers.\n *\n * @param {Array} ids - Records identifiers.\n * @returns {Array|Boolean}\n */\n getByIds: function (ids) {\n var result = [],\n hasData;\n\n hasData = ids.every(function (id) {\n var item = this.data[id];\n\n return item ? result.push(item) : false;\n }, this);\n\n return hasData ? result : false;\n },\n\n /**\n * Extracts identifiers of provided records.\n * If no records were provided then full list of\n * current data id's will be returned.\n *\n * @param {Object|Array} [data=this.data]\n * @returns {Array}\n */\n getIds: function (data) {\n data = data || this.data;\n\n return _.pluck(data, this.indexField);\n },\n\n /**\n * Extracts data which matches specified parameters.\n *\n * @param {Object} params - Request parameters.\n * @param {Object} [options={}]\n * @returns {jQueryPromise}\n */\n getData: function (params, options) {\n var cachedRequest;\n\n if (this.hasScopeChanged(params)) {\n this.clearRequests();\n } else {\n cachedRequest = this.getRequest(params);\n }\n\n options = options || {};\n\n return !options.refresh && cachedRequest ?\n this.getRequestData(cachedRequest) :\n this.requestData(params);\n },\n\n /**\n * Tells whether one of the parameters defined in the \"dataScope\" has\n * changed since the last request.\n *\n * @param {Object} params - Request parameters.\n * @returns {Boolean}\n */\n hasScopeChanged: function (params) {\n var lastRequest = _.last(this._requests),\n keys,\n diff;\n\n if (!lastRequest) {\n return false;\n }\n\n diff = utils.compare(lastRequest.params, params);\n\n keys = _.pluck(diff.changes, 'path');\n keys = keys.concat(Object.keys(diff.containers));\n\n return _.intersection(this.dataScope, keys).length > 0;\n },\n\n /**\n * Extends records of current data object\n * with the provided records collection.\n *\n * @param {Array} data - An array of records.\n * @returns {DataStorage} Chainable.\n */\n updateData: function (data) {\n var records = _.indexBy(data || [], this.indexField);\n\n _.extend(this.data, records);\n\n return this;\n },\n\n /**\n * Sends request to the server with provided parameters.\n *\n * @param {Object} params - Request parameters.\n * @returns {jQueryPromise}\n */\n requestData: function (params) {\n var query = utils.copy(params),\n handler = this.onRequestComplete.bind(this, query),\n request;\n\n this.requestConfig.data = query;\n request = $.ajax(this.requestConfig).done(handler);\n\n return request;\n },\n\n /**\n * Returns request's instance which\n * contains provided parameters.\n *\n * @param {Object} params - Request parameters.\n * @returns {Object} Instance of request.\n */\n getRequest: function (params) {\n return _.find(this._requests, function (request) {\n return _.isEqual(params, request.params);\n }, this);\n },\n\n /**\n * Forms data object associated with provided request.\n *\n * @param {Object} request - Request object.\n * @returns {jQueryPromise}\n */\n getRequestData: function (request) {\n var defer = $.Deferred(),\n resolve = defer.resolve.bind(defer),\n delay = this.cachedRequestDelay,\n result;\n\n result = {\n items: this.getByIds(request.ids),\n totalRecords: request.totalRecords\n };\n\n delay ?\n _.delay(resolve, delay, result) :\n resolve(result);\n\n return defer.promise();\n },\n\n /**\n * Caches requests object with provdided parameters\n * and data object associated with it.\n *\n * @param {Object} data - Data associated with request.\n * @param {Object} params - Request parameters.\n * @returns {DataStorage} Chainable.\n */\n cacheRequest: function (data, params) {\n var cached = this.getRequest(params);\n\n if (cached) {\n this.removeRequest(cached);\n }\n\n this._requests.push({\n ids: this.getIds(data.items),\n params: params,\n totalRecords: data.totalRecords\n });\n\n return this;\n },\n\n /**\n * Clears all cached requests.\n *\n * @returns {DataStorage} Chainable.\n */\n clearRequests: function () {\n this._requests.splice(0);\n\n return this;\n },\n\n /**\n * Removes provided request object from cached requests list.\n *\n * @param {Object} request - Request object.\n * @returns {DataStorage} Chainable.\n */\n removeRequest: function (request) {\n var requests = this._requests,\n index = requests.indexOf(request);\n\n if (~index) {\n requests.splice(index, 1);\n }\n\n return this;\n },\n\n /**\n * Checks if request with a specified parameters was cached.\n *\n * @param {Object} params - Parameters of the request.\n * @returns {Boolean}\n */\n wasRequested: function (params) {\n return !!this.getRequest(params);\n },\n\n /**\n * Handles successful data request.\n *\n * @param {Object} params - Request parameters.\n * @param {Object} data - Response data.\n */\n onRequestComplete: function (params, data) {\n this.updateData(data.items);\n\n if (this.cacheRequests) {\n this.cacheRequest(data, params);\n }\n }\n });\n});\n","Magento_Ui/js/grid/listing.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'underscore',\n 'Magento_Ui/js/lib/spinner',\n 'rjsResolver',\n 'uiLayout',\n 'uiCollection'\n], function (ko, _, loader, resolver, layout, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/listing',\n listTemplate: 'ui/list/listing',\n stickyTmpl: 'ui/grid/sticky/listing',\n viewSwitcherTmpl: 'ui/grid/view-switcher',\n positions: false,\n displayMode: 'grid',\n displayModes: {\n grid: {\n value: 'grid',\n label: 'Grid',\n template: '${ $.template }'\n },\n list: {\n value: 'list',\n label: 'List',\n template: '${ $.listTemplate }'\n }\n },\n dndConfig: {\n name: '${ $.name }_dnd',\n component: 'Magento_Ui/js/grid/dnd',\n columnsProvider: '${ $.name }',\n enabled: true\n },\n editorConfig: {\n name: '${ $.name }_editor',\n component: 'Magento_Ui/js/grid/editing/editor',\n columnsProvider: '${ $.name }',\n dataProvider: '${ $.provider }',\n enabled: false\n },\n resizeConfig: {\n name: '${ $.name }_resize',\n columnsProvider: '${ $.name }',\n component: 'Magento_Ui/js/grid/resize',\n enabled: false\n },\n imports: {\n rows: '${ $.provider }:data.items'\n },\n listens: {\n elems: 'updatePositions updateVisible',\n '${ $.provider }:reload': 'onBeforeReload',\n '${ $.provider }:reloaded': 'onDataReloaded'\n },\n modules: {\n dnd: '${ $.dndConfig.name }',\n resize: '${ $.resizeConfig.name }'\n },\n tracks: {\n displayMode: true\n },\n statefull: {\n displayMode: true\n }\n },\n\n /**\n * Initializes Listing component.\n *\n * @returns {Listing} Chainable.\n */\n initialize: function () {\n _.bindAll(this, 'updateVisible');\n\n this._super()\n .initDnd()\n .initEditor()\n .initResize();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Listing} Chainable.\n */\n initObservable: function () {\n this._super()\n .track({\n rows: [],\n visibleColumns: []\n });\n\n return this;\n },\n\n /**\n * Creates drag&drop widget instance.\n *\n * @returns {Listing} Chainable.\n */\n initDnd: function () {\n if (this.dndConfig.enabled) {\n layout([this.dndConfig]);\n }\n\n return this;\n },\n\n /**\n * Initializes resize component.\n *\n * @returns {Listing} Chainable.\n */\n initResize: function () {\n if (this.resizeConfig.enabled) {\n layout([this.resizeConfig]);\n }\n\n return this;\n },\n\n /**\n * Creates inline editing component.\n *\n * @returns {Listing} Chainable.\n */\n initEditor: function () {\n if (this.editorConfig.enabled) {\n layout([this.editorConfig]);\n }\n\n return this;\n },\n\n /**\n * Called when another element was added to current component.\n *\n * @returns {Listing} Chainable.\n */\n initElement: function (element) {\n var currentCount = this.elems().length,\n totalCount = this.initChildCount;\n\n if (totalCount === currentCount) {\n this.initPositions();\n }\n\n element.on('visible', this.updateVisible);\n\n return this._super();\n },\n\n /**\n * Defines initial order of child elements.\n *\n * @returns {Listing} Chainable.\n */\n initPositions: function () {\n this.on('positions', this.applyPositions.bind(this));\n\n this.setStatefull('positions');\n\n return this;\n },\n\n /**\n * Updates current state of child positions.\n *\n * @returns {Listing} Chainable.\n */\n updatePositions: function () {\n var positions = {};\n\n this.elems.each(function (elem, index) {\n positions[elem.index] = index;\n });\n\n this.set('positions', positions);\n\n return this;\n },\n\n /**\n * Resorts child elements array according to provided positions.\n *\n * @param {Object} positions - Object where key represents child\n * index and value is its' position.\n * @returns {Listing} Chainable.\n */\n applyPositions: function (positions) {\n var sorting;\n\n sorting = this.elems.map(function (elem) {\n return {\n elem: elem,\n position: positions[elem.index]\n };\n });\n\n this.insertChild(sorting);\n\n return this;\n },\n\n /**\n * Returns reference to 'visibleColumns' array.\n *\n * @returns {Array}\n */\n getVisible: function () {\n var observable = ko.getObservable(this, 'visibleColumns');\n\n return observable || this.visibleColumns;\n },\n\n /**\n * Returns path to the template\n * defined for a current display mode.\n *\n * @returns {String} Path to the template.\n */\n getTemplate: function () {\n var mode = this.displayModes[this.displayMode];\n\n return mode.template;\n },\n\n /**\n * Returns an array of available display modes.\n *\n * @returns {Array<Object>}\n */\n getDisplayModes: function () {\n var modes = this.displayModes;\n\n return _.values(modes);\n },\n\n /**\n * Sets display mode to provided value.\n *\n * @param {String} index\n * @returns {Listing} Chainable\n */\n setDisplayMode: function (index) {\n this.displayMode = index;\n\n return this;\n },\n\n /**\n * Returns total number of displayed columns in grid.\n *\n * @returns {Number}\n */\n countVisible: function () {\n return this.visibleColumns.length;\n },\n\n /**\n * Updates array of visible columns.\n *\n * @returns {Listing} Chainable.\n */\n updateVisible: function () {\n this.visibleColumns = this.elems.filter('visible');\n\n return this;\n },\n\n /**\n * Checks if grid has data.\n *\n * @returns {Boolean}\n */\n hasData: function () {\n return !!this.rows && !!this.rows.length;\n },\n\n /**\n * Hides loader.\n */\n hideLoader: function () {\n loader.get(this.name).hide();\n },\n\n /**\n * Shows loader.\n */\n showLoader: function () {\n loader.get(this.name).show();\n },\n\n /**\n * Handler of the data providers' 'reload' event.\n */\n onBeforeReload: function () {\n this.showLoader();\n },\n\n /**\n * Handler of the data providers' 'reloaded' event.\n */\n onDataReloaded: function () {\n resolver(this.hideLoader, this);\n }\n });\n});\n","Magento_Ui/js/grid/controls/columns.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'mage/translate',\n 'uiCollection'\n], function (_, utils, $t, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/controls/columns',\n minVisible: 1,\n maxVisible: 30,\n viewportSize: 18,\n displayArea: 'dataGridActions',\n columnsProvider: 'ns = ${ $.ns }, componentType = columns',\n imports: {\n addColumns: '${ $.columnsProvider }:elems'\n },\n templates: {\n headerMsg: $t('${ $.visible } out of ${ $.total } visible')\n }\n },\n\n /**\n * Resets columns visibility to theirs default state.\n *\n * @returns {Columns} Chainable.\n */\n reset: function () {\n this.elems.each('applyState', 'default', 'visible');\n\n return this;\n },\n\n /**\n * Applies last saved state of columns visibility.\n *\n * @returns {Columns} Chainable.\n */\n cancel: function () {\n this.elems.each('applyState', '', 'visible');\n\n return this;\n },\n\n /**\n * Adds columns whose visibility can be controlled to the component.\n *\n * @param {Array} columns - Elements array that will be added to component.\n * @returns {Columns} Chainable.\n */\n addColumns: function (columns) {\n columns = _.where(columns, {\n controlVisibility: true\n });\n\n this.insertChild(columns);\n\n return this;\n },\n\n /**\n * Defines whether child elements array length\n * is greater than the 'viewportSize' property.\n *\n * @returns {Boolean}\n */\n hasOverflow: function () {\n return this.elems().length > this.viewportSize;\n },\n\n /**\n * Helper, checks\n * - if less than one item choosen\n * - if more then viewportMaxSize choosen\n *\n * @param {Object} elem\n * @returns {Boolean}\n */\n isDisabled: function (elem) {\n var visible = this.countVisible();\n\n return elem.visible ?\n visible === this.minVisible :\n visible === this.maxVisible;\n },\n\n /**\n * Counts number of visible columns.\n *\n * @returns {Number}\n */\n countVisible: function () {\n return this.elems.filter('visible').length;\n },\n\n /**\n * Compile header message from headerMessage setting.\n *\n * @returns {String}\n */\n getHeaderMessage: function () {\n return utils.template(this.templates.headerMsg, {\n visible: this.countVisible(),\n total: this.elems().length\n });\n }\n });\n});\n","Magento_Ui/js/grid/controls/button/split.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 return function (data, element) {\n\n $(element).on('click.splitDefault', '.action-default', function () {\n $(this).siblings('.dropdown-menu').find('.item-default').trigger('click');\n });\n };\n});\n","Magento_Ui/js/grid/controls/bookmarks/storage.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'mageUtils',\n 'Magento_Ui/js/lib/core/storage/local',\n 'uiClass'\n], function ($, utils, storage, Class) {\n 'use strict';\n\n /**\n * Removes ns prefix for path.\n *\n * @param {String} ns\n * @param {String} path\n * @returns {String}\n */\n function removeNs(ns, path) {\n return path.replace(ns + '.', '');\n }\n\n return Class.extend({\n defaults: {\n ajaxSettings: {\n method: 'POST',\n data: {\n namespace: '${ $.namespace }'\n }\n }\n },\n\n /**\n * Delegates call to the localStorage adapter.\n */\n get: function () {\n return {};\n },\n\n /**\n * Sends request to store specified data.\n *\n * @param {String} path - Path by which data should be stored.\n * @param {*} value - Value to be sent.\n */\n set: function (path, value) {\n var property = removeNs(this.namespace, path),\n data = {},\n config;\n\n utils.nested(data, property, value);\n\n config = utils.extend({\n url: this.saveUrl,\n data: {\n data: JSON.stringify(data)\n }\n }, this.ajaxSettings);\n\n $.ajax(config);\n },\n\n /**\n * Sends request to remove specified data.\n *\n * @param {String} path - Path to the property to be removed.\n */\n remove: function (path) {\n var property = removeNs(this.namespace, path),\n config;\n\n config = utils.extend({\n url: this.deleteUrl,\n data: {\n data: property\n }\n }, this.ajaxSettings);\n\n $.ajax(config);\n }\n });\n});\n","Magento_Ui/js/grid/controls/bookmarks/bookmarks.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'mage/translate',\n 'rjsResolver',\n 'uiLayout',\n 'uiCollection'\n], function (_, utils, $t, resolver, layout, Collection) {\n 'use strict';\n\n /**\n * Removes 'current' namespace from a 'path' string.\n *\n * @param {String} path\n * @returns {String} Path without namespace.\n */\n function removeStateNs(path) {\n path = typeof path == 'string' ? path.split('.') : [];\n\n if (path[0] === 'current') {\n path.shift();\n }\n\n return path.join('.');\n }\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/controls/bookmarks/bookmarks',\n viewTmpl: 'ui/grid/controls/bookmarks/view',\n newViewLabel: $t('New View'),\n defaultIndex: 'default',\n activeIndex: 'default',\n viewsArray: [],\n storageConfig: {\n provider: '${ $.storageConfig.name }',\n name: '${ $.name }_storage',\n component: 'Magento_Ui/js/grid/controls/bookmarks/storage'\n },\n views: {\n default: {\n label: $t('Default View'),\n index: 'default',\n editable: false\n }\n },\n tracks: {\n editing: true,\n viewsArray: true,\n activeView: true,\n hasChanges: true,\n customLabel: true,\n customVisible: true\n },\n listens: {\n activeIndex: 'onActiveIndexChange',\n activeView: 'checkState',\n current: 'onStateChange'\n }\n },\n\n /**\n * Initializes bookmarks component.\n *\n * @returns {Bookmarks} Chainable.\n */\n initialize: function () {\n utils.limit(this, 'checkState', 5);\n utils.limit(this, 'saveState', 2000);\n\n this._super()\n .restore()\n .initStorage()\n .initViews();\n\n return this;\n },\n\n /**\n * Creates custom storage instance.\n *\n * @returns {Bookmarks} Chainable.\n */\n initStorage: function () {\n layout([this.storageConfig]);\n\n return this;\n },\n\n /**\n * Defines default data if it wasn't gathered previously.\n *\n * @private\n * @returns {Bookmarks} Chainbale.\n */\n initDefaultView: function () {\n var data = this.getViewData(this.defaultIndex);\n\n if (!_.size(data)) {\n this.setViewData(this.defaultIndex, this.current)\n .saveView(this.defaultIndex);\n }\n\n this.defaultDefined = true;\n\n return this;\n },\n\n /**\n * Creates instances of a previously saved views.\n *\n * @returns {Bookmarks} Chainable.\n */\n initViews: function () {\n _.each(this.views, function (config) {\n this.addView(config);\n }, this);\n\n this.activeView = this.getActiveView();\n\n return this;\n },\n\n /**\n * Creates complete configuration for a view.\n *\n * @param {Object} [config] - Additional configuration object.\n * @returns {Object}\n */\n buildView: function (config) {\n var view = {\n label: this.newViewLabel,\n index: '_' + Date.now(),\n editable: true\n };\n\n utils.extend(view, config || {});\n\n view.data = view.data || utils.copy(this.current);\n view.value = view.label;\n\n this.observe.call(view, true, 'label value');\n\n return view;\n },\n\n /**\n * Creates instance of a view with a provided configuration.\n *\n * @param {Object} [config] - View configuration.\n * @param {Boolean} [saveView=false] - Whether to save created view automatically or not.\n * @param {Boolean} [applyView=false] - Whether to apply created view automatically or not.\n * @returns {View} Created view.\n */\n addView: function (config, saveView, applyView) {\n var view = this.buildView(config),\n index = view.index;\n\n this.views[index] = view;\n\n if (saveView) {\n this.saveView(index);\n }\n\n if (applyView) {\n this.applyView(index);\n }\n\n this.updateArray();\n\n return view;\n },\n\n /**\n * Removes specified view.\n *\n * @param {String} index - Index of a view to be removed.\n * @returns {Bookmarks} Chainable.\n */\n removeView: function (index) {\n var viewPath = this.getViewPath(index);\n\n if (this.isViewActive(index)) {\n this.applyView(this.defaultIndex);\n }\n\n this.endEdit(index)\n .remove(viewPath)\n .removeStored(viewPath)\n .updateArray();\n\n return this;\n },\n\n /**\n * Saves data of a specified view.\n *\n * @param {String} index - Index of a view to be saved.\n * @returns {Bookmarks} Chainable.\n */\n saveView: function (index) {\n var viewPath = this.getViewPath(index);\n\n this.updateViewLabel(index)\n .endEdit(index)\n .store(viewPath)\n .checkState();\n\n return this;\n },\n\n /**\n * Sets specified view as active\n * and applies its' state.\n *\n * @param {String} index - Index of a view to be applied.\n * @returns {Bookmarks} Chainable.\n */\n applyView: function (index) {\n this.applyStateOf(index)\n .set('activeIndex', index);\n\n return this;\n },\n\n /**\n * Updates data of a specified view if it's\n * currently active and saves its' data.\n *\n * @param {String} index - Index of a view.\n * @returns {Bookmarks} Chainable.\n */\n updateAndSave: function (index) {\n if (this.isViewActive(index)) {\n this.updateActiveView(index);\n }\n\n this.saveView(index);\n\n return this;\n },\n\n /**\n * Returns instance of a specified view.\n *\n * @param {String} index - Index of a view to be retrieved.\n * @returns {View}\n */\n getView: function (index) {\n return this.views[index];\n },\n\n /**\n * Returns instance of an active view.\n *\n * @returns {View}\n */\n getActiveView: function () {\n return this.views[this.activeIndex];\n },\n\n /**\n * Checks if specified view is active.\n *\n * @param {String} index - Index of a view to be checked.\n * @returns {Boolean}\n */\n isViewActive: function (index) {\n return this.activeView === this.getView(index);\n },\n\n /**\n * Sets current state as a data of an active view.\n *\n * @returns {Bookmarks} Chainable.\n */\n updateActiveView: function () {\n this.setViewData(this.activeIndex, this.current);\n\n return this;\n },\n\n /**\n * Replaces label a view with a provided one.\n * If new label is not specified, then views'\n * 'value' property will be taken.\n *\n * @param {String} index - Index of a view.\n * @param {String} [label=view.value] - New labels' value.\n * @returns {Bookmarks} Chainable.\n */\n updateViewLabel: function (index, label) {\n var view = this.getView(index),\n current = view.label;\n\n label = (label || view.value).trim() || current;\n label = this.uniqueLabel(label, current);\n\n view.label = view.value = label;\n\n return this;\n },\n\n /**\n * Retrieves data of a specified view.\n *\n * @param {String} index - Index of a view whose data should be retrieved.\n * @param {String} [property] - If not specified then whole views' data will be retrieved.\n * @returns {Object} Views' data.\n */\n getViewData: function (index, property) {\n var view = this.getView(index),\n data = view.data;\n\n if (property) {\n data = utils.nested(data, property);\n }\n\n return utils.copy(data);\n },\n\n /**\n * Sets data to the specified view.\n *\n * @param {String} index - Index of a view whose data will be replaced.\n * @param {Object} data - New view data.\n * @returns {Bookmarks} Chainable.\n */\n setViewData: function (index, data) {\n var path = this.getViewPath(index) + '.data';\n\n this.set(path, utils.copy(data));\n\n return this;\n },\n\n /**\n * Starts editing of a specified view.\n *\n * @param {String} index - Index of a view.\n * @returns {Bookmarks} Chainable.\n */\n editView: function (index) {\n this.editing = index;\n\n return this;\n },\n\n /**\n * Ends editing of specified view\n * and restores its' label.\n *\n * @param {String} index - Index of a view.\n * @returns {Bookmarks} Chainable.\n */\n endEdit: function (index) {\n var view;\n\n if (!this.isEditing(index)) {\n return this;\n }\n\n index = index || this.editing;\n view = this.getView(index);\n\n view.value = view.label;\n\n this.editing = false;\n\n return this;\n },\n\n /**\n * Checks if specified view is in editing state.\n *\n * @param {String} index - Index of a view to be checked.\n * @returns {Boolean}\n */\n isEditing: function (index) {\n return this.editing === index;\n },\n\n /**\n * Generates label unique among present views, based\n * on the incoming label pattern.\n *\n * @param {String} [label=this.newViewLabel] - Label pattern.\n * @param {String} [exclude]\n * @returns {String}\n */\n uniqueLabel: function (label, exclude) {\n var labels = _.pluck(this.views, 'label'),\n hasParenth = _.last(label) === ')',\n index = 2,\n result,\n suffix;\n\n labels = _.without(labels, exclude);\n result = label = label || this.newViewLabel;\n\n for (index = 2; _.contains(labels, result); index++) {\n suffix = '(' + index + ')';\n\n if (!hasParenth) {\n suffix = ' ' + suffix;\n }\n\n result = label + suffix;\n }\n\n return result;\n },\n\n /**\n * Applies state of a specified view, without\n * making it active.\n *\n * @param {String} [state=this.activeIndex]\n * @param {String} [property]\n * @returns {Bookmarks} Chainable.\n */\n applyStateOf: function (state, property) {\n var index = state || this.activeIndex,\n dataPath = removeStateNs(property),\n viewData = this.getViewData(index, dataPath);\n\n dataPath = dataPath ?\n 'current.' + dataPath :\n 'current';\n\n this.set(dataPath, viewData);\n\n return this;\n },\n\n /**\n * Saves current state.\n *\n * @returns {Bookmarks} Chainable.\n */\n saveState: function () {\n this.store('current');\n\n return this;\n },\n\n /**\n * Applies state of an active view.\n *\n * @returns {Bookmarks} Chainable.\n */\n resetState: function () {\n this.applyStateOf(this.activeIndex);\n\n return this;\n },\n\n /**\n * Checks if current state is different\n * from the state of an active view.\n *\n * @returns {Bookmarks} Chainable.\n */\n checkState: function () {\n var viewData = this.getViewData(this.activeIndex),\n diff = utils.compare(viewData, this.current);\n\n this.hasChanges = !diff.equal;\n\n return this;\n },\n\n /**\n * Returns path to the view instance,\n * based on a provided index.\n *\n * @param {String} index - Index of a view.\n * @returns {String}\n */\n getViewPath: function (index) {\n return 'views.' + index;\n },\n\n /**\n * Updates the array of views.\n *\n * @returns {Bookmarks} Chainable\n */\n updateArray: function () {\n this.viewsArray = _.values(this.views);\n\n return this;\n },\n\n /**\n * Shows custom view field and creates unique label for it.\n *\n * @returns {Bookmarks} Chainable.\n */\n showCustom: function () {\n this.customLabel = this.uniqueLabel();\n this.customVisible = true;\n\n return this;\n },\n\n /**\n * Hides custom view field.\n *\n * @returns {Bookmarks} Chainable.\n */\n hideCustom: function () {\n this.customVisible = false;\n\n return this;\n },\n\n /**\n * Checks if custom view field is visible.\n *\n * @returns {Boolean}\n */\n isCustomVisible: function () {\n return this.customVisible;\n },\n\n /**\n * Creates new view instance with a label specified\n * in a custom view field.\n *\n * @returns {Bookmarks} Chainable.\n */\n applyCustom: function () {\n var label = this.customLabel.trim();\n\n this.hideCustom()\n .addView({\n label: this.uniqueLabel(label)\n }, true, true);\n\n return this;\n },\n\n /**\n * Listener of the activeIndex property.\n */\n onActiveIndexChange: function () {\n this.activeView = this.getActiveView();\n\n this.store('activeIndex');\n },\n\n /**\n * Listener of the activeIndex property.\n */\n onStateChange: function () {\n this.checkState();\n this.saveState();\n\n if (!this.defaultDefined) {\n resolver(this.initDefaultView, this);\n }\n }\n });\n});\n","Magento_Ui/js/grid/paging/paging.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'underscore',\n 'mageUtils',\n 'uiLayout',\n 'uiElement'\n], function (ko, _, utils, layout, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n template: 'ui/grid/paging/paging',\n totalTmpl: 'ui/grid/paging-total',\n pageSize: 20,\n current: 1,\n selectProvider: 'ns = ${ $.ns }, index = ids',\n\n sizesConfig: {\n component: 'Magento_Ui/js/grid/paging/sizes',\n name: '${ $.name }_sizes',\n storageConfig: {\n provider: '${ $.storageConfig.provider }',\n namespace: '${ $.storageConfig.namespace }'\n }\n },\n\n imports: {\n pageSize: '${ $.sizesConfig.name }:value',\n totalSelected: '${ $.selectProvider }:totalSelected',\n totalRecords: '${ $.provider }:data.totalRecords'\n },\n\n exports: {\n pageSize: '${ $.provider }:params.paging.pageSize',\n current: '${ $.provider }:params.paging.current'\n },\n\n listens: {\n 'pages': 'onPagesChange',\n 'pageSize': 'onPageSizeChange',\n 'totalRecords': 'updateCounter',\n '${ $.provider }:params.filters': 'goFirst'\n },\n\n modules: {\n sizes: '${ $.sizesConfig.name }'\n }\n },\n\n /**\n * Initializes paging component.\n *\n * @returns {Paging} Chainable.\n */\n initialize: function () {\n this._super()\n .initSizes()\n .updateCounter();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Paging} Chainable.\n */\n initObservable: function () {\n this._super()\n .track([\n 'totalSelected',\n 'totalRecords',\n 'pageSize',\n 'pages',\n 'current'\n ]);\n\n this._current = ko.pureComputed({\n read: ko.getObservable(this, 'current'),\n\n /**\n * Validates page change according to user's input.\n * Sets current observable to result of validation.\n * Calls reload method then.\n */\n write: function (value) {\n this.setPage(value)\n ._current.notifySubscribers(this.current);\n },\n\n owner: this\n });\n\n return this;\n },\n\n /**\n * Initializes sizes component.\n *\n * @returns {Paging} Chainable.\n */\n initSizes: function () {\n layout([this.sizesConfig]);\n\n return this;\n },\n\n /**\n * Gets first item index on current page.\n *\n * @returns {Number}\n */\n getFirstItemIndex: function () {\n return this.pageSize * (this.current - 1) + 1;\n },\n\n /**\n * Gets last item index on current page.\n *\n * @returns {Number}\n */\n getLastItemIndex: function () {\n var lastItem = this.getFirstItemIndex() + this.pageSize - 1;\n\n return this.totalRecords < lastItem ? this.totalRecords : lastItem;\n },\n\n /**\n * Sets cursor to the provied value.\n *\n * @param {(Number|String)} value - New value of the cursor.\n * @returns {Paging} Chainable.\n */\n setPage: function (value) {\n this.current = this.normalize(value);\n\n return this;\n },\n\n /**\n * Increments current page value.\n *\n * @returns {Paging} Chainable.\n */\n next: function () {\n this.setPage(this.current + 1);\n\n return this;\n },\n\n /**\n * Decrements current page value.\n *\n * @returns {Paging} Chainable.\n */\n prev: function () {\n this.setPage(this.current - 1);\n\n return this;\n },\n\n /**\n * Goes to the first page.\n *\n * @returns {Paging} Chainable.\n */\n goFirst: function () {\n this.current = 1;\n\n return this;\n },\n\n /**\n * Goes to the last page.\n *\n * @returns {Paging} Chainable.\n */\n goLast: function () {\n this.current = this.pages;\n\n return this;\n },\n\n /**\n * Checks if current page is the first one.\n *\n * @returns {Boolean}\n */\n isFirst: function () {\n return this.current === 1;\n },\n\n /**\n * Checks if current page is the last one.\n *\n * @returns {Boolean}\n */\n isLast: function () {\n return this.current === this.pages;\n },\n\n /**\n * Updates number of pages.\n */\n updateCounter: function () {\n this.pages = Math.ceil(this.totalRecords / this.pageSize) || 1;\n\n return this;\n },\n\n /**\n * Calculates new page cursor based on the\n * previous and current page size values.\n *\n * @returns {Number} Updated cursor value.\n */\n updateCursor: function () {\n var cursor = this.current - 1,\n size = this.pageSize,\n oldSize = this.previousSize,\n delta = cursor * (oldSize - size) / size;\n\n delta = size > oldSize ?\n Math.ceil(delta) :\n Math.floor(delta);\n\n cursor += delta + 1;\n\n this.previousSize = size;\n\n this.setPage(cursor);\n\n return this;\n },\n\n /**\n * Converts provided value to a number and puts\n * it in range between 1 and total amount of pages.\n *\n * @param {(Number|String)} value - Value to be normalized.\n * @returns {Number}\n */\n normalize: function (value) {\n value = +value;\n\n if (isNaN(value)) {\n return 1;\n }\n\n return utils.inRange(Math.round(value), 1, this.pages);\n },\n\n /**\n * Handles changes of the page size.\n */\n onPageSizeChange: function () {\n this.updateCounter()\n .updateCursor();\n },\n\n /**\n * Handles changes of the pages amount.\n */\n onPagesChange: function () {\n this.updateCursor();\n }\n });\n});\n","Magento_Ui/js/grid/paging/sizes.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'underscore',\n 'mageUtils',\n 'uiElement'\n], function (ko, _, utils, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n template: 'ui/grid/paging/sizes',\n value: 20,\n minSize: 1,\n maxSize: 999,\n options: {\n '20': {\n value: 20,\n label: 20\n },\n '30': {\n value: 30,\n label: 30\n },\n '50': {\n value: 50,\n label: 50\n },\n '100': {\n value: 100,\n label: 100\n },\n '200': {\n value: 200,\n label: 200\n }\n },\n statefull: {\n options: true,\n value: true\n },\n listens: {\n value: 'onValueChange',\n options: 'onSizesChange'\n }\n },\n\n /**\n * Initializes sizes component.\n *\n * @returns {Sizes} Chainable.\n */\n initialize: function () {\n this._super()\n .updateArray();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Sizes} Chainable.\n */\n initObservable: function () {\n this._super()\n .track([\n 'value',\n 'editing',\n 'customVisible',\n 'customValue'\n ])\n .track({\n optionsArray: []\n });\n\n this._value = ko.pureComputed({\n read: ko.getObservable(this, 'value'),\n\n /**\n * Validates input field prior to updating 'value' property.\n */\n write: function (value) {\n value = this.normalize(value);\n\n this.value = value;\n this._value.notifySubscribers(value);\n },\n\n owner: this\n });\n\n return this;\n },\n\n /**\n * Starts editing of the specified size.\n *\n * @param {Number} value - Value of the size.\n * @returns {Sizes} Chainable.\n */\n edit: function (value) {\n this.editing = value;\n\n return this;\n },\n\n /**\n * Discards changes made to the currently editable size.\n *\n * @returns {Sizes} Chainable.\n */\n discardEditing: function () {\n var value = this.editing;\n\n if (value) {\n this.updateSize(value, value);\n }\n\n return this;\n },\n\n /**\n * Invokes 'discardEditing' and 'discardCustom' actions.\n *\n * @returns {Sizes} Chainable.\n */\n discardAll: function () {\n this.discardEditing()\n .discardCustom();\n\n return this;\n },\n\n /**\n * Returns value of the first size.\n *\n * @returns {Number}\n */\n getFirst: function () {\n return this.optionsArray[0].value;\n },\n\n /**\n * Returns size which matches specified value.\n *\n * @param {Number} value - Value of the item.\n * @returns {Object|Undefined}\n */\n getSize: function (value) {\n return this.options[value];\n },\n\n /**\n * Sets current size to the specified value.\n *\n * @param {Number} value - Value of the size.\n * @returns {Sizes} Chainable.\n */\n setSize: function (value) {\n this.value = value;\n\n return this;\n },\n\n /**\n * Adds a new value to sizes list.\n *\n * @param {Number} value - Value to be added.\n * @returns {Sizes} Chainable.\n */\n addSize: function (value) {\n var size;\n\n if (!this.hasSize(value)) {\n size = this.createSize(value);\n\n this.set('options.' + value, size);\n }\n\n return this;\n },\n\n /**\n * Removes provided value from the sizes list.\n *\n * @param {Number} value - Value to be removed.\n * @returns {Sizes} Chainable.\n */\n removeSize: function (value) {\n if (!this.hasSize(value)) {\n return this;\n }\n\n this.remove('options.' + value);\n\n if (this.isSelected(value)) {\n this.setSize(this.getFirst());\n }\n\n return this;\n },\n\n /**\n * Updates existing value to the provided one. If new value\n * is not specified, then sizes' '_value' property will be taken.\n *\n * @param {Number} value - Existing value that should be updated.\n * @param {(Number|String)} [newValue=size._value] - New size value.\n * @returns {Sizes} Chainable.\n */\n updateSize: function (value, newValue) {\n var size = this.getSize(value);\n\n if (!size) {\n return this;\n }\n\n newValue = newValue || size._value;\n\n if (isNaN(+newValue)) {\n this.discardEditing();\n\n return this;\n }\n\n newValue = this.normalize(newValue);\n\n this.remove('options.' + value)\n .addSize(newValue);\n\n if (this.isSelected(value)) {\n this.setSize(newValue);\n }\n\n return this;\n },\n\n /**\n * Creates new editable size instance with the provided value.\n *\n * @param {Number} value - Value of the size.\n * @returns {Object}\n */\n createSize: function (value) {\n return {\n value: value,\n label: value,\n _value: value,\n editable: true\n };\n },\n\n /**\n * Checks if provided value exists in the sizes list.\n *\n * @param {Number} value - Value to be checked.\n * @returns {Boolean}\n */\n hasSize: function (value) {\n return !!this.getSize(value);\n },\n\n /**\n * Hides and clears custom field.\n *\n * @returns {Sizes} Chainable.\n */\n discardCustom: function () {\n this.hideCustom()\n .clearCustom();\n\n return this;\n },\n\n /**\n * Shows custom field.\n *\n * @returns {Sizes} Chainable.\n */\n showCustom: function () {\n this.customVisible = true;\n\n return this;\n },\n\n /**\n * Hides custom field.\n *\n * @returns {Sizes} Chainable.\n */\n hideCustom: function () {\n this.customVisible = false;\n\n return this;\n },\n\n /**\n * Empties value of the custom field.\n *\n * @returns {Sizes} Chainable.\n */\n clearCustom: function () {\n this.customValue = '';\n\n return this;\n },\n\n /**\n * Adds a new size specified in the custom field.\n *\n * @returns {Sizes} Chainable.\n */\n applyCustom: function () {\n var value = this.customValue;\n\n value = this.normalize(value);\n\n this.addSize(value)\n .setSize(value)\n .discardCustom();\n\n return this;\n },\n\n /**\n * Checks if custom field is visible.\n *\n * @returns {Boolean}\n */\n isCustomVisible: function () {\n return this.customVisible;\n },\n\n /**\n * Converts provided value to a number and puts\n * it in range between 'minSize' and 'maxSize' properties.\n *\n * @param {(Number|String)} value - Value to be normalized.\n * @returns {Number}\n */\n normalize: function (value) {\n value = +value;\n\n if (isNaN(value)) {\n return this.getFirst();\n }\n\n return utils.inRange(Math.round(value), this.minSize, this.maxSize);\n },\n\n /**\n * Updates the array of options.\n *\n * @returns {Sizes} Chainable.\n */\n updateArray: function () {\n var array = _.values(this.options);\n\n this.optionsArray = _.sortBy(array, 'value');\n\n return this;\n },\n\n /**\n * Checks if provided value is in editing state.\n *\n * @param {Number} value - Value to be checked.\n * @returns {Boolean}\n */\n isEditing: function (value) {\n return this.editing === value;\n },\n\n /**\n * Checks if provided value is selected.\n *\n * @param {Number} value - Value to be checked.\n * @returns {Boolean}\n */\n isSelected: function (value) {\n return this.value === value;\n },\n\n /**\n * Listener of the 'value' property changes.\n */\n onValueChange: function () {\n this.discardAll()\n .trigger('close');\n },\n\n /**\n * Listener of the 'options' object changes.\n */\n onSizesChange: function () {\n this.editing = false;\n\n this.updateArray();\n }\n });\n});\n","Magento_Ui/js/grid/editing/bulk.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n './record'\n], function (_, utils, Record) {\n 'use strict';\n\n /**\n * Removes empty properties from the provided object.\n *\n * @param {Object} data - Object to be processed.\n * @returns {Object}\n */\n function removeEmpty(data) {\n data = utils.flatten(data);\n data = _.omit(data, utils.isEmpty);\n\n return utils.unflatten(data);\n }\n\n return Record.extend({\n defaults: {\n template: 'ui/grid/editing/bulk',\n active: false,\n templates: {\n fields: {\n select: {\n caption: ' '\n }\n }\n },\n imports: {\n active: '${ $.editorProvider }:isMultiEditing'\n },\n listens: {\n data: 'updateState',\n active: 'updateState'\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Bulk} Chainable.\n */\n initObservable: function () {\n this._super()\n .track({\n hasData: false\n });\n\n return this;\n },\n\n /**\n * Extends original method to disable possible\n * 'required-entry' validation rule.\n *\n * @returns {Object} Columns' field definition.\n */\n buildField: function () {\n var field = this._super(),\n rules = field.validation;\n\n if (rules) {\n delete rules['required-entry'];\n }\n\n return field;\n },\n\n /**\n * Applies current data to all active records.\n *\n * @returns {Bulk} Chainable.\n */\n apply: function () {\n if (this.isValid()) {\n this.applyData()\n .clear();\n }\n\n return this;\n },\n\n /**\n * Sets available data to all active records.\n *\n * @param {Object} [data] - If not specified, then current fields data will be used.\n * @returns {Bulk} Chainable.\n */\n applyData: function (data) {\n data = data || this.getData();\n\n this.editor('setData', data, true);\n\n return this;\n },\n\n /**\n * Returns data of all non-empty fields.\n *\n * @returns {Object} Fields data without empty values.\n */\n getData: function () {\n return removeEmpty(this._super());\n },\n\n /**\n * Updates own 'hasData' property and defines\n * whether regular rows editing can be resumed.\n *\n * @returns {Bulk} Chainable.\n */\n updateState: function () {\n var fields = _.keys(this.getData()),\n hasData = !!fields.length;\n\n this.hasData = hasData;\n\n if (!this.active()) {\n fields = [];\n }\n\n this.editor('disableFields', fields);\n this.editor('canSave', !fields.length);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/grid/editing/client.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mageUtils',\n 'uiClass'\n], function ($, _, utils, Class) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n validateBeforeSave: true,\n requestConfig: {\n dataType: 'json',\n type: 'POST'\n }\n },\n\n /**\n * Initializes client instance.\n *\n * @returns {Client} Chainable.\n */\n initialize: function () {\n _.bindAll(this, 'onSuccess', 'onError');\n\n return this._super();\n },\n\n /**\n * Sends XMLHttpRequest with a provided configuration.\n *\n * @param {Object} config - Configuration of request.\n * @returns {jQueryPromise}\n */\n send: function (config) {\n var deffer = $.Deferred();\n\n config = utils.extend({}, this.requestConfig, config);\n\n $.ajax(config)\n .done(_.partial(this.onSuccess, deffer))\n .fail(_.partial(this.onError, deffer));\n\n return deffer.promise();\n },\n\n /**\n * Proxy save method which might invoke\n * data validation prior to its' saving.\n *\n * @param {Object} data - Data to be processed.\n * @returns {jQueryPromise}\n */\n save: function (data) {\n var save = this._save.bind(this, data);\n\n return this.validateBeforeSave ?\n this.validate(data).pipe(save) :\n save();\n },\n\n /**\n * Sends request to validate provided data.\n *\n * @param {Object} data - Data to be validated.\n * @returns {jQueryPromise}\n */\n validate: function (data) {\n return this.send({\n url: this.validateUrl,\n data: data\n });\n },\n\n /**\n * Sends request to save provided data.\n *\n * @private\n * @param {Object} data - Data to be validated.\n * @returns {jQueryPromise}\n */\n _save: function (data) {\n return this.send({\n url: this.saveUrl,\n data: data\n });\n },\n\n /**\n * Creates error object with a provided message.\n *\n * @param {String} msg - Errors' message.\n * @returns {Object}\n */\n createError: function (msg) {\n return {\n type: 'error',\n message: msg\n };\n },\n\n /**\n * Handles ajax error callback.\n *\n * @param {jQueryPromise} promise - Promise to be rejected.\n * @param {jQueryXHR} xhr - See 'jquery' ajax error callback.\n * @param {String} status - See 'jquery' ajax error callback.\n * @param {(String|Object)} err - See 'jquery' ajax error callback.\n */\n onError: function (promise, xhr, status, err) {\n var msg;\n\n msg = xhr.status !== 200 ?\n xhr.status + ' (' + xhr.statusText + ')' :\n err;\n\n promise.reject(this.createError(msg));\n },\n\n /**\n * Handles ajax success callback.\n *\n * @param {jQueryPromise} promise - Promise to be resolved.\n * @param {*} data - See 'jquery' ajax success callback.\n */\n onSuccess: function (promise, data) {\n var errors;\n\n if (data.error) {\n errors = _.map(data.messages, this.createError, this);\n\n promise.reject(errors);\n } else {\n promise.resolve(data);\n }\n }\n });\n});\n","Magento_Ui/js/grid/editing/record.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiLayout',\n 'uiCollection'\n], function (_, utils, layout, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n active: true,\n hasChanges: false,\n fields: [],\n errorsCount: 0,\n fieldTmpl: 'ui/grid/editing/field',\n rowTmpl: 'ui/grid/editing/row',\n templates: {\n fields: {\n base: {\n parent: '${ $.$data.record.name }',\n name: '${ $.$data.column.index }',\n provider: '${ $.$data.record.name }',\n dataScope: 'data.${ $.$data.column.index }',\n imports: {\n disabled: '${ $.$data.record.parentName }:fields.${ $.$data.column.index }.disabled'\n },\n isEditor: true\n },\n text: {\n component: 'Magento_Ui/js/form/element/abstract',\n template: 'ui/form/element/input'\n },\n date: {\n component: 'Magento_Ui/js/form/element/date',\n template: 'ui/form/element/date',\n dateFormat: 'MMM d, y h:mm:ss a'\n },\n select: {\n component: 'Magento_Ui/js/form/element/select',\n template: 'ui/form/element/select',\n options: '${ JSON.stringify($.$data.column.options) }'\n }\n }\n },\n ignoreTmpls: {\n data: true\n },\n listens: {\n elems: 'updateFields',\n data: 'updateState'\n },\n imports: {\n onColumnsUpdate: '${ $.columnsProvider }:elems'\n },\n modules: {\n columns: '${ $.columnsProvider }',\n editor: '${ $.editorProvider }'\n }\n },\n\n /**\n * Initializes record component.\n *\n * @returns {Record} Chainable.\n */\n initialize: function () {\n _.bindAll(this, 'countErrors');\n utils.limit(this, 'updateState', 10);\n\n return this._super();\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Record} Chainable.\n */\n initObservable: function () {\n this._super()\n .track('errorsCount hasChanges')\n .observe('active fields');\n\n return this;\n },\n\n /**\n * Adds listeners on a field.\n *\n * @returns {Record} Chainable.\n */\n initElement: function (field) {\n field.on('error', this.countErrors);\n\n return this._super();\n },\n\n /**\n * Creates new instance of a field.\n *\n * @param {Column} column - Column instance which contains field definition.\n * @returns {Record} Chainable.\n */\n initField: function (column) {\n var field = this.buildField(column);\n\n layout([field]);\n\n return this;\n },\n\n /**\n * Builds fields' configuration described in a provided column.\n *\n * @param {Column} column - Column instance which contains field definition.\n * @returns {Object} Complete fields' configuration.\n */\n buildField: function (column) {\n var fields = this.templates.fields,\n field = column.editor;\n\n if (_.isObject(field) && field.editorType) {\n field = utils.extend({}, fields[field.editorType], field);\n } else if (_.isString(field)) {\n field = fields[field];\n }\n\n field = utils.extend({}, fields.base, field);\n\n return utils.template(field, {\n record: this,\n column: column\n }, true, true);\n },\n\n /**\n * Creates fields for the specified columns.\n *\n * @param {Array} columns - An array of column instances.\n * @returns {Record} Chainable.\n */\n createFields: function (columns) {\n columns.forEach(function (column) {\n if (column.editor && !this.hasChild(column.index)) {\n this.initField(column);\n }\n }, this);\n\n return this;\n },\n\n /**\n * Returns instance of a column found by provided index.\n *\n * @param {String} index - Index of a column (e.g. 'title').\n * @returns {Column}\n */\n getColumn: function (index) {\n return this.columns().getChild(index);\n },\n\n /**\n * Returns records' current data object.\n *\n * @returns {Object}\n */\n getData: function () {\n return this.filterData(this.data);\n },\n\n /**\n * Returns saved records' data. Data will be processed\n * with a 'filterData' and 'normalizeData' methods.\n *\n * @returns {Object} Saved records' data.\n */\n getSavedData: function () {\n var editor = this.editor(),\n savedData = editor.getRowData(this.index);\n\n savedData = this.filterData(savedData);\n\n return this.normalizeData(savedData);\n },\n\n /**\n * Replaces current records' data with the provided one.\n *\n * @param {Object} data - New records data.\n * @param {Boolean} [partial=false] - Flag that defines whether\n * to completely replace current data or to extend it.\n * @returns {Record} Chainable.\n */\n setData: function (data, partial) {\n var currentData = partial ? this.data : {};\n\n data = this.normalizeData(data);\n data = utils.extend({}, currentData, data);\n\n this.set('data', data)\n .updateState();\n\n return this;\n },\n\n /**\n * Filters provided object extracting from it values\n * that can be matched with an existing fields.\n *\n * @param {Object} data - Object to be processed.\n * @returns {Object}\n */\n filterData: function (data) {\n var fields = _.pluck(this.elems(), 'index');\n\n _.each(this.preserveFields, function (enabled, field) {\n if (enabled && !_.contains(fields, field)) {\n fields.push(field);\n }\n });\n\n return _.pick(data, fields);\n },\n\n /**\n * Parses values of a provided object with\n * a 'normalizeData' method of a corresponding field.\n *\n * @param {Object} data - Data to be processed.\n * @returns {Object}\n */\n normalizeData: function (data) {\n var index;\n\n this.elems.each(function (elem) {\n index = elem.index;\n\n if (data.hasOwnProperty(index)) {\n data[index] = elem.normalizeData(data[index]);\n }\n });\n\n return data;\n },\n\n /**\n * Clears values of all fields.\n *\n * @returns {Record} Chainable.\n */\n clear: function () {\n this.elems.each('clear');\n\n return this;\n },\n\n /**\n * Validates all of the available fields.\n *\n * @returns {Array} An array with validation results.\n */\n validate: function () {\n return this.elems.map('validate');\n },\n\n /**\n * Checks if all fields are valid.\n *\n * @returns {Boolean}\n */\n isValid: function () {\n return _.every(this.validate(), 'valid');\n },\n\n /**\n * Counts total errors amount across all fields.\n *\n * @returns {Number}\n */\n countErrors: function () {\n var errorsCount = this.elems.filter('error').length;\n\n this.errorsCount = errorsCount;\n\n return errorsCount;\n },\n\n /**\n * Returns difference between current data and its'\n * initial state, retrieved from the records collection.\n *\n * @returns {Object} Object with changes descriptions.\n */\n checkChanges: function () {\n var savedData = this.getSavedData(),\n data = this.normalizeData(this.getData());\n\n return utils.compare(savedData, data);\n },\n\n /**\n * Updates 'fields' array filling it with available editors\n * or with column instances if associated field is not present.\n *\n * @returns {Record} Chainable.\n */\n updateFields: function () {\n var fields;\n\n fields = this.columns().elems.map(function (column) {\n return this.getChild(column.index) || column;\n }, this);\n\n this.fields(fields);\n\n return this;\n },\n\n /**\n * Updates state of a 'hasChanges' property.\n *\n * @returns {Record} Chainable.\n */\n updateState: function () {\n var diff = this.checkChanges(),\n changed = {};\n\n this.hasChanges = !diff.equal;\n changed[this.index] = this.data;\n this.editor().set('changed', [changed]);\n\n return this;\n },\n\n /**\n * Checks if provided column is an actions column.\n *\n * @param {Column} column - Column to be checked.\n * @returns {Boolean}\n */\n isActionsColumn: function (column) {\n return column.dataType === 'actions';\n },\n\n /**\n * Listener of columns provider child array changes.\n *\n * @param {Array} columns - Modified child elements array.\n */\n onColumnsUpdate: function (columns) {\n this.createFields(columns)\n .updateFields();\n }\n });\n});\n","Magento_Ui/js/grid/editing/editor.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiLayout',\n 'mage/translate',\n 'uiCollection'\n], function (_, utils, layout, $t, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n rowButtonsTmpl: 'ui/grid/editing/row-buttons',\n headerButtonsTmpl: 'ui/grid/editing/header-buttons',\n successMsg: $t('You have successfully saved your edits.'),\n errorsCount: 0,\n bulkEnabled: true,\n multiEditingButtons: true,\n singleEditingButtons: true,\n isMultiEditing: false,\n isSingleEditing: false,\n permanentlyActive: false,\n rowsData: [],\n fields: {},\n\n templates: {\n record: {\n parent: '${ $.$data.editor.name }',\n name: '${ $.$data.recordId }',\n component: 'Magento_Ui/js/grid/editing/record',\n columnsProvider: '${ $.$data.editor.columnsProvider }',\n editorProvider: '${ $.$data.editor.name }',\n preserveFields: {\n '${ $.$data.editor.indexField }': true\n }\n }\n },\n bulkConfig: {\n component: 'Magento_Ui/js/grid/editing/bulk',\n name: '${ $.name }_bulk',\n editorProvider: '${ $.name }',\n columnsProvider: '${ $.columnsProvider }'\n },\n clientConfig: {\n component: 'Magento_Ui/js/grid/editing/client',\n name: '${ $.name }_client'\n },\n viewConfig: {\n component: 'Magento_Ui/js/grid/editing/editor-view',\n name: '${ $.name }_view',\n model: '${ $.name }',\n columnsProvider: '${ $.columnsProvider }'\n },\n imports: {\n rowsData: '${ $.dataProvider }:data.items'\n },\n listens: {\n '${ $.dataProvider }:reloaded': 'cancel',\n '${ $.selectProvider }:selected': 'onSelectionsChange'\n },\n modules: {\n source: '${ $.dataProvider }',\n client: '${ $.clientConfig.name }',\n columns: '${ $.columnsProvider }',\n bulk: '${ $.bulkConfig.name }',\n selections: '${ $.selectProvider }'\n }\n },\n\n /**\n * Initializes editor component.\n *\n * @returns {Editor} Chainable.\n */\n initialize: function () {\n _.bindAll(this, 'updateState', 'countErrors', 'onDataSaved', 'onSaveError');\n\n this._super()\n .initBulk()\n .initClient()\n .initView();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Editor} Chainable.\n */\n initObservable: function () {\n this._super()\n .track([\n 'errorsCount',\n 'isMultiEditing',\n 'isSingleEditing',\n 'isSingleColumnEditing',\n 'changed'\n ])\n .observe({\n canSave: true,\n activeRecords: [],\n messages: []\n });\n\n return this;\n },\n\n /**\n * Initializes bulk editing component.\n *\n * @returns {Editor} Chainable.\n */\n initBulk: function () {\n if (this.bulkEnabled) {\n layout([this.bulkConfig]);\n }\n\n return this;\n },\n\n /**\n * Initializes editors' view component.\n *\n * @returns {Editor} Chainable.\n */\n initView: function () {\n layout([this.viewConfig]);\n\n return this;\n },\n\n /**\n * Initializes client component.\n *\n * @returns {Editor} Chainable.\n */\n initClient: function () {\n layout([this.clientConfig]);\n\n return this;\n },\n\n /**\n * Creates instance of a new record.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Editor} Chainable.\n */\n initRecord: function (id, isIndex) {\n var record = this.buildRecord(id, isIndex);\n\n layout([record]);\n\n return this;\n },\n\n /**\n * Adds listeners on a new record.\n *\n * @param {Record} record\n * @returns {Editor} Chainable.\n */\n initElement: function (record) {\n record.on({\n 'active': this.updateState,\n 'errorsCount': this.countErrors\n });\n\n this.updateState();\n\n return this._super();\n },\n\n /**\n * Creates configuration for a new record associated with a row data.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Object} Record configuration.\n */\n buildRecord: function (id, isIndex) {\n var recordId = this.getId(id, isIndex),\n recordTmpl = this.templates.record,\n record;\n\n if (this.getRecord(recordId)) {\n return this;\n }\n\n record = utils.template(recordTmpl, {\n editor: this,\n recordId: id\n });\n\n record.recordId = id;\n record.data = this.getRowData(id);\n\n return record;\n },\n\n /**\n * Starts editing of a specified record. If records'\n * instance doesn't exist, than it will be created.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Editor} Chainable.\n */\n edit: function (id, isIndex) {\n var recordId = this.getId(id, isIndex),\n record = this.getRecord(recordId);\n\n record ?\n record.active(true) :\n this.initRecord(recordId);\n\n return this;\n },\n\n /**\n * Drops list of selections while activating only the specified record.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Editor} Chainable.\n */\n startEdit: function (id, isIndex) {\n var recordId = this.getId(id, isIndex);\n\n this.selections()\n .deselectAll()\n .select(recordId);\n\n return this.edit(recordId);\n },\n\n /**\n * Hides records and resets theirs data.\n *\n * @returns {Editor} Chainable.\n */\n cancel: function () {\n this.reset()\n .hide()\n .clearMessages()\n .bulk('clear');\n\n return this;\n },\n\n /**\n * Hides records.\n *\n * @returns {Editor} Chainable.\n */\n hide: function () {\n this.activeRecords.each('active', false);\n\n return this;\n },\n\n /**\n * Resets active records.\n *\n * @returns {Editor} Chainable.\n */\n reset: function () {\n this.elems.each(function (record) {\n this.resetRecord(record.recordId);\n }, this);\n\n return this;\n },\n\n /**\n * Validates and saves data of active records.\n *\n * @returns {Editor} Chainable.\n */\n save: function () {\n var data;\n\n if (!this.isValid()) {\n return this;\n }\n\n data = {\n items: this.getData()\n };\n\n this.clearMessages()\n .columns('showLoader');\n\n this.client()\n .save(data)\n .done(this.onDataSaved)\n .fail(this.onSaveError);\n\n return this;\n },\n\n /**\n * Validates all active records.\n *\n * @returns {Array} An array of records and theirs validation results.\n */\n validate: function () {\n return this.activeRecords.map(function (record) {\n return {\n target: record,\n valid: record.isValid()\n };\n });\n },\n\n /**\n * Checks if all active records are valid.\n *\n * @returns {Boolean}\n */\n isValid: function () {\n return _.every(this.validate(), 'valid');\n },\n\n /**\n * Returns active records data, indexed by a theirs ids.\n *\n * @returns {Object} Collection of records data.\n */\n getData: function () {\n var data = this.activeRecords.map('getData');\n\n return _.indexBy(data, this.indexField);\n },\n\n /**\n * Sets provided data to all active records.\n *\n * @param {Object} data - See 'setData' method of a 'Record'.\n * @param {Boolean} partial - See 'setData' method of a 'Record'.\n * @returns {Editor} Chainable.\n */\n setData: function (data, partial) {\n this.activeRecords.each('setData', data, partial);\n\n return this;\n },\n\n /**\n * Resets specific records' data\n * to the data present in associated row.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Editor} Chainable.\n */\n resetRecord: function (id, isIndex) {\n var record = this.getRecord(id, isIndex),\n data = this.getRowData(id, isIndex);\n\n if (record && data) {\n record.setData(data);\n }\n\n return this;\n },\n\n /**\n * Returns instance of a specified record.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Record}\n */\n getRecord: function (id, isIndex) {\n return this.elems.findWhere({\n recordId: this.getId(id, isIndex)\n });\n },\n\n /**\n * Creates record name based on a provided id.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {String}\n */\n formRecordName: function (id, isIndex) {\n id = this.getId(id, isIndex);\n\n return this.name + '.' + id;\n },\n\n /**\n * Disables editing of specified fields.\n *\n * @param {Array} fields - An array of fields indexes to be disabled.\n * @returns {Editor} Chainable.\n */\n disableFields: function (fields) {\n var columns = this.columns().elems(),\n data = utils.copy(this.fields);\n\n columns.forEach(function (column) {\n var index = column.index,\n field = data[index] = data[index] || {};\n\n field.disabled = _.contains(fields, index);\n });\n\n this.set('fields', data);\n\n return this;\n },\n\n /**\n * Converts index of a row into the record id.\n *\n * @param {(Number|String)} id - Records' identifier or its' index in the rows array.\n * @param {Boolean} [isIndex=false] - Flag that indicates if first\n * parameter is an index or identifier.\n * @returns {String} Records' id.\n */\n getId: function (id, isIndex) {\n var rowsData = this.rowsData,\n record;\n\n if (isIndex === true) {\n record = rowsData[id];\n id = record ? record[this.indexField] : false;\n }\n\n return id;\n },\n\n /**\n * Returns data of a specified row.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See 'getId' method.\n * @returns {Object}\n */\n getRowData: function (id, isIndex) {\n id = this.getId(id, isIndex);\n\n return _.find(this.rowsData, function (row) {\n return row[this.indexField] === id;\n }, this);\n },\n\n /**\n * Checks if specified record is active.\n *\n * @param {(Number|String)} id - See 'getId' method.\n * @param {Boolean} [isIndex=false] - See'getId' method.\n * @returns {Boolean}\n */\n isActive: function (id, isIndex) {\n var record = this.getRecord(id, isIndex);\n\n return _.contains(this.activeRecords(), record);\n },\n\n /**\n * Checks if editor has active records.\n *\n * @returns {Boolean}\n */\n hasActive: function () {\n return !!this.activeRecords().length || this.permanentlyActive;\n },\n\n /**\n * Counts number of active records.\n *\n * @returns {Number}\n */\n countActive: function () {\n return this.activeRecords().length;\n },\n\n /**\n * Counts number of invalid fields across all active records.\n *\n * @returns {Number}\n */\n countErrors: function () {\n var errorsCount = 0;\n\n this.activeRecords.each(function (record) {\n errorsCount += record.errorsCount;\n });\n\n this.errorsCount = errorsCount;\n\n return errorsCount;\n },\n\n /**\n * Translatable error message text.\n *\n * @returns {String}\n */\n countErrorsMessage: function () {\n return $t('There are {placeholder} messages requires your attention.')\n .replace('{placeholder}', this.countErrors());\n },\n\n /**\n * Checks if editor has any errors.\n *\n * @returns {Boolean}\n */\n hasErrors: function () {\n return !!this.countErrors();\n },\n\n /**\n * Handles changes of the records 'active' property.\n *\n * @returns {Editor} Chainable.\n */\n updateState: function () {\n var active = this.elems.filter('active'),\n activeCount = active.length,\n columns = this.columns().elems;\n\n columns.each('disableAction', !!activeCount);\n\n this.isMultiEditing = activeCount > 1;\n this.isSingleEditing = activeCount === 1;\n\n this.activeRecords(active);\n\n return this;\n },\n\n /**\n * Returns list of selections from a current page.\n *\n * @returns {Array}\n */\n getSelections: function () {\n return this.selections().getPageSelections();\n },\n\n /**\n * Starts editing of selected records. If record\n * is not in the selections list, then it will get hidden.\n *\n * @returns {Editor} Chainable.\n */\n editSelected: function () {\n var selections = this.getSelections();\n\n this.elems.each(function (record) {\n if (!_.contains(selections, record.recordId)) {\n record.active(false);\n }\n });\n\n selections.forEach(function (id) {\n this.edit(id);\n }, this);\n\n return this;\n },\n\n /**\n * Checks if there is any additional messages.\n *\n * @returns {Boolean}\n */\n hasMessages: function () {\n return this.messages().length;\n },\n\n /**\n * Adds new additional message or a set of messages.\n *\n * @param {(Object|Array)} message - Messages to be added.\n * @returns {Editor} Chainable.\n */\n addMessage: function (message) {\n var messages = this.messages();\n\n Array.isArray(message) ?\n messages.push.apply(messages, message) :\n messages.push(message);\n\n this.messages(messages);\n\n return this;\n },\n\n /**\n * Removes all additional messages.\n *\n * @returns {Editor} Chainable.\n */\n clearMessages: function () {\n this.messages.removeAll();\n\n return this;\n },\n\n /**\n * Listener of the selections data changes.\n */\n onSelectionsChange: function () {\n if (this.hasActive()) {\n this.editSelected();\n }\n },\n\n /**\n * Handles successful save request.\n */\n onDataSaved: function () {\n var msg = {\n type: 'success',\n message: this.successMsg\n };\n\n this.addMessage(msg)\n .source('reload', {\n refresh: true\n });\n },\n\n /**\n * Handles failed save request.\n *\n * @param {(Array|Object)} errors - List of errors or a single error object.\n */\n onSaveError: function (errors) {\n this.addMessage(errors)\n .columns('hideLoader');\n }\n });\n});\n","Magento_Ui/js/grid/editing/editor-view.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'ko',\n 'Magento_Ui/js/lib/view/utils/async',\n 'underscore',\n 'uiRegistry',\n 'uiClass'\n], function (ko, $, _, registry, Class) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n rootSelector: '${ $.columnsProvider }:.admin__data-grid-wrap',\n tableSelector: '${ $.rootSelector } -> table',\n rowSelector: '${ $.tableSelector } tbody tr.data-row',\n headerButtonsTmpl:\n '<!-- ko template: headerButtonsTmpl --><!-- /ko -->',\n bulkTmpl:\n '<!-- ko scope: bulk -->' +\n '<!-- ko template: getTemplate() --><!-- /ko -->' +\n '<!-- /ko -->',\n rowTmpl:\n '<!-- ko with: _editor -->' +\n '<!-- ko if: isActive($row()._rowIndex, true) -->' +\n '<!-- ko with: getRecord($row()._rowIndex, true) -->' +\n '<!-- ko template: rowTmpl --><!-- /ko -->' +\n '<!-- /ko -->' +\n '<!-- ko if: isSingleEditing && singleEditingButtons -->' +\n '<!-- ko template: rowButtonsTmpl --><!-- /ko -->' +\n '<!-- /ko -->' +\n '<!-- /ko -->' +\n '<!-- /ko -->'\n },\n\n /**\n * Initializes view component.\n *\n * @returns {View} Chainable.\n */\n initialize: function () {\n _.bindAll(\n this,\n 'initRoot',\n 'initTable',\n 'initRow',\n 'rowBindings',\n 'tableBindings'\n );\n\n this._super();\n\n this.model = registry.get(this.model);\n\n $.async(this.rootSelector, this.initRoot);\n $.async(this.tableSelector, this.initTable);\n $.async(this.rowSelector, this.initRow);\n\n return this;\n },\n\n /**\n * Initializes columns root container.\n *\n * @param {HTMLElement} node\n * @returns {View} Chainable.\n */\n initRoot: function (node) {\n $(this.headerButtonsTmpl)\n .insertBefore(node)\n .applyBindings(this.model);\n\n return this;\n },\n\n /**\n * Initializes table element.\n *\n * @param {HTMLTableElement} table\n * @returns {View} Chainable.\n */\n initTable: function (table) {\n $(table).bindings(this.tableBindings);\n\n this.initBulk(table);\n\n return this;\n },\n\n /**\n * Initializes bulk editor element\n * for the provided table.\n *\n * @param {HTMLTableElement} table\n * @returns {View} Chainable.\n */\n initBulk: function (table) {\n var tableBody = $('tbody', table)[0];\n\n $(this.bulkTmpl)\n .prependTo(tableBody)\n .applyBindings(this.model);\n\n return this;\n },\n\n /**\n * Initializes table row.\n *\n * @param {HTMLTableRowElement} row\n * @returns {View} Chainable.\n */\n initRow: function (row) {\n var $editingRow;\n\n $(row).extendCtx({\n _editor: this.model\n }).bindings(this.rowBindings);\n\n $editingRow = $(this.rowTmpl)\n .insertBefore(row)\n .applyBindings(row);\n\n ko.utils.domNodeDisposal.addDisposeCallback(row, this.removeEditingRow.bind(this, $editingRow));\n\n return this;\n },\n\n /**\n * Returns row bindings.\n *\n * @param {Object} ctx - Current context of a row.\n * @returns {Object}\n */\n rowBindings: function (ctx) {\n var model = this.model;\n\n return {\n visible: ko.computed(function () {\n var record = ctx.$row(),\n index = record && record._rowIndex;\n\n return !model.isActive(index, true);\n })\n };\n },\n\n /**\n * Returns table bindings.\n *\n * @returns {Object}\n */\n tableBindings: function () {\n var model = this.model;\n\n return {\n css: {\n '_in-edit': ko.computed(function () {\n return model.hasActive() && !model.permanentlyActive;\n })\n }\n };\n },\n\n /**\n * Removes specified array of nodes.\n *\n * @param {ArrayLike} row\n */\n removeEditingRow: function (row) {\n _.toArray(row).forEach(ko.removeNode);\n }\n });\n});\n","Magento_Ui/js/grid/search/search.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiLayout',\n 'mage/translate',\n 'mageUtils',\n 'uiElement'\n], function (_, layout, $t, utils, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n template: 'ui/grid/search/search',\n placeholder: 'Search by keyword',\n label: $t('Keyword'),\n value: '',\n previews: [],\n chipsProvider: 'componentType = filtersChips, ns = ${ $.ns }',\n statefull: {\n value: true\n },\n tracks: {\n value: true,\n previews: true,\n inputValue: true\n },\n imports: {\n inputValue: 'value',\n updatePreview: 'value'\n },\n exports: {\n value: '${ $.provider }:params.search'\n },\n modules: {\n chips: '${ $.chipsProvider }'\n }\n },\n\n /**\n * Initializes search component.\n *\n * @returns {Search} Chainable.\n */\n initialize: function () {\n var urlParams = window.location.href.slice(window.location.href.search('[\\&\\?](search=)')).split('&'),\n searchTerm = [];\n\n this._super()\n .initChips();\n\n if (urlParams[0]) {\n searchTerm = urlParams[0].split('=');\n\n if (searchTerm[1]) {\n this.apply(decodeURIComponent(searchTerm[1]));\n }\n }\n\n return this;\n },\n\n /**\n * Initializes chips component.\n *\n * @returns {Search} Chainbale.\n */\n initChips: function () {\n this.chips('insertChild', this, 0);\n\n return this;\n },\n\n /**\n * Clears search.\n *\n * @returns {Search} Chainable.\n */\n clear: function () {\n this.value = '';\n\n return this;\n },\n\n /**\n * Resets input value to the last applied state.\n *\n * @returns {Search} Chainable.\n */\n cancel: function () {\n this.inputValue = this.value;\n\n return this;\n },\n\n /**\n * Applies search query.\n *\n * @param {String} [value=inputValue] - If not specified, then\n * value of the input field will be used.\n * @returns {Search} Chainable.\n */\n apply: function (value) {\n value = value || this.inputValue;\n\n this.value = this.inputValue = value.trim();\n\n return this;\n },\n\n /**\n * Updates preview data.\n *\n * @returns {Search} Chainable.\n */\n updatePreview: function () {\n var preview = [];\n\n if (this.value) {\n preview.push({\n elem: this,\n label: this.label,\n preview: this.value\n });\n }\n\n this.previews = preview;\n\n return this;\n }\n });\n});\n","Magento_Ui/js/grid/filters/filters.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiLayout',\n 'uiCollection',\n 'mage/translate',\n 'jquery'\n], function (_, utils, layout, Collection, $t, $) {\n 'use strict';\n\n /**\n * Extracts and formats preview of an element.\n *\n * @param {Object} elem - Element whose preview should be extracted.\n * @returns {Object} Formatted data.\n */\n function extractPreview(elem) {\n return {\n label: elem.label,\n preview: elem.getPreview(),\n elem: elem\n };\n }\n\n /**\n * Removes empty properties from the provided object.\n *\n * @param {Object} data - Object to be processed.\n * @returns {Object}\n */\n function removeEmpty(data) {\n var result = utils.mapRecursive(data, utils.removeEmptyValues.bind(utils));\n\n return utils.mapRecursive(result, function (value) {\n return _.isString(value) ? value.trim() : value;\n });\n }\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/filters/filters',\n stickyTmpl: 'ui/grid/sticky/filters',\n _processed: [],\n columnsProvider: 'ns = ${ $.ns }, componentType = columns',\n bookmarksProvider: 'ns = ${ $.ns }, componentType = bookmark',\n applied: {\n placeholder: true\n },\n filters: {\n placeholder: true\n },\n templates: {\n filters: {\n base: {\n parent: '${ $.$data.filters.name }',\n name: '${ $.$data.column.index }',\n provider: '${ $.$data.filters.name }',\n dataScope: '${ $.$data.column.index }',\n label: '${ $.$data.column.label }',\n imports: {\n visible: '${ $.$data.column.name }:visible'\n }\n },\n text: {\n component: 'Magento_Ui/js/form/element/abstract',\n template: 'ui/grid/filters/field'\n },\n select: {\n component: 'Magento_Ui/js/form/element/select',\n template: 'ui/grid/filters/field',\n options: '${ JSON.stringify($.$data.column.options) }',\n caption: ' '\n },\n dateRange: {\n component: 'Magento_Ui/js/grid/filters/range',\n rangeType: 'date'\n },\n textRange: {\n component: 'Magento_Ui/js/grid/filters/range',\n rangeType: 'text'\n }\n }\n },\n chipsConfig: {\n name: '${ $.name }_chips',\n provider: '${ $.chipsConfig.name }',\n component: 'Magento_Ui/js/grid/filters/chips'\n },\n listens: {\n active: 'updatePreviews',\n applied: 'cancel updateActive'\n },\n statefull: {\n applied: true\n },\n exports: {\n applied: '${ $.provider }:params.filters'\n },\n imports: {\n onColumnsUpdate: '${ $.columnsProvider }:elems',\n onBackendError: '${ $.provider }:lastError',\n bookmarksActiveIndex: '${ $.bookmarksProvider }:activeIndex'\n },\n modules: {\n columns: '${ $.columnsProvider }',\n chips: '${ $.chipsConfig.provider }'\n }\n },\n\n /**\n * Initializes filters component.\n *\n * @returns {Filters} Chainable.\n */\n initialize: function () {\n _.bindAll(this, 'updateActive');\n\n this._super()\n .initChips()\n .cancel();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Filters} Chainable.\n */\n initObservable: function () {\n this._super()\n .track({\n active: [],\n previews: []\n });\n\n return this;\n },\n\n /**\n * Initializes chips component.\n *\n * @returns {Filters} Chainable.\n */\n initChips: function () {\n layout([this.chipsConfig]);\n\n this.chips('insertChild', this.name);\n\n return this;\n },\n\n /**\n * Called when another element was added to filters collection.\n *\n * @returns {Filters} Chainable.\n */\n initElement: function (elem) {\n this._super();\n\n elem.on('elems', this.updateActive);\n\n this.updateActive();\n\n return this;\n },\n\n /**\n * Clears filters data.\n *\n * @param {Object} [filter] - If provided, then only specified\n * filter will be cleared. Otherwise, clears all data.\n * @returns {Filters} Chainable.\n */\n clear: function (filter) {\n filter ?\n filter.clear() :\n _.invoke(this.active, 'clear');\n\n this.apply();\n\n return this;\n },\n\n /**\n * Sets filters data to the applied state.\n *\n * @returns {Filters} Chainable.\n */\n apply: function () {\n this.set('applied', removeEmpty(this.filters));\n\n return this;\n },\n\n /**\n * Resets filters to the last applied state.\n *\n * @returns {Filters} Chainable.\n */\n cancel: function () {\n this.set('filters', utils.copy(this.applied));\n\n return this;\n },\n\n /**\n * Sets provided data to filter components (without applying it).\n *\n * @param {Object} data - Filters data.\n * @param {Boolean} [partial=false] - Flag that defines whether\n * to completely replace current filters data or to extend it.\n * @returns {Filters} Chainable.\n */\n setData: function (data, partial) {\n var filters = partial ? this.filters : {};\n\n data = utils.extend({}, filters, data);\n\n this.set('filters', data);\n\n return this;\n },\n\n /**\n * Creates instance of a filter associated with the provided column.\n *\n * @param {Column} column - Column component for which to create a filter.\n * @returns {Filters} Chainable.\n */\n addFilter: function (column) {\n var index = column.index,\n processed = this._processed,\n filter;\n\n if (!column.filter || _.contains(processed, index)) {\n return this;\n }\n\n filter = this.buildFilter(column);\n\n processed.push(index);\n\n layout([filter]);\n\n return this;\n },\n\n /**\n * Creates filter component configuration associated with the provided column.\n *\n * @param {Column} column - Column component with a basic filter declaration.\n * @returns {Object} Filters' configuration.\n */\n buildFilter: function (column) {\n var filters = this.templates.filters,\n filter = column.filter,\n type = filters[filter.filterType];\n\n if (_.isObject(filter) && type) {\n filter = utils.extend({}, type, filter);\n } else if (_.isString(filter)) {\n filter = filters[filter];\n }\n\n filter = utils.extend({}, filters.base, filter);\n\n return utils.template(filter, {\n filters: this,\n column: column\n }, true, true);\n },\n\n /**\n * Returns an array of range filters.\n *\n * @returns {Array}\n */\n getRanges: function () {\n return this.elems.filter(function (filter) {\n return filter.isRange;\n });\n },\n\n /**\n * Returns an array of non-range filters.\n *\n * @returns {Array}\n */\n getPlain: function () {\n return this.elems.filter(function (filter) {\n return !filter.isRange;\n });\n },\n\n /**\n * Tells wether specified filter should be visible.\n *\n * @param {Object} filter\n * @returns {Boolean}\n */\n isFilterVisible: function (filter) {\n return filter.visible() || this.isFilterActive(filter);\n },\n\n /**\n * Checks if specified filter is active.\n *\n * @param {Object} filter\n * @returns {Boolean}\n */\n isFilterActive: function (filter) {\n return _.contains(this.active, filter);\n },\n\n /**\n * Checks if collection has visible filters.\n *\n * @returns {Boolean}\n */\n hasVisible: function () {\n return this.elems.some(this.isFilterVisible, this);\n },\n\n /**\n * Finds filters with a not empty data\n * and sets them to the 'active' filters array.\n *\n * @returns {Filters} Chainable.\n */\n updateActive: function () {\n var applied = _.keys(this.applied);\n\n this.active = this.elems.filter(function (elem) {\n return _.contains(applied, elem.index);\n });\n\n return this;\n },\n\n /**\n * Returns number of applied filters.\n *\n * @returns {Number}\n */\n countActive: function () {\n return this.active.length;\n },\n\n /**\n * Extract previews of a specified filters.\n *\n * @param {Array} filters - Filters to be processed.\n * @returns {Filters} Chainable.\n */\n updatePreviews: function (filters) {\n var previews = filters.map(extractPreview);\n\n this.previews = _.compact(previews);\n\n return this;\n },\n\n /**\n * Listener of the columns provider children array changes.\n *\n * @param {Array} columns - Current columns list.\n */\n onColumnsUpdate: function (columns) {\n columns.forEach(this.addFilter, this);\n },\n\n /**\n * Provider ajax error listener.\n *\n * @param {bool} isError - Selected index of the filter.\n */\n onBackendError: function (isError) {\n var defaultMessage = 'Something went wrong with processing the default view and we have restored the ' +\n 'filter to its original state.',\n customMessage = 'Something went wrong with processing current custom view and filters have been ' +\n 'reset to its original state. Please edit filters then click apply.';\n\n if (isError) {\n this.clear();\n\n $('body').notification('clear')\n .notification('add', {\n error: true,\n message: $.mage.__(this.bookmarksActiveIndex !== 'default' ? customMessage : defaultMessage),\n\n /**\n * @param {String} message\n */\n insertMethod: function (message) {\n var $wrapper = $('<div/>').html(message);\n\n $('.page-main-actions').after($wrapper);\n }\n });\n }\n }\n });\n});\n","Magento_Ui/js/grid/filters/chips.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiCollection'\n], function (_, Collection) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n template: 'ui/grid/filters/chips',\n componentType: 'filtersChips'\n },\n\n /**\n * Defines if some of components' children has available previews.\n *\n * @returns {Boolean}\n */\n hasPreviews: function () {\n return this.elems().some(function (elem) {\n return !!elem.previews.length;\n });\n },\n\n /**\n * Calls clear method on all of its' children.\n *\n * @returns {Chips} Chainable.\n */\n clear: function () {\n _.invoke(this.elems(), 'clear');\n\n return this;\n }\n });\n});\n","Magento_Ui/js/grid/filters/range.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiLayout',\n 'mageUtils',\n 'Magento_Ui/js/form/components/group',\n 'mage/translate'\n], function (_, layout, utils, Group, $t) {\n 'use strict';\n\n return Group.extend({\n defaults: {\n template: 'ui/grid/filters/elements/group',\n isRange: true,\n templates: {\n base: {\n parent: '${ $.$data.group.name }',\n provider: '${ $.$data.group.provider }',\n template: 'ui/grid/filters/field'\n },\n date: {\n component: 'Magento_Ui/js/form/element/date',\n dateFormat: 'MM/dd/YYYY',\n shiftedValue: 'filter'\n },\n text: {\n component: 'Magento_Ui/js/form/element/abstract'\n },\n ranges: {\n from: {\n label: $t('from'),\n dataScope: 'from'\n },\n to: {\n label: $t('to'),\n dataScope: 'to'\n }\n }\n }\n },\n\n /**\n * Initializes range component.\n *\n * @returns {Range} Chainable.\n */\n initialize: function () {\n this._super()\n .initChildren();\n\n return this;\n },\n\n /**\n * Creates instances of child components.\n *\n * @returns {Range} Chainable.\n */\n initChildren: function () {\n var children = this.buildChildren();\n\n layout(children);\n\n return this;\n },\n\n /**\n * Creates configuration for the child components.\n *\n * @returns {Object}\n */\n buildChildren: function () {\n var templates = this.templates,\n typeTmpl = templates[this.rangeType],\n tmpl = utils.extend({}, templates.base, typeTmpl),\n children = {};\n\n _.each(templates.ranges, function (range, key) {\n children[key] = utils.extend({}, tmpl, range);\n });\n\n return utils.template(children, {\n group: this\n }, true, true);\n },\n\n /**\n * Clears childrens data.\n *\n * @returns {Range} Chainable.\n */\n clear: function () {\n this.elems.each('clear');\n\n return this;\n },\n\n /**\n * Checks if some children has data.\n *\n * @returns {Boolean}\n */\n hasData: function () {\n return this.elems.some('hasData');\n }\n });\n});\n","Magento_Ui/js/grid/columns/onoff.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mage/translate',\n './multiselect',\n 'uiRegistry'\n], function (_, $t, Column, registry) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n headerTmpl: 'ui/grid/columns/onoff',\n bodyTmpl: 'ui/grid/cells/onoff',\n fieldClass: {\n 'admin__scope-old': true,\n 'data-grid-onoff-cell': true,\n 'data-grid-checkbox-cell': false\n },\n imports: {\n selectedData: '${ $.provider }:data.selectedData'\n },\n listens: {\n '${ $.provider }:reloaded': 'setDefaultSelections'\n }\n },\n\n /**\n * @param {Number} id\n * @returns {*}\n */\n getLabel: function (id) {\n return this.selected.indexOf(id) !== -1 ? $t('On') : $t('Off');\n },\n\n /**\n * Sets the ids for preselected elements\n * @returns void\n */\n setDefaultSelections: function () {\n var positionCacheValid = registry.get('position_cache_valid'),\n selectedFromCache = registry.get('selected_cache'),\n key,\n i;\n\n if (positionCacheValid && this.selected().length === 0) {\n // Check selected data\n selectedFromCache = JSON.parse(selectedFromCache);\n\n for (i = 0; i < selectedFromCache.length; i++) {\n this.selected.push(selectedFromCache[i]);\n }\n\n registry.set('position_cache_valid', true);\n registry.set('selected_cache', JSON.stringify(this.selected()));\n\n return;\n }\n\n if (positionCacheValid && this.selected().length > 0) {\n registry.set('position_cache_valid', true);\n registry.set('selected_cache', JSON.stringify(this.selected()));\n\n return;\n }\n\n if (this.selectedData.length === 0) {\n registry.set('position_cache_valid', true);\n registry.set('selected_cache', JSON.stringify([]));\n\n return;\n }\n\n // Check selected data\n for (key in this.selectedData) {\n if (this.selectedData.hasOwnProperty(key) && this.selected().indexOf(key) === -1) {\n this.selected.push(key);\n }\n }\n // Uncheck unselected data\n for (i = 0; i < this.selected().length; i++) {\n key = this.selected()[i];\n this.selectedData.hasOwnProperty(key) || this.selected.splice(this.selected().indexOf(key), 1);\n this.selectedData.hasOwnProperty(key) || i--;\n }\n registry.set('position_cache_valid', true);\n registry.set('selected_cache', JSON.stringify(this.selected()));\n },\n\n /**\n * Show/hide action in the massaction menu\n * @param {Number} actionId\n * @returns {Boolean}\n */\n isActionRelevant: function (actionId) {\n var relevant = true;\n\n switch (actionId) {\n case 'selectPage':\n relevant = !this.isPageSelected(true);\n break;\n\n case 'deselectPage':\n relevant = this.isPageSelected();\n break;\n }\n\n return relevant;\n },\n\n /**\n * Updates values of the 'allSelected'\n * and 'indetermine' properties.\n *\n * @returns {Object} Chainable.\n */\n updateState: function () {\n var positionCacheValid = registry.get('position_cache_valid'),\n totalRecords = this.totalRecords(),\n selected = this.selected().length,\n excluded = this.excluded().length,\n totalSelected = this.totalSelected(),\n allSelected;\n\n if (positionCacheValid && this.selected().length > 0) {\n registry.set('position_cache_valid', true);\n registry.set('selected_cache', JSON.stringify(this.selected()));\n }\n\n // When filters are enabled then totalRecords is unknown\n if (this.getFiltering()) {\n if (this.getFiltering().search !== '') {\n totalRecords = -1;\n }\n }\n\n allSelected = totalRecords && totalSelected === totalRecords;\n\n if (this.excludeMode()) {\n if (excluded === totalRecords) {\n this.deselectAll();\n }\n } else if (totalRecords && selected === totalRecords) {\n this.selectAll();\n }\n\n this.allSelected(allSelected);\n this.indetermine(totalSelected && !allSelected);\n\n return this;\n }\n });\n});\n","Magento_Ui/js/grid/columns/multiselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mage/translate',\n './column'\n], function (_, $t, Column) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n headerTmpl: 'ui/grid/columns/multiselect',\n bodyTmpl: 'ui/grid/cells/multiselect',\n controlVisibility: false,\n sortable: false,\n draggable: false,\n menuVisible: false,\n excludeMode: false,\n allSelected: false,\n indetermine: false,\n preserveSelectionsOnFilter: false,\n disabled: [],\n selected: [],\n excluded: [],\n fieldClass: {\n 'data-grid-checkbox-cell': true\n },\n actions: [{\n value: 'selectAll',\n label: $t('Select All')\n }, {\n value: 'deselectAll',\n label: $t('Deselect All')\n }, {\n value: 'selectPage',\n label: $t('Select All on This Page')\n }, {\n value: 'deselectPage',\n label: $t('Deselect All on This Page')\n }],\n\n imports: {\n totalRecords: '${ $.provider }:data.totalRecords',\n rows: '${ $.provider }:data.items'\n },\n\n listens: {\n '${ $.provider }:params.filters': 'onFilter',\n selected: 'onSelectedChange',\n rows: 'onRowsChange'\n },\n\n modules: {\n source: '${ $.provider }'\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Multiselect} Chainable.\n */\n initObservable: function () {\n this._super()\n .observe([\n 'disabled',\n 'selected',\n 'excluded',\n 'excludeMode',\n 'totalSelected',\n 'allSelected',\n 'indetermine',\n 'totalRecords',\n 'rows'\n ]);\n\n return this;\n },\n\n /**\n * Selects specified record.\n *\n * @param {*} id - See definition of 'getId' method.\n * @param {Boolean} [isIndex=false] - See definition of 'getId' method.\n * @returns {Multiselect} Chainable.\n */\n select: function (id, isIndex) {\n this._setSelection(id, isIndex, true);\n\n return this;\n },\n\n /**\n * Deselects specified record.\n *\n * @param {*} id - See definition of 'getId' method.\n * @param {Boolean} [isIndex=false] - See definition of 'getId' method.\n * @returns {Multiselect} Chainable.\n */\n deselect: function (id, isIndex) {\n this._setSelection(id, isIndex, false);\n\n return this;\n },\n\n /**\n * Toggles selection of a specified record.\n *\n * @param {*} id - See definition of 'getId' method.\n * @param {Boolean} [isIndex=false] - See definition of 'getId' method.\n * @returns {Multiselect} Chainable.\n */\n toggleSelect: function (id, isIndex) {\n this._setSelection(id, isIndex, !this.isSelected(id, isIndex));\n\n return this;\n },\n\n /**\n * Checks if specified record is selected.\n *\n * @param {*} id - See definition of 'getId' method.\n * @param {Boolean} [isIndex=false] - See definition of 'getId' method.\n * @returns {Boolean}\n */\n isSelected: function (id, isIndex) {\n id = this.getId(id, isIndex);\n\n return this.selected.contains(id);\n },\n\n /**\n * Selects/deselects specified record base on a 'select' parameter value.\n *\n * @param {*} id - See definition of 'getId' method.\n * @param {Boolean} [isIndex=false] - See definition of 'getId' method.\n * @param {Boolean} select - Whether to select/deselect record.\n * @returns {Multiselect} Chainable.\n */\n _setSelection: function (id, isIndex, select) {\n var selected = this.selected;\n\n id = this.getId(id, isIndex);\n\n if (!select && this.isSelected(id)) {\n selected.remove(id);\n } else if (select) {\n selected.push(id);\n }\n\n return this;\n },\n\n /**\n * Selects all records, even those that\n * are not visible on the page.\n *\n * @returns {Multiselect} Chainable.\n */\n selectAll: function () {\n this.excludeMode(true);\n\n this.clearExcluded()\n .selectPage();\n\n return this;\n },\n\n /**\n * Deselects all records.\n *\n * @returns {Multiselect} Chainable.\n */\n deselectAll: function () {\n this.excludeMode(false);\n\n this.clearExcluded();\n this.selected.removeAll();\n\n return this;\n },\n\n /**\n * Selects or deselects all records.\n *\n * @returns {Multiselect} Chainable.\n */\n toggleSelectAll: function () {\n this.allSelected() ?\n this.deselectAll() :\n this.selectAll();\n\n return this;\n },\n\n /**\n * Selects all records on the current page.\n *\n * @returns {Multiselect} Chainable.\n */\n selectPage: function () {\n var selected = _.union(this.selected(), this.getIds());\n\n selected = _.difference(selected, this.disabled());\n\n this.selected(selected);\n\n return this;\n },\n\n /**\n * Deselects all records on the current page.\n *\n * @returns {Multiselect} Chainable.\n */\n deselectPage: function () {\n var pageIds = this.getIds();\n\n this.selected.remove(function (value) {\n return !!~pageIds.indexOf(value);\n });\n\n return this;\n },\n\n /**\n * Selects or deselects all records on the current page.\n *\n * @returns {Multiselect} Chainable.\n */\n togglePage: function () {\n return this.isPageSelected() ? this.deselectPage() : this.selectPage();\n },\n\n /**\n * Clears the array of not selected records.\n *\n * @returns {Multiselect} Chainable.\n */\n clearExcluded: function () {\n this.excluded.removeAll();\n\n return this;\n },\n\n /**\n * Retrieve all id's from available records.\n *\n * @param {Boolean} [exclude] - Whether to exclude not selected ids' from result.\n * @returns {Array} An array of ids'.\n */\n getIds: function (exclude) {\n var items = this.rows(),\n ids = _.pluck(items, this.indexField);\n\n return exclude ?\n _.difference(ids, this.excluded()) :\n ids;\n },\n\n /**\n * Returns identifier of a record.\n *\n * @param {*} id - Id of a record or its' index in a rows array.\n * @param {Boolean} [isIndex=false] - Flag that specifies with what\n * kind of identifier we are dealing with.\n * @returns {*}\n */\n getId: function (id, isIndex) {\n var record = this.rows()[id];\n\n if (isIndex && record) {\n id = record[this.indexField];\n }\n\n return id;\n },\n\n /**\n * Recalculates list of the excluded records.\n * Changes value of `excluded`.\n *\n * @param {Array} selected - List of the currently selected records.\n * @returns {Multiselect} Chainable.\n */\n updateExcluded: function (selected) {\n var excluded = this.excluded(),\n fromPage = _.difference(this.getIds(), selected);\n\n excluded = _.union(excluded, fromPage);\n excluded = _.difference(excluded, selected);\n\n this.excluded(excluded);\n\n return this;\n },\n\n /**\n * Calculates number of selected records and\n * updates 'totalSelected' property.\n *\n * @returns {Multiselect} Chainable.\n */\n countSelected: function () {\n var total = this.totalRecords(),\n excluded = this.excluded().length,\n selected = this.selected().length;\n\n if (this.excludeMode()) {\n selected = total - excluded;\n }\n\n this.totalSelected(selected);\n\n return this;\n },\n\n /**\n * Returns selected items on a current page.\n *\n * @returns {Array}\n */\n getPageSelections: function () {\n var ids = this.getIds();\n\n return this.selected.filter(function (id) {\n return _.contains(ids, id);\n });\n },\n\n /**\n * Returns selections data.\n *\n * @returns {Object}\n */\n getSelections: function () {\n return {\n excluded: this.excluded(),\n selected: this.selected(),\n total: this.totalSelected(),\n excludeMode: this.excludeMode(),\n params: this.getFiltering()\n };\n },\n\n /**\n * Extracts filtering data from data provider.\n *\n * @returns {Object} Current filters state.\n */\n getFiltering: function () {\n var source = this.source(),\n keys = ['filters', 'search', 'namespace'];\n\n if (!source) {\n return {};\n }\n\n return _.pick(source.get('params'), keys);\n },\n\n /**\n * Defines if provided select/deselect actions is relevant.\n * E.g. there is no need in a 'select page' action if only one\n * page is available.\n *\n * @param {String} actionId - Id of the action to be checked.\n * @returns {Boolean}\n */\n isActionRelevant: function (actionId) {\n var pageIds = this.getIds().length,\n multiplePages = pageIds < this.totalRecords(),\n relevant = true;\n\n switch (actionId) {\n case 'selectPage':\n relevant = multiplePages && !this.isPageSelected(true);\n break;\n\n case 'deselectPage':\n relevant = multiplePages && this.isPageSelected();\n break;\n\n case 'selectAll':\n relevant = !this.allSelected();\n break;\n\n case 'deselectAll':\n relevant = this.totalSelected() > 0;\n }\n\n return relevant;\n },\n\n /**\n * Checks if current page has selected records.\n *\n * @param {Boolean} [all=false] - If set to 'true' checks that every\n * record on the page is selected. Otherwise checks that\n * page has some selected records.\n * @returns {Boolean}\n */\n isPageSelected: function (all) {\n var pageIds = this.getIds(),\n selected = this.selected(),\n excluded = this.excluded(),\n iterator = all ? 'every' : 'some';\n\n if (this.allSelected()) {\n return true;\n }\n\n if (this.excludeMode()) {\n return pageIds[iterator](function (id) {\n return !~excluded.indexOf(id);\n });\n }\n\n return pageIds[iterator](function (id) {\n return !!~selected.indexOf(id);\n });\n },\n\n /**\n * Updates values of the 'allSelected'\n * and 'indetermine' properties.\n *\n * @returns {Multiselect} Chainable.\n */\n updateState: function () {\n var selected = this.selected().length,\n excluded = this.excluded().length,\n totalSelected = this.totalSelected(),\n totalRecords = this.totalRecords(),\n allSelected = totalRecords && totalSelected === totalRecords;\n\n if (this.excludeMode()) {\n if (excluded === totalRecords && !this.preserveSelectionsOnFilter) {\n this.deselectAll();\n }\n } else if (totalRecords && selected === totalRecords && !this.preserveSelectionsOnFilter) {\n this.selectAll();\n }\n\n this.allSelected(allSelected);\n this.indetermine(totalSelected && !allSelected);\n\n return this;\n },\n\n /**\n * Overrides base method, because this component\n * can't have global field action.\n *\n * @returns {Boolean} False.\n */\n hasFieldAction: function () {\n return false;\n },\n\n /**\n * Callback method to handle changes of selected items.\n *\n * @param {Array} selected - An array of currently selected items.\n */\n onSelectedChange: function (selected) {\n this.updateExcluded(selected)\n .countSelected()\n .updateState();\n },\n\n /**\n * Is invoked when rows has changed. Recalculates selected items\n * based on \"selectMode\" property.\n */\n onRowsChange: function () {\n var newSelections;\n\n if (this.excludeMode()) {\n newSelections = _.union(this.getIds(true), this.selected());\n\n this.selected(newSelections);\n }\n },\n\n /**\n * Is invoked when filtration is applied or removed\n */\n onFilter: function () {\n if (!this.preserveSelectionsOnFilter) {\n this.deselectAll();\n }\n }\n });\n});\n","Magento_Ui/js/grid/columns/column.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'uiRegistry',\n 'mageUtils',\n 'uiElement'\n], function (_, registry, utils, Element) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n headerTmpl: 'ui/grid/columns/text',\n bodyTmpl: 'ui/grid/cells/text',\n disableAction: false,\n controlVisibility: true,\n sortable: true,\n sorting: false,\n visible: true,\n draggable: true,\n fieldClass: {},\n ignoreTmpls: {\n fieldAction: true\n },\n statefull: {\n visible: true,\n sorting: true\n },\n imports: {\n exportSorting: 'sorting'\n },\n listens: {\n '${ $.provider }:params.sorting.field': 'onSortChange'\n },\n modules: {\n source: '${ $.provider }'\n }\n },\n\n /**\n * Initializes column component.\n *\n * @returns {Column} Chainable.\n */\n initialize: function () {\n this._super()\n .initFieldClass();\n\n return this;\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Column} Chainable.\n */\n initObservable: function () {\n this._super()\n .track([\n 'visible',\n 'sorting',\n 'disableAction'\n ])\n .observe([\n 'dragging'\n ]);\n\n return this;\n },\n\n /**\n * Extends list of field classes.\n *\n * @returns {Column} Chainable.\n */\n initFieldClass: function () {\n _.extend(this.fieldClass, {\n _dragging: this.dragging\n });\n\n return this;\n },\n\n /**\n * Applies specified stored state of a column or one of its' properties.\n *\n * @param {String} state - Defines what state should be used: saved or default.\n * @param {String} [property] - Defines what columns' property should be applied.\n * If not specified, then all columns stored properties will be used.\n * @returns {Column} Chainable.\n */\n applyState: function (state, property) {\n var namespace = this.storageConfig.root;\n\n if (property) {\n namespace += '.' + property;\n }\n\n this.storage('applyStateOf', state, namespace);\n\n return this;\n },\n\n /**\n * Sets columns' sorting. If column is currently sorted,\n * than its' direction will be toggled.\n *\n * @param {*} [enable=true] - If false, than sorting will\n * be removed from a column.\n * @returns {Column} Chainable.\n */\n sort: function (enable) {\n if (!this.sortable) {\n return this;\n }\n\n enable !== false ?\n this.toggleSorting() :\n this.sorting = false;\n\n return this;\n },\n\n /**\n * Sets descending columns' sorting.\n *\n * @returns {Column} Chainable.\n */\n sortDescending: function () {\n if (this.sortable) {\n this.sorting = 'desc';\n }\n\n return this;\n },\n\n /**\n * Sets ascending columns' sorting.\n *\n * @returns {Column} Chainable.\n */\n sortAscending: function () {\n if (this.sortable) {\n this.sorting = 'asc';\n }\n\n return this;\n },\n\n /**\n * Toggles sorting direction.\n *\n * @returns {Column} Chainable.\n */\n toggleSorting: function () {\n this.sorting === 'asc' ?\n this.sortDescending() :\n this.sortAscending();\n\n return this;\n },\n\n /**\n * Checks if column is sorted.\n *\n * @returns {Boolean}\n */\n isSorted: function () {\n return !!this.sorting;\n },\n\n /**\n * Exports sorting data to the dataProvider if\n * sorting of a column is enabled.\n */\n exportSorting: function () {\n if (!this.sorting) {\n return;\n }\n\n this.source('set', 'params.sorting', {\n field: this.index,\n direction: this.sorting\n });\n },\n\n /**\n * Checks if column has an assigned action that will\n * be performed when clicking on one of its' fields.\n *\n * @returns {Boolean}\n */\n hasFieldAction: function () {\n return !!this.fieldAction || !!this.fieldActions;\n },\n\n /**\n * Applies action described in a 'fieldAction' property\n * or actions described in 'fieldActions' property.\n *\n * @param {Number} rowIndex - Index of a row which initiates action.\n * @returns {Column} Chainable.\n *\n * @example Example of fieldAction definition, which is equivalent to\n * referencing to external component named 'listing.multiselect'\n * and calling its' method 'toggleSelect' with params [rowIndex, true] =>\n *\n * {\n * provider: 'listing.multiselect',\n * target: 'toggleSelect',\n * params: ['${ $.$data.rowIndex }', true]\n * }\n */\n applyFieldAction: function (rowIndex) {\n if (!this.hasFieldAction() || this.disableAction) {\n return this;\n }\n\n if (this.fieldActions) {\n this.fieldActions.forEach(this.applySingleAction.bind(this, rowIndex), this);\n } else {\n this.applySingleAction(rowIndex);\n }\n\n return this;\n },\n\n /**\n * Applies single action\n *\n * @param {Number} rowIndex - Index of a row which initiates action.\n * @param {Object} action - Action (fieldAction) to be applied\n *\n */\n applySingleAction: function (rowIndex, action) {\n var callback;\n\n action = action || this.fieldAction;\n action = utils.template(action, {\n column: this,\n rowIndex: rowIndex\n }, true);\n\n callback = this._getFieldCallback(action);\n\n if (_.isFunction(callback)) {\n callback();\n }\n },\n\n /**\n * Returns field action handler if it was specified.\n *\n * @param {Object} record - Record object with which action is associated.\n * @returns {Function|Undefined}\n */\n getFieldHandler: function (record) {\n if (this.hasFieldAction()) {\n return this.applyFieldAction.bind(this, record._rowIndex);\n }\n },\n\n /**\n * Creates action callback based on its' data.\n *\n * @param {Object} action - Actions' object.\n * @returns {Function|Boolean} Callback function or false\n * value if it was impossible create a callback.\n */\n _getFieldCallback: function (action) {\n var args = action.params || [],\n callback = action.target;\n\n if (action.provider && action.target) {\n args.unshift(action.target);\n\n callback = registry.async(action.provider);\n }\n\n if (!_.isFunction(callback)) {\n return false;\n }\n\n return function () {\n callback.apply(callback, args);\n };\n },\n\n /**\n * Ment to preprocess data associated with a current columns' field.\n *\n * @param {Object} record - Data to be preprocessed.\n * @returns {String}\n */\n getLabel: function (record) {\n return record[this.index];\n },\n\n /**\n * Returns list of classes that should be applied to a field.\n *\n * @returns {Object}\n */\n getFieldClass: function () {\n return this.fieldClass;\n },\n\n /**\n * Returns path to the columns' header template.\n *\n * @returns {String}\n */\n getHeader: function () {\n return this.headerTmpl;\n },\n\n /**\n * Returns path to the columns' body template.\n *\n * @returns {String}\n */\n getBody: function () {\n return this.bodyTmpl;\n },\n\n /**\n * Listener of the providers' sorting state changes.\n *\n * @param {Srting} field - Field by which current sorting is performed.\n */\n onSortChange: function (field) {\n if (field !== this.index) {\n this.sort(false);\n }\n }\n });\n});\n","Magento_Ui/js/grid/columns/expandable.js":"/**\n * Copyright \u00a9 2016 Magento. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n './column',\n 'underscore'\n], function (Column, _) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n bodyTmpl: 'ui/grid/cells/expandable',\n tooltipTmpl: 'ui/grid/cells/expandable/content',\n visibeItemsLimit: 5,\n tooltipTitle: ''\n },\n\n /**\n * Gets label from full options array.\n *\n * @param {Object} record - Record object.\n * @returns {String}\n */\n getFullLabel: function (record) {\n return this.getLabelsArray(record).join(', ');\n },\n\n /**\n * Gets label from options array limited by 'visibeItemsLimit'.\n *\n * @param {Object} record - Record object.\n * @returns {String}\n */\n getShortLabel: function (record) {\n return this.getLabelsArray(record).slice(0, this.visibeItemsLimit).join(', ');\n },\n\n /**\n * Extracts array of labels associated with provided values and sort it alphabetically.\n *\n * @param {Object} record - Record object.\n * @returns {Array}\n */\n getLabelsArray: function (record) {\n var values = this.getLabel(record),\n options = this.options || [],\n labels = [];\n\n if (_.isString(values)) {\n values = values.split(',');\n }\n\n if (!Array.isArray(values)) {\n values = [values];\n }\n\n values = values.map(function (value) {\n return value + '';\n });\n\n options = this.flatOptions(options);\n\n options.forEach(function (item) {\n if (_.contains(values, item.value + '')) {\n labels.push(item.label);\n }\n });\n\n return labels.sort();\n },\n\n /**\n * Transformation tree options structure to liner array.\n *\n * @param {Array} options\n * @returns {Array}\n */\n flatOptions: function (options) {\n var self = this;\n\n return options.reduce(function (opts, option) {\n if (_.isArray(option.value)) {\n opts = opts.concat(self.flatOptions(option.value));\n } else {\n opts.push(option);\n }\n\n return opts;\n }, []);\n },\n\n /**\n * Checks if amount of options is more than limit value.\n *\n * @param {Object} record - Data to be preprocessed.\n * @returns {Boolean}\n */\n isExpandable: function (record) {\n return this.getLabel(record).length > this.visibeItemsLimit;\n }\n });\n});\n","Magento_Ui/js/grid/columns/actions.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n 'mageUtils',\n 'uiRegistry',\n './column',\n 'Magento_Ui/js/modal/confirm',\n 'mage/dataPost'\n], function (_, utils, registry, Column, confirm, dataPost) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n bodyTmpl: 'ui/grid/cells/actions',\n sortable: false,\n draggable: false,\n actions: [],\n rows: [],\n rowsProvider: '${ $.parentName }',\n fieldClass: {\n 'data-grid-actions-cell': true\n },\n templates: {\n actions: {}\n },\n imports: {\n rows: '${ $.rowsProvider }:rows'\n },\n listens: {\n rows: 'updateActions'\n }\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {ActionsColumn} Chainable.\n */\n initObservable: function () {\n this._super()\n .track('actions');\n\n return this;\n },\n\n /**\n * Returns specific action of a specified row\n * or all action objects associated with it.\n *\n * @param {Number} rowIndex - Index of a row.\n * @param {String} [actionIndex] - Action identifier.\n * @returns {Array|Object}\n */\n getAction: function (rowIndex, actionIndex) {\n var rowActions = this.actions[rowIndex];\n\n return rowActions && actionIndex ?\n rowActions[actionIndex] :\n rowActions;\n },\n\n /**\n * Returns visible actions for a specified row.\n *\n * @param {Number} rowIndex - Index of a row.\n * @returns {Array} Visible actions.\n */\n getVisibleActions: function (rowIndex) {\n var rowActions = this.getAction(rowIndex);\n\n return _.filter(rowActions, this.isActionVisible, this);\n },\n\n /**\n * Adds new action. If an action with the specified identifier\n * already exists, then the original will be overridden.\n *\n * @param {String} index - Actions' identifier.\n * @param {Object} action - Actions' data.\n * @returns {ActionsColumn} Chainable.\n */\n addAction: function (index, action) {\n var actionTmpls = this.templates.actions;\n\n actionTmpls[index] = action;\n\n this.updateActions();\n\n return this;\n },\n\n /**\n * Recreates actions for each row.\n *\n * @returns {ActionsColumn} Chainable.\n */\n updateActions: function () {\n this.actions = this.rows.map(this._formatActions, this);\n\n return this;\n },\n\n /**\n * Processes actions, setting additional information to them and\n * evaluating their properties as string templates.\n *\n * @private\n * @param {Object} row - Row object.\n * @param {Number} rowIndex - Index of a row.\n * @returns {Array}\n */\n _formatActions: function (row, rowIndex) {\n var rowActions = row[this.index] || {},\n recordId = row[this.indexField],\n customActions = this.templates.actions;\n\n /**\n * Actions iterator.\n */\n function iterate(action, index) {\n action = utils.extend({\n index: index,\n rowIndex: rowIndex,\n recordId: recordId\n }, action);\n\n return utils.template(action, row, true);\n }\n\n rowActions = _.mapObject(rowActions, iterate);\n customActions = _.map(customActions, iterate);\n\n customActions.forEach(function (action) {\n rowActions[action.index] = action;\n });\n\n return rowActions;\n },\n\n /**\n * Applies specified action.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @param {Number} rowIndex - Index of a row.\n * @returns {ActionsColumn} Chainable.\n */\n applyAction: function (actionIndex, rowIndex) {\n var action = this.getAction(rowIndex, actionIndex),\n callback = this._getCallback(action);\n\n action.confirm ?\n this._confirm(action, callback) :\n callback();\n\n return this;\n },\n\n /**\n * Creates handler for the provided action if it's required.\n *\n * @param {Object} action - Action object.\n * @returns {Function|Undefined}\n */\n getActionHandler: function (action) {\n var index = action.index,\n rowIndex = action.rowIndex;\n\n if (this.isHandlerRequired(index, rowIndex)) {\n return this.applyAction.bind(this, index, rowIndex);\n }\n },\n\n /**\n * Returns target of action if it's been set.\n *\n * @param {Object} action - Action object.\n * @returns {String}\n */\n getTarget: function (action) {\n if (action.target) {\n return action.target;\n }\n\n return '_self';\n },\n\n /**\n * Checks if specified action requires a handler function.\n *\n * @param {String} actionIndex - Actions' identifier.\n * @param {Number} rowIndex - Index of a row.\n * @returns {Boolean}\n */\n isHandlerRequired: function (actionIndex, rowIndex) {\n var action = this.getAction(rowIndex, actionIndex);\n\n return _.isObject(action.callback) || action.confirm || !action.href;\n },\n\n /**\n * Creates action callback based on it's data. If the action doesn't specify\n * a callback function than the default one will be used.\n *\n * @private\n * @param {Object} action - Action's object.\n * @returns {Function} Callback function.\n */\n _getCallback: function (action) {\n var args = [action.index, action.recordId, action],\n callback = action.callback;\n\n if (utils.isObject(callback)) {\n args.unshift(callback.target);\n\n callback = registry.async(callback.provider);\n } else if (_.isArray(callback)) {\n return this._getCallbacks(action);\n } else if (!_.isFunction(callback)) {\n callback = this.defaultCallback.bind(this);\n }\n\n return function () {\n callback.apply(callback, args);\n };\n },\n\n /**\n * Creates action callback for multiple actions.\n *\n * @private\n * @param {Object} action - Action's object.\n * @returns {Function} Callback function.\n */\n _getCallbacks: function (action) {\n var callback = action.callback,\n callbacks = [],\n tmpCallback;\n\n _.each(callback, function (cb) {\n tmpCallback = {\n action: registry.async(cb.provider),\n args: _.compact([cb.target, cb.params])\n };\n callbacks.push(tmpCallback);\n });\n\n return function () {\n _.each(callbacks, function (cb) {\n cb.action.apply(cb.action, cb.args);\n });\n };\n },\n\n /**\n * Default action callback. Redirects to\n * the specified in action's data url.\n *\n * @param {String} actionIndex - Action's identifier.\n * @param {(Number|String)} recordId - Id of the record associated\n * with a specified action.\n * @param {Object} action - Action's data.\n */\n defaultCallback: function (actionIndex, recordId, action) {\n if (action.post) {\n dataPost().postData({\n action: action.href,\n data: {}\n });\n } else {\n window.location.href = action.href;\n }\n },\n\n /**\n * Shows actions' confirmation window.\n *\n * @param {Object} action - Action's data.\n * @param {Function} callback - Callback that will be\n * invoked if action is confirmed.\n */\n _confirm: function (action, callback) {\n var confirmData = action.confirm;\n\n confirm({\n title: confirmData.title,\n content: confirmData.message,\n actions: {\n confirm: callback\n }\n });\n },\n\n /**\n * Checks if row has only one visible action.\n *\n * @param {Number} rowIndex - Row index.\n * @returns {Boolean}\n */\n isSingle: function (rowIndex) {\n return this.getVisibleActions(rowIndex).length === 1;\n },\n\n /**\n * Checks if row has more than one visible action.\n *\n * @param {Number} rowIndex - Row index.\n * @returns {Boolean}\n */\n isMultiple: function (rowIndex) {\n return this.getVisibleActions(rowIndex).length > 1;\n },\n\n /**\n * Checks if action should be displayed.\n *\n * @param {Object} action - Action object.\n * @returns {Boolean}\n */\n isActionVisible: function (action) {\n return action.hidden !== true;\n },\n\n /**\n * Overrides base method, because this component\n * can't have global field action.\n *\n * @returns {Boolean} False.\n */\n hasFieldAction: function () {\n return false;\n }\n });\n});\n","Magento_Ui/js/grid/columns/date.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'mageUtils',\n 'moment',\n './column'\n], function (utils, moment, Column) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n dateFormat: 'MMM d, YYYY h:mm:ss A'\n },\n\n /**\n * Overrides base method to normalize date format.\n *\n * @returns {DateColumn} Chainable.\n */\n initConfig: function () {\n this._super();\n\n this.dateFormat = utils.normalizeDate(this.dateFormat);\n\n return this;\n },\n\n /**\n * Formats incoming date based on the 'dateFormat' property.\n *\n * @returns {String} Formatted date.\n */\n getLabel: function (value, format) {\n var date = moment(this._super());\n\n date = date.isValid() && value[this.index] ?\n date.format(format || this.dateFormat) :\n '';\n\n return date;\n }\n });\n});\n","Magento_Ui/js/grid/columns/thumbnail.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n './column',\n 'jquery',\n 'mage/template',\n 'text!Magento_Ui/templates/grid/cells/thumbnail/preview.html',\n 'underscore',\n 'Magento_Ui/js/modal/modal',\n 'mage/translate'\n], function (Column, $, mageTemplate, thumbnailPreviewTemplate, _) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n bodyTmpl: 'ui/grid/cells/thumbnail',\n fieldClass: {\n 'data-grid-thumbnail-cell': true\n }\n },\n\n /**\n * Get image source data per row.\n *\n * @param {Object} row\n * @returns {String}\n */\n getSrc: function (row) {\n return row[this.index + '_src'];\n },\n\n /**\n * Get original image source data per row.\n *\n * @param {Object} row\n * @returns {String}\n */\n getOrigSrc: function (row) {\n return row[this.index + '_orig_src'];\n },\n\n /**\n * Get link data per row.\n *\n * @param {Object} row\n * @returns {String}\n */\n getLink: function (row) {\n return row[this.index + '_link'];\n },\n\n /**\n * Get alternative text data per row.\n *\n * @param {Object} row\n * @returns {String}\n */\n getAlt: function (row) {\n return _.escape(row[this.index + '_alt']);\n },\n\n /**\n * Check if preview available.\n *\n * @returns {Boolean}\n */\n isPreviewAvailable: function () {\n return this['has_preview'] || false;\n },\n\n /**\n * Build preview.\n *\n * @param {Object} row\n */\n preview: function (row) {\n var modalHtml = mageTemplate(\n thumbnailPreviewTemplate,\n {\n src: this.getOrigSrc(row), alt: this.getAlt(row), link: this.getLink(row),\n linkText: $.mage.__('Go to Details Page')\n }\n ),\n previewPopup = $('<div/>').html(modalHtml);\n\n previewPopup.modal({\n title: this.getAlt(row),\n innerScroll: true,\n modalClass: '_image-box',\n buttons: []\n }).trigger('openModal');\n },\n\n /**\n * Get field handler per row.\n *\n * @param {Object} row\n * @returns {Function}\n */\n getFieldHandler: function (row) {\n if (this.isPreviewAvailable()) {\n return this.preview.bind(this, row);\n }\n }\n });\n});\n","Magento_Ui/js/grid/columns/link.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n './column',\n 'mageUtils'\n], function (Column, utils) {\n 'use strict';\n\n return Column.extend({\n defaults: {\n link: 'link',\n bodyTmpl: 'ui/grid/cells/link'\n },\n\n /**\n * Returns link to given record.\n *\n * @param {Object} record - Data to be preprocessed.\n * @returns {String}\n */\n getLink: function (record) {\n return utils.nested(record, this.link);\n },\n\n /**\n * Check if link parameter exist in record.\n * @param {Object} record - Data to be preprocessed.\n * @returns {Boolean}\n */\n isLink: function (record) {\n return !!utils.nested(record, this.link);\n }\n });\n});\n","Magento_Ui/js/grid/columns/select.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'underscore',\n './column'\n], function (_, Column) {\n 'use strict';\n\n return Column.extend({\n /**\n * Retrieves label associated with a provided value.\n *\n * @returns {String}\n */\n getLabel: function () {\n var options = this.options || [],\n values = this._super(),\n label = [];\n\n if (_.isString(values)) {\n values = values.split(',');\n }\n\n if (!_.isArray(values)) {\n values = [values];\n }\n\n values = values.map(function (value) {\n return value + '';\n });\n\n options = this.flatOptions(options);\n\n options.forEach(function (item) {\n if (_.contains(values, item.value + '')) {\n label.push(item.label);\n }\n });\n\n return label.join(', ');\n },\n\n /**\n * Transformation tree options structure to liner array.\n *\n * @param {Array} options\n * @returns {Array}\n */\n flatOptions: function (options) {\n var self = this;\n\n if (!_.isArray(options)) {\n options = _.values(options);\n }\n\n return options.reduce(function (opts, option) {\n if (_.isArray(option.value)) {\n opts = opts.concat(self.flatOptions(option.value));\n } else {\n opts.push(option);\n }\n\n return opts;\n }, []);\n }\n });\n});\n","Magento_Ui/js/grid/sticky/sticky.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/lib/view/utils/async',\n 'underscore',\n 'uiComponent',\n 'Magento_Ui/js/lib/view/utils/raf'\n], function ($, _, Component, raf) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n listingSelector: '${ $.listingProvider }::not([data-role = \"sticky-el-root\"])',\n toolbarSelector: '${ $.toolbarProvider }::not([data-role = \"sticky-el-root\"])',\n bulkRowSelector: '[data-role = \"data-grid-bulk-row\"]',\n bulkRowHeaderSelector: '.data-grid-info-panel:visible',\n tableSelector: 'table',\n columnSelector: 'thead tr th',\n rowSelector: 'tbody tr',\n toolbarCollapsiblesSelector: '[data-role=\"toolbar-menu-item\"]',\n toolbarCollapsiblesActiveClass: '_active',\n template: 'ui/grid/sticky/sticky',\n stickyContainerSelector: '.sticky-header',\n stickyElementSelector: '[data-role = \"sticky-el-root\"]',\n leftDataGridCapSelector: '.data-grid-cap-left',\n rightDataGridCapSelector: '.data-grid-cap-right',\n visible: false,\n enableToolbar: true,\n enableHeader: true,\n modules: {\n toolbar: '${ $.toolbarProvider }',\n listing: '${ $.listingProvider }'\n },\n otherStickyElsSize: 77,\n containerNode: null,\n listingNode: null,\n toolbarNode: null,\n stickyListingNode: null,\n stickyToolbarNode: null,\n storedOriginalToolbarElements: [],\n cache: {},\n flags: {},\n dirtyFlag: 'dirty'\n },\n\n /**\n * Initializes Sticky component.\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n this._super();\n _.bindAll(this,\n 'adjustStickyElems',\n 'initListingNode',\n 'initToolbarNode',\n 'initContainerNode',\n 'initColumns',\n 'initStickyListingNode',\n 'initStickyToolbarNode',\n 'initLeftDataGridCap',\n 'initRightDataGridCap'\n );\n\n $.async(this.listingSelector,\n this.initListingNode);\n $.async(this.toolbarSelector,\n this.initToolbarNode);\n\n $.async(this.stickyContainerSelector,\n this,\n this.initContainerNode);\n\n return this;\n },\n\n /**\n * Init observables\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super()\n .track('visible');\n\n return this;\n },\n\n /**\n * Init original listing node\n *\n * @param {HTMLElement} node\n */\n initListingNode: function (node) {\n if ($(node).is(this.stickyElementSelector)) {\n return;\n }\n this.listingNode = $(node);\n $.async(this.columnSelector, node, this.initColumns);\n },\n\n /**\n * Init original toolbar node\n *\n * @param {HTMLElement} node\n */\n initToolbarNode: function (node) {\n if ($(node).is(this.stickyElementSelector)) {\n return;\n }\n this.toolbarNode = $(node);\n },\n\n /**\n * Init sticky listing node\n *\n * @param {HTMLElement} node\n */\n initStickyListingNode: function (node) {\n this.stickyListingNode = $(node);\n this.checkPos();\n this.initListeners();\n },\n\n /**\n * Init sticky toolbar node\n *\n * @param {HTMLElement} node\n */\n initStickyToolbarNode: function (node) {\n this.stickyToolbarNode = $(node);\n },\n\n /**\n * Init sticky header container node\n *\n * @param {HTMLElement} node\n */\n initContainerNode: function (node) {\n this.containerNode = $(node);\n\n $.async(this.leftDataGridCapSelector,\n node,\n this.initLeftDataGridCap);\n $.async(this.rightDataGridCapSelector,\n node,\n this.initRightDataGridCap);\n\n $.async(this.stickyElementSelector,\n this.listing(),\n this.initStickyListingNode);\n $.async(this.stickyElementSelector,\n this.toolbar(),\n this.initStickyToolbarNode);\n },\n\n /**\n * Init columns (each time when amount of columns is changed)\n *\n */\n initColumns: function () {\n this.columns = this.listingNode.find(this.columnSelector);\n },\n\n /**\n * Init left DataGridCap\n *\n * @param {HTMLElement} node\n */\n initLeftDataGridCap: function (node) {\n this.leftDataGridCap = $(node);\n },\n\n /**\n * Init right DataGridCap\n *\n * @param {HTMLElement} node\n */\n initRightDataGridCap: function (node) {\n this.rightDataGridCap = $(node);\n },\n\n /**\n * Init listeners\n *\n * @returns {Object} Chainable.\n */\n initListeners: function () {\n this.adjustStickyElems();\n this.initOnResize()\n .initOnScroll()\n .initOnListingScroll();\n\n return this;\n },\n\n /**\n * Start to listen to window scroll event\n *\n * @returns {Object} Chainable.\n */\n initOnScroll: function () {\n this.lastHorizontalScrollPos = $(window).scrollLeft();\n document.addEventListener('scroll', function () {\n this.flags.scrolled = true;\n }.bind(this));\n\n return this;\n },\n\n /**\n * Start to listen to original listing scroll event\n *\n * @returns {Object} Chainable.\n */\n initOnListingScroll: function () {\n $(this.listingNode).scroll(function (e) {\n this.flags.listingScrolled = true;\n this.flags.listingScrolledValue = $(e.target).scrollLeft();\n }.bind(this));\n\n return this;\n },\n\n /**\n * Start to listen to window resize event\n *\n * @returns {Object} Chainable.\n */\n initOnResize: function () {\n $(window).resize(function () {\n this.flags.resized = true;\n }.bind(this));\n\n return this;\n },\n\n /**\n * Adjust sticky header elements according to flags of the events that have happened in the endless RAF loop\n */\n adjustStickyElems: function () {\n if (this.flags.resized ||\n this.flags.scrolled) {\n this.checkPos();\n }\n\n if (this.visible) {\n this.checkTableElemsWidth();\n\n if (this.flags.originalWidthChanged) {\n this.adjustContainerElemsWidth();\n }\n\n if (this.flags.resized) {\n this.onResize();\n }\n\n if (this.flags.scrolled) {\n this.onWindowScroll();\n }\n\n if (this.flags.listingScrolled) {\n this.onListingScroll(this.flags.listingScrolledValue);\n }\n }\n _.each(this.flags, function (val, key) {\n if (val === this.dirtyFlag) {\n this.flags[key] = false;\n } else if (val) {\n this.flags[key] = this.dirtyFlag;\n }\n }, this);\n\n raf(this.adjustStickyElems);\n },\n\n /**\n * Handles window scroll\n */\n onWindowScroll: function () {\n var scrolled = $(window).scrollLeft(),\n horizontal = this.lastHorizontalScrollPos !== scrolled;\n\n if (horizontal) {\n this.adjustOffset()\n .adjustDataGridCapPositions();\n this.lastHorizontalScrollPos = scrolled;\n } else {\n this.checkPos();\n }\n },\n\n /**\n * Handles original listing scroll\n *\n * @param {Number} scrolled\n */\n onListingScroll: function (scrolled) {\n this.adjustOffset(scrolled);\n },\n\n /**\n * Handles window resize\n */\n onResize: function () {\n this.checkPos();\n this.adjustContainerElemsWidth()\n .adjustDataGridCapPositions();\n },\n\n /**\n * Check if original table or columns change it dimensions and sets appropriate flag\n */\n checkTableElemsWidth: function () {\n var newWidth = this.getTableWidth();\n\n if (this.cache.tableWidth !== newWidth) {\n this.cache.tableWidth = newWidth;\n this.flags.originalWidthChanged = true;\n } else if (this.cache.colChecksum !== this.getColsChecksum()) {\n this.cache.colChecksum = this.getColsChecksum();\n this.flags.originalWidthChanged = true;\n }\n },\n\n /**\n * Get the checksum of original columns width\n *\n * @returns {Number}.\n */\n getColsChecksum: function () {\n return _.reduce(this.columns,\n function (pv, cv) {\n return ($(pv).width() || pv) + '' + $(cv).width();\n });\n },\n\n /**\n * Get the width of the sticky table wrapper\n *\n * @returns {Number}.\n */\n getListingWidth: function () {\n return this.listingNode.width();\n },\n\n /**\n * Get the width of the original table\n *\n * @returns {Number}.\n */\n getTableWidth: function () {\n return this.listingNode.find(this.tableSelector).width();\n },\n\n /**\n * Get the top elem: header or toolbar\n *\n * @returns {HTMLElement}.\n */\n getTopElement: function () {\n return this.toolbarNode || this.listingNode;\n },\n\n /**\n * Get the height of the other sticky elem (Page header)\n *\n * @returns {Number}.\n */\n getOtherStickyElementsSize: function () {\n return this.otherStickyElsSize;\n },\n\n /**\n * Get original bulk row height, if is visible\n *\n * @returns {Number}.\n */\n getBulkRowHeight: function () {\n return this.listingNode.find(this.bulkRowSelector).filter(':visible').height();\n },\n\n /**\n * Get top Y coord of the sticky header\n *\n * @returns {Number}.\n */\n getListingTopYCoord: function () {\n var bulkRowHeight = this.getBulkRowHeight();\n\n return this.listingNode.find('tbody').offset().top -\n this.containerNode.height() -\n $(window).scrollTop() +\n bulkRowHeight;\n },\n\n /**\n * Check if sticky header must be visible\n *\n * @returns {Boolean}.\n */\n getMustBeSticky: function () {\n var stickyTopCondition = this.getListingTopYCoord() - this.getOtherStickyElementsSize(),\n stickyBottomCondition = this.listingNode.offset().top +\n this.listingNode.height() -\n $(window).scrollTop() +\n this.getBulkRowHeight() -\n this.getOtherStickyElementsSize();\n\n return stickyTopCondition < 0 && stickyBottomCondition > 0;\n },\n\n /**\n * Resize sticky header and cols\n *\n * @returns {Object} Chainable.\n */\n adjustContainerElemsWidth: function () {\n this.resizeContainer()\n .resizeCols()\n .resizeBulk();\n\n return this;\n },\n\n /**\n * Resize sticky header\n *\n * @returns {Object} Chainable.\n */\n resizeContainer: function () {\n var listingWidth = this.getListingWidth();\n\n this.stickyListingNode.innerWidth(listingWidth);\n this.stickyListingNode.find(this.tableSelector).innerWidth(this.getTableWidth());\n\n if (this.stickyToolbarNode) {\n this.stickyToolbarNode.innerWidth(listingWidth);\n }\n\n return this;\n },\n\n /**\n * Resize sticky cols\n *\n * @returns {Object} Chainable.\n */\n resizeCols: function () {\n var cols = this.listingNode.find(this.columnSelector);\n\n this.stickyListingNode.find(this.columnSelector).each(function (ind) {\n var originalColWidth = $(cols[ind]).width();\n\n $(this).width(originalColWidth);\n });\n\n return this;\n },\n\n /**\n * Resize bulk row header\n *\n * @returns {Object} Chainable.\n */\n resizeBulk: function () {\n var bulk = this.containerNode.find(this.bulkRowHeaderSelector)[0];\n\n if (bulk) {\n $(bulk).innerWidth(this.getListingWidth());\n }\n\n return this;\n },\n\n /**\n * Reset viewport to the top of listing\n */\n resetToTop: function () {\n var posOfTopEl = this.getTopElement().offset().top - this.getOtherStickyElementsSize() || 0;\n\n $(window).scrollTop(posOfTopEl);\n },\n\n /**\n * Adjust sticky header offset\n *\n * @param {Number} val\n * @returns {Object} Chainable.\n */\n adjustOffset: function (val) {\n val = val || this.listingNode.scrollLeft();\n this.stickyListingNode.offset({\n left: this.listingNode.offset().left - val\n });\n\n return this;\n },\n\n /**\n * Adjust both DataGridCap position\n *\n * @returns {Object} Chainable.\n */\n adjustDataGridCapPositions: function () {\n this.adjustLeftDataGridCapPos()\n .adjustRightDataGridCapPos();\n\n return this;\n },\n\n /**\n * Adjust left DataGridCap position\n *\n * @returns {Object} Chainable.\n */\n adjustLeftDataGridCapPos: function () {\n this.leftDataGridCap.offset({\n left: this.listingNode.offset().left - this.leftDataGridCap.width()\n });\n\n return this;\n },\n\n /**\n * Adjust right DataGridCap position\n *\n * @returns {Object} Chainable.\n */\n adjustRightDataGridCapPos: function () {\n this.rightDataGridCap.offset({\n left: this.listingNode.offset().left + this.listingNode.width()\n });\n\n return this;\n },\n\n /**\n * Hides the oiginal toolbar opened dropdowns/collapsibles etc\n */\n collapseOriginalElements: function () {\n this.toolbarNode\n .find(this.toolbarCollapsiblesSelector)\n .css('visibility', 'hidden');\n $(this.listingNode.find(this.bulkRowSelector)[0]).css('visibility', 'hidden');\n },\n\n /**\n * Restores the oiginal toolbar opened dropdowns/collapsibles etc\n */\n restoreOriginalElements: function () {\n this.toolbarNode\n .find(this.toolbarCollapsiblesSelector)\n .css('visibility', 'visible');\n $(this.listingNode.find(this.bulkRowSelector)[0]).css('visibility', 'visible');\n },\n\n /**\n * Toggle the visibility of sticky header\n *\n * @returns {Object} Chainable.\n */\n toggleContainerVisibility: function () {\n this.visible = !this.visible;\n\n return this;\n },\n\n /**\n * Checks position of the listing to know if need to show/hide sticky header\n *\n * @returns {Boolean} whether the visibility of the sticky header was toggled.\n */\n checkPos: function () {\n var isSticky = this.visible,\n mustBeSticky = this.getMustBeSticky(),\n needChange = isSticky !== mustBeSticky;\n\n if (needChange) {\n if (mustBeSticky) {\n this.collapseOriginalElements();\n this.toggleContainerVisibility();\n this.adjustContainerElemsWidth()\n .adjustOffset()\n .adjustDataGridCapPositions();\n\n } else {\n this.toggleContainerVisibility();\n this.restoreOriginalElements();\n }\n }\n\n return needChange;\n }\n });\n});\n","Magento_Ui/js/modal/alert.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'jquery/ui',\n 'Magento_Ui/js/modal/confirm',\n 'mage/translate'\n], function ($, _) {\n 'use strict';\n\n $.widget('mage.alert', $.mage.confirm, {\n options: {\n modalClass: 'confirm',\n title: $.mage.__('Attention'),\n actions: {\n\n /**\n * Callback always - called on all actions.\n */\n always: function () {}\n },\n buttons: [{\n text: $.mage.__('OK'),\n class: 'action-primary action-accept',\n\n /**\n * Click handler.\n */\n click: function () {\n this.closeModal(true);\n }\n }]\n },\n\n /**\n * Close modal window.\n */\n closeModal: function () {\n this.options.actions.always();\n this.element.bind('alertclosed', _.bind(this._remove, this));\n\n return this._super();\n }\n });\n\n return function (config) {\n return $('<div></div>').html(config.content).alert(config);\n };\n});\n","Magento_Ui/js/modal/modal.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mage/template',\n 'text!ui/template/modal/modal-popup.html',\n 'text!ui/template/modal/modal-slide.html',\n 'text!ui/template/modal/modal-custom.html',\n 'Magento_Ui/js/lib/key-codes',\n 'jquery/ui',\n 'mage/translate'\n], function ($, _, template, popupTpl, slideTpl, customTpl, keyCodes) {\n 'use strict';\n\n /**\n * Detect browser transition end event.\n * @return {String|undefined} - transition event.\n */\n var transitionEvent = (function () {\n var transition,\n elementStyle = document.createElement('div').style,\n transitions = {\n 'transition': 'transitionend',\n 'OTransition': 'oTransitionEnd',\n 'MozTransition': 'transitionend',\n 'WebkitTransition': 'webkitTransitionEnd'\n };\n\n for (transition in transitions) {\n if (elementStyle[transition] !== undefined && transitions.hasOwnProperty(transition)) {\n return transitions[transition];\n }\n }\n })();\n\n /**\n * Modal Window Widget\n */\n $.widget('mage.modal', {\n options: {\n id: null,\n type: 'popup',\n title: '',\n subTitle: '',\n modalClass: '',\n focus: '[data-role=\"closeBtn\"]',\n autoOpen: false,\n clickableOverlay: true,\n popupTpl: popupTpl,\n slideTpl: slideTpl,\n customTpl: customTpl,\n modalVisibleClass: '_show',\n parentModalClass: '_has-modal',\n innerScrollClass: '_inner-scroll',\n responsive: false,\n innerScroll: false,\n modalTitle: '[data-role=\"title\"]',\n modalSubTitle: '[data-role=\"subTitle\"]',\n modalBlock: '[data-role=\"modal\"]',\n modalCloseBtn: '[data-role=\"closeBtn\"]',\n modalContent: '[data-role=\"content\"]',\n modalAction: '[data-role=\"action\"]',\n focusableScope: '[data-role=\"focusable-scope\"]',\n focusableStart: '[data-role=\"focusable-start\"]',\n focusableEnd: '[data-role=\"focusable-end\"]',\n appendTo: 'body',\n wrapperClass: 'modals-wrapper',\n overlayClass: 'modals-overlay',\n responsiveClass: 'modal-slide',\n trigger: '',\n modalLeftMargin: 45,\n closeText: $.mage.__('Close'),\n buttons: [{\n text: $.mage.__('Ok'),\n class: '',\n attr: {},\n\n /**\n * Default action on button click\n */\n click: function (event) {\n this.closeModal(event);\n }\n }],\n keyEventHandlers: {\n\n /**\n * Tab key press handler,\n * set focus to elements\n */\n tabKey: function () {\n if (document.activeElement === this.modal[0]) {\n this._setFocus('start');\n }\n },\n\n /**\n * Escape key press handler,\n * close modal window\n * @param {Object} event - event\n */\n escapeKey: function (event) {\n if (this.options.isOpen && this.modal.find(document.activeElement).length ||\n this.options.isOpen && this.modal[0] === document.activeElement) {\n this.closeModal(event);\n }\n }\n }\n },\n\n /**\n * Creates modal widget.\n */\n _create: function () {\n _.bindAll(\n this,\n 'keyEventSwitcher',\n '_tabSwitcher',\n 'closeModal'\n );\n\n this.options.id = this.uuid;\n this.options.transitionEvent = transitionEvent;\n this._createWrapper();\n this._renderModal();\n this._createButtons();\n $(this.options.trigger).on('click', _.bind(this.toggleModal, this));\n this._on(this.modal.find(this.options.modalCloseBtn), {\n 'click': this.options.modalCloseBtnHandler ? this.options.modalCloseBtnHandler : this.closeModal\n });\n this._on(this.element, {\n 'openModal': this.openModal,\n 'closeModal': this.closeModal\n });\n this.options.autoOpen ? this.openModal() : false;\n },\n\n /**\n * Returns element from modal node.\n * @return {Object} - element.\n */\n _getElem: function (elem) {\n return this.modal.find(elem);\n },\n\n /**\n * Gets visible modal count.\n * * @return {Number} - visible modal count.\n */\n _getVisibleCount: function () {\n var modals = this.modalWrapper.find(this.options.modalBlock);\n\n return modals.filter('.' + this.options.modalVisibleClass).length;\n },\n\n /**\n * Gets count of visible modal by slide type.\n * * @return {Number} - visible modal count.\n */\n _getVisibleSlideCount: function () {\n var elems = this.modalWrapper.find('[data-type=\"slide\"]');\n\n return elems.filter('.' + this.options.modalVisibleClass).length;\n },\n\n /**\n * Listener key events.\n * Call handler function if it exists\n */\n keyEventSwitcher: function (event) {\n var key = keyCodes[event.keyCode];\n\n if (this.options.keyEventHandlers.hasOwnProperty(key)) {\n this.options.keyEventHandlers[key].apply(this, arguments);\n }\n },\n\n /**\n * Set title for modal.\n *\n * @param {String} title\n */\n setTitle: function (title) {\n var $title = $(this.options.modalTitle),\n $subTitle = this.modal.find(this.options.modalSubTitle);\n\n $title.text(title);\n $title.append($subTitle);\n },\n\n /**\n * Set sub title for modal.\n *\n * @param {String} subTitle\n */\n setSubTitle: function (subTitle) {\n this.options.subTitle = subTitle;\n this.modal.find(this.options.modalSubTitle).html(subTitle);\n },\n\n /**\n * Toggle modal.\n * * @return {Element} - current element.\n */\n toggleModal: function () {\n if (this.options.isOpen === true) {\n this.closeModal();\n } else {\n this.openModal();\n }\n },\n\n /**\n * Open modal.\n * * @return {Element} - current element.\n */\n openModal: function () {\n this.options.isOpen = true;\n this.focussedElement = document.activeElement;\n this._createOverlay();\n this._setActive();\n this._setKeyListener();\n this.modal.one(this.options.transitionEvent, _.bind(this._setFocus, this, 'end', 'opened'));\n this.modal.one(this.options.transitionEvent, _.bind(this._trigger, this, 'opened'));\n this.modal.addClass(this.options.modalVisibleClass);\n\n if (!this.options.transitionEvent) {\n this._trigger('opened');\n }\n\n return this.element;\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 * @param {String} type - can be \"opened\" or false\n * If type is \"opened\" - looks to \"this.options.focus\"\n * property and sets focus\n */\n _setFocus: function (position, type) {\n var focusableElements,\n infelicity;\n\n if (type === 'opened' && this.options.focus) {\n this.modal.find($(this.options.focus)).focus();\n } else if (type === 'opened' && !this.options.focus) {\n this.modal.find(this.options.focusableScope).focus();\n } else if (position === 'end') {\n this.modal.find(this.options.modalCloseBtn).focus();\n } else if (position === 'start') {\n infelicity = 2; //Constant for find last focusable element\n focusableElements = this.modal.find(':focusable');\n focusableElements.eq(focusableElements.length - infelicity).focus();\n }\n },\n\n /**\n * Set events listener when modal is opened.\n */\n _setKeyListener: function () {\n this.modal.find(this.options.focusableStart).bind('focusin', this._tabSwitcher);\n this.modal.find(this.options.focusableEnd).bind('focusin', this._tabSwitcher);\n this.modal.bind('keydown', this.keyEventSwitcher);\n },\n\n /**\n * Remove events listener when modal is closed.\n */\n _removeKeyListener: function () {\n this.modal.find(this.options.focusableStart).unbind('focusin', this._tabSwitcher);\n this.modal.find(this.options.focusableEnd).unbind('focusin', this._tabSwitcher);\n this.modal.unbind('keydown', this.keyEventSwitcher);\n },\n\n /**\n * Switcher for focus event.\n * @param {Object} e - event\n */\n _tabSwitcher: function (e) {\n var target = $(e.target);\n\n if (target.is(this.options.focusableStart)) {\n this._setFocus('start');\n } else if (target.is(this.options.focusableEnd)) {\n this._setFocus('end');\n }\n },\n\n /**\n * Close modal.\n * * @return {Element} - current element.\n */\n closeModal: function () {\n var that = this;\n\n this._removeKeyListener();\n this.options.isOpen = false;\n this.modal.one(this.options.transitionEvent, function () {\n that._close();\n });\n this.modal.removeClass(this.options.modalVisibleClass);\n\n if (!this.options.transitionEvent) {\n that._close();\n }\n\n return this.element;\n },\n\n /**\n * Helper for closeModal function.\n */\n _close: function () {\n var trigger = _.bind(this._trigger, this, 'closed', this.modal);\n\n $(this.focussedElement).focus();\n this._destroyOverlay();\n this._unsetActive();\n _.defer(trigger, this);\n },\n\n /**\n * Set z-index and margin for modal and overlay.\n */\n _setActive: function () {\n var zIndex = this.modal.zIndex(),\n baseIndex = zIndex + this._getVisibleCount();\n\n if (this.modal.data('active')) {\n return;\n }\n\n this.modal.data('active', true);\n\n this.overlay.zIndex(++baseIndex);\n this.prevOverlayIndex = this.overlay.zIndex();\n this.modal.zIndex(this.overlay.zIndex() + 1);\n\n if (this._getVisibleSlideCount()) {\n this.modal.css('marginLeft', this.options.modalLeftMargin * this._getVisibleSlideCount());\n }\n },\n\n /**\n * Unset styles for modal and set z-index for previous modal.\n */\n _unsetActive: function () {\n this.modal.removeAttr('style');\n this.modal.data('active', false);\n\n if (this.overlay) {\n this.overlay.zIndex(this.prevOverlayIndex - 1);\n }\n },\n\n /**\n * Creates wrapper to hold all modals.\n */\n _createWrapper: function () {\n this.modalWrapper = $(this.options.appendTo).find('.' + this.options.wrapperClass);\n\n if (!this.modalWrapper.length) {\n this.modalWrapper = $('<div></div>')\n .addClass(this.options.wrapperClass)\n .appendTo(this.options.appendTo);\n }\n },\n\n /**\n * Compile template and append to wrapper.\n */\n _renderModal: function () {\n $(template(\n this.options[this.options.type + 'Tpl'],\n {\n data: this.options\n })).appendTo(this.modalWrapper);\n this.modal = this.modalWrapper.find(this.options.modalBlock).last();\n this.element.appendTo(this._getElem(this.options.modalContent));\n\n if (this.element.is(':hidden')) {\n this.element.show();\n }\n },\n\n /**\n * Creates buttons pane.\n */\n _createButtons: function () {\n this.buttons = this._getElem(this.options.modalAction);\n _.each(this.options.buttons, function (btn, key) {\n var button = this.buttons[key];\n\n if (btn.attr) {\n $(button).attr(btn.attr);\n }\n\n if (btn.class) {\n $(button).addClass(btn.class);\n }\n\n if (!btn.click) {\n btn.click = this.closeModal;\n }\n $(button).on('click', _.bind(btn.click, this));\n }, this);\n },\n\n /**\n * Creates overlay, append it to wrapper, set previous click event on overlay.\n */\n _createOverlay: function () {\n var events,\n outerClickHandler = this.options.outerClickHandler || this.closeModal;\n\n this.overlay = $('.' + this.options.overlayClass);\n\n if (!this.overlay.length) {\n $(this.options.appendTo).addClass(this.options.parentModalClass);\n this.overlay = $('<div></div>')\n .addClass(this.options.overlayClass)\n .appendTo(this.modalWrapper);\n }\n events = $._data(this.overlay.get(0), 'events');\n events ? this.prevOverlayHandler = events.click[0].handler : false;\n this.options.clickableOverlay ? this.overlay.unbind().on('click', outerClickHandler) : false;\n },\n\n /**\n * Destroy overlay.\n */\n _destroyOverlay: function () {\n if (this._getVisibleCount()) {\n this.overlay.unbind().on('click', this.prevOverlayHandler);\n } else {\n $(this.options.appendTo).removeClass(this.options.parentModalClass);\n this.overlay.remove();\n this.overlay = null;\n }\n }\n });\n\n return $.mage.modal;\n});\n","Magento_Ui/js/modal/prompt.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mage/template',\n 'text!ui/template/modal/modal-prompt-content.html',\n 'jquery/ui',\n 'Magento_Ui/js/modal/modal',\n 'mage/translate'\n], function ($, _, template, promptContentTmpl) {\n 'use strict';\n\n $.widget('mage.prompt', $.mage.modal, {\n options: {\n modalClass: 'prompt',\n promptContentTmpl: promptContentTmpl,\n promptField: '[data-role=\"promptField\"]',\n attributesForm: {},\n attributesField: {},\n value: '',\n validation: false,\n validationRules: [],\n actions: {\n\n /**\n * Callback always - called on all actions.\n */\n always: function () {},\n\n /**\n * Callback confirm.\n */\n confirm: function () {},\n\n /**\n * Callback cancel.\n */\n cancel: function () {}\n },\n buttons: [{\n text: $.mage.__('Cancel'),\n class: 'action-secondary action-dismiss',\n\n /**\n * Click handler.\n */\n click: function () {\n this.closeModal();\n }\n }, {\n text: $.mage.__('OK'),\n class: 'action-primary action-accept',\n\n /**\n * Click handler.\n */\n click: function () {\n this.closeModal(true);\n }\n }]\n },\n\n /**\n * Create widget.\n */\n _create: function () {\n this.options.focus = this.options.promptField;\n this.options.validation = this.options.validation && this.options.validationRules.length;\n this._super();\n this.modal.find(this.options.modalContent).append(this.getFormTemplate());\n this.modal.find(this.options.modalCloseBtn).off().on('click', _.bind(this.closeModal, this, false));\n\n if (this.options.validation) {\n this.setValidationClasses();\n }\n\n this.openModal();\n },\n\n /**\n * Form template getter.\n *\n * @returns {Object} Form template.\n */\n getFormTemplate: function () {\n var formTemplate,\n formAttr = '',\n inputAttr = '',\n attributeName;\n\n for (attributeName in this.options.attributesForm) {\n if (this.options.attributesForm.hasOwnProperty(attributeName)) {\n formAttr = formAttr + ' ' + attributeName + '=\"' +\n this.options.attributesForm[attributeName] + '\"';\n }\n }\n\n for (attributeName in this.options.attributesField) {\n if (this.options.attributesField.hasOwnProperty(attributeName)) {\n inputAttr = inputAttr + ' ' + attributeName + '=\"' +\n this.options.attributesField[attributeName] + '\"';\n }\n }\n\n formTemplate = $(template(this.options.promptContentTmpl, {\n data: this.options,\n formAttr: formAttr,\n inputAttr: inputAttr\n }));\n\n return formTemplate;\n },\n\n /**\n * Remove widget\n */\n _remove: function () {\n this.modal.remove();\n },\n\n /**\n * Validate prompt field\n */\n validate: function () {\n return $.validator.validateSingleElement(this.options.promptField);\n },\n\n /**\n * Add validation classes to prompt field\n */\n setValidationClasses: function () {\n this.modal.find(this.options.promptField).attr('class', $.proxy(function (i, val) {\n return val + ' ' + this.options.validationRules.join(' ');\n }, this));\n },\n\n /**\n * Open modal window\n */\n openModal: function () {\n this._super();\n this.modal.find(this.options.promptField).val(this.options.value);\n },\n\n /**\n * Close modal window\n */\n closeModal: function (result) {\n var value;\n\n if (result) {\n if (this.options.validation && !this.validate()) {\n return false;\n }\n\n value = this.modal.find(this.options.promptField).val();\n this.options.actions.confirm.call(this, value);\n } else {\n this.options.actions.cancel.call(this, result);\n }\n\n this.options.actions.always();\n this.element.bind('promptclosed', _.bind(this._remove, this));\n\n return this._super();\n }\n });\n\n return function (config) {\n return $('<div class=\"prompt-message\"></div>').html(config.content).prompt(config);\n };\n});\n","Magento_Ui/js/modal/modal-component.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'Magento_Ui/js/lib/view/utils/async',\n 'uiCollection',\n 'uiRegistry',\n 'underscore',\n './modal'\n], function ($, Collection, registry, _) {\n 'use strict';\n\n return Collection.extend({\n defaults: {\n template: 'ui/modal/modal-component',\n title: '',\n subTitle: '',\n options: {\n modalClass: '',\n title: '',\n subTitle: '',\n buttons: [],\n keyEventHandlers: {}\n },\n valid: true,\n links: {\n title: 'options.title',\n subTitle: 'options.subTitle'\n },\n listens: {\n state: 'onState',\n title: 'setTitle',\n 'options.subTitle': 'setSubTitle'\n },\n modalClass: 'modal-component',\n onCancel: 'closeModal'\n },\n\n /**\n * Initializes component.\n *\n * @returns {Object} Chainable.\n */\n initialize: function () {\n this._super();\n _.bindAll(this,\n 'initModal',\n 'openModal',\n 'closeModal',\n 'toggleModal',\n 'setPrevValues',\n 'validate');\n this.initializeContent();\n\n return this;\n },\n\n /**\n * Initializes modal configuration\n *\n * @returns {Object} Chainable.\n */\n initConfig: function () {\n return this._super()\n .initSelector()\n .initModalEvents();\n },\n\n /**\n * Configure modal selector\n *\n * @returns {Object} Chainable.\n */\n initSelector: function () {\n var modalClass = this.name.replace(/\\./g, '_');\n\n this.contentSelector = '.' + this.modalClass;\n this.options.modalClass = this.options.modalClass + ' ' + modalClass;\n this.rootSelector = '.' + modalClass;\n\n return this;\n },\n\n /**\n * Configure modal keyboard handlers\n * and outer click\n *\n * @returns {Object} Chainable.\n */\n initModalEvents: function () {\n this.options.keyEventHandlers.escapeKey = this.options.outerClickHandler = this[this.onCancel].bind(this);\n\n return this;\n },\n\n /**\n * Initialize modal's content components\n */\n initializeContent: function () {\n $.async({\n component: this.name\n }, this.initModal);\n },\n\n /**\n * Init toolbar section so other components will be able to place something in it\n */\n initToolbarSection: function () {\n this.set('toolbarSection', this.modal.data('mage-modal').modal.find('header').get(0));\n },\n\n /**\n * Initializes observable properties.\n *\n * @returns {Object} Chainable.\n */\n initObservable: function () {\n this._super();\n this.observe(['state', 'focused']);\n\n return this;\n },\n\n /**\n * Wrap content in a modal of certain type\n *\n * @param {HTMLElement} element\n * @returns {Object} Chainable.\n */\n initModal: function (element) {\n if (!this.modal) {\n this.overrideModalButtonCallback();\n this.options.modalCloseBtnHandler = this[this.onCancel].bind(this);\n this.modal = $(element).modal(this.options);\n this.initToolbarSection();\n\n if (this.waitCbk) {\n this.waitCbk();\n this.waitCbk = null;\n }\n }\n\n return this;\n },\n\n /**\n * Open modal\n */\n openModal: function () {\n if (this.modal) {\n this.state(true);\n } else {\n this.waitCbk = this.openModal;\n }\n },\n\n /**\n * Close modal\n */\n closeModal: function () {\n if (this.modal) {\n this.state(false);\n } else {\n this.waitCbk = this.closeModal;\n }\n },\n\n /**\n * Toggle modal\n */\n toggleModal: function () {\n if (this.modal) {\n this.state(!this.state());\n } else {\n this.waitCbk = this.toggleModal;\n }\n },\n\n /**\n * Sets title for modal\n *\n * @param {String} title\n */\n setTitle: function (title) {\n if (this.title !== title) {\n this.title = title;\n }\n\n if (this.modal) {\n this.modal.modal('setTitle', title);\n }\n },\n\n /**\n * Sets subTitle for modal\n *\n * @param {String} subTitle\n */\n setSubTitle: function (subTitle) {\n if (this.subTitle !== subTitle) {\n this.subTitle = subTitle;\n }\n\n if (this.modal) {\n this.modal.modal('setSubTitle', subTitle);\n }\n },\n\n /**\n * Wrap content in a modal of certain type\n *\n * @param {Boolean} state\n */\n onState: function (state) {\n if (state) {\n this.modal.modal('openModal');\n this.applyData();\n } else {\n this.modal.modal('closeModal');\n }\n },\n\n /**\n * Validate everything validatable in modal\n */\n validate: function (elem) {\n if (typeof elem === 'undefined') {\n return;\n }\n\n if (typeof elem.validate === 'function') {\n this.valid = this.valid & elem.validate().valid;\n } else if (elem.elems) {\n elem.elems().forEach(this.validate, this);\n }\n },\n\n /**\n * Reset data from provider\n */\n resetData: function () {\n this.elems().forEach(this.resetValue, this);\n },\n\n /**\n * Update 'applied' property with data from modal content\n */\n applyData: function () {\n var applied = {};\n\n this.elems().forEach(this.gatherValues.bind(this, applied), this);\n this.applied = applied;\n },\n\n /**\n * Gather values from modal content\n *\n * @param {Array} applied\n * @param {HTMLElement} elem\n */\n gatherValues: function (applied, elem) {\n if (typeof elem.value === 'function') {\n applied[elem.name] = elem.value();\n } else if (elem.elems) {\n elem.elems().forEach(this.gatherValues.bind(this, applied), this);\n }\n },\n\n /**\n * Set to previous values from modal content\n *\n * @param {HTMLElement} elem\n */\n setPrevValues: function (elem) {\n if (typeof elem.value === 'function') {\n this.modal.focus();\n elem.value(this.applied[elem.name]);\n } else if (elem.elems) {\n elem.elems().forEach(this.setPrevValues, this);\n }\n },\n\n /**\n * Triggers some method in every modal child elem, if this method is defined\n *\n * @param {Object} action - action configuration,\n * must contain actionName and targetName and\n * can contain params\n */\n triggerAction: function (action) {\n var targetName = action.targetName,\n params = action.params || [],\n actionName = action.actionName,\n target;\n\n target = registry.async(targetName);\n\n if (target && typeof target === 'function' && actionName) {\n params.unshift(actionName);\n target.apply(target, params);\n }\n },\n\n /**\n * Override modal buttons callback placeholders with real callbacks\n */\n overrideModalButtonCallback: function () {\n var buttons = this.options.buttons;\n\n if (buttons && buttons.length) {\n buttons.forEach(function (button) {\n button.click = this.getButtonClickHandler(button.actions);\n }, this);\n }\n },\n\n /**\n * Generate button click handler based on button's 'actions' configuration\n */\n getButtonClickHandler: function (actionsConfig) {\n var actions = actionsConfig.map(\n function (actionConfig) {\n if (_.isObject(actionConfig)) {\n return this.triggerAction.bind(this, actionConfig);\n }\n\n return this[actionConfig] ? this[actionConfig].bind(this) : function () {};\n }, this);\n\n return function () {\n actions.forEach(\n function (action) {\n action();\n }\n );\n };\n },\n\n /**\n * Cancels changes in modal:\n * returning elems values to the previous state,\n * and close modal\n */\n actionCancel: function () {\n this.elems().forEach(this.setPrevValues, this);\n this.closeModal();\n },\n\n /**\n * Accept changes in modal by not preventing them.\n * Can be extended by exporting 'gatherValues' result somewhere\n */\n actionDone: function () {\n this.valid = true;\n this.elems().forEach(this.validate, this);\n\n if (this.valid) {\n this.closeModal();\n }\n }\n });\n});\n","Magento_Ui/js/modal/modalToggle.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'Magento_Ui/js/modal/modal'\n], function ($) {\n 'use strict';\n\n return function (config, el) {\n var widget,\n content;\n\n if (config.contentSelector) {\n content = $(config.contentSelector);\n } else if (config.content) {\n content = $('<div />').html(config.content);\n } else {\n content = $('<div />');\n }\n\n widget = content.modal(config);\n\n $(el).on(config.toggleEvent, function () {\n var state = widget.data('mage-modal').options.isOpen;\n\n if (state) {\n widget.modal('closeModal');\n } else {\n widget.modal('openModal');\n }\n\n return false;\n });\n\n return widget;\n };\n});\n","Magento_Ui/js/modal/confirm.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mage/translate',\n 'jquery/ui',\n 'Magento_Ui/js/modal/modal'\n], function ($, _, $t) {\n 'use strict';\n\n $.widget('mage.confirm', $.mage.modal, {\n options: {\n modalClass: 'confirm',\n title: '',\n focus: '.action-accept',\n actions: {\n\n /**\n * Callback always - called on all actions.\n */\n always: function () {},\n\n /**\n * Callback confirm.\n */\n confirm: function () {},\n\n /**\n * Callback cancel.\n */\n cancel: function () {}\n },\n buttons: [{\n text: $t('Cancel'),\n class: 'action-secondary action-dismiss',\n\n /**\n * Click handler.\n */\n click: function (event) {\n this.closeModal(event);\n }\n }, {\n text: $t('OK'),\n class: 'action-primary action-accept',\n\n /**\n * Click handler.\n */\n click: function (event) {\n this.closeModal(event, true);\n }\n }]\n },\n\n /**\n * Create widget.\n */\n _create: function () {\n this._super();\n this.modal.find(this.options.modalCloseBtn).off().on('click', _.bind(this.closeModal, this));\n this.openModal();\n },\n\n /**\n * Remove modal window.\n */\n _remove: function () {\n this.modal.remove();\n },\n\n /**\n * Open modal window.\n */\n openModal: function () {\n return this._super();\n },\n\n /**\n * Close modal window.\n */\n closeModal: function (event, result) {\n result = result || false;\n\n if (result) {\n this.options.actions.confirm(event);\n } else {\n this.options.actions.cancel(event);\n }\n this.options.actions.always(event);\n this.element.bind('confirmclosed', _.bind(this._remove, this));\n\n return this._super();\n }\n });\n\n return function (config) {\n return $('<div></div>').html(config.content).confirm(config);\n };\n});\n","Magento_CatalogInventory/js/components/use-config-settings.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'Magento_Ui/js/form/element/single-checkbox'\n], function (checkbox) {\n 'use strict';\n\n return checkbox.extend({\n defaults: {\n valueFromConfig: '',\n linkedValue: ''\n },\n\n /**\n * @returns {Element}\n */\n initObservable: function () {\n return this\n ._super()\n .observe(['valueFromConfig', 'linkedValue']);\n },\n\n /**\n * @inheritdoc\n */\n 'onCheckedChanged': function (newChecked) {\n if (newChecked) {\n this.linkedValue(this.valueFromConfig());\n }\n\n this._super(newChecked);\n }\n });\n});\n","Magento_CatalogInventory/js/components/qty-validator-changer.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'Magento_Ui/js/form/element/abstract'\n], function (Abstract) {\n 'use strict';\n\n return Abstract.extend({\n defaults: {\n valueUpdate: 'input'\n },\n\n /**\n * Change validator\n */\n handleChanges: function (value) {\n var isDigits = value !== 1;\n\n this.validation['validate-integer'] = isDigits;\n this.validation['less-than-equals-to'] = isDigits ? 99999999 : 99999999.9999;\n this.validate();\n }\n });\n});\n","Magento_CatalogInventory/js/components/use-config-min-sale-qty.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'Magento_Ui/js/form/element/single-checkbox',\n 'underscore',\n 'uiRegistry'\n], function (checkbox, _, registry) {\n 'use strict';\n\n return checkbox.extend({\n defaults: {\n valueFromConfig: ''\n },\n\n /**\n * @returns {Element}\n */\n initObservable: function () {\n return this\n ._super()\n .observe(['valueFromConfig']);\n },\n\n /**\n * @inheritdoc\n */\n initialize: function () {\n this._super();\n this.onCheckedChanged(this.checked());\n\n return this;\n },\n\n /**\n * @inheritdoc\n */\n 'onCheckedChanged': function (newChecked) {\n var valueFromConfig = this.valueFromConfig();\n\n if (newChecked && (_.isArray(valueFromConfig) && valueFromConfig.length === 0 || valueFromConfig === 1)) {\n this.changeVisibleDisabled(this.inputField, true, true, 1);\n } else if (newChecked && _.isObject(valueFromConfig)) {\n this.changeVisibleDisabled(this.inputField, false, true, null);\n this.changeVisibleDisabled(this.dynamicRowsField, true, true, null);\n } else if (newChecked && _.isNumber(valueFromConfig)) {\n this.changeVisibleDisabled(this.inputField, true, true, null);\n this.changeVisibleDisabled(this.dynamicRowsField, false, true, null);\n } else {\n this.changeVisibleDisabled(this.inputField, true, false, null);\n this.changeVisibleDisabled(this.dynamicRowsField, false, true, null);\n }\n\n this._super(newChecked);\n },\n\n /**\n * Change visible and disabled\n *\n * @param {String} filter\n * @param {Boolean} visible\n * @param {Boolean} disabled\n * @param {Null|Number} valueFromConfig\n */\n changeVisibleDisabled: function (filter, visible, disabled, valueFromConfig) {\n registry.async(filter)(\n function (currentComponent) {\n var initialValue = currentComponent.initialValue;\n\n if (_.isString(initialValue) || initialValue === 0 || valueFromConfig === 1) {\n currentComponent.value(1);\n } else if (initialValue) {\n currentComponent.value(initialValue);\n }\n\n currentComponent.visible(visible);\n currentComponent.disabled(disabled);\n }\n );\n }\n });\n});\n","Magento_Vault/js/vault.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\n/* @api */\ndefine([\n 'jquery',\n 'uiComponent'\n], function ($, Class) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n $selector: null,\n selector: 'edit_form',\n fieldset: '',\n active: false,\n imports: {\n onActiveChange: 'active'\n }\n },\n\n /**\n * Set list of observable attributes\n * @returns {exports.initObservable}\n */\n initObservable: function () {\n var self = this;\n\n self.$selector = $('#' + self.selector);\n this._super()\n .observe(['active']);\n\n // re-init payment method events\n self.$selector.off('changePaymentMethod.' + this.getCode())\n .on('changePaymentMethod.' + this.getCode(), this.changePaymentMethod.bind(this));\n\n if (this.active()) {\n $('#' + this.fieldset + ' input:radio:first').trigger('click');\n }\n\n return this;\n },\n\n /**\n * Enable/disable current payment method\n * @param {Object} event\n * @param {String} method\n * @returns {exports.changePaymentMethod}\n */\n changePaymentMethod: function (event, method) {\n this.active(method === this.getCode());\n\n return this;\n },\n\n /**\n * Triggered when payment changed\n * @param {Boolean} isActive\n */\n onActiveChange: function (isActive) {\n if (!isActive) {\n this.$selector.trigger('setVaultNotActive.' + this.getCode());\n\n return;\n }\n\n $('#' + this.fieldset + ' input:radio:first').trigger('click');\n window.order.addExcludedPaymentMethod(this.getCode());\n },\n\n /**\n * Get payment method code\n * @returns {String}\n */\n getCode: function () {\n return this.code;\n }\n });\n});\n","varien/form.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Most likely it's depracated classes.\n * Not used anywhere among pages.\n */\nVarienForm = Class.create();\nVarienForm.prototype = {\n initialize: function(formId, firstFieldFocus){\n this.form = $(formId);\n if (!this.form) {\n return;\n }\n this.cache = $A();\n this.currLoader = false;\n this.currDataIndex = false;\n this.validator = new Validation(this.form);\n this.elementFocus = this.elementOnFocus.bindAsEventListener(this);\n this.elementBlur = this.elementOnBlur.bindAsEventListener(this);\n this.childLoader = this.onChangeChildLoad.bindAsEventListener(this);\n this.highlightClass = 'highlight';\n this.extraChildParams = '';\n this.firstFieldFocus= firstFieldFocus || false;\n this.bindElements();\n if(this.firstFieldFocus){\n try{\n Form.Element.focus(Form.findFirstElement(this.form))\n }\n catch(e){}\n }\n },\n\n submit : function(url){\n if(this.validator && this.validator.validate()){\n this.form.submit();\n }\n return false;\n },\n\n bindElements:function (){\n var elements = Form.getElements(this.form);\n for (var row in elements) {\n if (elements[row].id) {\n Event.observe(elements[row],'focus',this.elementFocus);\n Event.observe(elements[row],'blur',this.elementBlur);\n }\n }\n },\n\n elementOnFocus: function(event){\n var element = Event.findElement(event, 'fieldset');\n if(element){\n Element.addClassName(element, this.highlightClass);\n }\n },\n\n elementOnBlur: function(event){\n var element = Event.findElement(event, 'fieldset');\n if(element){\n Element.removeClassName(element, this.highlightClass);\n }\n },\n\n setElementsRelation: function(parent, child, dataUrl, first){\n if (parent=$(parent)) {\n // TODO: array of relation and caching\n if (!this.cache[parent.id]){\n this.cache[parent.id] = $A();\n this.cache[parent.id]['child'] = child;\n this.cache[parent.id]['dataUrl'] = dataUrl;\n this.cache[parent.id]['data'] = $A();\n this.cache[parent.id]['first'] = first || false;\n }\n Event.observe(parent,'change',this.childLoader);\n }\n },\n\n onChangeChildLoad: function(event){\n element = Event.element(event);\n this.elementChildLoad(element);\n },\n\n elementChildLoad: function(element, callback){\n this.callback = callback || false;\n if (element.value) {\n this.currLoader = element.id;\n this.currDataIndex = element.value;\n if (this.cache[element.id]['data'][element.value]) {\n this.setDataToChild(this.cache[element.id]['data'][element.value]);\n }\n else{\n new Ajax.Request(this.cache[this.currLoader]['dataUrl'],{\n method: 'post',\n parameters: {\"parent\":element.value},\n onComplete: this.reloadChildren.bind(this)\n });\n }\n }\n },\n\n reloadChildren: function(transport){\n var data = eval('(' + transport.responseText + ')');\n this.cache[this.currLoader]['data'][this.currDataIndex] = data;\n this.setDataToChild(data);\n },\n\n setDataToChild: function(data){\n if (data.length) {\n var child = $(this.cache[this.currLoader]['child']);\n if (child){\n var html = '<select name=\"'+child.name+'\" id=\"'+child.id+'\" class=\"'+child.className+'\" title=\"'+child.title+'\" '+this.extraChildParams+'>';\n if(this.cache[this.currLoader]['first']){\n html+= '<option value=\"\">'+this.cache[this.currLoader]['first']+'</option>';\n }\n for (var i in data){\n if(data[i].value) {\n html+= '<option value=\"'+data[i].value+'\"';\n if(child.value && (child.value == data[i].value || child.value == data[i].label)){\n html+= ' selected';\n }\n html+='>'+data[i].label+'</option>';\n }\n }\n html+= '</select>';\n Element.insert(child, {before: html});\n Element.remove(child);\n }\n }\n else{\n var child = $(this.cache[this.currLoader]['child']);\n if (child){\n var html = '<input type=\"text\" name=\"'+child.name+'\" id=\"'+child.id+'\" class=\"'+child.className+'\" title=\"'+child.title+'\" '+this.extraChildParams+'>';\n Element.insert(child, {before: html});\n Element.remove(child);\n }\n }\n\n this.bindElements();\n if (this.callback) {\n this.callback();\n }\n }\n}\n\nRegionUpdater = Class.create();\nRegionUpdater.prototype = {\n initialize: function (countryEl, regionTextEl, regionSelectEl, regions, disableAction, zipEl)\n {\n this.countryEl = $(countryEl);\n this.regionTextEl = $(regionTextEl);\n this.regionSelectEl = $(regionSelectEl);\n this.zipEl = $(zipEl);\n this.config = regions['config'];\n delete regions.config;\n this.regions = regions;\n\n this.disableAction = (typeof disableAction=='undefined') ? 'hide' : disableAction;\n this.zipOptions = (typeof zipOptions=='undefined') ? false : zipOptions;\n\n if (this.regionSelectEl.options.length<=1) {\n this.update();\n }\n\n Event.observe(this.countryEl, 'change', this.update.bind(this));\n },\n\n _checkRegionRequired: function()\n {\n var label, wildCard;\n var elements = [this.regionTextEl, this.regionSelectEl];\n var that = this;\n if (typeof this.config == 'undefined') {\n return;\n }\n var regionRequired = this.config.regions_required.indexOf(this.countryEl.value) >= 0;\n\n elements.each(function(currentElement) {\n Validation.reset(currentElement);\n label = $$('label[for=\"' + currentElement.id + '\"]')[0];\n if (label) {\n wildCard = label.down('em') || label.down('span.required');\n if (!that.config.show_all_regions) {\n if (regionRequired) {\n label.up().show();\n } else {\n label.up().hide();\n }\n }\n }\n\n if (label && wildCard) {\n if (!regionRequired) {\n wildCard.hide();\n if (label.hasClassName('required')) {\n label.removeClassName('required');\n }\n } else if (regionRequired) {\n wildCard.show();\n if (!label.hasClassName('required')) {\n label.addClassName('required')\n }\n }\n }\n\n if (!regionRequired) {\n if (currentElement.hasClassName('required-entry')) {\n currentElement.removeClassName('required-entry');\n }\n if ('select' == currentElement.tagName.toLowerCase() &&\n currentElement.hasClassName('validate-select')) {\n currentElement.removeClassName('validate-select');\n }\n } else {\n if (!currentElement.hasClassName('required-entry')) {\n currentElement.addClassName('required-entry');\n }\n if ('select' == currentElement.tagName.toLowerCase() &&\n !currentElement.hasClassName('validate-select')) {\n currentElement.addClassName('validate-select');\n }\n }\n });\n },\n\n update: function()\n {\n if (this.regions[this.countryEl.value]) {\n var i, option, region, def;\n\n def = this.regionSelectEl.getAttribute('defaultValue');\n if (this.regionTextEl) {\n if (!def) {\n def = this.regionTextEl.value.toLowerCase();\n }\n this.regionTextEl.value = '';\n }\n\n this.regionSelectEl.options.length = 1;\n for (regionId in this.regions[this.countryEl.value]) {\n region = this.regions[this.countryEl.value][regionId];\n\n option = document.createElement('OPTION');\n option.value = regionId;\n option.text = region.name.stripTags();\n option.title = region.name;\n\n if (this.regionSelectEl.options.add) {\n this.regionSelectEl.options.add(option);\n } else {\n this.regionSelectEl.appendChild(option);\n }\n\n if (regionId==def || (region.name && region.name.toLowerCase()==def) ||\n (region.name && region.code.toLowerCase()==def)\n ) {\n this.regionSelectEl.value = regionId;\n }\n }\n\n if (this.disableAction=='hide') {\n if (this.regionTextEl) {\n this.regionTextEl.style.display = 'none';\n }\n\n this.regionSelectEl.style.display = '';\n } else if (this.disableAction=='disable') {\n if (this.regionTextEl) {\n this.regionTextEl.disabled = true;\n }\n this.regionSelectEl.disabled = false;\n }\n this.setMarkDisplay(this.regionSelectEl, true);\n } else {\n this.regionSelectEl.options.length = 1;\n if (this.disableAction=='hide') {\n if (this.regionTextEl) {\n this.regionTextEl.style.display = '';\n }\n this.regionSelectEl.style.display = 'none';\n Validation.reset(this.regionSelectEl);\n } else if (this.disableAction=='disable') {\n if (this.regionTextEl) {\n this.regionTextEl.disabled = false;\n }\n this.regionSelectEl.disabled = true;\n } else if (this.disableAction=='nullify') {\n this.regionSelectEl.options.length = 1;\n this.regionSelectEl.value = '';\n this.regionSelectEl.selectedIndex = 0;\n this.lastCountryId = '';\n }\n this.setMarkDisplay(this.regionSelectEl, false);\n }\n\n this._checkRegionRequired();\n // Make Zip and its label required/optional\n var zipUpdater = new ZipUpdater(this.countryEl.value, this.zipEl);\n zipUpdater.update();\n },\n\n setMarkDisplay: function(elem, display){\n elem = $(elem);\n var labelElement = elem.up(0).down('label > span.required') ||\n elem.up(1).down('label > span.required') ||\n elem.up(0).down('label.required > em') ||\n elem.up(1).down('label.required > em');\n if(labelElement) {\n inputElement = labelElement.up().next('input');\n if (display) {\n labelElement.show();\n if (inputElement) {\n inputElement.addClassName('required-entry');\n }\n } else {\n labelElement.hide();\n if (inputElement) {\n inputElement.removeClassName('required-entry');\n }\n }\n }\n }\n}\n\nZipUpdater = Class.create();\nZipUpdater.prototype = {\n initialize: function(country, zipElement)\n {\n this.country = country;\n this.zipElement = $(zipElement);\n },\n\n update: function()\n {\n // Country ISO 2-letter codes must be pre-defined\n if (typeof optionalZipCountries == 'undefined') {\n return false;\n }\n\n // Ajax-request and normal content load compatibility\n if (this.zipElement != undefined) {\n this._setPostcodeOptional();\n } else {\n Event.observe(window, \"load\", this._setPostcodeOptional.bind(this));\n }\n },\n\n _setPostcodeOptional: function()\n {\n this.zipElement = $(this.zipElement);\n if (this.zipElement == undefined) {\n return false;\n }\n\n // find label\n var label = $$('label[for=\"' + this.zipElement.id + '\"]')[0];\n if (label != undefined) {\n var wildCard = label.down('em') || label.down('span.required');\n }\n\n // Make Zip and its label required/optional\n if (optionalZipCountries.indexOf(this.country) != -1) {\n while (this.zipElement.hasClassName('required-entry')) {\n this.zipElement.removeClassName('required-entry');\n }\n if (wildCard != undefined) {\n wildCard.hide();\n }\n } else {\n this.zipElement.addClassName('required-entry');\n if (wildCard != undefined) {\n wildCard.show();\n }\n }\n }\n}\n","Magento_ProductVideo/js/video-modal.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'jquery',\n 'productGallery',\n 'jquery/ui',\n 'Magento_Ui/js/modal/modal',\n 'mage/translate',\n 'mage/backend/tree-suggest',\n 'mage/backend/validation',\n 'newVideoDialog'\n], function ($, productGallery) {\n 'use strict';\n\n $.widget('mage.productGallery', productGallery, {\n\n /**\n * Bind events\n * @private\n */\n _bind: function () {\n var events = {},\n itemId;\n\n this._super();\n\n /**\n * Add item_id value to opened modal\n * @param {Object} event\n */\n events['click ' + this.options.imageSelector] = function (event) {\n if (!$(event.currentTarget).is('.ui-sortable-helper')) {\n itemId = $(event.currentTarget).find('input')[0].name.match(/\\[([^\\]]*)\\]/g)[2];\n this.videoDialog.find('#item_id').val(itemId);\n }\n };\n this._on(events);\n this.element.prev().find('[data-role=\"add-video-button\"]').on('click', this.showModal.bind(this));\n this.element.on('openDialog', '.gallery.ui-sortable', $.proxy(this._onOpenDialog, this));\n },\n\n /**\n * @private\n */\n _create: function () {\n this._super();\n this.videoDialog = this.element.find('#new-video');\n this.videoDialog.mage('newVideoDialog', this.videoDialog.data('modalInfo'));\n },\n\n /**\n * Open dialog for external video\n * @private\n */\n _onOpenDialog: function (e, imageData) {\n\n if (imageData['media_type'] !== 'external-video') {\n this._superApply(arguments);\n } else {\n this.showModal();\n }\n },\n\n /**\n * Fired on trigger \"openModal\"\n */\n showModal: function () {\n this.videoDialog.modal('openModal');\n }\n });\n\n return $.mage.productGallery;\n});\n","Magento_ProductVideo/js/new-video-dialog.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'jquery',\n 'underscore',\n 'jquery/ui',\n 'Magento_Ui/js/modal/modal',\n 'mage/translate',\n 'mage/backend/tree-suggest',\n 'mage/backend/validation',\n 'Magento_ProductVideo/js/get-video-information'\n], function ($, _) {\n 'use strict';\n\n $.widget('mage.createVideoPlayer', {\n options: {\n videoId: '',\n videoProvider: '',\n container: '.video-player-container',\n videoClass: 'product-video',\n reset: false,\n useYoutubeNocookie: false,\n metaData: {\n DOM: {\n title: '.video-information.title span',\n uploaded: '.video-information.uploaded span',\n uploader: '.video-information.uploader span',\n duration: '.video-information.duration span',\n all: '.video-information span',\n wrapper: '.video-information'\n },\n data: {\n title: '',\n uploaded: '',\n uploader: '',\n uploaderUrl: '',\n duration: ''\n }\n }\n },\n\n _FINISH_CREATE_VIDEO_TRIGGER: 'finish_create_video',\n\n _FINISH_UPDATE_VIDEO_TRIGGER: 'finish_update_video',\n\n /**\n * @private\n */\n _init: function () {\n if (this.options.reset) {\n this.reset();\n } else {\n this.update();\n }\n\n this.element.on('reset', $.proxy(this.reset, this));\n\n },\n\n /**\n * @returns {Boolean}\n */\n update: function () {\n var checkVideoID = this.element.find(this.options.container).find(\n '.' + this.options.videoClass\n ).data('code'),\n eventVideoData = {\n oldVideoId: checkVideoID ? checkVideoID.toString() : checkVideoID,\n newVideoId: this.options.videoId ? this.options.videoId.toString() : this.options.videoId\n };\n\n if (checkVideoID && checkVideoID !== this.options.videoId) {\n this._doUpdate();\n this.element.trigger(this._FINISH_UPDATE_VIDEO_TRIGGER, eventVideoData);\n } else if (checkVideoID && checkVideoID === this.options.videoId) {\n return false;\n } else if (!checkVideoID) {\n this._doUpdate();\n this.element.trigger(this._FINISH_CREATE_VIDEO_TRIGGER, eventVideoData);\n }\n\n },\n\n /**\n * @private\n */\n _doUpdate: function () {\n var uploaderLinkUrl,\n uploaderLink;\n\n this.reset();\n this.element.find(this.options.container).append(\n '<div class=\"' +\n this.options.videoClass +\n '\" data-type=\"' +\n this.options.videoProvider +\n '\" data-code=\"' +\n this.options.videoId +\n '\" data-youtubenocookie=\"' +\n this.options.useYoutubeNocookie +\n '\" data-width=\"100%\" data-height=\"100%\"></div>'\n );\n this.element.find(this.options.metaData.DOM.wrapper).show();\n this.element.find(this.options.metaData.DOM.title).text(this.options.metaData.data.title);\n this.element.find(this.options.metaData.DOM.uploaded).text(this.options.metaData.data.uploaded);\n this.element.find(this.options.metaData.DOM.duration).text(this.options.metaData.data.duration);\n\n if (this.options.videoProvider === 'youtube') {\n uploaderLinkUrl = 'https://youtube.com/channel/' + this.options.metaData.data.uploaderUrl;\n } else if (this.options.videoProvider === 'vimeo') {\n uploaderLinkUrl = this.options.metaData.data.uploaderUrl;\n }\n uploaderLink = document.createElement('a');\n uploaderLink.setAttribute('href', uploaderLinkUrl);\n uploaderLink.setAttribute('target', '_blank');\n uploaderLink.innerText = this.options.metaData.data.uploader;\n this.element.find(this.options.metaData.DOM.uploader)[0].appendChild(uploaderLink);\n this.element.find('.' + this.options.videoClass).productVideoLoader();\n\n },\n\n /**\n * Reset\n */\n reset: function () {\n this.element.find(this.options.container).find('.' + this.options.videoClass).remove();\n this.element.find(this.options.metaData.DOM.wrapper).hide();\n this.element.find(this.options.metaData.DOM.all).text('');\n\n }\n });\n\n $.widget('mage.updateInputFields', {\n options: {\n reset: false,\n DOM: {\n urlField: 'input[name=\"video_url\"]',\n titleField: 'input[name=\"video_title\"]',\n fileField: '#file_name',\n descriptionField: 'textarea[name=\"video_description\"]',\n thumbnailLocation: '.field-new_video_screenshot_preview .admin__field-control'\n },\n data: {\n url: '',\n title: '',\n description: '',\n thumbnail: ''\n }\n },\n\n /**\n * @private\n */\n _init: function () {\n if (this.options.reset) {\n this.reset();\n } else {\n this.update();\n }\n },\n\n /**\n * Update\n */\n update: function () {\n $(this.options.DOM.titleField).val(this.options.data.title);\n $(this.options.DOM.descriptionField).val(this.options.data.description);\n },\n\n /**\n * Reset\n */\n reset: function () {\n $(this.options.DOM.fileField).val('');\n $(this.options.DOM.urlField).val('');\n $(this.options.DOM.titleField).val('');\n $(this.options.DOM.descriptionField).val('');\n }\n });\n\n /**\n */\n $.widget('mage.newVideoDialog', {\n\n _previewImage: null,\n\n clickedElement: '',\n\n _images: {},\n\n _imageTypes: [\n '.jpeg',\n '.pjpeg',\n '.jpeg',\n '.jpg',\n '.pjpeg',\n '.png',\n '.gif'\n ],\n\n _imageProductGalleryWrapperSelector: '#image-container',\n\n _videoPreviewInputSelector: '#new_video_screenshot',\n\n _videoPreviewRemoteSelector: '',\n\n _videoDisableinputSelector: '#new_video_disabled',\n\n _videoPreviewImagePointer: '#new_video_screenshot_preview',\n\n _videoFormSelector: '#new_video_form',\n\n _itemIdSelector: '#item_id',\n\n _videoUrlSelector: '[name=\"video_url\"]',\n\n _videoImageFilenameselector: '#file_name',\n\n _videoUrlWidget: null,\n\n _videoInformationBtnSelector: '[name=\"new_video_get\"]',\n\n _editVideoBtnSelector: '.image',\n\n _deleteGalleryVideoSelector: '[data-role=delete-button]',\n\n _deleteGalleryVideoSelectorBtn: null,\n\n _videoInformationGetBtn: null,\n\n _videoInformationGetUrlField: null,\n\n _videoInformationGetEditBtn: null,\n\n _isEditPage: false,\n\n _onlyVideoPlayer: false,\n\n _tempPreviewImageData: null,\n\n _videoPlayerSelector: '.mage-new-video-dialog',\n\n _videoRequestComplete: null,\n\n _gallery: null,\n\n /**\n * Bind events\n * @private\n */\n _bind: function () {\n var events = {\n 'setImage': '_onSetImage'\n };\n\n this._on(events);\n\n this._videoUrlWidget = this.element.find(this._videoUrlSelector).videoData({\n youtubeKey: this.options.youTubeApiKey,\n eventSource: 'focusout'\n });\n\n this._videoInformationGetBtn = this.element.find(this._videoInformationBtnSelector);\n this._videoInformationGetUrlField = this.element.find(this._videoUrlSelector);\n this._videoInformationGetEditBtn = this._gallery.find(this._editVideoBtnSelector);\n\n this._videoInformationGetBtn.on('click', $.proxy(this._onGetVideoInformationClick, this));\n this._videoInformationGetUrlField.on('focusout', $.proxy(this._onGetVideoInformationFocusOut, this));\n\n this._videoUrlWidget.on('updated_video_information', $.proxy(this._onGetVideoInformationSuccess, this));\n this._videoUrlWidget.on('error_updated_information', $.proxy(this._onGetVideoInformationError, this));\n this._videoUrlWidget.on(\n 'request_video_information',\n $.proxy(this._onGetVideoInformationStartRequest, this)\n );\n },\n\n /**\n * Fired when user click on button \"Get video information\"\n * @private\n */\n _onGetVideoInformationClick: function () {\n this._onlyVideoPlayer = false;\n this._isEditPage = false;\n this._videoUrlWidget.trigger('update_video_information');\n },\n\n /**\n * Fired when user do focus out from url field\n * @private\n */\n _onGetVideoInformationFocusOut: function () {\n this._videoUrlWidget.trigger('update_video_information');\n },\n\n /**\n * @private\n */\n _onGetVideoInformationStartRequest: function () {\n this._videoRequestComplete = false;\n },\n\n /**\n * Fired when user click Edit Video button\n * @private\n */\n _onGetVideoInformationEditClick: function () {\n this._onlyVideoPlayer = true;\n this._isEditPage = true;\n this._videoUrlWidget.trigger('update_video_information');\n },\n\n /**\n * Fired when successfully received information about the video.\n * @param {Object} e\n * @param {Object} data\n * @private\n */\n _onGetVideoInformationSuccess: function (e, data) {\n var self = this;\n\n self.element.on('finish_update_video finish_create_video', $.proxy(function (element, playerData) {\n if (!self._onlyVideoPlayer ||\n !self._isEditPage && playerData.oldVideoId !== playerData.newVideoId ||\n playerData.oldVideoId && playerData.oldVideoId !== playerData.newVideoId\n ) {\n self.element.updateInputFields({\n reset: false,\n data: {\n title: data.title,\n description: data.description\n }\n });\n this._loadRemotePreview(data.thumbnail);\n }\n self._onlyVideoPlayer = true;\n }, this))\n .createVideoPlayer({\n videoId: data.videoId,\n videoProvider: data.videoProvider,\n useYoutubeNocookie: data.useYoutubeNocookie,\n reset: false,\n metaData: {\n DOM: {\n title: '.video-information.title span',\n uploaded: '.video-information.uploaded span',\n uploader: '.video-information.uploader span',\n duration: '.video-information.duration span',\n all: '.video-information span',\n wrapper: '.video-information'\n },\n data: {\n title: data.title,\n uploaded: data.uploaded,\n uploader: data.channel,\n duration: data.duration,\n uploaderUrl: data.channelId\n }\n }\n })\n .off('finish_update_video finish_create_video');\n\n this._videoRequestComplete = true;\n },\n\n /**\n * Load preview from youtube/vimeo\n * @param {String} sourceUrl\n * @private\n */\n _loadRemotePreview: function (sourceUrl) {\n var url = this.options.saveRemoteVideoUrl,\n self = this;\n\n this._getPreviewImage().attr('src', sourceUrl).hide();\n this._blockActionButtons(true, true);\n $.ajax({\n url: url,\n data: 'remote_image=' + sourceUrl,\n type: 'post',\n success: $.proxy(function (result) {\n this._tempPreviewImageData = result;\n this._getPreviewImage().attr('src', sourceUrl).show();\n this._blockActionButtons(false, true);\n }, self)\n });\n },\n\n /**\n * Fired when receiving information about the video ended with error\n * @private\n */\n _onGetVideoInformationError: function () {\n },\n\n /**\n * Remove \".tmp\"\n * @param {String} name\n * @returns {*}\n * @private\n */\n __prepareFilename: function (name) {\n var tmppost = '.tmp';\n\n if (!name) {\n return name;\n }\n\n if (name.endsWith(tmppost)) {\n name = name.slice(0, name.length - tmppost.length);\n }\n\n return name;\n },\n\n /**\n * Set image data\n * @param {String} file\n * @param {Object} imageData\n * @private\n */\n _setImage: function (file, imageData) {\n file = this.__prepareFilename(file);\n this._images[file] = imageData;\n this._gallery.trigger('addItem', imageData);\n this.element.trigger('setImage', imageData);\n this._addVideoClass(imageData.url);\n },\n\n /**\n * Get image data\n *\n * @param {String} file\n * @returns {*}\n * @private\n */\n _getImage: function (file) {\n file = this.__prepareFilename(file);\n\n return this._images[file];\n },\n\n /**\n * Replace image (update)\n * @param {String} oldFile\n * @param {String} newFile\n * @param {Object} imageData\n * @private\n */\n _replaceImage: function (oldFile, newFile, imageData) {\n var tmpNewFile = newFile,\n tmpOldImage,\n newImageId,\n oldNewFilePosition,\n fc,\n suff,\n searchsuff,\n key,\n oldValIdElem;\n\n oldFile = this.__prepareFilename(oldFile);\n newFile = this.__prepareFilename(newFile);\n tmpOldImage = this._images[oldFile];\n\n if (newFile === oldFile) {\n this._images[newFile] = imageData;\n this.saveImageRoles(imageData);\n this._updateVisibility(imageData);\n this._updateImageTitle(imageData);\n\n return null;\n }\n\n this._removeImage(oldFile);\n this._setImage(newFile, imageData);\n\n if (!oldFile || !imageData.oldFile) {\n return null;\n }\n\n newImageId = this.findElementId(tmpNewFile);\n fc = this.element.find(this._itemIdSelector).val();\n\n suff = 'product[media_gallery][images]' + fc;\n\n searchsuff = 'input[name=\"' + suff + '[value_id]\"]';\n key = this._gallery.find(searchsuff).val();\n\n if (!key) {\n return null;\n }\n\n oldValIdElem = document.createElement('input');\n this._gallery.find('form[data-form=\"edit-product\"]').append(oldValIdElem);\n $(oldValIdElem).attr({\n type: 'hidden',\n name: 'product[media_gallery][images][' + newImageId + '][save_data_from]'\n }).val(key);\n\n oldNewFilePosition = parseInt(tmpOldImage.position, 10);\n imageData.position = oldNewFilePosition;\n\n this._gallery.trigger('setPosition', {\n imageData: imageData,\n position: oldNewFilePosition\n });\n },\n\n /**\n * Remove image data\n * @param {String} file\n * @private\n */\n _removeImage: function (file) {\n var imageData = this._getImage(file);\n\n if (!imageData) {\n return null;\n }\n\n this._gallery.trigger('removeItem', imageData);\n this.element.trigger('removeImage', imageData);\n delete this._images[file];\n },\n\n /**\n * Fired when image setted\n * @param {Event} event\n * @param {Object} imageData\n * @private\n */\n _onSetImage: function (event, imageData) {\n this.saveImageRoles(imageData);\n },\n\n /**\n *\n * Wrap _uploadFile\n * @param {String} file\n * @param {String} oldFile\n * @param {Function} callback\n * @private\n */\n _uploadImage: function (file, oldFile, callback) {\n var url = this.options.saveVideoUrl,\n data = {\n files: file,\n url: url\n };\n\n this._blockActionButtons(true, true);\n this._uploadFile(data, $.proxy(function (result) {\n this._onImageLoaded(result, file, oldFile, callback);\n this._blockActionButtons(false);\n }, this));\n\n },\n\n /**\n * @param {String} result\n * @param {String} file\n * @param {String} oldFile\n * @param {Function} callback\n * @private\n */\n _onImageLoaded: function (result, file, oldFile, callback) {\n var data = JSON.parse(result);\n\n if (this.element.find('#video_url').parent().find('.image-upload-error').length > 0) {\n this.element.find('.image-upload-error').remove();\n }\n\n if (data.errorcode || data.error) {\n this.element.find('#video_url').parent().append('<div class=\"image-upload-error\">' +\n '<div class=\"image-upload-error-cross\"></div><span>' + data.error + '</span></div>');\n\n return;\n }\n $.each(this.element.find(this._videoFormSelector).serializeArray(), function (i, field) {\n data[field.name] = field.value;\n });\n data.disabled = this.element.find(this._videoDisableinputSelector).attr('checked') ? 1 : 0;\n data['media_type'] = 'external-video';\n data.oldFile = oldFile;\n\n oldFile ?\n this._replaceImage(oldFile, data.file, data) :\n this._setImage(data.file, data);\n callback.call(0, data);\n },\n\n /**\n * File uploader\n * @private\n */\n _uploadFile: function (data, callback) {\n var fu = this.element.find(this._videoPreviewInputSelector),\n tmpInput = document.createElement('input'),\n fileUploader = null;\n\n $(tmpInput).attr({\n 'name': fu.attr('name'),\n 'value': fu.val(),\n 'type': 'file',\n 'data-ui-ud': fu.attr('data-ui-ud')\n }).css('display', 'none');\n fu.parent().append(tmpInput);\n fileUploader = $(tmpInput).fileupload();\n fileUploader.fileupload('send', data).success(function (result, textStatus, jqXHR) {\n tmpInput.remove();\n callback.call(null, result, textStatus, jqXHR);\n });\n },\n\n /**\n * Update style\n * @param {String} url\n * @private\n */\n _addVideoClass: function (url) {\n var classVideo = 'video-item';\n\n this._gallery.find('img[src=\"' + url + '\"]').addClass(classVideo);\n },\n\n /**\n * Build widget\n * @private\n */\n _create: function () {\n var imgs = _.values(this.element.closest(this.options.videoSelector).data('images')) || [],\n widget,\n uploader,\n tmp,\n i;\n\n this._gallery = this.element.closest(this.options.videoSelector);\n\n for (i = 0; i < imgs.length; i++) {\n tmp = imgs[i];\n this._images[tmp.file] = tmp;\n\n if (tmp['media_type'] === 'external-video') {\n tmp.subclass = 'video-item';\n this._addVideoClass(tmp.url);\n }\n }\n\n this._gallery.on('openDialog', $.proxy(this._onOpenDialog, this));\n this._bind();\n this.createVideoItemIcons();\n widget = this;\n uploader = this.element.find(this._videoPreviewInputSelector);\n uploader.on('change', this._onImageInputChange.bind(this));\n uploader.attr('accept', this._imageTypes.join(','));\n\n this.element.modal({\n type: 'slide',\n //appendTo: this._gallery,\n modalClass: 'mage-new-video-dialog form-inline',\n title: $.mage.__('New Video'),\n buttons: [\n {\n text: $.mage.__('Save'),\n class: 'action-primary video-create-button',\n click: $.proxy(widget._onCreate, widget)\n },\n {\n text: $.mage.__('Cancel'),\n class: 'video-cancel-button',\n click: $.proxy(widget._onCancel, widget)\n },\n {\n text: $.mage.__('Delete'),\n class: 'video-delete-button',\n click: $.proxy(widget._onDelete, widget)\n },\n {\n text: $.mage.__('Save'),\n class: 'action-primary video-edit',\n click: $.proxy(widget._onUpdate, widget)\n }\n ],\n\n /**\n * @returns {null}\n */\n opened: function () {\n var roles,\n file,\n modalTitleElement,\n imageData,\n modal = widget.element.closest('.mage-new-video-dialog');\n\n widget.element.find('#video_url').focus();\n roles = widget.element.find('.video_image_role');\n roles.prop('disabled', false);\n file = widget.element.find('#file_name').val();\n widget._onGetVideoInformationEditClick();\n modalTitleElement = modal.find('.modal-title');\n\n if (!file) {\n widget._blockActionButtons(true);\n\n modal.find('.video-delete-button').hide();\n modal.find('.video-edit').hide();\n modal.find('.video-create-button').show();\n roles.prop('checked', widget._gallery.find('.image.item:not(.removed)').length < 1);\n modalTitleElement.text($.mage.__('New Video'));\n widget._isEditPage = false;\n\n return null;\n }\n widget._blockActionButtons(false);\n modalTitleElement.text($.mage.__('Edit Video'));\n widget._isEditPage = true;\n imageData = widget._getImage(file);\n\n if (!imageData) {\n imageData = {\n url: _.find(widget._gallery.find('.product-image'), function (image) {\n return image.src.indexOf(file) > -1;\n }).src\n };\n }\n\n widget._onPreview(null, imageData.url, false);\n },\n\n /**\n * Closed\n */\n closed: function () {\n widget._onClose();\n widget.createVideoItemIcons();\n }\n });\n this.toggleButtons();\n },\n\n /**\n * @param {String} status\n * @private\n */\n _blockActionButtons: function (status) {\n this.element\n .closest('.mage-new-video-dialog')\n .find('.page-actions-buttons button.video-create-button, .page-actions-buttons button.video-edit')\n .attr('disabled', status);\n },\n\n /**\n * Check form\n * @param {Function} callback\n */\n isValid: function (callback) {\n var videoForm = this.element.find(this._videoFormSelector),\n videoLoaded = true;\n\n this._blockActionButtons(true);\n\n this._videoUrlWidget.trigger('validate_video_url', $.proxy(function () {\n\n videoForm.mage('validation', {\n\n /**\n * @param {jQuery} error\n * @param {jQuery} element\n */\n errorPlacement: function (error, element) {\n error.insertAfter(element);\n }\n }).on('highlight.validate', function () {\n $(this).validation('option');\n });\n\n videoForm.validation();\n\n if (this._videoRequestComplete === false) {\n videoLoaded = false;\n }\n\n callback(videoForm.valid() && videoLoaded);\n }, this));\n\n this._blockActionButtons(false);\n },\n\n /**\n * Create video item icons\n */\n createVideoItemIcons: function () {\n var $imageWidget = this._gallery.find('.product-image.video-item'),\n $productGalleryWrapper = $(this._imageProductGalleryWrapperSelector).find('.product-image.video-item');\n\n $imageWidget.parent().addClass('video-item');\n $productGalleryWrapper.parent().addClass('video-item');\n $imageWidget.removeClass('video-item');\n $productGalleryWrapper.removeClass('video-item');\n $('.video-item .action-delete').attr('title', $.mage.__('Delete video'));\n $('.video-item .action-delete span').html($.mage.__('Delete video'));\n },\n\n /**\n * Fired when click on create video\n * @private\n */\n _onCreate: function () {\n var nvs = this.element.find(this._videoPreviewInputSelector),\n file = nvs.get(0),\n reqClass = 'required-entry _required';\n\n if (file && file.files && file.files.length) {\n file = file.files[0];\n } else {\n file = null;\n }\n\n if (!file && !this._tempPreviewImageData) {\n nvs.addClass(reqClass);\n }\n\n this.isValid($.proxy(\n function (videoValidStatus) {\n\n if (!videoValidStatus) {\n return;\n }\n\n if (this._tempPreviewImageData) {\n this._onImageLoaded(this._tempPreviewImageData, null, null, $.proxy(this.close, this));\n } else {\n this._uploadImage(file, null, $.proxy(function () {\n this.close();\n }, this));\n }\n\n nvs.removeClass(reqClass);\n }, this\n ));\n },\n\n /**\n * Fired when click on update video\n * @private\n */\n _onUpdate: function () {\n var inputFile, itemId, _inputSelector, mediaFields, imageData, flagChecked, fileName, callback;\n\n this.isValid($.proxy(\n function (videoValidStatus) {\n\n if (!videoValidStatus) {\n return;\n }\n\n imageData = this.imageData || {};\n inputFile = this.element.find(this._videoPreviewInputSelector);\n itemId = this.element.find(this._itemIdSelector).val();\n itemId = itemId.slice(1, itemId.length - 1);\n _inputSelector = '[name*=\"[' + itemId + ']\"]';\n mediaFields = this._gallery.find('input' + _inputSelector);\n $.each(mediaFields, function (i, el) {\n var elName = el.name,\n start = elName.indexOf(itemId) + itemId.length + 2,\n fieldName = elName.substring(start, el.name.length - 1),\n _field = this.element.find('#' + fieldName),\n _tmp;\n\n if (_field.length > 0) {\n _tmp = _inputSelector.slice(0, _inputSelector.length - 2) + '[' + fieldName + ']\"]';\n this._gallery.find(_tmp).val(_field.val());\n imageData[fieldName] = _field.val();\n }\n }.bind(this));\n flagChecked = this.element.find(this._videoDisableinputSelector).attr('checked') ? 1 : 0;\n this._gallery.find('input[name*=\"' + itemId + '][disabled]\"]').val(flagChecked);\n this._gallery.find(_inputSelector).siblings('.image-fade').css(\n 'visibility', flagChecked ? 'visible' : 'hidden'\n );\n imageData.disabled = flagChecked;\n\n if (this._tempPreviewImageData) {\n this._onImageLoaded(\n this._tempPreviewImageData,\n null,\n imageData.file,\n $.proxy(this.close, this)\n );\n\n return;\n }\n fileName = inputFile.get(0).files;\n\n if (!fileName || !fileName.length) {\n fileName = null;\n }\n inputFile.replaceWith(inputFile);\n\n callback = $.proxy(function () {\n this.close();\n }, this);\n\n if (fileName) {\n this._uploadImage(fileName, imageData.file, callback);\n } else {\n this._replaceImage(imageData.file, imageData.file, imageData);\n callback(0, imageData);\n }\n }, this\n ));\n },\n\n /**\n * Delegates call to producwt gallery to update video visibility.\n *\n * @param {Object} imageData\n */\n _updateVisibility: function (imageData) {\n this._gallery.trigger('updateVisibility', {\n disabled: imageData.disabled,\n imageData: imageData\n });\n },\n\n /**\n * Delegates call to product gallery to update video title.\n *\n * @param {Object} imageData\n */\n _updateImageTitle: function (imageData) {\n this._gallery.trigger('updateImageTitle', {\n imageData: imageData\n });\n },\n\n /**\n * Fired when clicked on cancel\n * @private\n */\n _onCancel: function () {\n this.close();\n },\n\n /**\n * Fired when clicked on delete\n * @private\n */\n _onDelete: function () {\n var filename = this.element.find(this._videoImageFilenameselector).val();\n\n this._removeImage(filename);\n this.close();\n },\n\n /**\n * @param {String} file\n * @param {Function} callback\n * @private\n */\n _readPreviewLocal: function (file, callback) {\n var fr = new FileReader;\n\n if (!window.FileReader) {\n return;\n }\n\n /**\n * On load end\n */\n fr.onloadend = function () {\n callback(fr.result);\n };\n fr.readAsDataURL(file);\n },\n\n /**\n * Image file input handler\n * @private\n */\n _onImageInputChange: function () {\n var jFile = this.element.find(this._videoPreviewInputSelector),\n file = jFile[0],\n val = jFile.val(),\n prev = this._getPreviewImage(),\n ext = '.' + val.split('.').pop();\n\n if (!val) {\n return;\n }\n ext = ext ? ext.toLowerCase() : '';\n\n if (\n ext.length < 2 ||\n this._imageTypes.indexOf(ext.toLowerCase()) === -1 || !file.files || !file.files.length\n ) {\n prev.remove();\n this._previewImage = null;\n jFile.val('');\n\n return;\n } // end if\n file = file.files[0];\n this._tempPreviewImageData = null;\n this._onPreview(null, file, true);\n },\n\n /**\n * Change Preview\n * @param {String} error\n * @param {String} src\n * @param {Boolean} local\n * @private\n */\n _onPreview: function (error, src, local) {\n var img, renderImage;\n\n img = this._getPreviewImage();\n\n /**\n * Callback\n * @param {String} source\n */\n renderImage = function (source) {\n img.attr({\n 'src': source\n }).show();\n };\n\n if (error) {\n return;\n }\n\n if (!local) {\n renderImage(src);\n } else {\n this._readPreviewLocal(src, renderImage);\n }\n },\n\n /**\n *\n * Return preview image imstance\n * @returns {null}\n * @private\n */\n _getPreviewImage: function () {\n\n if (!this._previewImage) {\n this._previewImage = $(document.createElement('img')).css({\n 'width': '100%',\n 'display': 'none',\n 'src': ''\n });\n $(this._previewImage).insertAfter(this.element.find(this._videoPreviewImagePointer));\n $(this._previewImage).attr('data-role', 'video_preview_image');\n }\n\n return this._previewImage;\n },\n\n /**\n * Close slideout dialog\n */\n close: function () {\n this.element.modal('closeModal');\n },\n\n /**\n * Close dialog wrap\n * @private\n */\n _onClose: function () {\n var newVideoForm;\n\n this._isEditPage = true;\n this.imageData = null;\n\n if (this._previewImage) {\n this._previewImage.remove();\n this._previewImage = null;\n }\n this._tempPreviewImageData = null;\n this.element.trigger('reset');\n newVideoForm = this.element.find(this._videoFormSelector);\n\n $(newVideoForm).find('input[type=\"hidden\"][name!=\"form_key\"]').val('');\n this._gallery.find('input[name*=\"' + this.element.find(\n this._itemIdSelector).val() + '\"]'\n ).parent().removeClass('active');\n\n try {\n newVideoForm.validation('clearError');\n } catch (e) {\n\n }\n newVideoForm.trigger('reset');\n },\n\n /**\n * Find element by fileName\n * @param {String} file\n */\n findElementId: function (file) {\n var elem = this._gallery.find('.image.item').find('input[value=\"' + file + '\"]');\n\n if (!elem.length) {\n return null;\n }\n\n return $(elem).attr('name').replace('product[media_gallery][images][', '').replace('][file]', '');\n },\n\n /**\n * Save image roles\n * @param {Object} imageData\n */\n saveImageRoles: function (imageData) {\n var data = imageData.file,\n self = this,\n containers;\n\n if (data && data.length > 0) {\n containers = this._gallery.find('.image-placeholder').siblings('input');\n $.each(containers, function (i, el) {\n var start = el.name.indexOf('[') + 1,\n end = el.name.indexOf(']'),\n imageType = el.name.substring(start, end),\n imageCheckbox = self.element.find(\n self._videoFormSelector + ' input[value=\"' + imageType + '\"]'\n );\n\n self._changeRole(imageType, imageCheckbox.attr('checked'), imageData);\n });\n }\n },\n\n /**\n * Change image role\n * @param {String} imageType - role name\n * @param {bool} isEnabled - role active status\n * @param {Object} imageData - image data object\n * @private\n */\n _changeRole: function (imageType, isEnabled, imageData) {\n var needCheked = true;\n\n if (!isEnabled) {\n needCheked = this._gallery.find('input[name=\"product[' + imageType + ']\"]').val() === imageData.file;\n }\n\n if (!needCheked) {\n return null;\n }\n\n this._gallery.trigger('setImageType', {\n type: imageType,\n imageData: isEnabled ? imageData : null\n });\n },\n\n /**\n * On open dialog\n * @param {Object} e\n * @param {Object} imageData\n * @private\n */\n _onOpenDialog: function (e, imageData) {\n var formFields, flagChecked, file,\n modal = this.element.closest('.mage-new-video-dialog');\n\n if (imageData['media_type'] === 'external-video') {\n this.imageData = imageData;\n modal.find('.video-create-button').hide();\n modal.find('.video-delete-button').show();\n modal.find('.video-edit').show();\n modal.createVideoPlayer({\n reset: true\n }).createVideoPlayer('reset');\n\n formFields = modal.find(this._videoFormSelector).find('.edited-data');\n\n $.each(formFields, function (i, field) {\n $(field).val(imageData[field.name]);\n });\n\n flagChecked = imageData.disabled > 0;\n modal.find(this._videoDisableinputSelector).prop('checked', flagChecked);\n\n file = modal.find('#file_name').val(imageData.file);\n\n $.each(modal.find('.video_image_role'), function () {\n $(this).prop('checked', false).prop('disabled', false);\n });\n\n $.each(this._gallery.find('.image-placeholder').siblings('input:hidden'), function () {\n var start, end, imageRole;\n\n if ($(this).val() === file.val()) {\n start = this.name.indexOf('[') + 1;\n end = this.name.length - 1;\n imageRole = this.name.substring(start, end);\n modal.find('#new_video_form input[value=\"' + imageRole + '\"]').prop('checked', true);\n }\n });\n }\n\n },\n\n /**\n * Toggle buttons\n */\n toggleButtons: function () {\n var self = this,\n modal = this.element.closest('.mage-new-video-dialog');\n\n modal.find('.video-placeholder, .add-video-button-container > button').click(function () {\n modal.find('.video-create-button').show();\n modal.find('.video-delete-button').hide();\n modal.find('.video-edit').hide();\n modal.createVideoPlayer({\n reset: true\n }).createVideoPlayer('reset').updateInputFields({\n reset: true\n }).updateInputFields('reset');\n });\n this._gallery.on('click', '.item.video-item', function () {\n modal.find('.video-create-button').hide();\n modal.find('.video-delete-button').show();\n modal.find('.video-edit').show();\n modal.find('.mage-new-video-dialog').createVideoPlayer({\n reset: true\n }).createVideoPlayer('reset');\n });\n this._gallery.on('click', '.item.video-item:not(.removed)', function () {\n var flagChecked,\n file,\n formFields = modal.find('.edited-data'),\n container = $(this);\n\n $.each(formFields, function (i, field) {\n $(field).val(container.find('input[name*=\"' + field.name + '\"]').val());\n });\n\n flagChecked = container.find('input[name*=\"disabled\"]').val() > 0;\n self._gallery.find(self._videoDisableinputSelector).attr('checked', flagChecked);\n\n file = self._gallery.find('#file_name').val(container.find('input[name*=\"file\"]').val());\n\n $.each(self._gallery.find('.video_image_role'), function () {\n $(this).prop('checked', false).prop('disabled', false);\n });\n\n $.each(self._gallery.find('.image-placeholder').siblings('input:hidden'), function () {\n var start, end, imageRole;\n\n if ($(this).val() !== file.val()) {\n return null;\n }\n\n start = this.name.indexOf('[') + 1;\n end = this.name.length - 1;\n imageRole = this.name.substring(start, end);\n self._gallery.find('input[value=\"' + imageRole + '\"]').prop('checked', true);\n });\n });\n }\n });\n\n $('#group-fields-image-management > legend > span').text($.mage.__('Images and Videos'));\n\n return $.mage.newVideoDialog;\n});\n","Magento_ProductVideo/js/get-video-information.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'jquery',\n 'Magento_Ui/js/modal/alert',\n 'jquery/ui',\n 'mage/translate'\n], function ($, alert) {\n 'use strict';\n\n var videoRegister = {\n _register: {},\n\n /**\n * Checks, if api is already registered\n *\n * @param {String} api\n * @returns {bool}\n */\n isRegistered: function (api) {\n return this._register[api] !== undefined;\n },\n\n /**\n * Checks, if api is loaded\n *\n * @param {String} api\n * @returns {bool}\n */\n isLoaded: function (api) {\n return this._register[api] !== undefined && this._register[api] === true;\n },\n\n /**\n * Register new video api\n * @param {String} api\n * @param {bool} loaded\n */\n register: function (api, loaded) {\n loaded = loaded || false;\n this._register[api] = loaded;\n }\n };\n\n $.widget('mage.productVideoLoader', {\n\n /**\n * @private\n */\n _create: function () {\n switch (this.element.data('type')) {\n case 'youtube':\n this.element.videoYoutube();\n this._player = this.element.data('mageVideoYoutube');\n break;\n\n case 'vimeo':\n this.element.videoVimeo();\n this._player = this.element.data('mageVideoVimeo');\n break;\n default:\n throw {\n name: $.mage.__('Video Error'),\n message: $.mage.__('Unknown video type'),\n\n /**\n * Return string\n */\n toString: function () {\n return this.name + ': ' + this.message;\n }\n };\n }\n },\n\n /**\n * Initializes variables\n * @private\n */\n _initialize: function () {\n this._params = this.element.data('params') || {};\n this._code = this.element.data('code');\n this._width = this.element.data('width');\n this._height = this.element.data('height');\n this._autoplay = !!this.element.data('autoplay');\n this._playing = this._autoplay || false;\n this.useYoutubeNocookie = this.element.data('youtubenocookie') || false;\n\n this._responsive = this.element.data('responsive') !== false;\n\n if (this._responsive === true) {\n this.element.addClass('responsive');\n }\n\n this._calculateRatio();\n },\n\n /**\n * Abstract play command\n */\n play: function () {\n this._player.play();\n },\n\n /**\n * Abstract pause command\n */\n pause: function () {\n this._player.pause();\n },\n\n /**\n * Abstract stop command\n */\n stop: function () {\n this._player.stop();\n },\n\n /**\n * Abstract playing command\n */\n playing: function () {\n return this._player.playing();\n },\n\n /**\n * Abstract destroying command\n */\n destroy: function () {\n this._player.destroy();\n },\n\n /**\n * Calculates ratio for responsive videos\n * @private\n */\n _calculateRatio: function () {\n if (!this._responsive) {\n return;\n }\n this.element.css('paddingBottom', this._height / this._width * 100 + '%');\n }\n });\n\n $.widget('mage.videoYoutube', $.mage.productVideoLoader, {\n\n /**\n * Initialization of the Youtube widget\n * @private\n */\n _create: function () {\n var self = this;\n\n this._initialize();\n\n this.element.append('<div/>');\n\n this._on(window, {\n\n /**\n * Youtube state check\n * @private\n */\n 'youtubeapiready': function () {\n var host = 'https://www.youtube.com';\n\n if (self.useYoutubeNocookie) {\n host = 'https://www.youtube-nocookie.com';\n }\n\n if (self._player !== undefined) {\n return;\n }\n\n if (self._autoplay) {\n self._params.autoplay = 1;\n }\n self._params.rel = 0;\n\n self._player = new window.YT.Player(self.element.children(':first')[0], {\n height: self._height,\n width: self._width,\n videoId: self._code,\n playerVars: self._params,\n host: host,\n events: {\n\n /**\n * @private\n */\n 'onReady': function onPlayerReady() {\n self._player.getDuration();\n },\n\n /**\n * State change flag init\n */\n onStateChange: function (data) {\n switch (window.parseInt(data.data, 10)) {\n case 1:\n self._playing = true;\n break;\n default:\n self._playing = false;\n break;\n }\n\n self._trigger('statechange', {}, data);\n }\n }\n\n });\n }\n });\n\n this._loadApi();\n },\n\n /**\n * Loads Youtube API and triggers event, when loaded\n * @private\n */\n _loadApi: function () {\n var element,\n scriptTag;\n\n if (videoRegister.isRegistered('youtube')) {\n if (videoRegister.isLoaded('youtube')) {\n $(window).trigger('youtubeapiready');\n }\n\n return;\n }\n videoRegister.register('youtube');\n\n element = document.createElement('script');\n scriptTag = document.getElementsByTagName('script')[0];\n\n element.async = true;\n element.src = 'https://www.youtube.com/iframe_api';\n scriptTag.parentNode.insertBefore(element, scriptTag);\n\n /**\n * Trigger youtube api ready event\n */\n window.onYouTubeIframeAPIReady = function () {\n $(window).trigger('youtubeapiready');\n videoRegister.register('youtube', true);\n };\n },\n\n /**\n * Play command for Youtube\n */\n play: function () {\n this._player.playVideo();\n this._playing = true;\n },\n\n /**\n * Pause command for Youtube\n */\n pause: function () {\n this._player.pauseVideo();\n this._playing = false;\n },\n\n /**\n * Stop command for Youtube\n */\n stop: function () {\n this._player.stopVideo();\n this._playing = false;\n },\n\n /**\n * Playing command for Youtube\n */\n playing: function () {\n return this._playing;\n },\n\n /**\n * stops and unloads player\n * @private\n */\n destroy: function () {\n this.stop();\n this._player.destroy();\n }\n });\n\n $.widget('mage.videoVimeo', $.mage.productVideoLoader, {\n\n /**\n * Initialize the Vimeo widget\n * @private\n */\n _create: function () {\n var timestamp,\n src,\n additionalParams;\n\n this._initialize();\n timestamp = new Date().getTime();\n\n if (this._autoplay) {\n additionalParams += '&autoplay=1';\n }\n\n src = 'https://player.vimeo.com/video/' +\n this._code + '?api=1&player_id=vimeo' +\n this._code +\n timestamp +\n additionalParams;\n this.element.append(\n $('<iframe/>')\n .attr('frameborder', 0)\n .attr('id', 'vimeo' + this._code + timestamp)\n .attr('width', this._width)\n .attr('height', this._height)\n .attr('src', src)\n );\n\n }\n });\n\n $.widget('mage.videoData', {\n options: {\n youtubeKey: '',\n eventSource: '' //where is data going from - focus out or click on button\n },\n\n _REQUEST_VIDEO_INFORMATION_TRIGGER: 'request_video_information',\n\n _UPDATE_VIDEO_INFORMATION_TRIGGER: 'updated_video_information',\n\n _START_UPDATE_INFORMATION_TRIGGER: 'update_video_information',\n\n _ERROR_UPDATE_INFORMATION_TRIGGER: 'error_updated_information',\n\n _FINISH_UPDATE_INFORMATION_TRIGGER: 'finish_update_information',\n\n _VIDEO_URL_VALIDATE_TRIGGER: 'validate_video_url',\n\n _videoInformation: null,\n\n _currentVideoUrl: null,\n\n /**\n * @private\n */\n _init: function () {\n this.element.on(this._START_UPDATE_INFORMATION_TRIGGER, $.proxy(this._onRequestHandler, this));\n this.element.on(this._ERROR_UPDATE_INFORMATION_TRIGGER, $.proxy(this._onVideoInvalid, this));\n this.element.on(this._FINISH_UPDATE_INFORMATION_TRIGGER, $.proxy(\n function () {\n this._currentVideoUrl = null;\n }, this\n ));\n this.element.on(this._VIDEO_URL_VALIDATE_TRIGGER, $.proxy(this._onUrlValidateHandler, this));\n },\n\n /**\n * @private\n */\n _onUrlValidateHandler: function (event, callback, forceVideo) {\n var url = this.element.val(),\n videoInfo;\n\n videoInfo = this._validateURL(url, forceVideo);\n\n if (videoInfo) {\n callback();\n } else {\n this._onRequestError($.mage.__('Invalid video url'));\n }\n },\n\n /**\n * @private\n */\n _onRequestHandler: function () {\n var url = this.element.val(),\n self = this,\n videoInfo,\n type,\n id,\n googleapisUrl;\n\n if (this._currentVideoUrl === url) {\n return;\n }\n\n this._currentVideoUrl = url;\n\n this.element.trigger(this._REQUEST_VIDEO_INFORMATION_TRIGGER, {\n url: url\n });\n\n if (!url) {\n return;\n }\n\n videoInfo = this._validateURL(url);\n\n if (!videoInfo) {\n this._onRequestError($.mage.__('Invalid video url'));\n\n return;\n }\n\n /**\n *\n * @param {Object} data\n * @private\n */\n function _onYouTubeLoaded(data) {\n var tmp,\n uploadedFormatted,\n respData,\n createErrorMessage;\n\n /**\n * Create errors message\n *\n * @returns {String}\n */\n createErrorMessage = function () {\n var error = data.error,\n errors = error.errors,\n i,\n errLength = errors.length,\n tmpError,\n errReason,\n errorsMessage = [];\n\n for (i = 0; i < errLength; i++) {\n tmpError = errors[i];\n errReason = tmpError.reason;\n\n if (['keyInvalid'].indexOf(errReason) !== -1) {\n errorsMessage.push($.mage.__('Youtube API key is invalid'));\n\n break;\n }\n\n errorsMessage.push(tmpError.message);\n }\n\n return $.mage.__('Video cant be shown due to the following reason: ') +\n $.unique(errorsMessage).join(', ');\n };\n\n if (data.error && [400, 402, 403].indexOf(data.error.code) !== -1) {\n this._onRequestError(createErrorMessage());\n\n return;\n }\n\n if (!data.items || data.items.length < 1) {\n this._onRequestError($.mage.__('Video not found'));\n\n return;\n }\n\n tmp = data.items[0];\n uploadedFormatted = tmp.snippet.publishedAt.replace('T', ' ').replace(/\\..+/g, '');\n respData = {\n duration: this._formatYoutubeDuration(tmp.contentDetails.duration),\n channel: tmp.snippet.channelTitle,\n channelId: tmp.snippet.channelId,\n uploaded: uploadedFormatted,\n title: tmp.snippet.localized.title,\n description: tmp.snippet.description,\n thumbnail: tmp.snippet.thumbnails.high.url,\n videoId: videoInfo.id,\n videoProvider: videoInfo.type,\n useYoutubeNocookie: videoInfo.useYoutubeNocookie\n };\n this._videoInformation = respData;\n this.element.trigger(this._UPDATE_VIDEO_INFORMATION_TRIGGER, respData);\n this.element.trigger(this._FINISH_UPDATE_INFORMATION_TRIGGER, true);\n }\n\n /**\n * @private\n */\n function _onVimeoLoaded(data) {\n var tmp,\n respData;\n\n if (data.length < 1) {\n this._onRequestError($.mage.__('Video not found'));\n\n return null;\n }\n tmp = data[0];\n respData = {\n duration: this._formatVimeoDuration(tmp.duration),\n channel: tmp['user_name'],\n channelId: tmp['user_url'],\n uploaded: tmp['upload_date'],\n title: tmp.title,\n description: tmp.description.replace(/( |<([^>]+)>)/ig, ''),\n thumbnail: tmp['thumbnail_large'],\n videoId: videoInfo.id,\n videoProvider: videoInfo.type\n };\n this._videoInformation = respData;\n this.element.trigger(this._UPDATE_VIDEO_INFORMATION_TRIGGER, respData);\n this.element.trigger(this._FINISH_UPDATE_INFORMATION_TRIGGER, true);\n }\n\n type = videoInfo.type;\n id = videoInfo.id;\n\n if (type === 'youtube') {\n googleapisUrl = 'https://www.googleapis.com/youtube/v3/videos?id=' +\n id +\n '&part=snippet,contentDetails,statistics,status&key=' +\n this.options.youtubeKey + '&alt=json&callback=?';\n $.getJSON(googleapisUrl,\n {\n format: 'json'\n },\n $.proxy(_onYouTubeLoaded, self)\n ).fail(\n function () {\n self._onRequestError('Video not found');\n }\n );\n } else if (type === 'vimeo') {\n $.ajax({\n url: 'https://www.vimeo.com/api/v2/video/' + id + '.json',\n dataType: 'jsonp',\n data: {\n format: 'json'\n },\n timeout: 5000,\n success: $.proxy(_onVimeoLoaded, self),\n\n /**\n * @private\n */\n error: function () {\n self._onRequestError($.mage.__('Video not found'));\n }\n });\n }\n },\n\n /**\n * @private\n */\n _onVideoInvalid: function (event, data) {\n this._videoInformation = null;\n this.element.val('');\n alert({\n content: 'Error: \"' + data + '\"'\n });\n },\n\n /**\n * @private\n */\n _onRequestError: function (error) {\n this.element.trigger(this._ERROR_UPDATE_INFORMATION_TRIGGER, error);\n this.element.trigger(this._FINISH_UPDATE_INFORMATION_TRIGGER, false);\n this._currentVideoUrl = null;\n },\n\n /**\n * @private\n */\n _formatYoutubeDuration: function (duration) {\n var match = duration.match(/PT(\\d+H)?(\\d+M)?(\\d+S)?/),\n hours = parseInt(match[1], 10) || 0,\n minutes = parseInt(match[2], 10) || 0,\n seconds = parseInt(match[3], 10) || 0;\n\n return this._formatVimeoDuration(hours * 3600 + minutes * 60 + seconds);\n },\n\n /**\n * @private\n */\n _formatVimeoDuration: function (seconds) {\n return (new Date(seconds * 1000)).toUTCString().match(/(\\d\\d:\\d\\d:\\d\\d)/)[0];\n },\n\n /**\n * @private\n */\n _parseHref: function (href) {\n var a = document.createElement('a');\n\n a.href = href;\n\n return a;\n },\n\n /**\n * @private\n */\n _validateURL: function (href, forceVideo) {\n var id,\n type,\n ampersandPosition,\n vimeoRegex,\n useYoutubeNocookie = false;\n\n if (typeof href !== 'string') {\n return href;\n }\n href = this._parseHref(href);\n\n if (href.host.match(/youtube\\.com/) && href.search) {\n\n id = href.search.split('v=')[1];\n\n if (id) {\n ampersandPosition = id.indexOf('&');\n type = 'youtube';\n }\n\n if (id && ampersandPosition !== -1) {\n id = id.substring(0, ampersandPosition);\n }\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\n if (href.host.match(/youtube-nocookie.com/)) {\n useYoutubeNocookie = true;\n }\n } else if (href.host.match(/vimeo\\.com/)) {\n type = 'vimeo';\n vimeoRegex = new RegExp(['https?:\\\\/\\\\/(?:www\\\\.|player\\\\.)?vimeo.com\\\\/(?:channels\\\\/(?:\\\\w+\\\\/)',\n '?|groups\\\\/([^\\\\/]*)\\\\/videos\\\\/|album\\\\/(\\\\d+)\\\\/video\\\\/|video\\\\/|)(\\\\d+)(?:$|\\\\/|\\\\?)'\n ].join(''));\n\n if (href.href.match(vimeoRegex) != null) {\n id = href.href.match(vimeoRegex)[3];\n }\n }\n\n if ((!id || !type) && forceVideo) {\n id = href.href;\n type = 'custom';\n }\n\n return id ? {\n id: id, type: type, s: href.search.replace(/^\\?/, ''), useYoutubeNocookie: useYoutubeNocookie\n } : false;\n }\n });\n });\n","MSP_TwoFactorAuth/js/error.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'Magento_Ui/js/modal/alert',\n 'mage/translate'\n], function ($, alert) {\n return {\n /**\n * Display an error message\n * @param {String} message\n */\n display: function (message) {\n alert({\n title: $.mage.__('Error'),\n content: message\n });\n }\n };\n});\n","MSP_TwoFactorAuth/js/registry.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'ko'\n], function (ko) {\n return {\n trustThisDevice: ko.observable(false)\n };\n});\n","MSP_TwoFactorAuth/js/trust_device.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/registry'\n], function (Component, registry) {\n return Component.extend({\n checked: registry.trustThisDevice,\n\n defaults: {\n template: 'MSP_TwoFactorAuth/trust_device'\n }\n });\n});\n","MSP_TwoFactorAuth/js/change_provider.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'uiComponent',\n 'ko'\n], function (Component, ko) {\n return Component.extend({\n showChangeMethod: ko.observable(false),\n\n providers: [],\n switchIcon: '',\n\n defaults: {\n template: 'MSP_TwoFactorAuth/change_provider'\n },\n\n /**\n * Get switch icon URL\n * @returns {String}\n */\n getSwitchIconUrl: function () {\n return this.switchIcon;\n },\n\n /**\n * Show available alternative 2FA providers\n */\n displayChangeMethod: function () {\n this.showChangeMethod(true);\n },\n\n /**\n * Return a list of alternative providers\n * @returns {Array}\n */\n getProviders: function () {\n return this.providers;\n }\n });\n});\n","MSP_TwoFactorAuth/js/form/provider.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'Magento_Ui/js/form/provider'\n], function (Provider) {\n return Provider.extend({\n /**\n * @see Magento_Ui/js/form/provider\n * @returns {Element}\n */\n save: function () {\n // Disable independent save (we have a parent form with own validation)\n return this;\n }\n });\n});\n","MSP_TwoFactorAuth/js/form/element/trusted_devices.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'Magento_Ui/js/form/element/abstract'\n], function (Abstract) {\n return Abstract.extend({\n /**\n * Get a list of trusted devices\n * @returns {Array}\n */\n getTrustedDevices: function () {\n return this.source.data['trusted_devices'] ? this.source.data['trusted_devices'] : [];\n },\n\n /**\n * Revoke a trusted device\n * @param {Object} item\n */\n revokeDevice: function (item) {\n self.location.href = item['revoke_url'];\n }\n });\n});\n","MSP_TwoFactorAuth/js/form/element/reset_providers.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'Magento_Ui/js/form/element/abstract',\n 'Magento_Ui/js/modal/confirm'\n], function (Abstract, confirm) {\n return Abstract.extend({\n /**\n * Get a list of providers with reset option\n * @returns {Array}\n */\n getResetProviders: function () {\n return this.source.data['reset_providers'] ? this.source.data['reset_providers'] : [];\n },\n\n /**\n * Reset a provider\n * @param {Object} item\n */\n resetProvider: function (item) {\n confirm({\n title: 'Confirm',\n content: 'Are you sure you want to reset ' + item.label + ' provider?',\n actions: {\n confirm: function () { // jscs:ignore jsDoc\n self.location.href = item.url;\n }\n }\n });\n }\n });\n});\n","MSP_TwoFactorAuth/js/form/element/providers.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine(['Magento_Ui/js/form/element/abstract'], function (Abstract) {\n return Abstract.extend({\n /**\n * Get a list of forced providers\n * @returns {Array}\n */\n getForcedProviders: function () {\n return this.forced_providers;\n },\n\n /**\n * Get a list of enabled providers\n * @returns {Array}\n */\n getEnabledProviders: function () {\n return this.enabled_providers;\n },\n\n /**\n * Return true if a provider is selected\n * @param {String} provider\n * @returns {Boolean}\n */\n isSelected: function (provider) {\n var i, providers = this.value();\n\n for (i = 0; i < providers.length; i++) {\n if (providers[i] === provider) {\n return true;\n }\n }\n\n return false;\n }\n });\n});\n","MSP_TwoFactorAuth/js/authy/auth.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/registry',\n 'mage/translate'\n], function ($, ko, Component, error, registry) {\n return Component.extend({\n selectedMethod: ko.observable(''),\n waitingText: ko.observable(''),\n success: ko.observable(false),\n tokenCode: ko.observable(''),\n\n trustThisDevice: registry.trustThisDevice,\n\n defaults: {\n template: 'MSP_TwoFactorAuth/authy/auth'\n },\n\n waitForOneTouchApprovalTimeout: 0,\n\n postUrl: '',\n tokenRequestUrl: '',\n oneTouchUrl: '',\n verifyOneTouchUrl: '',\n\n /**\n * Get auth post URL\n * @returns {String}\n */\n getPostUrl: function () {\n return this.postUrl;\n },\n\n /**\n * Get token request URL\n * @returns {String}\n */\n getTokenRequestUrl: function () {\n return this.tokenRequestUrl;\n },\n\n /**\n * Get one touch request URL\n * @returns {String}\n */\n getOneTouchUrl: function () {\n return this.oneTouchUrl;\n },\n\n /**\n * Get one touch verification URL\n * @returns {String}\n */\n getVerifyOneTouchUrl: function () {\n return this.verifyOneTouchUrl;\n },\n\n /**\n * Get success URL\n * @returns {String}\n */\n getSuccessUrl: function () {\n return this.successUrl;\n },\n\n /**\n * Go to login page\n */\n login: function () {\n this.success(true);\n self.location.href = this.getSuccessUrl();\n },\n\n /**\n * Stop onetouch approval background approval\n */\n stopWaitingOnetouchApproval: function () {\n if (this.waitForOneTouchApprovalTimeout) {\n window.clearTimeout(this.waitForOneTouchApprovalTimeout);\n this.waitForOneTouchApprovalTimeout = 0;\n }\n },\n\n /**\n * Switch to authy code validation\n * @param {String} via\n */\n runSendCode: function (via) {\n var me = this;\n\n this.selectedMethod('code');\n\n if (via !== 'token') {\n $.getJSON(\n this.getTokenRequestUrl() + '?via=' +\n via + '&tfa_trust_device=' + (me.trustThisDevice() ? 1 : 0)\n )\n .fail(function () {\n error.display('There was an error trying to contact Authy services');\n me.switchAnotherMethod();\n });\n }\n },\n\n /**\n * Switch to authy token code validation\n */\n runSendCodeToken: function () {\n this.runSendCode('token');\n },\n\n /**\n * Switch to authy sms code validation\n */\n runSendCodeSms: function () {\n this.runSendCode('sms');\n },\n\n /**\n * Switch to authy call code validation\n */\n runSendCodeCall: function () {\n this.runSendCode('call');\n },\n\n /**\n * Switch to one touch validation\n */\n runOneTouch: function () {\n var me = this;\n\n this.selectedMethod('onetouch');\n this.waitingText('Sending push notification...');\n this.success(false);\n\n this.stopWaitingOnetouchApproval();\n\n $.getJSON(this.getOneTouchUrl() + '?tfa_trust_device=' + (me.trustThisDevice() ? 1 : 0))\n .done(function () {\n me.waitForOneTouchApproval();\n })\n .fail(function () {\n error.display('There was an error trying to contact Authy services');\n me.switchAnotherMethod();\n });\n },\n\n /**\n * Start background one touch approval check\n */\n waitForOneTouchApproval: function () {\n var me = this;\n\n this.waitingText('Waiting for approval...');\n\n $.getJSON(this.getVerifyOneTouchUrl())\n .done(function (res) {\n if (res.status === 'retry') {\n me.waitForOneTouchApprovalTimeout = window.setTimeout(function () {\n me.waitForOneTouchApproval();\n }, 1000);\n } else if (res.status === 'expired') {\n error.display($.mage.__('Your request has been expired'));\n me.switchAnotherMethod();\n } else if (res.status === 'denied') {\n error.display($.mage.__('Your request has been rejected'));\n me.switchAnotherMethod();\n } else if (res.status === 'approved') {\n me.login();\n }\n })\n .fail(function () {\n error.display('There was an error trying to contact Authy services');\n this.switchAnotherMethod();\n });\n },\n\n /**\n * Switch back to method selection\n */\n switchAnotherMethod: function () {\n this.selectedMethod('');\n this.waitingText('');\n this.success(false);\n },\n\n /**\n * Verify authy code\n */\n verifyCode: function () {\n var me = this;\n\n this.waitingText('Please wait...');\n\n $.post(this.getPostUrl(), {\n 'tfa_code': this.tokenCode\n })\n .done(function (res) {\n if (res.success) {\n me.login();\n } else {\n error.display(res.message);\n me.waitingText('');\n me.tokenCode('');\n }\n })\n .fail(function () {\n error.display('There was an internal error trying to verify your code');\n });\n }\n });\n});\n","MSP_TwoFactorAuth/js/authy/configure.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/authy/configure/registry'\n], function (ko, Component, registry) {\n return Component.extend({\n currentStep: registry.currentStep,\n defaults: {\n template: 'MSP_TwoFactorAuth/authy/configure'\n }\n });\n});\n","MSP_TwoFactorAuth/js/authy/configure/registry.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'ko'\n], function (ko) {\n return {\n currentStep: ko.observable('register'),\n messageText: ko.observable(''),\n secondsToExpire: ko.observable(0)\n };\n});\n","MSP_TwoFactorAuth/js/authy/configure/verify.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/authy/configure/registry',\n 'mage/translate'\n], function ($, ko, Component, error, registry) {\n return Component.extend({\n verifyCode: ko.observable(''),\n messageText: registry.messageText,\n waitText: ko.observable(''),\n\n verifyPostUrl: '',\n successUrl: '',\n\n defaults: {\n template: 'MSP_TwoFactorAuth/authy/configure/verify'\n },\n\n /**\n * Get verification post URL\n * @returns {String}\n */\n getVerifyPostUrl: function () {\n return this.verifyPostUrl;\n },\n\n /**\n * Go to next step\n */\n nextStep: function () {\n registry.currentStep('login');\n self.location.href = this.successUrl;\n },\n\n /**\n * Verify auth code\n */\n doVerify: function () {\n var me = this;\n\n this.waitText('Please wait...');\n $.post(this.getVerifyPostUrl(), {\n 'tfa_verify': this.verifyCode()\n })\n .done(function (res) {\n if (res.success) {\n me.nextStep();\n } else {\n error.display(res.message);\n }\n me.waitText('');\n })\n .fail(function () {\n error.display('There was an internal error trying to verify your code');\n me.waitText('');\n });\n },\n\n /**\n * Go to previous step to change phone number\n */\n changePhoneNumber: function () {\n registry.currentStep('register');\n }\n });\n});\n","MSP_TwoFactorAuth/js/authy/configure/register.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/authy/configure/registry',\n 'mage/translate'\n], function ($, ko, Component, error, registry) {\n return Component.extend({\n configurePostUrl: '',\n countries: [],\n\n country: ko.observable(''),\n phone: ko.observable(''),\n method: ko.observable(''),\n\n waitText: ko.observable(''),\n\n defaults: {\n template: 'MSP_TwoFactorAuth/authy/configure/register'\n },\n\n /**\n * Get configure POST url\n * @returns {String}\n */\n getConfigurePostUrl: function () {\n return this.configurePostUrl;\n },\n\n /**\n * Get a list of available countries\n * @returns {Array}\n */\n getCountries: function () {\n return this.countries;\n },\n\n /**\n * Go to next step\n */\n nextStep: function () {\n registry.currentStep('verify');\n window.setTimeout(function () {\n registry.currentStep('register');\n }, registry.secondsToExpire() * 1000);\n },\n\n /**\n * Start Authy registration procedure\n */\n doRegister: function () {\n var me = this;\n\n this.waitText('Please wait...');\n $.post(this.getConfigurePostUrl(), {\n 'tfa_country': this.country(),\n 'tfa_phone': this.phone(),\n 'tfa_method': this.method()\n\n })\n .done(function (res) {\n if (res.success) {\n registry.messageText(res.message);\n registry.secondsToExpire(res['seconds_to_expire']);\n me.nextStep();\n } else {\n error.display(res.message);\n }\n me.waitText('');\n })\n .fail(function () {\n error.display('There was an internal error trying to verify your code');\n me.waitText('');\n });\n }\n });\n});\n","MSP_TwoFactorAuth/js/duo/auth.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/duo/api'\n], function (ko, Component, duo) {\n return Component.extend({\n currentStep: ko.observable('register'),\n\n defaults: {\n template: 'MSP_TwoFactorAuth/duo/auth'\n },\n\n signature: '',\n apiHost: '',\n postUrl: '',\n authenticateData: {},\n\n /**\n * Start waiting loop\n */\n onAfterRender: function () {\n window.setTimeout(function () {\n duo.init();\n }, 100);\n },\n\n /**\n * Get POST URL\n * @returns {String}\n */\n getPostUrl: function () {\n return this.postUrl;\n },\n\n /**\n * Get signature\n * @returns {String}\n */\n getSignature: function () {\n return this.signature;\n },\n\n /**\n * Get API host\n * @returns {String}\n */\n getApiHost: function () {\n return this.apiHost;\n }\n });\n});\n","MSP_TwoFactorAuth/js/duo/api.js":"/**\n * Duo Web SDK v2\n * Copyright 2017, Duo Security\n */\n\n/* eslint-disable */\n// jscs:disable\n\n(function (root, factory) {\n/*eslint-disable */\nif (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n /*eslint-enable */\n} else if (typeof module === 'object' && module.exports) {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n} else {\n // Browser globals (root is window)\n var Duo = factory();\n // If the Javascript was loaded via a script tag, attempt to autoload\n // the frame.\n\n Duo._onReady(Duo.init);\n\n // Attach Duo to the `window` object\n root.Duo = Duo;\n}\n}(this, function () {\n var DUO_MESSAGE_FORMAT = /^(?:AUTH|ENROLL)+\\|[A-Za-z0-9\\+\\/=]+\\|[A-Za-z0-9\\+\\/=]+$/;\n var DUO_ERROR_FORMAT = /^ERR\\|[\\w\\s\\.\\(\\)]+$/;\n var DUO_OPEN_WINDOW_FORMAT = /^DUO_OPEN_WINDOW\\|/;\n var VALID_OPEN_WINDOW_DOMAINS = [\n 'duo.com',\n 'duosecurity.com',\n 'duomobile.s3-us-west-1.amazonaws.com'\n ];\n\n var iframeId = 'duo_iframe',\n postAction = '',\n postArgument = 'sig_response',\n host,\n sigRequest,\n duoSig,\n appSig,\n iframe,\n submitCallback;\n\n function throwError(message, url) {\n throw new Error(\n 'Duo Web SDK error: ' + message +\n (url ? '\\n' + 'See ' + url + ' for more information' : '')\n );\n }\n\n function hyphenize(str) {\n return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase();\n }\n\n // cross-browser data attributes\n function getDataAttribute(element, name) {\n if ('dataset' in element) {\n return element.dataset[name];\n }\n\n return element.getAttribute('data-' + hyphenize(name));\n\n }\n\n // cross-browser event binding/unbinding\n function on(context, event, fallbackEvent, callback) {\n if ('addEventListener' in window) {\n context.addEventListener(event, callback, false);\n } else {\n context.attachEvent(fallbackEvent, callback);\n }\n }\n\n function off(context, event, fallbackEvent, callback) {\n if ('removeEventListener' in window) {\n context.removeEventListener(event, callback, false);\n } else {\n context.detachEvent(fallbackEvent, callback);\n }\n }\n\n function onReady(callback) {\n on(document, 'DOMContentLoaded', 'onreadystatechange', callback);\n }\n\n function offReady(callback) {\n off(document, 'DOMContentLoaded', 'onreadystatechange', callback);\n }\n\n function onMessage(callback) {\n on(window, 'message', 'onmessage', callback);\n }\n\n function offMessage(callback) {\n off(window, 'message', 'onmessage', callback);\n }\n\n /**\n * Parse the sig_request parameter, throwing errors if the token contains\n * a server error or if the token is invalid.\n *\n * @param {String} sig Request token\n */\n function parseSigRequest(sig) {\n if (!sig) {\n // nothing to do\n return;\n }\n\n // see if the token contains an error, throwing it if it does\n if (sig.indexOf('ERR|') === 0) {\n throwError(sig.split('|')[1]);\n }\n\n // validate the token\n if (sig.indexOf(':') === -1 || sig.split(':').length !== 2) {\n throwError(\n 'Duo was given a bad token. This might indicate a configuration ' +\n 'problem with one of Duo\\'s client libraries.',\n 'https://www.duosecurity.com/docs/duoweb#first-steps'\n );\n }\n\n var sigParts = sig.split(':');\n\n // hang on to the token, and the parsed duo and app sigs\n sigRequest = sig;\n duoSig = sigParts[0];\n appSig = sigParts[1];\n\n return {\n sigRequest: sig,\n duoSig: sigParts[0],\n appSig: sigParts[1]\n };\n }\n\n /**\n * This function is set up to run when the DOM is ready, if the iframe was\n * not available during `init`.\n */\n function onDOMReady() {\n iframe = document.getElementById(iframeId);\n\n if (!iframe) {\n throw new Error(\n 'This page does not contain an iframe for Duo to use.' +\n 'Add an element like <iframe id=\"duo_iframe\"></iframe> ' +\n 'to this page. ' +\n 'See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe ' +\n 'for more information.'\n );\n }\n\n // we've got an iframe, away we go!\n ready();\n\n // always clean up after yourself\n offReady(onDOMReady);\n }\n\n /**\n * Validate that a MessageEvent came from the Duo service, and that it\n * is a properly formatted payload.\n *\n * The Google Chrome sign-in page injects some JS into pages that also\n * make use of postMessage, so we need to do additional validation above\n * and beyond the origin.\n *\n * @param {MessageEvent} event Message received via postMessage\n */\n function isDuoMessage(event) {\n return Boolean(\n event.origin === 'https://' + host &&\n typeof event.data === 'string' &&\n (\n event.data.match(DUO_MESSAGE_FORMAT) ||\n event.data.match(DUO_ERROR_FORMAT) ||\n event.data.match(DUO_OPEN_WINDOW_FORMAT)\n )\n );\n }\n\n /**\n * Validate the request token and prepare for the iframe to become ready.\n *\n * All options below can be passed into an options hash to `Duo.init`, or\n * specified on the iframe using `data-` attributes.\n *\n * Options specified using the options hash will take precedence over\n * `data-` attributes.\n *\n * Example using options hash:\n * ```javascript\n * Duo.init({\n * iframe: \"some_other_id\",\n * host: \"api-main.duo.test\",\n * sig_request: \"...\",\n * post_action: \"/auth\",\n * post_argument: \"resp\"\n * });\n * ```\n *\n * Example using `data-` attributes:\n * ```\n * <iframe id=\"duo_iframe\"\n * data-host=\"api-main.duo.test\"\n * data-sig-request=\"...\"\n * data-post-action=\"/auth\"\n * data-post-argument=\"resp\"\n * >\n * </iframe>\n * ```\n *\n * @param {Object} options\n * @param {String} options.iframe The iframe, or id of an iframe to set up\n * @param {String} options.host Hostname\n * @param {String} options.sig_request Request token\n * @param {String} [options.post_action=''] URL to POST back to after successful auth\n * @param {String} [options.post_argument='sig_response'] Parameter name to use for response token\n * @param {Function} [options.submit_callback] If provided, duo will not submit the form instead execute\n * the callback function with reference to the \"duo_form\" form object\n * submit_callback can be used to prevent the webpage from reloading.\n */\n function init(options) {\n if (options) {\n if (options.host) {\n host = options.host;\n }\n\n if (options.sig_request) {\n parseSigRequest(options.sig_request);\n }\n\n if (options.post_action) {\n postAction = options.post_action;\n }\n\n if (options.post_argument) {\n postArgument = options.post_argument;\n }\n\n if (options.iframe) {\n if (options.iframe.tagName) {\n iframe = options.iframe;\n } else if (typeof options.iframe === 'string') {\n iframeId = options.iframe;\n }\n }\n\n if (typeof options.submit_callback === 'function') {\n submitCallback = options.submit_callback;\n }\n }\n\n // if we were given an iframe, no need to wait for the rest of the DOM\n if (iframe) {\n ready();\n } else {\n // try to find the iframe in the DOM\n iframe = document.getElementById(iframeId);\n\n // iframe is in the DOM, away we go!\n if (iframe) {\n ready();\n } else {\n // wait until the DOM is ready, then try again\n onReady(onDOMReady);\n }\n }\n\n // always clean up after yourself!\n offReady(init);\n }\n\n /**\n * This function is called when a message was received from another domain\n * using the `postMessage` API. Check that the event came from the Duo\n * service domain, and that the message is a properly formatted payload,\n * then perform the post back to the primary service.\n *\n * @param event Event object (contains origin and data)\n */\n function onReceivedMessage(event) {\n if (isDuoMessage(event)) {\n if (event.data.match(DUO_OPEN_WINDOW_FORMAT)) {\n var url = event.data.substring('DUO_OPEN_WINDOW|'.length);\n\n if (isValidUrlToOpen(url)) {\n // Open the URL that comes after the DUO_WINDOW_OPEN token.\n window.open(url, '_self');\n }\n } else {\n // the event came from duo, do the post back\n doPostBack(event.data);\n\n // always clean up after yourself!\n offMessage(onReceivedMessage);\n }\n }\n }\n\n /**\n * Validate that this passed in URL is one that we will actually allow to\n * be opened.\n * @param url String URL that the message poster wants to open\n * @returns {boolean} true if we allow this url to be opened in the window\n */\n function isValidUrlToOpen(url) {\n if (!url) {\n return false;\n }\n\n var parser = document.createElement('a');\n\n parser.href = url;\n\n if (parser.protocol === 'duotrustedendpoints:') {\n return true;\n } else if (parser.protocol !== 'https:') {\n return false;\n }\n\n for (var i = 0; i < VALID_OPEN_WINDOW_DOMAINS.length; i++) {\n if (parser.hostname.endsWith('.' + VALID_OPEN_WINDOW_DOMAINS[i]) ||\n parser.hostname === VALID_OPEN_WINDOW_DOMAINS[i]) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Point the iframe at Duo, then wait for it to postMessage back to us.\n */\n function ready() {\n if (!host) {\n host = getDataAttribute(iframe, 'host');\n\n if (!host) {\n throwError(\n 'No API hostname is given for Duo to use. Be sure to pass ' +\n 'a `host` parameter to Duo.init, or through the `data-host` ' +\n 'attribute on the iframe element.',\n 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'\n );\n }\n }\n\n if (!duoSig || !appSig) {\n parseSigRequest(getDataAttribute(iframe, 'sigRequest'));\n\n if (!duoSig || !appSig) {\n throwError(\n 'No valid signed request is given. Be sure to give the ' +\n '`sig_request` parameter to Duo.init, or use the ' +\n '`data-sig-request` attribute on the iframe element.',\n 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'\n );\n }\n }\n\n // if postAction/Argument are defaults, see if they are specified\n // as data attributes on the iframe\n if (postAction === '') {\n postAction = getDataAttribute(iframe, 'postAction') || postAction;\n }\n\n if (postArgument === 'sig_response') {\n postArgument = getDataAttribute(iframe, 'postArgument') || postArgument;\n }\n\n // point the iframe at Duo\n iframe.src = [\n 'https://', host, '/frame/web/v1/auth?tx=', duoSig,\n '&parent=', encodeURIComponent(document.location.href),\n '&v=2.6'\n ].join('');\n\n // listen for the 'message' event\n onMessage(onReceivedMessage);\n }\n\n /**\n * We received a postMessage from Duo. POST back to the primary service\n * with the response token, and any additional user-supplied parameters\n * given in form#duo_form.\n */\n function doPostBack(response) {\n // create a hidden input to contain the response token\n var input = document.createElement('input');\n\n input.type = 'hidden';\n input.name = postArgument;\n input.value = response + ':' + appSig;\n\n // user may supply their own form with additional inputs\n var form = document.getElementById('duo_form');\n\n // if the form doesn't exist, create one\n if (!form) {\n form = document.createElement('form');\n\n // insert the new form after the iframe\n iframe.parentElement.insertBefore(form, iframe.nextSibling);\n }\n\n // make sure we are actually posting to the right place\n form.method = 'POST';\n form.action = postAction;\n\n // add the response token input to the form\n form.appendChild(input);\n\n // away we go!\n if (typeof submitCallback === 'function') {\n submitCallback.call(null, form);\n } else {\n form.submit();\n }\n }\n\n return {\n init: init,\n _onReady: onReady,\n _parseSigRequest: parseSigRequest,\n _isDuoMessage: isDuoMessage,\n _doPostBack: doPostBack\n };\n}));\n\n/* eslint-enable */\n","MSP_TwoFactorAuth/js/google/auth.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/registry'\n], function ($, ko, Component, error, registry) {\n return Component.extend({\n currentStep: ko.observable('register'),\n waitText: ko.observable(''),\n verifyCode: ko.observable(''),\n defaults: {\n template: 'MSP_TwoFactorAuth/google/auth'\n },\n\n trustThisDevice: registry.trustThisDevice,\n\n qrCodeUrl: '',\n postUrl: '',\n successUrl: '',\n\n /**\n * Get QR code URL\n * @returns {String}\n */\n getQrCodeUrl: function () {\n return this.qrCodeUrl;\n },\n\n /**\n * Get POST URL\n * @returns {String}\n */\n getPostUrl: function () {\n return this.postUrl;\n },\n\n /**\n * Go to next step\n */\n nextStep: function () {\n this.currentStep('login');\n self.location.href = this.successUrl;\n },\n\n /**\n * Verify auth code\n */\n doVerify: function () {\n var me = this;\n\n this.waitText('Please wait...');\n $.post(this.getPostUrl(), {\n 'tfa_code': this.verifyCode(),\n 'tfa_trust_device': me.trustThisDevice() ? 1 : 0\n })\n .done(function (res) {\n if (res.success) {\n me.nextStep();\n } else {\n error.display(res.message);\n me.verifyCode('');\n }\n me.waitText('');\n })\n .fail(function () {\n error.display('There was an internal error trying to verify your code');\n me.waitText('');\n });\n }\n });\n});\n","MSP_TwoFactorAuth/js/u2fkey/auth.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/registry',\n 'MSP_TwoFactorAuth/js/u2fkey/api'\n], function ($, ko, Component, error, registry) {\n return Component.extend({\n currentStep: ko.observable('register'),\n trustThisDevice: registry.trustThisDevice,\n\n defaults: {\n template: 'MSP_TwoFactorAuth/u2fkey/auth'\n },\n\n postUrl: '',\n successUrl: '',\n touchImageUrl: '',\n authenticateData: {},\n\n /**\n * Start waiting loop\n */\n onAfterRender: function () {\n this.waitForTouch();\n },\n\n /**\n * Get touch image URL\n * @returns {String}\n */\n getTouchImageUrl: function () {\n return this.touchImageUrl;\n },\n\n /**\n * Get POST URL\n * @returns {String}\n */\n getPostUrl: function () {\n return this.postUrl;\n },\n\n /**\n * Get success URL\n * @returns {String}\n */\n getSuccessUrl: function () {\n return this.successUrl;\n },\n /**\n * Wait for key touch\n */\n waitForTouch: function () {\n var requestData = this.authenticateData,\n me = this;\n\n // eslint-disable-next-line no-undef\n u2f.sign(\n requestData,\n function (signResponse) {\n $.post(me.getPostUrl(), {\n 'request': requestData,\n 'response': signResponse,\n 'tfa_trust_device': me.trustThisDevice() ? 1 : 0\n }).done(function (res) {\n if (res.success) {\n me.currentStep('login');\n self.location.href = me.getSuccessUrl();\n } else {\n me.waitForTouch();\n }\n }).fail(function () {\n error.display('Invalid device');\n });\n }, 120\n );\n }\n });\n});\n","MSP_TwoFactorAuth/js/u2fkey/api.js":"// Copyright 2014-2015 Google Inc. All rights reserved.\n//\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file or at\n// https://developers.google.com/open-source/licenses/bsd\n\n/* eslint-disable */\n// jscs:disable\n\n/**\n * @fileoverview The U2F api.\n */\n\n'use strict';\n\n/** Namespace for the U2F api.\n * @type {Object}\n */\nvar u2f = u2f || {};\n\n/**\n * The U2F extension id\n * @type {String}\n * @const\n */\nu2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';\n\n/**\n * Message types for messsages to/from the extension\n * @const\n * @enum {String}\n */\nu2f.MessageTypes = {\n 'U2F_REGISTER_REQUEST': 'u2f_register_request',\n 'U2F_SIGN_REQUEST': 'u2f_sign_request',\n 'U2F_REGISTER_RESPONSE': 'u2f_register_response',\n 'U2F_SIGN_RESPONSE': 'u2f_sign_response'\n};\n\n/**\n * Response status codes\n * @const\n * @enum {number}\n */\nu2f.ErrorCodes = {\n 'OK': 0,\n 'OTHER_ERROR': 1,\n 'BAD_REQUEST': 2,\n 'CONFIGURATION_UNSUPPORTED': 3,\n 'DEVICE_INELIGIBLE': 4,\n 'TIMEOUT': 5\n};\n\n/**\n * A message type for registration requests\n * @typedef {{\n * type: u2f.MessageTypes,\n * signRequests: Array<u2f.SignRequest>,\n * registerRequests: ?Array<u2f.RegisterRequest>,\n * timeoutSeconds: ?number,\n * requestId: ?number\n * }}\n */\nu2f.Request;\n\n/**\n * A message for registration responses\n * @typedef {{\n * type: u2f.MessageTypes,\n * responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),\n * requestId: ?number\n * }}\n */\nu2f.Response;\n\n/**\n * An error object for responses\n * @typedef {{\n * errorCode: u2f.ErrorCodes,\n * errorMessage: ?string\n * }}\n */\nu2f.Error;\n\n/**\n * Data object for a single sign request.\n * @typedef {{\n * version: string,\n * challenge: string,\n * keyHandle: string,\n * appId: string\n * }}\n */\nu2f.SignRequest;\n\n/**\n * Data object for a sign response.\n * @typedef {{\n * keyHandle: string,\n * signatureData: string,\n * clientData: string\n * }}\n */\nu2f.SignResponse;\n\n/**\n * Data object for a registration request.\n * @typedef {{\n * version: string,\n * challenge: string,\n * appId: string\n * }}\n */\nu2f.RegisterRequest;\n\n/**\n * Data object for a registration response.\n * @typedef {{\n * registrationData: string,\n * clientData: string\n * }}\n */\nu2f.RegisterResponse;\n\n\n// Low level MessagePort API support\n\n/**\n * Sets up a MessagePort to the U2F extension using the\n * available mechanisms.\n * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback\n */\nu2f.getMessagePort = function (callback) {\n if (typeof chrome != 'undefined' && chrome.runtime) {\n // The actual message here does not matter, but we need to get a reply\n // for the callback to run. Thus, send an empty signature request\n // in order to get a failure response.\n var msg = {\n type: u2f.MessageTypes.U2F_SIGN_REQUEST,\n signRequests: []\n };\n\n chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function () {\n if (!chrome.runtime.lastError) {\n // We are on a whitelisted origin and can talk directly\n // with the extension.\n u2f.getChromeRuntimePort_(callback);\n } else {\n // chrome.runtime was available, but we couldn't message\n // the extension directly, use iframe\n u2f.getIframePort_(callback);\n }\n });\n } else if (u2f.isAndroidChrome_()) {\n u2f.getAuthenticatorPort_(callback);\n } else {\n // chrome.runtime was not available at all, which is normal\n // when this origin doesn't have access to any extensions.\n u2f.getIframePort_(callback);\n }\n};\n\n/**\n * Detect chrome running on android based on the browser's useragent.\n * @private\n */\nu2f.isAndroidChrome_ = function () {\n var userAgent = navigator.userAgent;\n\n return userAgent.indexOf('Chrome') != -1 &&\n userAgent.indexOf('Android') != -1;\n};\n\n/**\n * Connects directly to the extension via chrome.runtime.connect\n * @param {function(u2f.WrappedChromeRuntimePort_)} callback\n * @private\n */\nu2f.getChromeRuntimePort_ = function (callback) {\n var port = chrome.runtime.connect(u2f.EXTENSION_ID,\n {\n 'includeTlsChannelId': true\n });\n\n setTimeout(function () {\n callback(new u2f.WrappedChromeRuntimePort_(port));\n }, 0);\n};\n\n/**\n * Return a 'port' abstraction to the Authenticator app.\n * @param {function(u2f.WrappedAuthenticatorPort_)} callback\n * @private\n */\nu2f.getAuthenticatorPort_ = function (callback) {\n setTimeout(function () {\n callback(new u2f.WrappedAuthenticatorPort_());\n }, 0);\n};\n\n/**\n * A wrapper for chrome.runtime.Port that is compatible with MessagePort.\n * @param {Port} port\n * @constructor\n * @private\n */\nu2f.WrappedChromeRuntimePort_ = function (port) {\n this.port_ = port;\n};\n\n/**\n * Format a return a sign request.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {number} timeoutSeconds\n * @param {number} reqId\n * @return {Object}\n */\nu2f.WrappedChromeRuntimePort_.prototype.formatSignRequest_ =\n function (signRequests, timeoutSeconds, reqId) {\n return {\n type: u2f.MessageTypes.U2F_SIGN_REQUEST,\n signRequests: signRequests,\n timeoutSeconds: timeoutSeconds,\n requestId: reqId\n };\n };\n\n/**\n * Format a return a register request.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {Array<u2f.RegisterRequest>} signRequests\n * @param {number} timeoutSeconds\n * @param {number} reqId\n * @return {Object}\n */\nu2f.WrappedChromeRuntimePort_.prototype.formatRegisterRequest_ =\n function (signRequests, registerRequests, timeoutSeconds, reqId) {\n return {\n type: u2f.MessageTypes.U2F_REGISTER_REQUEST,\n signRequests: signRequests,\n registerRequests: registerRequests,\n timeoutSeconds: timeoutSeconds,\n requestId: reqId\n };\n };\n\n/**\n * Posts a message on the underlying channel.\n * @param {Object} message\n */\nu2f.WrappedChromeRuntimePort_.prototype.postMessage = function (message) {\n this.port_.postMessage(message);\n};\n\n/**\n * Emulates the HTML 5 addEventListener interface. Works only for the\n * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.\n * @param {String} eventName\n * @param {function({data: Object})} handler\n */\nu2f.WrappedChromeRuntimePort_.prototype.addEventListener =\n function (eventName, handler) {\n var name = eventName.toLowerCase();\n\n if (name == 'message' || name == 'onmessage') {\n this.port_.onMessage.addListener(function (message) {\n // Emulate a minimal MessageEvent object\n handler({\n 'data': message\n });\n });\n } else {\n console.error('WrappedChromeRuntimePort only supports onMessage');\n }\n };\n\n/**\n * Wrap the Authenticator app with a MessagePort interface.\n * @constructor\n * @private\n */\nu2f.WrappedAuthenticatorPort_ = function () {\n this.requestId_ = -1;\n this.requestObject_ = null;\n};\n\n/**\n * Launch the Authenticator intent.\n * @param {Object} message\n */\nu2f.WrappedAuthenticatorPort_.prototype.postMessage = function (message) {\n var intentLocation = /** @type {String} */ message;\n\n document.location = intentLocation;\n};\n\n/**\n * Emulates the HTML 5 addEventListener interface.\n * @param {String} eventName\n * @param {function({data: Object})} handler\n */\nu2f.WrappedAuthenticatorPort_.prototype.addEventListener =\n function (eventName, handler) {\n var name = eventName.toLowerCase();\n\n if (name == 'message') {\n var self = this;\n\n /* Register a callback to that executes when\n * chrome injects the response. */\n\n window.addEventListener(\n 'message', self.onRequestUpdate_.bind(self, handler), false);\n } else {\n console.error('WrappedAuthenticatorPort only supports message');\n }\n };\n\n/**\n * Callback invoked when a response is received from the Authenticator.\n * @param function({data: Object}) callback\n * @param {Object} message message Object\n */\nu2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =\n function (callback, message) {\n var messageObject = JSON.parse(message.data);\n var intentUrl = messageObject['intentURL'];\n\n var errorCode = messageObject['errorCode'];\n var responseObject = null;\n\n if (messageObject.hasOwnProperty('data')) {\n responseObject = /** @type {Object} */\n JSON.parse(messageObject['data']);\n responseObject['requestId'] = this.requestId_;\n }\n\n /* Sign responses from the authenticator do not conform to U2F,\n * convert to U2F here. */\n responseObject = this.doResponseFixups_(responseObject);\n callback({\n 'data': responseObject\n });\n };\n\n/**\n * Fixup the response provided by the Authenticator to conform with\n * the U2F spec.\n * @param {Object} responseData\n * @return {Object} the U2F compliant response object\n */\nu2f.WrappedAuthenticatorPort_.prototype.doResponseFixups_ =\n function (responseObject) {\n if (responseObject.hasOwnProperty('responseData')) {\n return responseObject;\n } else if (this.requestObject_['type'] != u2f.MessageTypes.U2F_SIGN_REQUEST) {\n // Only sign responses require fixups. If this is not a response\n // to a sign request, then an internal error has occurred.\n return {\n 'type': u2f.MessageTypes.U2F_REGISTER_RESPONSE,\n 'responseData': {\n 'errorCode': u2f.ErrorCodes.OTHER_ERROR,\n 'errorMessage': 'Internal error: invalid response from Authenticator'\n }\n };\n }\n\n /* Non-conformant sign response, do fixups. */\n var encodedChallengeObject = responseObject['challenge'];\n\n if (typeof encodedChallengeObject !== 'undefined') {\n var challengeObject = JSON.parse(atob(encodedChallengeObject));\n var serverChallenge = challengeObject['challenge'];\n var challengesList = this.requestObject_['signData'];\n var requestChallengeObject = null;\n\n for (var i = 0; i < challengesList.length; i++) {\n var challengeObject = challengesList[i];\n\n if (challengeObject['keyHandle'] == responseObject['keyHandle']) {\n requestChallengeObject = challengeObject;\n break;\n }\n }\n }\n var responseData = {\n 'errorCode': responseObject['resultCode'],\n 'keyHandle': responseObject['keyHandle'],\n 'signatureData': responseObject['signature'],\n 'clientData': encodedChallengeObject\n };\n\n return {\n 'type': u2f.MessageTypes.U2F_SIGN_RESPONSE,\n 'responseData': responseData,\n 'requestId': responseObject['requestId']\n };\n };\n\n/**\n * Base URL for intents to Authenticator.\n * @const\n * @private\n */\nu2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =\n 'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';\n\n/**\n * Format a return a sign request.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {number} timeoutSeconds (ignored for now)\n * @param {number} reqId\n * @return {String}\n */\nu2f.WrappedAuthenticatorPort_.prototype.formatSignRequest_ =\n function (signRequests, timeoutSeconds, reqId) {\n if (!signRequests || signRequests.length == 0) {\n return null;\n }\n\n /* TODO(fixme): stash away requestId, as the authenticator app does\n * not return it for sign responses. */\n this.requestId_ = reqId;\n\n /* TODO(fixme): stash away the signRequests, to deal with the legacy\n * response format returned by the Authenticator app. */\n this.requestObject_ = {\n 'type': u2f.MessageTypes.U2F_SIGN_REQUEST,\n 'signData': signRequests,\n 'requestId': reqId,\n 'timeout': timeoutSeconds\n };\n\n var appId = signRequests[0]['appId'];\n var intentUrl =\n u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +\n ';S.appId=' + encodeURIComponent(appId) +\n ';S.eventId=' + reqId +\n ';S.challenges=' +\n encodeURIComponent(\n JSON.stringify(this.getBrowserDataList_(signRequests))) + ';end';\n\n return intentUrl;\n };\n\n/**\n * Get the browser data objects from the challenge list\n * @param {Array} challenges list of challenges\n * @return {Array} list of browser data objects\n * @private\n */\nu2f.WrappedAuthenticatorPort_\n .prototype.getBrowserDataList_ = function (challenges) {\n return challenges\n .map(function (challenge) {\n var browserData = {\n 'typ': 'navigator.id.getAssertion',\n 'challenge': challenge['challenge']\n };\n var challengeObject = {\n 'challenge': browserData,\n 'keyHandle': challenge['keyHandle']\n };\n\n return challengeObject;\n });\n};\n\n/**\n * Format a return a register request.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {Array<u2f.RegisterRequest>} enrollChallenges\n * @param {number} timeoutSeconds (ignored for now)\n * @param {number} reqId\n * @return {Object}\n */\nu2f.WrappedAuthenticatorPort_.prototype.formatRegisterRequest_ =\n function (signRequests, enrollChallenges, timeoutSeconds, reqId) {\n if (!enrollChallenges || enrollChallenges.length == 0) {\n return null;\n }\n // Assume the appId is the same for all enroll challenges.\n var appId = enrollChallenges[0]['appId'];\n var registerRequests = [];\n\n for (var i = 0; i < enrollChallenges.length; i++) {\n var registerRequest = {\n 'challenge': enrollChallenges[i]['challenge'],\n 'version': enrollChallenges[i]['version']\n };\n\n if (enrollChallenges[i]['appId'] != appId) {\n // Only include the appId when it differs from the first appId.\n registerRequest['appId'] = enrollChallenges[i]['appId'];\n }\n registerRequests.push(registerRequest);\n }\n var registeredKeys = [];\n\n if (signRequests) {\n for (i = 0; i < signRequests.length; i++) {\n var key = {\n 'keyHandle': signRequests[i]['keyHandle'],\n 'version': signRequests[i]['version']\n };\n // Only include the appId when it differs from the appId that's\n // being registered now.\n\n if (signRequests[i]['appId'] != appId) {\n key['appId'] = signRequests[i]['appId'];\n }\n registeredKeys.push(key);\n }\n }\n var request = {\n 'type': u2f.MessageTypes.U2F_REGISTER_REQUEST,\n 'appId': appId,\n 'registerRequests': registerRequests,\n 'registeredKeys': registeredKeys,\n 'requestId': reqId,\n 'timeoutSeconds': timeoutSeconds\n };\n var intentUrl =\n u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +\n ';S.request=' + encodeURIComponent(JSON.stringify(request)) +\n ';end';\n\n /* TODO(fixme): stash away requestId, this is is not necessary for\n * register requests, but here to keep parity with sign.\n */\n\n this.requestId_ = reqId;\n\n return intentUrl;\n };\n\n\n/**\n * Sets up an embedded trampoline iframe, sourced from the extension.\n * @param {function(MessagePort)} callback\n * @private\n */\nu2f.getIframePort_ = function (callback) {\n // Create the iframe\n var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;\n var iframe = document.createElement('iframe');\n\n iframe.src = iframeOrigin + '/u2f-comms.html';\n iframe.setAttribute('style', 'display:none');\n document.body.appendChild(iframe);\n\n var channel = new MessageChannel();\n var ready = function (message) {\n if (message.data == 'ready') {\n channel.port1.removeEventListener('message', ready);\n callback(channel.port1);\n } else {\n console.error('First event on iframe port was not \"ready\"');\n }\n };\n\n channel.port1.addEventListener('message', ready);\n channel.port1.start();\n\n iframe.addEventListener('load', function () {\n // Deliver the port to the iframe and initialize\n iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);\n });\n};\n\n\n// High-level JS API\n\n/**\n * Default extension response timeout in seconds.\n * @const\n */\nu2f.EXTENSION_TIMEOUT_SEC = 30;\n\n/**\n * A singleton instance for a MessagePort to the extension.\n * @type {MessagePort|u2f.WrappedChromeRuntimePort_}\n * @private\n */\nu2f.port_ = null;\n\n/**\n * Callbacks waiting for a port\n * @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}\n * @private\n */\nu2f.waitingForPort_ = [];\n\n/**\n * A counter for requestIds.\n * @type {number}\n * @private\n */\nu2f.reqCounter_ = 0;\n\n/**\n * A map from requestIds to client callbacks\n * @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))\n * |function((u2f.Error|u2f.SignResponse)))>}\n * @private\n */\nu2f.callbackMap_ = {};\n\n/**\n * Creates or retrieves the MessagePort singleton to use.\n * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback\n * @private\n */\nu2f.getPortSingleton_ = function (callback) {\n if (u2f.port_) {\n callback(u2f.port_);\n } else {\n if (u2f.waitingForPort_.length == 0) {\n u2f.getMessagePort(function (port) {\n u2f.port_ = port;\n u2f.port_.addEventListener('message',\n /** @type {function(Event)} */ u2f.responseHandler_);\n\n // Careful, here be async callbacks. Maybe.\n while (u2f.waitingForPort_.length)\n u2f.waitingForPort_.shift()(u2f.port_);\n });\n }\n u2f.waitingForPort_.push(callback);\n }\n};\n\n/**\n * Handles response messages from the extension.\n * @param {MessageEvent.<u2f.Response>} message\n * @private\n */\nu2f.responseHandler_ = function (message) {\n var response = message.data;\n var reqId = response['requestId'];\n\n if (!reqId || !u2f.callbackMap_[reqId]) {\n console.error('Unknown or missing requestId in response.');\n\n return;\n }\n var cb = u2f.callbackMap_[reqId];\n\n delete u2f.callbackMap_[reqId];\n cb(response['responseData']);\n};\n\n/**\n * Dispatches an array of sign requests to available U2F tokens.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {function((u2f.Error|u2f.SignResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.sign = function (signRequests, callback, opt_timeoutSeconds) {\n u2f.getPortSingleton_(function (port) {\n var reqId = ++u2f.reqCounter_;\n\n u2f.callbackMap_[reqId] = callback;\n var timeoutSeconds = typeof opt_timeoutSeconds !== 'undefined' ?\n opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC;\n var req = port.formatSignRequest_(signRequests, timeoutSeconds, reqId);\n\n port.postMessage(req);\n });\n};\n\n/**\n * Dispatches register requests to available U2F tokens. An array of sign\n * requests identifies already registered tokens.\n * @param {Array<u2f.RegisterRequest>} registerRequests\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {function((u2f.Error|u2f.RegisterResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.register = function (registerRequests, signRequests,\n callback, opt_timeoutSeconds) {\n u2f.getPortSingleton_(function (port) {\n var reqId = ++u2f.reqCounter_;\n\n u2f.callbackMap_[reqId] = callback;\n var timeoutSeconds = typeof opt_timeoutSeconds !== 'undefined' ?\n opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC;\n var req = port.formatRegisterRequest_(\n signRequests, registerRequests, timeoutSeconds, reqId);\n\n port.postMessage(req);\n });\n};\n\n/* eslint-enable */\n","MSP_TwoFactorAuth/js/u2fkey/configure.js":"/**\n * MageSpecialist\n *\n * NOTICE OF LICENSE\n *\n * This source file is subject to the Open Software License (OSL 3.0)\n * that is bundled with this package in the file LICENSE.txt.\n * It is also available through the world-wide-web at this URL:\n * http://opensource.org/licenses/osl-3.0.php\n * If you did not receive a copy of the license and are unable to\n * obtain it through the world-wide-web, please send an email\n * to info@magespecialist.it so we can send you a copy immediately.\n *\n * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)\n * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)\n */\n\n'use strict';\n\ndefine([\n 'jquery',\n 'ko',\n 'uiComponent',\n 'MSP_TwoFactorAuth/js/error',\n 'MSP_TwoFactorAuth/js/u2fkey/api'\n], function ($, ko, Component, error) {\n return Component.extend({\n currentStep: ko.observable('register'),\n\n defaults: {\n template: 'MSP_TwoFactorAuth/u2fkey/configure'\n },\n\n postUrl: '',\n successUrl: '',\n touchImageUrl: '',\n registerData: {},\n\n /**\n * Start waiting loop\n */\n onAfterRender: function () {\n this.waitForTouch();\n },\n\n /**\n * Get touch image URL\n * @returns {String}\n */\n getTouchImageUrl: function () {\n return this.touchImageUrl;\n },\n\n /**\n * Get POST URL\n * @returns {String}\n */\n getPostUrl: function () {\n return this.postUrl;\n },\n\n /**\n * Get success URL\n * @returns {String}\n */\n getSuccessUrl: function () {\n return this.successUrl;\n },\n\n /**\n * Wait for key touch\n */\n waitForTouch: function () {\n var requestData = this.registerData[0],\n signs = this.registerData[1],\n me = this;\n\n // eslint-disable-next-line no-undef\n u2f.register(\n [requestData],\n signs,\n function (registerResponse) {\n $.post(me.getPostUrl(), {\n 'request': requestData,\n 'response': registerResponse\n }).done(function (res) {\n if (res.success) {\n me.currentStep('login');\n self.location.href = me.getSuccessUrl();\n } else {\n me.waitForTouch();\n }\n }).fail(function () {\n error.display('Unable to register your device');\n });\n }, 120\n );\n }\n });\n});\n","js/theme.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine('globalNavigationScroll', [\n 'jquery'\n], function ($) {\n 'use strict';\n\n var win = $(window),\n subMenuClass = '.submenu',\n fixedClassName = '_fixed',\n menu = $('.menu-wrapper'),\n content = $('.page-wrapper'),\n menuItems = $('#nav').children('li'),\n winHeight,\n menuHeight = menu.height(),\n menuScrollMax = 0,\n submenuHeight = 0,\n contentHeight,\n winTop = 0,\n winTopLast = 0,\n scrollStep = 0,\n nextTop = 0;\n\n /**\n * Check if menu is fixed\n * @returns {Boolean}\n */\n function isMenuFixed() {\n return menuHeight < contentHeight && contentHeight > winHeight;\n }\n\n /**\n * Check if class exist than add or do nothing\n * @param {jQuery} el\n * @param {String} $class\n */\n function checkAddClass(el, $class) {\n if (!el.hasClass($class)) {\n el.addClass($class);\n }\n }\n\n /**\n * Check if class exist than remove or do nothing\n * @param {jQuery} el\n * @param {String} $class\n */\n function checkRemoveClass(el, $class) {\n if (el.hasClass($class)) {\n el.removeClass($class);\n }\n }\n\n /**\n * Calculate and apply menu position\n */\n function positionMenu() {\n\n // Spotting positions and heights\n winHeight = win.height();\n contentHeight = content.height();\n winTop = win.scrollTop();\n scrollStep = winTop - winTopLast;\n\n if (isMenuFixed()) { // fixed menu cases\n\n checkAddClass(menu, fixedClassName);\n\n if (menuHeight > winHeight) { // smart scroll cases\n\n if (winTop > winTopLast) { //eslint-disable-line max-depth\n\n menuScrollMax = menuHeight - winHeight;\n\n nextTop < menuScrollMax - scrollStep ?\n nextTop += scrollStep : nextTop = menuScrollMax;\n\n menu.css('top', -nextTop);\n\n } else if (winTop <= winTopLast) { // scroll up\n\n nextTop > -scrollStep ?\n nextTop += scrollStep : nextTop = 0;\n\n menu.css('top', -nextTop);\n\n }\n\n }\n\n } else { // static menu cases\n checkRemoveClass(menu, fixedClassName);\n }\n\n // Save previous window scrollTop\n winTopLast = winTop;\n\n }\n\n positionMenu(); // page start calculation\n\n // Change position on scroll\n win.on('scroll', function () {\n positionMenu();\n });\n\n win.on('resize', function () {\n\n winHeight = win.height();\n\n // Reset position if fixed and out of smart scroll\n if (menuHeight < contentHeight && menuHeight <= winHeight) {\n menu.removeAttr('style');\n menuItems.off();\n }\n\n });\n\n // Add event to menuItems to check submenu overlap\n menuItems.on('click', function () {\n\n var submenu = $(this).children(subMenuClass),\n delta,\n logo = $('.logo')[0].offsetHeight;\n\n submenuHeight = submenu.height();\n\n if (submenuHeight > menuHeight && menuHeight + logo > winHeight) {\n menu.height(submenuHeight - logo);\n delta = -menu.position().top;\n window.scrollTo(0, 0);\n positionMenu();\n window.scrollTo(0, delta);\n positionMenu();\n menuHeight = submenuHeight;\n }\n });\n\n});\n\ndefine('globalNavigation', [\n 'jquery',\n 'jquery/ui',\n 'globalNavigationScroll'\n], function ($) {\n 'use strict';\n\n $.widget('mage.globalNavigation', {\n options: {\n selectors: {\n menu: '#nav',\n currentItem: '._current',\n topLevelItem: '.level-0',\n topLevelHref: '> a',\n subMenu: '> .submenu',\n closeSubmenuBtn: '[data-role=\"close-submenu\"]'\n },\n overlayTmpl: '<div class=\"admin__menu-overlay\"></div>'\n },\n\n /** @inheritdoc */\n _create: function () {\n var selectors = this.options.selectors;\n\n this.menu = this.element;\n this.menuLinks = $(selectors.topLevelHref, selectors.topLevelItem);\n this.closeActions = $(selectors.closeSubmenuBtn);\n\n this._initOverlay()\n ._bind();\n },\n\n /**\n * @return {Object}\n * @private\n */\n _initOverlay: function () {\n this.overlay = $(this.options.overlayTmpl).appendTo('body').hide(0);\n\n return this;\n },\n\n /**\n * @private\n */\n _bind: function () {\n var focus = this._focus.bind(this),\n open = this._open.bind(this),\n blur = this._blur.bind(this),\n keyboard = this._keyboard.bind(this);\n\n this.menuLinks\n .on('focus', focus)\n .on('click', open);\n\n this.menuLinks.last().on('blur', blur);\n\n this.closeActions.on('keydown', keyboard);\n },\n\n /**\n * Remove active class from current menu item\n * Turn back active class to current page menu item\n */\n _blur: function (e) {\n var selectors = this.options.selectors,\n menuItem = $(e.target).closest(selectors.topLevelItem),\n currentItem = $(selectors.menu).find(selectors.currentItem);\n\n menuItem.removeClass('_active');\n currentItem.addClass('_active');\n },\n\n /**\n * Add focus to active menu item\n */\n _keyboard: function (e) {\n var selectors = this.options.selectors,\n menuItem = $(e.target).closest(selectors.topLevelItem);\n\n if (e.which === 13) {\n this._close(e);\n $(selectors.topLevelHref, menuItem).focus();\n }\n },\n\n /**\n * Toggle active state on focus\n */\n _focus: function (e) {\n var selectors = this.options.selectors,\n menuItem = $(e.target).closest(selectors.topLevelItem);\n\n menuItem.addClass('_active')\n .siblings(selectors.topLevelItem)\n .removeClass('_active');\n },\n\n /**\n * @param {jQuery.Event} e\n * @private\n */\n _closeSubmenu: function (e) {\n var selectors = this.options.selectors,\n currentItem = $(selectors.menu).find(selectors.currentItem);\n\n this._close(e);\n\n currentItem.addClass('_active');\n },\n\n /**\n * @param {jQuery.Event} e\n * @private\n */\n _open: function (e) {\n var selectors = this.options.selectors,\n menuItemSelector = selectors.topLevelItem,\n menuItem = $(e.target).closest(menuItemSelector),\n subMenu = $(selectors.subMenu, menuItem),\n close = this._closeSubmenu.bind(this),\n closeBtn = subMenu.find(selectors.closeSubmenuBtn);\n\n if (subMenu.length) {\n e.preventDefault();\n }\n\n menuItem.addClass('_show')\n .siblings(menuItemSelector)\n .removeClass('_show');\n\n subMenu.attr('aria-expanded', 'true');\n\n closeBtn.on('click', close);\n\n this.overlay.show(0).on('click', close);\n this.menuLinks.last().off('blur');\n },\n\n /**\n * @param {jQuery.Event} e\n * @private\n */\n _close: function (e) {\n var selectors = this.options.selectors,\n menuItem = this.menu.find(selectors.topLevelItem + '._show'),\n subMenu = $(selectors.subMenu, menuItem),\n closeBtn = subMenu.find(selectors.closeSubmenuBtn),\n blur = this._blur.bind(this);\n\n e.preventDefault();\n\n this.overlay.hide(0).off('click');\n\n this.menuLinks.last().on('blur', blur);\n\n closeBtn.off('click');\n\n subMenu.attr('aria-expanded', 'false');\n\n menuItem.removeClass('_show _active');\n }\n });\n\n return $.mage.globalNavigation;\n});\n\ndefine('globalSearch', [\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.globalSearch', {\n options: {\n field: '.search-global-field',\n fieldActiveClass: '_active',\n input: '#search-global'\n },\n\n /** @inheritdoc */\n _create: function () {\n this.field = $(this.options.field);\n this.input = $(this.options.input);\n this._events();\n },\n\n /**\n * @private\n */\n _events: function () {\n var self = this;\n\n this.input.on('blur.resetGlobalSearchForm', function () {\n if (!self.input.val()) {\n self.field.removeClass(self.options.fieldActiveClass);\n }\n });\n\n this.input.on('focus.activateGlobalSearchForm', function () {\n self.field.addClass(self.options.fieldActiveClass);\n });\n }\n });\n\n return $.mage.globalSearch;\n});\n\ndefine('modalPopup', [\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.modalPopup', {\n options: {\n popup: '.popup',\n btnDismiss: '[data-dismiss=\"popup\"]',\n btnHide: '[data-hide=\"popup\"]'\n },\n\n /** @inheritdoc */\n _create: function () {\n this.fade = this.element;\n this.popup = $(this.options.popup, this.fade);\n this.btnDismiss = $(this.options.btnDismiss, this.popup);\n this.btnHide = $(this.options.btnHide, this.popup);\n\n this._events();\n },\n\n /**\n * @private\n */\n _events: function () {\n var self = this;\n\n this.btnDismiss\n .on('click.dismissModalPopup', function () {\n self.fade.remove();\n });\n\n this.btnHide\n .on('click.hideModalPopup', function () {\n self.fade.hide();\n });\n }\n });\n\n return $.mage.modalPopup;\n});\n\ndefine('useDefault', [\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.useDefault', {\n options: {\n field: '.field',\n useDefault: '.use-default',\n checkbox: '.use-default-control',\n label: '.use-default-label'\n },\n\n /** @inheritdoc */\n _create: function () {\n this.el = this.element;\n this.field = $(this.el).closest(this.options.field);\n this.useDefault = $(this.options.useDefault, this.field);\n this.checkbox = $(this.options.checkbox, this.useDefault);\n this.label = $(this.options.label, this.useDefault);\n this.origValue = this.el.attr('data-store-label');\n\n this._events();\n },\n\n /**\n * @private\n */\n _events: function () {\n var self = this;\n\n this.el.on(\n 'change.toggleUseDefaultVisibility keyup.toggleUseDefaultVisibility',\n $.proxy(this._toggleUseDefaultVisibility, this)\n ).trigger('change.toggleUseDefaultVisibility');\n\n this.checkboxon('change.setOrigValue', function () {\n if ($(this).prop('checked')) {\n self.el\n .val(self.origValue)\n .trigger('change.toggleUseDefaultVisibility');\n\n $(this).prop('checked', false);\n }\n });\n },\n\n /**\n * @private\n */\n _toggleUseDefaultVisibility: function () {\n var curValue = this.el.val(),\n origValue = this.origValue;\n\n this[curValue != origValue ? '_show' : '_hide'](); //eslint-disable-line eqeqeq\n },\n\n /**\n * @private\n */\n _show: function () {\n this.useDefault.show();\n },\n\n /**\n * @private\n */\n _hide: function () {\n this.useDefault.hide();\n }\n });\n\n return $.mage.useDefault;\n});\n\ndefine('loadingPopup', [\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.loadingPopup', {\n options: {\n message: 'Please wait...',\n timeout: 5000,\n timeoutId: null,\n callback: null,\n template: null\n },\n\n /** @inheritdoc */\n _create: function () {\n this.template =\n '<div class=\"popup popup-loading\">' +\n '<div class=\"popup-inner\">' + this.options.message + '</div>' +\n '</div>';\n\n this.popup = $(this.template);\n\n this._show();\n this._events();\n },\n\n /**\n * @private\n */\n _events: function () {\n var self = this;\n\n this.element\n .on('showLoadingPopup', function () {\n self._show();\n })\n .on('hideLoadingPopup', function () {\n self._hide();\n });\n },\n\n /**\n * @private\n */\n _show: function () {\n var options = this.options,\n timeout = options.timeout;\n\n $('body').trigger('processStart');\n\n if (timeout) {\n options.timeoutId = setTimeout(this._delayedHide.bind(this), timeout);\n }\n },\n\n /**\n * @private\n */\n _hide: function () {\n $('body').trigger('processStop');\n },\n\n /**\n * @private\n */\n _delayedHide: function () {\n this._hide();\n\n this.options.callback && this.options.callback();\n\n this.options.timeoutId && clearTimeout(this.options.timeoutId);\n }\n });\n\n return $.mage.loadingPopup;\n});\n\ndefine('collapsable', [\n 'jquery',\n 'jquery/ui',\n 'jquery/jquery.tabs'\n], function ($) {\n 'use strict';\n\n $.widget('mage.collapsable', {\n options: {\n parent: null,\n openedClass: 'opened',\n wrapper: '.fieldset-wrapper'\n },\n\n /** @inheritdoc */\n _create: function () {\n this._events();\n },\n\n /** @inheritdoc */\n _events: function () {\n var self = this;\n\n this.element\n .on('show', function (e) {\n var fieldsetWrapper = $(this).closest(self.options.wrapper);\n\n fieldsetWrapper.addClass(self.options.openedClass);\n e.stopPropagation();\n })\n .on('hide', function (e) {\n var fieldsetWrapper = $(this).closest(self.options.wrapper);\n\n fieldsetWrapper.removeClass(self.options.openedClass);\n e.stopPropagation();\n });\n }\n });\n\n return $.mage.collapsable;\n});\n\ndefine('js/theme', [\n 'jquery',\n 'mage/smart-keyboard-handler',\n 'mage/ie-class-fixer',\n 'collapsable',\n 'domReady!'\n], function ($, keyboardHandler) {\n 'use strict';\n\n /* @TODO refactor collapsible as widget and avoid logic binding with such a general selectors */\n $('.collapse').collapsable();\n\n $.each($('.entry-edit'), function (i, entry) {\n $('.collapse:first', entry).filter(function () {\n return $(this).data('collapsed') !== true;\n }).collapse('show');\n });\n\n keyboardHandler.apply();\n});\n","Magento_Tax/js/validate.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], function (jQuery) {\n 'use strict';\n\n return function (data, element) {\n jQuery(element).mage('form').mage('validation');\n };\n});\n","Magento_Tax/js/bootstrap.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\nrequire([\n 'mage/backend/editablemultiselect'\n]);\n","Magento_Tax/js/price/adjustment.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'Magento_Ui/js/grid/columns/column',\n 'mage/translate'\n], function (Element, $t) {\n 'use strict';\n\n return Element.extend({\n defaults: {\n bodyTmpl: 'Magento_Tax/price/adjustment',\n taxPriceType: 'final_price',\n taxPriceCssClass: 'price-including-tax',\n bothPrices: 3,\n inclTax: 2,\n exclTax: 1,\n modules: {\n price: '${ $.parentName }'\n },\n listens: {\n price: 'initializePriceAttributes'\n }\n },\n\n /**\n * {@inheritdoc}\n */\n initialize: function () {\n this._super()\n .initializePriceAttributes();\n\n return this;\n },\n\n /**\n * Update parent price.\n *\n * @returns {Object} Chainable.\n */\n initializePriceAttributes: function () {\n if (this.displayBothPrices && this.price()) {\n this.price().priceWrapperCssClasses = this.taxPriceCssClass;\n this.price().priceWrapperAttr = {\n 'data-label': $t('Incl. Tax')\n };\n }\n\n return this;\n },\n\n /**\n * Get price tax adjustment.\n *\n * @param {Object} row\n * @return {HTMLElement} tax html\n */\n getTax: function (row) {\n return row['price_info']['extension_attributes']['tax_adjustments']['formatted_prices'][this.taxPriceType];\n },\n\n /**\n * Set price tax type.\n *\n * @param {String} priceType\n * @return {Object}\n */\n setPriceType: function (priceType) {\n this.taxPriceType = priceType;\n\n return this;\n },\n\n /**\n * Return whether display setting is to display\n * both price including tax and price excluding tax.\n *\n * @return {Boolean}\n */\n displayBothPrices: function () {\n return +this.source.data.displayTaxes === this.bothPrices;\n },\n\n /**\n * Return whether display setting is to display price including tax.\n *\n * @return {Boolean}\n */\n displayPriceIncludeTax: function () {\n return +this.source.data.displayTaxes === this.inclTax;\n },\n\n /**\n * Return whether display setting is to display price excluding tax.\n *\n * @return {Boolean}\n */\n displayPriceExclTax: function () {\n return +this.source.data.displayTaxes === this.exclTax;\n }\n });\n});\n","Magento_Authorizenet/js/direct-post.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/backend/validation',\n 'prototype'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function (jQuery) {\n\n window.directPost = Class.create();\n directPost.prototype = {\n initialize: function (methodCode, iframeId, controller, orderSaveUrl, cgiUrl, nativeAction) {\n var prepare = function (event, method) {\n if (method === 'authorizenet_directpost') {\n this.preparePayment();\n } else {\n jQuery('#edit_form')\n .off('submitOrder.authorizenet');\n }\n };\n\n this.iframeId = iframeId;\n this.controller = controller;\n this.orderSaveUrl = orderSaveUrl;\n this.nativeAction = nativeAction;\n this.cgiUrl = cgiUrl;\n this.code = methodCode;\n this.inputs = ['cc_type', 'cc_number', 'expiration', 'expiration_yr', 'cc_cid'];\n this.headers = [];\n this.isValid = true;\n this.paymentRequestSent = false;\n this.orderIncrementId = false;\n this.successUrl = false;\n this.hasError = false;\n this.tmpForm = false;\n\n this.onLoadIframe = this.loadIframe.bindAsEventListener(this);\n this.onLoadOrderIframe = this.loadOrderIframe.bindAsEventListener(this);\n this.onSubmitAdminOrder = this.submitAdminOrder.bindAsEventListener(this);\n\n jQuery('#edit_form').on('changePaymentMethod', prepare.bind(this));\n\n jQuery('#edit_form').trigger(\n 'changePaymentMethod',\n [\n jQuery('#edit_form').find(':radio[name=\"payment[method]\"]:checked').val()\n ]\n );\n },\n\n validate: function () {\n this.isValid = true;\n this.inputs.each(function (elemIndex) {\n if ($(this.code + '_' + elemIndex)) {\n if (!jQuery.validator.validateElement($(this.code + '_' + elemIndex))) {\n this.isValid = false;\n }\n }\n }, this);\n\n return this.isValid;\n },\n\n changeInputOptions: function (param, value) {\n this.inputs.each(function (elemIndex) {\n if ($(this.code + '_' + elemIndex)) {\n $(this.code + '_' + elemIndex).writeAttribute(param, value);\n }\n }, this);\n },\n\n preparePayment: function () {\n this.changeInputOptions('autocomplete', 'off');\n jQuery('#edit_form')\n .off('submitOrder')\n .on('submitOrder.authorizenet', this.submitAdminOrder.bind(this));\n\n if ($(this.iframeId)) {\n // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality\n jQuery('.scalable.save:not(disabled)').removeAttr('onclick');\n jQuery(document).off('click.directPost');\n jQuery(document).on(\n 'click.directPost',\n '.scalable.save:not(disabled)',\n jQuery.proxy(this.onSubmitAdminOrder, this)\n );\n $('order-' + this.iframeId).observe('load', this.onLoadOrderIframe);\n $(this.iframeId).observe('load', this.onLoadIframe);\n }\n },\n\n loadIframe: function () {\n if (this.paymentRequestSent) {\n if (!this.orderRequestSent) {\n this.paymentRequestSent = false;\n\n if (!this.hasError) {\n this.returnQuote();\n } else {\n this.changeInputOptions('disabled', false);\n jQuery('body').trigger('processStop');\n enableElements('save');\n }\n }\n\n if (this.tmpForm) {\n document.body.removeChild(this.tmpForm);\n }\n }\n },\n\n loadOrderIframe: function () {\n if (this.orderRequestSent) {\n $(this.iframeId).hide();\n var data = $('order-' + this.iframeId).contentWindow.document.body.getElementsByTagName('pre')[0].innerHTML;\n\n this.saveAdminOrderSuccess(data);\n this.orderRequestSent = false;\n }\n },\n\n showError: function (msg) {\n this.hasError = true;\n\n if (this.controller == 'onepage') {\n $(this.iframeId).hide();\n this.resetLoadWaiting();\n }\n alert(msg);\n },\n\n returnQuote: function () {\n var url = this.orderSaveUrl.replace('place', 'returnQuote');\n\n new Ajax.Request(url, {\n onSuccess: function (transport) {\n try {\n response = transport.responseText.evalJSON(true);\n } catch (e) {\n response = {};\n }\n\n if (response.error_message) {\n alert(response.error_message);\n }\n $(this.iframeId).show();\n this.changeInputOptions('disabled', false);\n jQuery('body').trigger('processStop');\n enableElements('save');\n }.bind(this)\n });\n },\n\n setLoadWaiting: function () {\n this.headers.each(function (header) {\n header.removeClassName('allow');\n });\n checkout.setLoadWaiting('review');\n },\n\n resetLoadWaiting: function () {\n this.headers.each(function (header) {\n header.addClassName('allow');\n });\n checkout.setLoadWaiting(false);\n },\n\n submitAdminOrder: function () {\n // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality\n var editForm = jQuery('#edit_form');\n\n if (editForm.valid()) {\n // Temporary solution will be removed after refactoring Authorize.Net (sales) functionality\n paymentMethodEl = editForm.find(':radio[name=\"payment[method]\"]:checked');\n this.hasError = false;\n\n if (paymentMethodEl.val() == this.code) {\n jQuery('body').trigger('processStart');\n setLoaderPosition();\n this.changeInputOptions('disabled', 'disabled');\n this.paymentRequestSent = true;\n this.orderRequestSent = true;\n // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality\n editForm.attr('action', this.orderSaveUrl);\n editForm.attr('target',\n jQuery('#order-' + this.iframeId).attr('name'));\n editForm.append(this.createHiddenElement('controller', this.controller));\n disableElements('save');\n // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality\n order._realSubmit();\n } else {\n editForm.attr('action', this.nativeAction);\n editForm.attr('target', '_top');\n disableElements('save');\n // Temporary solutions will be removed after refactoring Authorize.Net (sales) functionality\n order._realSubmit();\n }\n }\n },\n\n recollectQuote: function () {\n var area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'];\n\n area = order.prepareArea(area);\n var url = order.loadBaseUrl + 'block/' + area;\n var info = $('order-items_grid').select('input', 'select', 'textarea');\n var data = {};\n\n for (var i = 0; i < info.length; i++) {\n if (!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) {\n data[info[i].name] = info[i].getValue();\n }\n }\n data.reset_shipping = true;\n data.update_items = true;\n\n if ($('coupons:code') && $F('coupons:code')) {\n data['order[coupon][code]'] = $F('coupons:code');\n }\n data.json = true;\n new Ajax.Request(url, {\n parameters: data,\n loaderArea: 'html-body',\n onSuccess: function (transport) {\n jQuery('#edit_form').submit();\n }\n });\n\n },\n\n saveAdminOrderSuccess: function (data) {\n try {\n response = data.evalJSON(true);\n } catch (e) {\n response = {};\n }\n\n if (response.directpost) {\n this.orderIncrementId = response.directpost.fields.x_invoice_num;\n var paymentData = {};\n\n for (var key in response.directpost.fields) {\n paymentData[key] = response.directpost.fields[key];\n }\n var preparedData = this.preparePaymentRequest(paymentData);\n\n this.sendPaymentRequest(preparedData);\n } else {\n if (response.redirect) {\n window.location = response.redirect;\n }\n\n if (response.error_messages) {\n var msg = response.error_messages;\n\n if (typeof msg == 'object') {\n msg = msg.join('\\n');\n }\n\n if (msg) {\n alert(msg);\n }\n }\n }\n },\n\n preparePaymentRequest: function (data) {\n if ($(this.code + '_cc_cid')) {\n data.x_card_code = $(this.code + '_cc_cid').value;\n }\n var year = $(this.code + '_expiration_yr').value;\n\n if (year.length > 2) {\n year = year.substring(2);\n }\n var month = parseInt($(this.code + '_expiration').value, 10);\n\n if (month < 10) {\n month = '0' + month;\n }\n\n data.x_exp_date = month + '/' + year;\n data.x_card_num = $(this.code + '_cc_number').value;\n\n return data;\n },\n\n sendPaymentRequest: function (preparedData) {\n this.recreateIframe();\n this.tmpForm = document.createElement('form');\n this.tmpForm.style.display = 'none';\n this.tmpForm.enctype = 'application/x-www-form-urlencoded';\n this.tmpForm.method = 'POST';\n document.body.appendChild(this.tmpForm);\n this.tmpForm.action = this.cgiUrl;\n this.tmpForm.target = $(this.iframeId).readAttribute('name');\n this.tmpForm.setAttribute('target', $(this.iframeId).readAttribute('name'));\n\n for (var param in preparedData) {\n this.tmpForm.appendChild(this.createHiddenElement(param, preparedData[param]));\n }\n\n this.paymentRequestSent = true;\n this.tmpForm.submit();\n },\n\n createHiddenElement: function (name, value) {\n var field;\n\n if (isIE) {\n field = document.createElement('input');\n field.setAttribute('type', 'hidden');\n field.setAttribute('name', name);\n field.setAttribute('value', value);\n } else {\n field = document.createElement('input');\n field.type = 'hidden';\n field.name = name;\n field.value = value;\n }\n\n return field;\n },\n\n recreateIframe: function () {\n if ($(this.iframeId)) {\n var nextElement = $(this.iframeId).next();\n var src = $(this.iframeId).readAttribute('src');\n var name = $(this.iframeId).readAttribute('name');\n\n $(this.iframeId).stopObserving();\n $(this.iframeId).remove();\n var iframe = '<iframe id=\"' + this.iframeId +\n '\" allowtransparency=\"true\" frameborder=\"0\" name=\"' + name +\n '\" style=\"display:none;width:100%;background-color:transparent\" src=\"' + src + '\" />';\n\n Element.insert(nextElement, {\n 'before': iframe\n });\n $(this.iframeId).observe('load', this.onLoadIframe);\n }\n }\n };\n}));\n","modernizr/modernizr.js":"/*!\n * Modernizr v2.6.1\n * www.modernizr.com\n *\n * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton\n * Available under the BSD and MIT licenses: www.modernizr.com/license/\n */\n\n/*\n * Modernizr tests which native CSS3 and HTML5 features are available in\n * the current UA and makes the results available to you in two ways:\n * as properties on a global Modernizr object, and as classes on the\n * <html> element. This information allows you to progressively enhance\n * your pages with a granular level of control over the experience.\n *\n * Modernizr has an optional (not included) conditional resource loader\n * called Modernizr.load(), based on Yepnope.js (yepnopejs.com).\n * To get a build that includes Modernizr.load(), as well as choosing\n * which tests to include, go to www.modernizr.com/download/\n *\n * Authors Faruk Ates, Paul Irish, Alex Sexton\n * Contributors Ryan Seddon, Ben Alman\n */\n\nwindow.Modernizr = (function( window, document, undefined ) {\n\n var version = '2.6.1',\n\n Modernizr = {},\n\n /*>>cssclasses*/\n // option for enabling the HTML classes to be added\n enableClasses = true,\n /*>>cssclasses*/\n\n docElement = document.documentElement,\n\n /**\n * Create our \"modernizr\" element that we do most feature tests on.\n */\n mod = 'modernizr',\n modElem = document.createElement(mod),\n mStyle = modElem.style,\n\n /**\n * Create the input element for various Web Forms feature tests.\n */\n inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,\n\n /*>>smile*/\n smile = ':)',\n /*>>smile*/\n\n toString = {}.toString,\n\n // TODO :: make the prefixes more granular\n /*>>prefixes*/\n // List of property values to set for css tests. See ticket #21\n prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),\n /*>>prefixes*/\n\n /*>>domprefixes*/\n // Following spec is to expose vendor-specific style properties as:\n // elem.style.WebkitBorderRadius\n // and the following would be incorrect:\n // elem.style.webkitBorderRadius\n\n // Webkit ghosts their properties in lowercase but Opera & Moz do not.\n // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+\n // erik.eae.net/archives/2008/03/10/21.48.10/\n\n // More here: github.com/Modernizr/Modernizr/issues/issue/21\n omPrefixes = 'Webkit Moz O ms',\n\n cssomPrefixes = omPrefixes.split(' '),\n\n domPrefixes = omPrefixes.toLowerCase().split(' '),\n /*>>domprefixes*/\n\n /*>>ns*/\n ns = {'svg': 'http://www.w3.org/2000/svg'},\n /*>>ns*/\n\n tests = {},\n inputs = {},\n attrs = {},\n\n classes = [],\n\n slice = classes.slice,\n\n featureName, // used in testing loop\n\n\n /*>>teststyles*/\n // Inject element with style element and some CSS rules\n injectElementWithStyles = function( rule, callback, nodes, testnames ) {\n\n var style, ret, node,\n div = document.createElement('div'),\n // After page load injecting a fake body doesn't work so check if body exists\n body = document.body,\n // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.\n fakeBody = body ? body : document.createElement('body');\n\n if ( parseInt(nodes, 10) ) {\n // In order not to give false positives we create a node for each test\n // This also allows the method to scale for unspecified uses\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> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed\n // when injected with innerHTML. To get around this you need to prepend the 'NoScope' element\n // with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements.\n // msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx\n // Documents served as xml will throw if using ­ so use xml friendly encoded version. See issue #277\n style = ['­','<style id=\"s', mod, '\">', rule, '</style>'].join('');\n div.id = mod;\n // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.\n // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270\n (body ? div : fakeBody).innerHTML += style;\n fakeBody.appendChild(div);\n if ( !body ) {\n //avoid crashing IE8, if background image is used\n fakeBody.style.background = \"\";\n docElement.appendChild(fakeBody);\n }\n\n ret = callback(div, rule);\n // If this is done after page load we don't want to remove the body so check if body exists\n !body ? fakeBody.parentNode.removeChild(fakeBody) : div.parentNode.removeChild(div);\n\n return !!ret;\n\n },\n /*>>teststyles*/\n\n /*>>mq*/\n // adapted from matchMedia polyfill\n // by Scott Jehl and Paul Irish\n // gist.github.com/786768\n testMediaQuery = function( mq ) {\n\n var matchMedia = window.matchMedia || window.msMatchMedia;\n if ( matchMedia ) {\n return matchMedia(mq).matches;\n }\n\n var bool;\n\n injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {\n bool = (window.getComputedStyle ?\n getComputedStyle(node, null) :\n node.currentStyle)['position'] == 'absolute';\n });\n\n return bool;\n\n },\n /*>>mq*/\n\n\n /*>>hasevent*/\n //\n // isEventSupported determines if a given element supports the given event\n // kangax.github.com/iseventsupported/\n //\n // The following results are known incorrect:\n // Modernizr.hasEvent(\"webkitTransitionEnd\", elem) // false negative\n // Modernizr.hasEvent(\"textInput\") // in Webkit. github.com/Modernizr/Modernizr/issues/333\n // ...\n isEventSupported = (function() {\n\n var TAGNAMES = {\n 'select': 'input', 'change': 'input',\n 'submit': 'form', 'reset': 'form',\n 'error': 'img', 'load': 'img', 'abort': 'img'\n };\n\n function isEventSupported( eventName, element ) {\n\n element = element || document.createElement(TAGNAMES[eventName] || 'div');\n eventName = 'on' + eventName;\n\n // When using `setAttribute`, IE skips \"unload\", WebKit skips \"unload\" and \"resize\", whereas `in` \"catches\" those\n var isSupported = eventName in element;\n\n if ( !isSupported ) {\n // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element\n if ( !element.setAttribute ) {\n element = document.createElement('div');\n }\n if ( element.setAttribute && element.removeAttribute ) {\n element.setAttribute(eventName, '');\n isSupported = is(element[eventName], 'function');\n\n // If property was created, \"remove it\" (by setting value to `undefined`)\n if ( !is(element[eventName], 'undefined') ) {\n element[eventName] = undefined;\n }\n element.removeAttribute(eventName);\n }\n }\n\n element = null;\n return isSupported;\n }\n return isEventSupported;\n })(),\n /*>>hasevent*/\n\n // TODO :: Add flag for hasownprop ? didn't last time\n\n // hasOwnProperty shim by kangax needed for Safari 2.0 support\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) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */\n return ((property in object) && is(object.constructor.prototype[property], 'undefined'));\n };\n }\n\n // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js\n // es5.github.com/#x15.3.4.5\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 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 /**\n * setCss applies given styles to the Modernizr DOM node.\n */\n function setCss( str ) {\n mStyle.cssText = str;\n }\n\n /**\n * setCssAll extrapolates all vendor-specific css strings.\n */\n function setCssAll( str1, str2 ) {\n return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));\n }\n\n /**\n * is returns a boolean for if typeof obj is exactly type.\n */\n function is( obj, type ) {\n return typeof obj === type;\n }\n\n /**\n * contains returns a boolean for if substr is found within str.\n */\n function contains( str, substr ) {\n return !!~('' + str).indexOf(substr);\n }\n\n /*>>testprop*/\n\n // testProps is a generic CSS / DOM property test.\n\n // In testing support for a given CSS property, it's legit to test:\n // `elem.style[styleName] !== undefined`\n // If the property is supported it will return an empty string,\n // if unsupported it will return undefined.\n\n // We'll take advantage of this quick test and skip setting a style\n // on our modernizr element, but instead just testing undefined vs\n // empty string.\n\n // Because the testing of the CSS property names (with \"-\", as\n // opposed to the camelCase DOM properties) is non-portable and\n // non-standard but works in WebKit and IE (but not Gecko or Opera),\n // we explicitly reject properties with dashes so that authors\n // developing in WebKit or IE first don't end up with\n // browser-specific content by accident.\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 /*>>testprop*/\n\n // TODO :: add testDOMProps\n /**\n * testDOMProps is a generic DOM property test; if a browser supports\n * a certain property, it won't return undefined for it.\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 // return the property name as a string\n if (elem === false) return props[i];\n\n // let's bind a function\n if (is(item, 'function')){\n // default to autobind unless override\n return item.bind(elem || obj);\n }\n\n // return the unbound function or obj or value\n return item;\n }\n }\n return false;\n }\n\n /*>>testallprops*/\n /**\n * testPropsAll tests a list of DOM properties we want to check against.\n * We specify literally ALL possible (known and/or likely) properties on\n * the element including the non-vendor prefixed one, for forward-\n * compatibility.\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 // did they call .prefixed('boxSizing') or are we just testing a prop?\n if(is(prefixed, \"string\") || is(prefixed, \"undefined\")) {\n return testProps(props, prefixed);\n\n // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])\n } else {\n props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');\n return testDOMProps(props, prefixed, elem);\n }\n }\n /*>>testallprops*/\n\n\n /**\n * Tests\n * -----\n */\n\n // The *new* flexbox\n // dev.w3.org/csswg/css3-flexbox\n\n tests['flexbox'] = function() {\n return testPropsAll('flexWrap');\n };\n\n // The *old* flexbox\n // www.w3.org/TR/2009/WD-css3-flexbox-20090723/\n\n tests['flexboxlegacy'] = function() {\n return testPropsAll('boxDirection');\n };\n\n // On the S60 and BB Storm, getContext exists, but always returns undefined\n // so we actually have to call getContext() to verify\n // github.com/Modernizr/Modernizr/issues/issue/97/\n\n tests['canvas'] = function() {\n var elem = document.createElement('canvas');\n return !!(elem.getContext && elem.getContext('2d'));\n };\n\n tests['canvastext'] = function() {\n return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));\n };\n\n // webk.it/70117 is tracking a legit WebGL feature detect proposal\n\n // We do a soft detect which may false positive in order to avoid\n // an expensive context creation: bugzil.la/732441\n\n tests['webgl'] = function() {\n return !!window.WebGLRenderingContext;\n };\n\n /*\n * The Modernizr.touch test only indicates if the browser supports\n * touch events, which does not necessarily reflect a touchscreen\n * device, as evidenced by tablets running Windows 7 or, alas,\n * the Palm Pre / WebOS (touch) phones.\n *\n * Additionally, Chrome (desktop) used to lie about its support on this,\n * but that has since been rectified: crbug.com/36415\n *\n * We also test for Firefox 4 Multitouch Support.\n *\n * For more info, see: modernizr.github.com/Modernizr/touch.html\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\n\n // geolocation is often considered a trivial feature detect...\n // Turns out, it's quite tricky to get right:\n //\n // Using !!navigator.geolocation does two things we don't want. It:\n // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513\n // 2. Disables page caching in WebKit: webk.it/43956\n //\n // Meanwhile, in Firefox < 8, an about:config setting could expose\n // a false positive that would throw an exception: bugzil.la/688158\n\n tests['geolocation'] = function() {\n return 'geolocation' in navigator;\n };\n\n\n tests['postmessage'] = function() {\n return !!window.postMessage;\n };\n\n\n // Chrome incognito mode used to throw an exception when using openDatabase\n // It doesn't anymore.\n tests['websqldatabase'] = function() {\n return !!window.openDatabase;\n };\n\n // Vendors had inconsistent prefixing with the experimental Indexed DB:\n // - Webkit's implementation is accessible through webkitIndexedDB\n // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB\n // For speed, we don't test the legacy (and beta-only) indexedDB\n tests['indexedDB'] = function() {\n return !!testPropsAll(\"indexedDB\", window);\n };\n\n // documentMode logic from YUI to filter out IE8 Compat Mode\n // which false positives.\n tests['hashchange'] = function() {\n return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);\n };\n\n // Per 1.6:\n // This used to be Modernizr.historymanagement but the longer\n // name has been deprecated in favor of a shorter and property-matching one.\n // The old API is still available in 1.6, but as of 2.0 will throw a warning,\n // and in the first release thereafter disappear entirely.\n tests['history'] = function() {\n return !!(window.history && history.pushState);\n };\n\n tests['draganddrop'] = function() {\n var div = document.createElement('div');\n return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);\n };\n\n // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10\n // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.\n // FF10 still uses prefixes, so check for it until then.\n // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/\n tests['websockets'] = function() {\n return 'WebSocket' in window || 'MozWebSocket' in window;\n };\n\n\n // css-tricks.com/rgba-browser-support/\n tests['rgba'] = function() {\n // Set an rgba() color and check the returned value\n\n setCss('background-color:rgba(150,255,150,.5)');\n\n return contains(mStyle.backgroundColor, 'rgba');\n };\n\n tests['hsla'] = function() {\n // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,\n // except IE9 who retains it as hsla\n\n setCss('background-color:hsla(120,40%,100%,.5)');\n\n return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');\n };\n\n tests['multiplebgs'] = function() {\n // Setting multiple images AND a color on the background shorthand property\n // and then querying the style.background property value for the number of\n // occurrences of \"url(\" is a reliable method for detecting ACTUAL support for this!\n\n setCss('background:url(https://),url(https://),red url(https://)');\n\n // If the UA supports multiple backgrounds, there should be three occurrences\n // of the string \"url(\" in the return value for elemStyle.background\n\n return (/(url\\s*\\(.*?){3}/).test(mStyle.background);\n };\n\n\n\n // this will false positive in Opera Mini\n // github.com/Modernizr/Modernizr/issues/396\n\n tests['backgroundsize'] = function() {\n return testPropsAll('backgroundSize');\n };\n\n tests['borderimage'] = function() {\n return testPropsAll('borderImage');\n };\n\n\n // Super comprehensive table about all the unique implementations of\n // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance\n\n tests['borderradius'] = function() {\n return testPropsAll('borderRadius');\n };\n\n // WebOS unfortunately false positives on this test.\n tests['boxshadow'] = function() {\n return testPropsAll('boxShadow');\n };\n\n // FF3.0 will false positive on this test\n tests['textshadow'] = function() {\n return document.createElement('div').style.textShadow === '';\n };\n\n\n tests['opacity'] = function() {\n // Browsers that actually have CSS Opacity implemented have done so\n // according to spec, which means their return values are within the\n // range of [0.0,1.0] - including the leading zero.\n\n setCssAll('opacity:.55');\n\n // The non-literal . in this regex is intentional:\n // German Chrome returns this value as 0,55\n // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632\n return (/^0.55$/).test(mStyle.opacity);\n };\n\n\n // Note, Android < 4 will pass this test, but can only animate\n // a single property at a time\n // daneden.me/2011/12/putting-up-with-androids-bullshit/\n tests['cssanimations'] = function() {\n return testPropsAll('animationName');\n };\n\n\n tests['csscolumns'] = function() {\n return testPropsAll('columnCount');\n };\n\n\n tests['cssgradients'] = function() {\n /**\n * For CSS Gradients syntax, please see:\n * webkit.org/blog/175/introducing-css-gradients/\n * developer.mozilla.org/en/CSS/-moz-linear-gradient\n * developer.mozilla.org/en/CSS/-moz-radial-gradient\n * dev.w3.org/csswg/css3-images/#gradients-\n */\n\n var str1 = 'background-image:',\n str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',\n str3 = 'linear-gradient(left top,#9f9, white);';\n\n setCss(\n // legacy webkit syntax (FIXME: remove when syntax not in use anymore)\n (str1 + '-webkit- '.split(' ').join(str2 + str1) +\n // standard syntax // trailing 'background-image:'\n prefixes.join(str3 + str1)).slice(0, -str1.length)\n );\n\n return contains(mStyle.backgroundImage, 'gradient');\n };\n\n\n tests['cssreflections'] = function() {\n return testPropsAll('boxReflect');\n };\n\n\n tests['csstransforms'] = function() {\n return !!testPropsAll('transform');\n };\n\n\n tests['csstransforms3d'] = function() {\n\n var ret = !!testPropsAll('perspective');\n\n // Webkit's 3D transforms are passed off to the browser's own graphics renderer.\n // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in\n // some conditions. As a result, Webkit typically recognizes the syntax but\n // will sometimes throw a false positive, thus we must do a more thorough check:\n if ( ret && 'webkitPerspective' in docElement.style ) {\n\n // Webkit allows this media query to succeed only if the feature is enabled.\n // `@media (transform-3d),(-webkit-transform-3d){ ... }`\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 /*>>fontface*/\n // @font-face detection routine by Diego Perini\n // javascript.nwbox.com/CSSSupport/\n\n // false positives:\n // WebOS github.com/Modernizr/Modernizr/issues/342\n // WP7 github.com/Modernizr/Modernizr/issues/538\n tests['fontface'] = function() {\n var bool;\n\n injectElementWithStyles('@font-face {font-family:\"font\";src:url(\"https://\")}', function( node, rule ) {\n var style = document.getElementById('smodernizr'),\n sheet = style.sheet || style.styleSheet,\n cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';\n\n bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;\n });\n\n return bool;\n };\n /*>>fontface*/\n\n // CSS generated content detection\n tests['generatedcontent'] = function() {\n var bool;\n\n injectElementWithStyles(['#modernizr:after{content:\"',smile,'\";visibility:hidden}'].join(''), function( node ) {\n bool = node.offsetHeight >= 1;\n });\n\n return bool;\n };\n\n\n\n // These tests evaluate support of the video/audio elements, as well as\n // testing what types of content they support.\n //\n // We're using the Boolean constructor here, so that we can extend the value\n // e.g. Modernizr.video // true\n // Modernizr.video.ogg // 'probably'\n //\n // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845\n // thx to NielsLeenheer and zcorpan\n\n // Note: in some older browsers, \"no\" was a return value instead of empty string.\n // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2\n // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5\n\n tests['video'] = function() {\n var elem = document.createElement('video'),\n bool = false;\n\n // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224\n try {\n if ( bool = !!elem.canPlayType ) {\n bool = new Boolean(bool);\n bool.ogg = elem.canPlayType('video/ogg; codecs=\"theora\"') .replace(/^no$/,'');\n\n // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546\n bool.h264 = elem.canPlayType('video/mp4; codecs=\"avc1.42E01E\"') .replace(/^no$/,'');\n\n bool.webm = elem.canPlayType('video/webm; codecs=\"vp8, vorbis\"').replace(/^no$/,'');\n }\n\n } catch(e) { }\n\n return bool;\n };\n\n tests['audio'] = function() {\n var elem = document.createElement('audio'),\n bool = false;\n\n try {\n if ( bool = !!elem.canPlayType ) {\n bool = new Boolean(bool);\n bool.ogg = elem.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/,'');\n bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');\n\n // Mimetypes accepted:\n // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements\n // bit.ly/iphoneoscodecs\n bool.wav = elem.canPlayType('audio/wav; codecs=\"1\"') .replace(/^no$/,'');\n bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||\n elem.canPlayType('audio/aac;')) .replace(/^no$/,'');\n }\n } catch(e) { }\n\n return bool;\n };\n\n\n // In FF4, if disabled, window.localStorage should === null.\n\n // Normally, we could not test that directly and need to do a\n // `('localStorage' in window) && ` test first because otherwise Firefox will\n // throw bugzil.la/365772 if cookies are disabled\n\n // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem\n // will throw the exception:\n // QUOTA_EXCEEDED_ERRROR DOM Exception 22.\n // Peculiarly, getItem and removeItem calls do not throw.\n\n // Because we are forced to try/catch this, we'll go aggressive.\n\n // Just FWIW: IE8 Compat mode supports these features completely:\n // www.quirksmode.org/dom/html5.html\n // But IE8 doesn't support either with local files\n\n tests['localstorage'] = function() {\n try {\n localStorage.setItem(mod, mod);\n localStorage.removeItem(mod);\n return true;\n } catch(e) {\n return false;\n }\n };\n\n tests['sessionstorage'] = function() {\n try {\n sessionStorage.setItem(mod, mod);\n sessionStorage.removeItem(mod);\n return true;\n } catch(e) {\n return false;\n }\n };\n\n\n tests['webworkers'] = function() {\n return !!window.Worker;\n };\n\n\n tests['applicationcache'] = function() {\n return !!window.applicationCache;\n };\n\n\n // Thanks to Erik Dahlstrom\n tests['svg'] = function() {\n return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;\n };\n\n // specifically for SVG inline in HTML, not within XHTML\n // test page: paulirish.com/demo/inline-svg\n tests['inlinesvg'] = function() {\n var div = document.createElement('div');\n div.innerHTML = '<svg/>';\n return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;\n };\n\n // SVG SMIL animation\n tests['smil'] = function() {\n return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));\n };\n\n // This test is only for clip paths in SVG proper, not clip paths on HTML content\n // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg\n\n // However read the comments to dig into applying SVG clippaths to HTML content here:\n // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491\n tests['svgclippaths'] = function() {\n return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));\n };\n\n /*>>webforms*/\n // input features and input types go directly onto the ret object, bypassing the tests loop.\n // Hold this guy to execute in a moment.\n function webforms() {\n /*>>input*/\n // Run through HTML5's new input attributes to see if the UA understands any.\n // We're using f which is the <input> element created early on\n // Mike Taylr has created a comprehensive resource for testing these attributes\n // when applied to all input types:\n // miketaylr.com/code/input-type-attr.html\n // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary\n\n // Only input placeholder is tested while textarea's placeholder is not.\n // Currently Safari 4 and Opera 11 have support only for the input placeholder\n // Both tests are available in feature-detects/forms-placeholder.js\n Modernizr['input'] = (function( props ) {\n for ( var i = 0, len = props.length; i < len; i++ ) {\n attrs[ props[i] ] = !!(props[i] in inputElem);\n }\n if (attrs.list){\n // safari false positive's on datalist: webk.it/74252\n // see also github.com/Modernizr/Modernizr/issues/146\n attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);\n }\n return attrs;\n })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));\n /*>>input*/\n\n /*>>inputtypes*/\n // Run through HTML5's new input types to see if the UA understands any.\n // This is put behind the tests runloop because it doesn't return a\n // true/false like all the other tests; instead, it returns an object\n // containing each input type with its corresponding true/false value\n\n // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/\n Modernizr['inputtypes'] = (function(props) {\n\n for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {\n\n inputElem.setAttribute('type', inputElemType = props[i]);\n bool = inputElem.type !== 'text';\n\n // We first check to see if the type we give it sticks..\n // If the type does, we feed it a textual value, which shouldn't be valid.\n // If the value doesn't stick, we know there's input sanitization which infers a custom UI\n if ( bool ) {\n\n inputElem.value = smile;\n inputElem.style.cssText = 'position:absolute;visibility:hidden;';\n\n if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {\n\n docElement.appendChild(inputElem);\n defaultView = document.defaultView;\n\n // Safari 2-4 allows the smiley as a value, despite making a slider\n bool = defaultView.getComputedStyle &&\n defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&\n // Mobile android web browser has false positive, so must\n // check the height to see if the widget is actually there.\n (inputElem.offsetHeight !== 0);\n\n docElement.removeChild(inputElem);\n\n } else if ( /^(search|tel)$/.test(inputElemType) ){\n // Spec doesn't define any special parsing or detectable UI\n // behaviors so we pass these through as true\n\n // Interestingly, opera fails the earlier test, so it doesn't\n // even make it here.\n\n } else if ( /^(url|email)$/.test(inputElemType) ) {\n // Real url and email support comes with prebaked validation.\n bool = inputElem.checkValidity && inputElem.checkValidity() === false;\n\n } else {\n // If the upgraded input component rejects the :) text, we got a winner\n bool = inputElem.value != smile;\n }\n }\n\n inputs[ props[i] ] = !!bool;\n }\n return inputs;\n })('search tel url email datetime date month week time datetime-local number range color'.split(' '));\n /*>>inputtypes*/\n }\n /*>>webforms*/\n\n\n // End of test definitions\n // -----------------------\n\n\n\n // Run through all tests and detect their support in the current UA.\n // todo: hypothetically we could be doing an array of tests and use a basic loop here.\n for ( var feature in tests ) {\n if ( hasOwnProp(tests, feature) ) {\n // run the test, throw the return value into the Modernizr,\n // then based on that boolean, define an appropriate className\n // and push it into an array of classes we'll join later.\n featureName = feature.toLowerCase();\n Modernizr[featureName] = tests[feature]();\n\n classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);\n }\n }\n\n /*>>webforms*/\n // input tests need to run.\n Modernizr.input || webforms();\n /*>>webforms*/\n\n\n /**\n * addTest allows the user to define their own feature tests\n * the result will be added onto the Modernizr object,\n * as well as an appropriate className set on the html element\n *\n * @param feature - String naming the feature\n * @param test - Function returning true if feature is supported, false if not\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 // we're going to quit if you're trying to overwrite an existing test\n // if we were to allow it, we'd do this:\n // var re = new RegExp(\"\\\\b(no-)?\" + feature + \"\\\\b\");\n // docElement.className = docElement.className.replace( re, '' );\n // but, no rly, stuff 'em.\n return Modernizr;\n }\n\n test = typeof test == 'function' ? test() : test;\n\n if (enableClasses) {\n docElement.className += ' ' + (test ? '' : 'no-') + feature;\n }\n Modernizr[feature] = test;\n\n }\n\n return Modernizr; // allow chaining.\n };\n\n\n // Reset modElem.cssText to nothing to reduce memory footprint.\n setCss('');\n modElem = inputElem = null;\n\n /*>>shiv*/\n /*! HTML5 Shiv v3.6 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */\n ;(function(window, document) {\n /*jshint evil:true */\n /** Preset options */\n var options = window.html5 || {};\n\n /** Used to skip problem elements */\n var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;\n\n /** Not all elements can be cloned in IE (this list can be shortend) **/\n var saveClones = /^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i;\n\n /** Detect whether the browser supports default html5 styles */\n var supportsHtml5Styles;\n\n /** Name of the expando, to work with multiple documents or to re-shiv one document */\n var expando = '_html5shiv';\n\n /** The id for the documents expando */\n var expanID = 0;\n\n /** Cached data for each document */\n var expandoData = {};\n\n /** Detect whether the browser supports unknown elements */\n var supportsUnknownElements;\n\n (function() {\n try {\n var a = document.createElement('a');\n a.innerHTML = '<xyz></xyz>';\n //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles\n supportsHtml5Styles = ('hidden' in a);\n\n supportsUnknownElements = a.childNodes.length == 1 || (function() {\n // assign a false positive if unable to shiv\n (document.createElement)('a');\n var frag = document.createDocumentFragment();\n return (\n typeof frag.cloneNode == 'undefined' ||\n typeof frag.createDocumentFragment == 'undefined' ||\n typeof frag.createElement == 'undefined'\n );\n }());\n } catch(e) {\n supportsHtml5Styles = true;\n supportsUnknownElements = true;\n }\n\n }());\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * Creates a style sheet with the given CSS text and adds it to the document.\n * @private\n * @param {Document} ownerDocument The document.\n * @param {String} cssText The CSS text.\n * @returns {StyleSheet} The style element.\n */\n function addStyleSheet(ownerDocument, cssText) {\n var p = ownerDocument.createElement('p'),\n parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;\n\n p.innerHTML = 'x<style>' + cssText + '</style>';\n return parent.insertBefore(p.lastChild, parent.firstChild);\n }\n\n /**\n * Returns the value of `html5.elements` as an array.\n * @private\n * @returns {Array} An array of shived element node names.\n */\n function getElements() {\n var elements = html5.elements;\n return typeof elements == 'string' ? elements.split(' ') : elements;\n }\n\n /**\n * Returns the data associated to the given document\n * @private\n * @param {Document} ownerDocument The document.\n * @returns {Object} An object of data.\n */\n function getExpandoData(ownerDocument) {\n var data = expandoData[ownerDocument[expando]];\n if (!data) {\n data = {};\n expanID++;\n ownerDocument[expando] = expanID;\n expandoData[expanID] = data;\n }\n return data;\n }\n\n /**\n * returns a shived element for the given nodeName and document\n * @memberOf html5\n * @param {String} nodeName name of the element\n * @param {Document} ownerDocument The context document.\n * @returns {Object} The shived element.\n */\n function createElement(nodeName, ownerDocument, data){\n if (!ownerDocument) {\n ownerDocument = document;\n }\n if(supportsUnknownElements){\n return ownerDocument.createElement(nodeName);\n }\n if (!data) {\n data = getExpandoData(ownerDocument);\n }\n var node;\n\n if (data.cache[nodeName]) {\n node = data.cache[nodeName].cloneNode();\n } else if (saveClones.test(nodeName)) {\n node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();\n } else {\n node = data.createElem(nodeName);\n }\n\n // Avoid adding some elements to fragments in IE < 9 because\n // * Attributes like `name` or `type` cannot be set/changed once an element\n // is inserted into a document/fragment\n // * Link elements with `src` attributes that are inaccessible, as with\n // a 403 response, will cause the tab/window to crash\n // * Script elements appended to fragments will execute when their `src`\n // or `text` property is set\n return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;\n }\n\n /**\n * returns a shived DocumentFragment for the given document\n * @memberOf html5\n * @param {Document} ownerDocument The context document.\n * @returns {Object} The shived DocumentFragment.\n */\n function createDocumentFragment(ownerDocument, data){\n if (!ownerDocument) {\n ownerDocument = document;\n }\n if(supportsUnknownElements){\n return ownerDocument.createDocumentFragment();\n }\n data = data || getExpandoData(ownerDocument);\n var clone = data.frag.cloneNode(),\n i = 0,\n elems = getElements(),\n l = elems.length;\n for(;i<l;i++){\n clone.createElement(elems[i]);\n }\n return clone;\n }\n\n /**\n * Shivs the `createElement` and `createDocumentFragment` methods of the document.\n * @private\n * @param {Document|DocumentFragment} ownerDocument The document.\n * @param {Object} data of the document.\n */\n function shivMethods(ownerDocument, data) {\n if (!data.cache) {\n data.cache = {};\n data.createElem = ownerDocument.createElement;\n data.createFrag = ownerDocument.createDocumentFragment;\n data.frag = data.createFrag();\n }\n\n\n ownerDocument.createElement = function(nodeName) {\n //abort shiv\n if (!html5.shivMethods) {\n return data.createElem(nodeName);\n }\n return createElement(nodeName, ownerDocument, data);\n };\n\n ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +\n 'var n=f.cloneNode(),c=n.createElement;' +\n 'h.shivMethods&&(' +\n // unroll the `createElement` calls\n getElements().join().replace(/\\w+/g, function(nodeName) {\n data.createElem(nodeName);\n data.frag.createElement(nodeName);\n return 'c(\"' + nodeName + '\")';\n }) +\n ');return n}'\n )(html5, data.frag);\n }\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * Shivs the given document.\n * @memberOf html5\n * @param {Document} ownerDocument The document to shiv.\n * @returns {Document} The shived document.\n */\n function shivDocument(ownerDocument) {\n if (!ownerDocument) {\n ownerDocument = document;\n }\n var data = getExpandoData(ownerDocument);\n\n if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {\n data.hasCSS = !!addStyleSheet(ownerDocument,\n // corrects block display not defined in IE6/7/8/9\n 'article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +\n // adds styling not present in IE6/7/8/9\n 'mark{background:#FF0;color:#000}'\n );\n }\n if (!supportsUnknownElements) {\n shivMethods(ownerDocument, data);\n }\n return ownerDocument;\n }\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * The `html5` object is exposed so that more elements can be shived and\n * existing shiving can be detected on iframes.\n * @type Object\n * @example\n *\n * // options can be changed before the script is included\n * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };\n */\n var html5 = {\n\n /**\n * An array or space separated string of node names of the elements to shiv.\n * @memberOf html5\n * @type Array|String\n */\n 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',\n\n /**\n * A flag to indicate that the HTML5 style sheet should be inserted.\n * @memberOf html5\n * @type Boolean\n */\n 'shivCSS': (options.shivCSS !== false),\n\n /**\n * Is equal to true if a browser supports creating unknown/HTML5 elements\n * @memberOf html5\n * @type boolean\n */\n 'supportsUnknownElements': supportsUnknownElements,\n\n /**\n * A flag to indicate that the document's `createElement` and `createDocumentFragment`\n * methods should be overwritten.\n * @memberOf html5\n * @type Boolean\n */\n 'shivMethods': (options.shivMethods !== false),\n\n /**\n * A string to describe the type of `html5` object (\"default\" or \"default print\").\n * @memberOf html5\n * @type String\n */\n 'type': 'default',\n\n // shivs the document according to the specified `html5` object options\n 'shivDocument': shivDocument,\n\n //creates a shived element\n createElement: createElement,\n\n //creates a shived documentFragment\n createDocumentFragment: createDocumentFragment\n };\n\n /*--------------------------------------------------------------------------*/\n\n // expose html5\n window.html5 = html5;\n\n // shiv the document\n shivDocument(document);\n\n }(this, document));\n /*>>shiv*/\n\n // Assign private properties to the return object with prefix\n Modernizr._version = version;\n\n // expose these for the plugin API. Look in the source for how to join() them against your input\n /*>>prefixes*/\n Modernizr._prefixes = prefixes;\n /*>>prefixes*/\n /*>>domprefixes*/\n Modernizr._domPrefixes = domPrefixes;\n Modernizr._cssomPrefixes = cssomPrefixes;\n /*>>domprefixes*/\n\n /*>>mq*/\n // Modernizr.mq tests a given media query, live against the current state of the window\n // A few important notes:\n // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false\n // * A max-width or orientation query will be evaluated against the current state, which may change later.\n // * You must specify values. Eg. If you are testing support for the min-width media query use:\n // Modernizr.mq('(min-width:0)')\n // usage:\n // Modernizr.mq('only screen and (max-width:768)')\n Modernizr.mq = testMediaQuery;\n /*>>mq*/\n\n /*>>hasevent*/\n // Modernizr.hasEvent() detects support for a given event, with an optional element to test on\n // Modernizr.hasEvent('gesturestart', elem)\n Modernizr.hasEvent = isEventSupported;\n /*>>hasevent*/\n\n /*>>testprop*/\n // Modernizr.testProp() investigates whether a given style property is recognized\n // Note that the property names must be provided in the camelCase variant.\n // Modernizr.testProp('pointerEvents')\n Modernizr.testProp = function(prop){\n return testProps([prop]);\n };\n /*>>testprop*/\n\n /*>>testallprops*/\n // Modernizr.testAllProps() investigates whether a given style property,\n // or any of its vendor-prefixed variants, is recognized\n // Note that the property names must be provided in the camelCase variant.\n // Modernizr.testAllProps('boxSizing')\n Modernizr.testAllProps = testPropsAll;\n /*>>testallprops*/\n\n\n /*>>teststyles*/\n // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards\n // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })\n Modernizr.testStyles = injectElementWithStyles;\n /*>>teststyles*/\n\n\n /*>>prefixed*/\n // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input\n // Modernizr.prefixed('boxSizing') // 'MozBoxSizing'\n\n // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.\n // Return values will also be the camelCase variant, if you need to translate that to hypenated style use:\n //\n // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');\n\n // If you're trying to ascertain which transition end event to bind to, you might do something like...\n //\n // var transEndEventNames = {\n // 'WebkitTransition' : 'webkitTransitionEnd',\n // 'MozTransition' : 'transitionend',\n // 'OTransition' : 'oTransitionEnd',\n // 'msTransition' : 'MSTransitionEnd',\n // 'transition' : 'transitionend'\n // },\n // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];\n\n Modernizr.prefixed = function(prop, obj, elem){\n if(!obj) {\n return testPropsAll(prop, 'pfx');\n } else {\n // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'\n return testPropsAll(prop, obj, elem);\n }\n };\n /*>>prefixed*/\n\n\n /*>>cssclasses*/\n // Remove \"no-js\" class from <html> element, if it exists:\n docElement.className = docElement.className.replace(/(^|\\s)no-js(\\s|$)/, '$1$2') +\n\n // Add the new classes to the <html> element.\n (enableClasses ? ' js ' + classes.join(' ') : '');\n /*>>cssclasses*/\n\n return Modernizr;\n\n})(this, this.document);","modernizr/modernizr.2.0.6.js":"/* Modernizr 2.0.6 (Custom Build) | MIT & BSD\n * Build: http://www.modernizr.com/download/#-csstransforms-csstransforms3d-cssclasses-prefixed-teststyles-testprop-testallprops-prefixes-domprefixes\n */\n;window.Modernizr=function(a,b,c){function C(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+\" \"+o.join(c+\" \")+c).split(\" \");return B(d,b)}function B(a,b){for(var d in a)if(k[a[d]]!==c)return b==\"pfx\"?a[d]:!0;return!1}function A(a,b){return!!~(\"\"+a).indexOf(b)}function z(a,b){return typeof a===b}function y(a,b){return x(n.join(a+\";\")+(b||\"\"))}function x(a){k.cssText=a}var d=\"2.0.6\",e={},f=!0,g=b.documentElement,h=b.head||b.getElementsByTagName(\"head\")[0],i=\"modernizr\",j=b.createElement(i),k=j.style,l,m=Object.prototype.toString,n=\" -webkit- -moz- -o- -ms- -khtml- \".split(\" \"),o=\"Webkit Moz O ms Khtml\".split(\" \"),p={},q={},r={},s=[],t=function(a,c,d,e){var f,h,j,k=b.createElement(\"div\");if(parseInt(d,10))while(d--)j=b.createElement(\"div\"),j.id=e?e[d]:i+(d+1),k.appendChild(j);f=[\"­\",\"<style>\",a,\"</style>\"].join(\"\"),k.id=i,k.innerHTML+=f,g.appendChild(k),h=c(k,a),k.parentNode.removeChild(k);return!!h},u,v={}.hasOwnProperty,w;!z(v,c)&&!z(v.call,c)?w=function(a,b){return v.call(a,b)}:w=function(a,b){return b in a&&z(a.constructor.prototype[b],c)};var D=function(a,c){var d=a.join(\"\"),f=c.length;t(d,function(a,c){var d=b.styleSheets[b.styleSheets.length-1],g=d.cssRules&&d.cssRules[0]?d.cssRules[0].cssText:d.cssText||\"\",h=a.childNodes,i={};while(f--)i[h[f].id]=h[f];e.csstransforms3d=i.csstransforms3d.offsetLeft===9},f,c)}([,[\"@media (\",n.join(\"transform-3d),(\"),i,\")\",\"{#csstransforms3d{left:9px;position:absolute}}\"].join(\"\")],[,\"csstransforms3d\"]);p.csstransforms=function(){return!!B([\"transformProperty\",\"WebkitTransform\",\"MozTransform\",\"OTransform\",\"msTransform\"])},p.csstransforms3d=function(){var a=!!B([\"perspectiveProperty\",\"WebkitPerspective\",\"MozPerspective\",\"OPerspective\",\"msPerspective\"]);a&&\"webkitPerspective\"in g.style&&(a=e.csstransforms3d);return a};for(var E in p)w(p,E)&&(u=E.toLowerCase(),e[u]=p[E](),s.push((e[u]?\"\":\"no-\")+u));x(\"\"),j=l=null,e._version=d,e._prefixes=n,e._domPrefixes=o,e.testProp=function(a){return B([a])},e.testAllProps=C,e.testStyles=t,e.prefixed=function(a){return C(a,\"pfx\")},g.className=g.className.replace(/\\bno-js\\b/,\"\")+(f?\" js \"+s.join(\" \"):\"\");return e}(this,this.document);","modernizr/modernizr.details.js":"// By @mathias, based on http://mths.be/axh\ndefine([\n 'modernizr/modernizr'\n], function(){\n\n Modernizr.addTest('details', function() {\n var doc = document,\n el = doc.createElement('details'),\n fake,\n root,\n diff;\n if (!('open' in el)) { // return early if possible; thanks @aFarkas!\n return false;\n }\n root = doc.body || (function() {\n var de = doc.documentElement;\n fake = true;\n return de.insertBefore(doc.createElement('body'), de.firstElementChild || de.firstChild);\n }());\n el.innerHTML = '<summary>a</summary>b';\n el.style.display = 'block';\n root.appendChild(el);\n diff = el.offsetHeight;\n el.open = true;\n diff = diff != el.offsetHeight;\n root.removeChild(el);\n fake && root.parentNode.removeChild(root);\n return diff;\n });\n\n});","Magento_Checkout/js/model/default-post-code-resolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([], function () {\n 'use strict';\n\n /**\n * Define necessity of using default post code value\n */\n var useDefaultPostCode;\n\n return {\n /**\n * Resolve default post code\n *\n * @returns {String|null}\n */\n resolve: function () {\n return useDefaultPostCode ? window.checkoutConfig.defaultPostcode : null;\n },\n\n /**\n * Set state to useDefaultPostCode variable\n *\n * @param {Boolean} shouldUseDefaultPostCode\n * @returns {underscore}\n */\n setUseDefaultPostCode: function (shouldUseDefaultPostCode) {\n useDefaultPostCode = shouldUseDefaultPostCode;\n\n return this;\n }\n };\n});\n","Magento_Payment/transparent.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/alert',\n 'Magento_Payment/js/model/credit-card-validation/validator'\n], function ($, mageTemplate, alert) {\n 'use strict';\n\n $.widget('mage.transparent', {\n options: {\n editFormSelector: '#edit_form',\n hiddenFormTmpl:\n '<form target=\"<%= data.target %>\" action=\"<%= data.action %>\"' +\n 'method=\"POST\" hidden' +\n 'enctype=\"application/x-www-form-urlencoded\" class=\"no-display\">' +\n '<% _.each(data.inputs, function(val, key){ %>' +\n '<input value=\"<%= val %>\" name=\"<%= key %>\" type=\"hidden\">' +\n '<% }); %>' +\n '</form>',\n cgiUrl: null,\n orderSaveUrl: null,\n controller: null,\n gateway: null,\n dateDelim: null,\n cardFieldsMap: null,\n expireYearLength: 2\n },\n\n /**\n * @private\n */\n _create: function () {\n this.hiddenFormTmpl = mageTemplate(this.options.hiddenFormTmpl);\n\n $(this.options.editFormSelector).on('changePaymentMethod', this._setPlaceOrderHandler.bind(this));\n $(this.options.editFormSelector).trigger('changePaymentMethod', [\n $(this.options.editFormSelector).find(':radio[name=\"payment[method]\"]:checked').val()\n ]);\n },\n\n /**\n * Handler for form submit.\n *\n * @param {Object} event\n * @param {String} method\n */\n _setPlaceOrderHandler: function (event, method) {\n if (method === this.options.gateway) {\n $(this.options.editFormSelector)\n .off('submitOrder')\n .on('submitOrder.' + this.options.gateway, this._placeOrderHandler.bind(this));\n } else {\n $(this.options.editFormSelector)\n .off('submitOrder.' + this.options.gateway);\n }\n },\n\n /**\n * Handler for form submit to call gateway for credit card validation.\n *\n * @return {Boolean}\n * @private\n */\n _placeOrderHandler: function () {\n if ($(this.options.editFormSelector).valid()) {\n this._orderSave();\n } else {\n $('body').trigger('processStop');\n }\n\n return false;\n },\n\n /**\n * Handler for Place Order button to call gateway for credit card validation.\n * Save order and generate post data for gateway call.\n *\n * @private\n */\n _orderSave: function () {\n // jscs:ignore disallowTrailingWhitespace\n /*eslint no-undef:0*/\n var postData = {\n 'form_key': FORM_KEY,\n 'cc_type': this.ccType()\n };\n\n $.ajax({\n url: this.options.orderSaveUrl,\n type: 'post',\n context: this,\n data: postData,\n dataType: 'json',\n //eslint-disable-line lines-around-comment\n /**\n * Success callback\n * @param {Object} response\n */\n success: function (response) {\n if (response.success && response[this.options.gateway]) {\n this._postPaymentToGateway(response);\n } else {\n this._processErrors(response);\n }\n },\n complete: function () {\n $('body').trigger('processStop');\n }\n });\n },\n\n /**\n * Post data to gateway for credit card validation.\n *\n * @param {Object} response\n * @private\n */\n _postPaymentToGateway: function (response) {\n var $iframeSelector = $('[data-container=\"' + this.options.gateway + '-transparent-iframe\"]'),\n data,\n tmpl,\n iframe;\n\n data = this._preparePaymentData(response);\n tmpl = this.hiddenFormTmpl({\n data: {\n target: $iframeSelector.attr('name'),\n action: this.options.cgiUrl,\n inputs: data\n }\n });\n\n iframe = $iframeSelector\n .on('submit', function (event) {\n event.stopPropagation();\n });\n $(tmpl).appendTo(iframe).submit();\n iframe.html('');\n },\n\n /**\n * @returns {String}\n */\n ccType: function () {\n return this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-type\"]'\n ).val();\n },\n\n /**\n * Add credit card fields to post data for gateway.\n *\n * @param {Object} response\n * @private\n */\n _preparePaymentData: function (response) {\n var ccfields,\n data,\n preparedata;\n\n data = response[this.options.gateway].fields;\n ccfields = this.options.cardFieldsMap;\n\n if (this.element.find('[data-container=\"' + this.options.gateway + '-cc-cvv\"]').length) {\n data[ccfields.cccvv] = this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-cvv\"]'\n ).val();\n }\n preparedata = this._prepareExpDate();\n data[ccfields.ccexpdate] = preparedata.month + this.options.dateDelim + preparedata.year;\n data[ccfields.ccnum] = this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-number\"]'\n ).val();\n\n return data;\n },\n\n /**\n * Grab Month and Year into one\n * @returns {Object}\n * @private\n */\n _prepareExpDate: function () {\n var year = this.element.find('[data-container=\"' + this.options.gateway + '-cc-year\"]').val(),\n month = parseInt(\n this.element.find('[data-container=\"' + this.options.gateway + '-cc-month\"]').val(), 10\n );\n\n if (year.length > this.options.expireYearLength) {\n year = year.substring(year.length - this.options.expireYearLength);\n }\n\n if (month < 10) {\n month = '0' + month;\n }\n\n return {\n month: month, year: year\n };\n },\n\n /**\n * Processing errors\n *\n * @param {Object} response\n * @private\n */\n _processErrors: function (response) {\n var msg = response['error_messages'];\n\n if (typeof msg === 'object') {\n alert({\n content: msg.join('\\n')\n });\n }\n\n if (msg) {\n alert({\n content: msg\n });\n }\n }\n });\n\n return $.mage.transparent;\n});\n","Magento_Payment/js/transparent.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global FORM_KEY */\n/* @api */\ndefine([\n 'jquery',\n 'mage/template',\n 'Magento_Ui/js/modal/alert',\n 'Magento_Payment/js/model/credit-card-validation/validator'\n], function ($, mageTemplate, alert) {\n 'use strict';\n\n $.widget('mage.transparent', {\n options: {\n editFormSelector: '#edit_form',\n hiddenFormTmpl:\n '<form target=\"<%= data.target %>\" action=\"<%= data.action %>\"' +\n 'method=\"POST\" hidden' +\n 'enctype=\"application/x-www-form-urlencoded\" class=\"no-display\">' +\n '<% _.each(data.inputs, function(val, key){ %>' +\n '<input value=\"<%= val %>\" name=\"<%= key %>\" type=\"hidden\">' +\n '<% }); %>' +\n '</form>',\n cgiUrl: null,\n orderSaveUrl: null,\n controller: null,\n gateway: null,\n dateDelim: null,\n cardFieldsMap: null,\n expireYearLength: 2\n },\n\n /**\n * @private\n */\n _create: function () {\n this.hiddenFormTmpl = mageTemplate(this.options.hiddenFormTmpl);\n\n $(this.options.editFormSelector).on('changePaymentMethod', this._setPlaceOrderHandler.bind(this));\n $(this.options.editFormSelector).trigger('changePaymentMethod', [\n $(this.options.editFormSelector).find(':radio[name=\"payment[method]\"]:checked').val()\n ]);\n },\n\n /**\n * Handler for form submit.\n *\n * @param {Object} event\n * @param {String} method\n */\n _setPlaceOrderHandler: function (event, method) {\n if (method === this.options.gateway) {\n $(this.options.editFormSelector)\n .off('submitOrder')\n .on('submitOrder.' + this.options.gateway, this._placeOrderHandler.bind(this));\n } else {\n $(this.options.editFormSelector)\n .off('submitOrder.' + this.options.gateway);\n }\n },\n\n /**\n * Handler for form submit to call gateway for credit card validation.\n *\n * @return {Boolean}\n * @private\n */\n _placeOrderHandler: function () {\n if ($(this.options.editFormSelector).valid()) {\n this._orderSave();\n } else {\n $('body').trigger('processStop');\n }\n\n return false;\n },\n\n /**\n * Handler for Place Order button to call gateway for credit card validation.\n * Save order and generate post data for gateway call.\n *\n * @private\n */\n _orderSave: function () {\n var postData = {\n 'form_key': FORM_KEY,\n 'cc_type': this.ccType()\n };\n\n $.ajax({\n url: this.options.orderSaveUrl,\n type: 'post',\n context: this,\n data: postData,\n dataType: 'json',\n\n /**\n * Success callback\n * @param {Object} response\n */\n success: function (response) {\n if (response.success && response[this.options.gateway]) {\n this._postPaymentToGateway(response);\n } else {\n this._processErrors(response);\n }\n },\n\n /** @inheritdoc */\n complete: function () {\n $('body').trigger('processStop');\n }\n });\n },\n\n /**\n * Post data to gateway for credit card validation.\n *\n * @param {Object} response\n * @private\n */\n _postPaymentToGateway: function (response) {\n var $iframeSelector = $('[data-container=\"' + this.options.gateway + '-transparent-iframe\"]'),\n data,\n tmpl,\n iframe;\n\n data = this._preparePaymentData(response);\n tmpl = this.hiddenFormTmpl({\n data: {\n target: $iframeSelector.attr('name'),\n action: this.options.cgiUrl,\n inputs: data\n }\n });\n\n iframe = $iframeSelector\n .on('submit', function (event) {\n event.stopPropagation();\n });\n $(tmpl).appendTo(iframe).submit();\n iframe.html('');\n },\n\n /**\n * @returns {String}\n */\n ccType: function () {\n return this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-type\"]'\n ).val();\n },\n\n /**\n * Add credit card fields to post data for gateway.\n *\n * @param {Object} response\n * @private\n */\n _preparePaymentData: function (response) {\n var ccfields,\n data,\n preparedata;\n\n data = response[this.options.gateway].fields;\n ccfields = this.options.cardFieldsMap;\n\n if (this.element.find('[data-container=\"' + this.options.gateway + '-cc-cvv\"]').length) {\n data[ccfields.cccvv] = this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-cvv\"]'\n ).val();\n }\n preparedata = this._prepareExpDate();\n data[ccfields.ccexpdate] = preparedata.month + this.options.dateDelim + preparedata.year;\n data[ccfields.ccnum] = this.element.find(\n '[data-container=\"' + this.options.gateway + '-cc-number\"]'\n ).val();\n\n return data;\n },\n\n /**\n * Grab Month and Year into one\n * @returns {Object}\n * @private\n */\n _prepareExpDate: function () {\n var year = this.element.find('[data-container=\"' + this.options.gateway + '-cc-year\"]').val(),\n month = parseInt(\n this.element.find('[data-container=\"' + this.options.gateway + '-cc-month\"]').val(), 10\n );\n\n if (year.length > this.options.expireYearLength) {\n year = year.substring(year.length - this.options.expireYearLength);\n }\n\n if (month < 10) {\n month = '0' + month;\n }\n\n return {\n month: month, year: year\n };\n },\n\n /**\n * Processing errors\n *\n * @param {Object} response\n * @private\n */\n _processErrors: function (response) {\n var msg = response['error_messages'];\n\n if (typeof msg === 'object') {\n alert({\n content: msg.join('\\n')\n });\n }\n\n if (msg) {\n alert({\n content: msg\n });\n }\n }\n });\n\n return $.mage.transparent;\n});\n","Magento_Payment/js/model/credit-card-validation/cvv-validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([], function () {\n 'use strict';\n\n /**\n * @param {*} isValid\n * @param {*} isPotentiallyValid\n * @return {Object}\n */\n function resultWrapper(isValid, isPotentiallyValid) {\n return {\n isValid: isValid,\n isPotentiallyValid: isPotentiallyValid\n };\n }\n\n /**\n * CVV number validation.\n * Validate digit count for CVV code.\n *\n * @param {*} value\n * @param {Number} maxLength\n * @return {Object}\n */\n return function (value, maxLength) {\n var DEFAULT_LENGTH = 3;\n\n maxLength = maxLength || DEFAULT_LENGTH;\n\n if (!/^\\d*$/.test(value)) {\n return resultWrapper(false, false);\n }\n\n if (value.length === maxLength) {\n return resultWrapper(true, true);\n }\n\n if (value.length < maxLength) {\n return resultWrapper(false, true);\n }\n\n if (value.length > maxLength) {\n return resultWrapper(false, false);\n }\n };\n});\n","Magento_Payment/js/model/credit-card-validation/credit-card-data.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([], function () {\n 'use strict';\n\n return {\n creditCard: null,\n creditCardNumber: null,\n expirationMonth: null,\n expirationYear: null,\n cvvCode: null\n };\n});\n","Magento_Payment/js/model/credit-card-validation/credit-card-number-validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'mageUtils',\n 'Magento_Payment/js/model/credit-card-validation/credit-card-number-validator/luhn10-validator',\n 'Magento_Payment/js/model/credit-card-validation/credit-card-number-validator/credit-card-type'\n], function (utils, luhn10, creditCardTypes) {\n 'use strict';\n\n /**\n * @param {*} card\n * @param {*} isPotentiallyValid\n * @param {*} isValid\n * @return {Object}\n */\n function resultWrapper(card, isPotentiallyValid, isValid) {\n return {\n card: card,\n isValid: isValid,\n isPotentiallyValid: isPotentiallyValid\n };\n }\n\n return function (value) {\n var potentialTypes,\n cardType,\n valid,\n i,\n maxLength;\n\n if (utils.isEmpty(value)) {\n return resultWrapper(null, false, false);\n }\n\n value = value.replace(/|\\s/g, '');\n\n if (!/^\\d*$/.test(value)) {\n return resultWrapper(null, false, false);\n }\n\n potentialTypes = creditCardTypes.getCardTypes(value);\n\n if (potentialTypes.length === 0) {\n return resultWrapper(null, false, false);\n } else if (potentialTypes.length !== 1) {\n return resultWrapper(null, true, false);\n }\n\n cardType = potentialTypes[0];\n\n if (cardType.type === 'unionpay') { // UnionPay is not Luhn 10 compliant\n valid = true;\n } else {\n valid = luhn10(value);\n }\n\n for (i = 0; i < cardType.lengths.length; i++) {\n if (cardType.lengths[i] === value.length) {\n return resultWrapper(cardType, valid, valid);\n }\n }\n\n maxLength = Math.max.apply(null, cardType.lengths);\n\n if (value.length < maxLength) {\n return resultWrapper(cardType, true, false);\n }\n\n return resultWrapper(cardType, false, false);\n };\n});\n","Magento_Payment/js/model/credit-card-validation/expiration-date-validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'mageUtils',\n 'Magento_Payment/js/model/credit-card-validation/expiration-date-validator/parse-date',\n 'Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-month-validator',\n 'Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-year-validator'\n], function (utils, parseDate, expirationMonth, expirationYear) {\n 'use strict';\n\n /**\n * @param {*} isValid\n * @param {*} isPotentiallyValid\n * @param {*} month\n * @param {*} year\n * @return {Object}\n */\n function resultWrapper(isValid, isPotentiallyValid, month, year) {\n return {\n isValid: isValid,\n isPotentiallyValid: isPotentiallyValid,\n month: month,\n year: year\n };\n }\n\n return function (value) {\n var date,\n monthValid,\n yearValid;\n\n if (utils.isEmpty(value)) {\n return resultWrapper(false, false, null, null);\n }\n\n value = value.replace(/^(\\d\\d) (\\d\\d(\\d\\d)?)$/, '$1/$2');\n date = parseDate(value);\n monthValid = expirationMonth(date.month);\n yearValid = expirationYear(date.year);\n\n if (monthValid.isValid && yearValid.isValid) {\n return resultWrapper(true, true, date.month, date.year);\n }\n\n if (monthValid.isPotentiallyValid && yearValid.isPotentiallyValid) {\n return resultWrapper(false, true, null, null);\n }\n\n return resultWrapper(false, false, null, null);\n };\n});\n","Magento_Payment/js/model/credit-card-validation/validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\n(function (factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'Magento_Payment/js/model/credit-card-validation/cvv-validator',\n 'Magento_Payment/js/model/credit-card-validation/credit-card-number-validator',\n 'Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-year-validator',\n 'Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-month-validator',\n 'Magento_Payment/js/model/credit-card-validation/credit-card-data',\n 'mage/translate'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($, cvvValidator, creditCardNumberValidator, yearValidator, monthValidator, creditCardData) {\n 'use strict';\n\n $('.payment-method-content input[type=\"number\"]').on('keyup', function () {\n if ($(this).val() < 0) {\n $(this).val($(this).val().replace(/^-/, ''));\n }\n });\n\n $.each({\n 'validate-card-type': [\n function (number, item, allowedTypes) {\n var cardInfo,\n i,\n l;\n\n if (!creditCardNumberValidator(number).isValid) {\n return false;\n }\n\n cardInfo = creditCardNumberValidator(number).card;\n\n for (i = 0, l = allowedTypes.length; i < l; i++) {\n if (cardInfo.title == allowedTypes[i].type) { //eslint-disable-line eqeqeq\n return true;\n }\n }\n\n return false;\n },\n $.mage.__('Please enter a valid credit card type number.')\n ],\n 'validate-card-number': [\n\n /**\n * Validate credit card number based on mod 10\n *\n * @param {*} number - credit card number\n * @return {Boolean}\n */\n function (number) {\n return creditCardNumberValidator(number).isValid;\n },\n $.mage.__('Please enter a valid credit card number.')\n ],\n 'validate-card-date': [\n\n /**\n * Validate credit card expiration month\n *\n * @param {String} date - month\n * @return {Boolean}\n */\n function (date) {\n return monthValidator(date).isValid;\n },\n $.mage.__('Incorrect credit card expiration month.')\n ],\n 'validate-card-cvv': [\n\n /**\n * Validate cvv\n *\n * @param {String} cvv - card verification value\n * @return {Boolean}\n */\n function (cvv) {\n var maxLength = creditCardData.creditCard ? creditCardData.creditCard.code.size : 3;\n\n return cvvValidator(cvv, maxLength).isValid;\n },\n $.mage.__('Please enter a valid credit card verification number.')\n ],\n 'validate-card-year': [\n\n /**\n * Validate credit card expiration year\n *\n * @param {String} date - year\n * @return {Boolean}\n */\n function (date) {\n return yearValidator(date).isValid;\n },\n $.mage.__('Incorrect credit card expiration year.')\n ]\n\n }, function (i, rule) {\n rule.unshift(i);\n $.validator.addMethod.apply($.validator, rule);\n });\n}));\n","Magento_Payment/js/model/credit-card-validation/credit-card-number-validator/credit-card-type.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'jquery',\n 'mageUtils'\n], function ($, utils) {\n 'use strict';\n\n var types = [\n {\n title: 'Visa',\n type: 'VI',\n pattern: '^4\\\\d*$',\n gaps: [4, 8, 12],\n lengths: [16],\n code: {\n name: 'CVV',\n size: 3\n }\n },\n {\n title: 'MasterCard',\n type: 'MC',\n pattern: '^(?: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 gaps: [4, 8, 12],\n lengths: [16],\n code: {\n name: 'CVC',\n size: 3\n }\n },\n {\n title: 'American Express',\n type: 'AE',\n pattern: '^3([47]\\\\d*)?$',\n isAmex: true,\n gaps: [4, 10],\n lengths: [15],\n code: {\n name: 'CID',\n size: 4\n }\n },\n {\n title: 'Diners',\n type: 'DN',\n pattern: '^(3(0[0-5]|095|6|[8-9]))\\\\d*$',\n gaps: [4, 10],\n lengths: [14, 16, 17, 18, 19],\n code: {\n name: 'CVV',\n size: 3\n }\n },\n {\n title: 'Discover',\n type: 'DI',\n pattern: '^(6011(0|[2-4]|74|7[7-9]|8[6-9]|9)|6(4[4-9]|5))\\\\d*$',\n gaps: [4, 8, 12],\n lengths: [16, 17, 18, 19],\n code: {\n name: 'CID',\n size: 3\n }\n },\n {\n title: 'JCB',\n type: 'JCB',\n pattern: '^35(2[8-9]|[3-8])\\\\d*$',\n gaps: [4, 8, 12],\n lengths: [16, 17, 18, 19],\n code: {\n name: 'CVV',\n size: 3\n }\n },\n {\n title: 'UnionPay',\n type: 'UN',\n pattern: '^(622(1(2[6-9]|[3-9])|[3-8]|9([[0-1]|2[0-5]))|62[4-6]|628([2-8]))\\\\d*?$',\n gaps: [4, 8, 12],\n lengths: [16, 17, 18, 19],\n code: {\n name: 'CVN',\n size: 3\n }\n },\n {\n title: 'Maestro International',\n type: 'MI',\n pattern: '^(5(0|[6-9])|63|67(?!59|6770|6774))\\\\d*$',\n gaps: [4, 8, 12],\n lengths: [12, 13, 14, 15, 16, 17, 18, 19],\n code: {\n name: 'CVC',\n size: 3\n }\n },\n {\n title: 'Maestro Domestic',\n type: 'MD',\n pattern: '^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\\\d*$',\n gaps: [4, 8, 12],\n lengths: [12, 13, 14, 15, 16, 17, 18, 19],\n code: {\n name: 'CVC',\n size: 3\n }\n }\n ];\n\n return {\n /**\n * @param {*} cardNumber\n * @return {Array}\n */\n getCardTypes: function (cardNumber) {\n var i, value,\n result = [];\n\n if (utils.isEmpty(cardNumber)) {\n return result;\n }\n\n if (cardNumber === '') {\n return $.extend(true, {}, types);\n }\n\n for (i = 0; i < types.length; i++) {\n value = types[i];\n\n if (new RegExp(value.pattern).test(cardNumber)) {\n result.push($.extend(true, {}, value));\n }\n }\n\n return result;\n }\n };\n});\n","Magento_Payment/js/model/credit-card-validation/credit-card-number-validator/luhn10-validator.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 /**\n * Luhn algorithm verification\n */\n return function (a, b, c, d, e) {\n for (d = +a[b = a.length - 1], e = 0; b--;) {\n c = +a[b];\n d += ++e % 2 ? 2 * c % 10 + (c > 4) : c;\n }\n\n return !(d % 10);\n };\n});\n","Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-year-validator.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 /**\n * @param {*} isValid\n * @param {*} isPotentiallyValid\n * @return {Object}\n */\n function resultWrapper(isValid, isPotentiallyValid) {\n return {\n isValid: isValid,\n isPotentiallyValid: isPotentiallyValid\n };\n }\n\n return function (value) {\n var currentYear = new Date().getFullYear(),\n len = value.length,\n valid,\n expMaxLifetime = 19;\n\n if (value.replace(/\\s/g, '') === '') {\n return resultWrapper(false, true);\n }\n\n if (!/^\\d*$/.test(value)) {\n return resultWrapper(false, false);\n }\n\n if (len !== 4) {\n return resultWrapper(false, true);\n }\n\n value = parseInt(value, 10);\n valid = value >= currentYear && value <= currentYear + expMaxLifetime;\n\n return resultWrapper(valid, valid);\n };\n});\n","Magento_Payment/js/model/credit-card-validation/expiration-date-validator/expiration-month-validator.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 /**\n * @param {*} isValid\n * @param {*} isPotentiallyValid\n * @return {Object}\n */\n function resultWrapper(isValid, isPotentiallyValid) {\n return {\n isValid: isValid,\n isPotentiallyValid: isPotentiallyValid\n };\n }\n\n return function (value) {\n var month,\n monthValid;\n\n if (value.replace(/\\s/g, '') === '' || value === '0') {\n return resultWrapper(false, true);\n }\n\n if (!/^\\d*$/.test(value)) {\n return resultWrapper(false, false);\n }\n\n if (isNaN(value)) {\n return resultWrapper(false, false);\n }\n\n month = parseInt(value, 10);\n monthValid = month > 0 && month < 13;\n\n return resultWrapper(monthValid, monthValid);\n };\n});\n","Magento_Payment/js/model/credit-card-validation/expiration-date-validator/parse-date.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 function (value) {\n var month, len;\n\n if (value.match('/')) {\n value = value.split(/\\s*\\/\\s*/g);\n\n return {\n month: value[0],\n year: value.slice(1).join()\n };\n }\n\n len = value[0] === '0' || value.length > 5 || value.length === 4 || value.length === 3 ? 2 : 1;\n month = value.substr(0, len);\n\n return {\n month: month,\n year: value.substr(month.length, 4)\n };\n };\n});\n"} }});