require.config({"config": { "jsbuild":{"knockoutjs/knockout-repeat.js":"// REPEAT binding for Knockout http://knockoutjs.com/\n// (c) Michael Best\n// License: MIT (http://www.opensource.org/licenses/mit-license.php)\n// Version 2.1.0\n\n(function(factory) {\n if (typeof define === 'function' && define.amd) {\n // [1] AMD anonymous module\n define(['knockout'], factory);\n } else if (typeof exports === 'object') {\n // [2] commonJS\n factory(require('knockout'));\n } else {\n // [3] No module loader (plain <script> tag) - put directly in global namespace\n factory(window.ko);\n }\n})(function(ko) {\n\nif (!ko.virtualElements)\n throw Error('Repeat requires at least Knockout 2.1');\n\nvar ko_bindingFlags = ko.bindingFlags || {};\nvar ko_unwrap = ko.utils.unwrapObservable;\n\nvar koProtoName = '__ko_proto__';\n\nif (ko.version >= \"3.0.0\") {\n // In Knockout 3.0.0, use the node preprocessor to replace a node with a repeat binding with a virtual element\n var provider = ko.bindingProvider.instance, previousPreprocessFn = provider.preprocessNode;\n provider.preprocessNode = function(node) {\n var newNodes, nodeBinding;\n if (!previousPreprocessFn || !(newNodes = previousPreprocessFn.call(this, node))) {\n if (node.nodeType === 1 && (nodeBinding = node.getAttribute('data-bind'))) {\n if (/^\\s*repeat\\s*:/.test(nodeBinding)) {\n var leadingComment = node.ownerDocument.createComment('ko ' + nodeBinding),\n trailingComment = node.ownerDocument.createComment('/ko');\n node.parentNode.insertBefore(leadingComment, node);\n node.parentNode.insertBefore(trailingComment, node.nextSibling);\n node.removeAttribute('data-bind');\n newNodes = [leadingComment, node, trailingComment];\n }\n }\n }\n return newNodes;\n };\n}\n\nko.virtualElements.allowedBindings.repeat = true;\nko.bindingHandlers.repeat = {\n flags: ko_bindingFlags.contentBind | ko_bindingFlags.canUseVirtual,\n init: function(element, valueAccessor, allBindingsAccessor, xxx, bindingContext) {\n\n // Read and set fixed options--these options cannot be changed\n var repeatParam = ko_unwrap(valueAccessor());\n if (repeatParam && typeof repeatParam == 'object' && !('length' in repeatParam)) {\n var repeatIndex = repeatParam.index,\n repeatData = repeatParam.item,\n repeatStep = repeatParam.step,\n repeatReversed = repeatParam.reverse,\n repeatBind = repeatParam.bind,\n repeatInit = repeatParam.init,\n repeatUpdate = repeatParam.update;\n }\n // Set default values for options that need it\n repeatIndex = repeatIndex || '$index';\n repeatData = repeatData || ko.bindingHandlers.repeat.itemName || '$item';\n repeatStep = repeatStep || 1;\n repeatReversed = repeatReversed || false;\n\n var parent = element.parentNode, placeholder;\n if (element.nodeType == 8) { // virtual element\n // Extract the \"children\" and find the single element node\n var childNodes = ko.utils.arrayFilter(ko.virtualElements.childNodes(element), function(node) { return node.nodeType == 1;});\n if (childNodes.length !== 1) {\n throw Error(\"Repeat binding requires a single element to repeat\");\n }\n ko.virtualElements.emptyNode(element);\n\n // The placeholder is the closing comment normally, or the opening comment if reversed\n placeholder = repeatReversed ? element : element.nextSibling;\n // The element to repeat is the contained element\n element = childNodes[0];\n } else { // regular element\n // First clean the element node and remove node's binding\n var origBindString = element.getAttribute('data-bind');\n ko.cleanNode(element);\n element.removeAttribute('data-bind');\n\n // Original element is no longer needed: delete it and create a placeholder comment\n placeholder = element.ownerDocument.createComment('ko_repeatplaceholder ' + origBindString);\n parent.replaceChild(placeholder, element);\n }\n\n // extract and remove a data-repeat-bind attribute, if present\n if (!repeatBind) {\n repeatBind = element.getAttribute('data-repeat-bind');\n if (repeatBind) {\n element.removeAttribute('data-repeat-bind');\n }\n }\n\n // Make a copy of the element node to be copied for each repetition\n var cleanNode = element.cloneNode(true);\n if (typeof repeatBind == \"string\") {\n cleanNode.setAttribute('data-bind', repeatBind);\n repeatBind = null;\n }\n\n // Set up persistent data\n var lastRepeatCount = 0,\n notificationObservable = ko.observable(),\n repeatArray, arrayObservable;\n\n if (repeatInit) {\n repeatInit(parent);\n }\n\n var subscribable = ko.computed(function() {\n function makeArrayItemAccessor(index) {\n var f = function(newValue) {\n var item = repeatArray[index];\n // Reading the value of the item\n if (!arguments.length) {\n notificationObservable(); // for dependency tracking\n return ko_unwrap(item);\n }\n // Writing a value to the item\n if (ko.isObservable(item)) {\n item(newValue);\n } else if (arrayObservable && arrayObservable.splice) {\n arrayObservable.splice(index, 1, newValue);\n } else {\n repeatArray[index] = newValue;\n }\n return this;\n };\n // Pretend that our accessor function is an observable\n f[koProtoName] = ko.observable;\n return f;\n }\n\n function makeBinding(item, index, context) {\n return repeatArray\n ? function() { return repeatBind.call(bindingContext.$data, item, index, context); }\n : function() { return repeatBind.call(bindingContext.$data, index, context); }\n }\n\n // Read and set up variable options--these options can change and will update the binding\n var paramObservable = valueAccessor(), repeatParam = ko_unwrap(paramObservable), repeatCount = 0;\n if (repeatParam && typeof repeatParam == 'object') {\n if ('length' in repeatParam) {\n repeatArray = repeatParam;\n repeatCount = repeatArray.length;\n } else {\n if ('foreach' in repeatParam) {\n repeatArray = ko_unwrap(paramObservable = repeatParam.foreach);\n if (repeatArray && typeof repeatArray == 'object' && 'length' in repeatArray) {\n repeatCount = repeatArray.length || 0;\n } else {\n repeatCount = repeatArray || 0;\n repeatArray = null;\n }\n }\n // If a count value is provided (>0), always output that number of items\n if ('count' in repeatParam)\n repeatCount = ko_unwrap(repeatParam.count) || repeatCount;\n // If a limit is provided, don't output more than the limit\n if ('limit' in repeatParam)\n repeatCount = Math.min(repeatCount, ko_unwrap(repeatParam.limit)) || repeatCount;\n }\n arrayObservable = repeatArray && ko.isObservable(paramObservable) ? paramObservable : null;\n } else {\n repeatCount = repeatParam || 0;\n }\n\n // Remove nodes from end if array is shorter\n for (; lastRepeatCount > repeatCount; lastRepeatCount-=repeatStep) {\n ko.removeNode(repeatReversed ? placeholder.nextSibling : placeholder.previousSibling);\n }\n\n // Notify existing nodes of change\n notificationObservable.notifySubscribers();\n\n // Add nodes to end if array is longer (also initially populates nodes)\n for (; lastRepeatCount < repeatCount; lastRepeatCount+=repeatStep) {\n // Clone node and add to document\n var newNode = cleanNode.cloneNode(true);\n parent.insertBefore(newNode, repeatReversed ? placeholder.nextSibling : placeholder);\n newNode.setAttribute('data-repeat-index', lastRepeatCount);\n\n // Apply bindings to inserted node\n if (repeatArray && repeatData == '$data') {\n var newContext = bindingContext.createChildContext(makeArrayItemAccessor(lastRepeatCount));\n } else {\n var newContext = bindingContext.extend();\n if (repeatArray)\n newContext[repeatData] = makeArrayItemAccessor(lastRepeatCount);\n }\n newContext[repeatIndex] = lastRepeatCount;\n if (repeatBind) {\n var result = ko.applyBindingsToNode(newNode, makeBinding(newContext[repeatData], lastRepeatCount, newContext), newContext, true),\n shouldBindDescendants = result && result.shouldBindDescendants;\n }\n if (!repeatBind || (result && shouldBindDescendants !== false)) {\n ko.applyBindings(newContext, newNode);\n }\n }\n if (repeatUpdate) {\n repeatUpdate(parent);\n }\n }, null, {disposeWhenNodeIsRemoved: placeholder});\n\n return { controlsDescendantBindings: true, subscribable: subscribable };\n }\n};\n});","knockoutjs/knockout-fast-foreach.js":"/*!\n Knockout Fast Foreach v0.4.1 (2015-07-17T14:06:15.974Z)\n By: Brian M Hunt (C) 2015\n License: MIT\n\n Adds `fastForEach` to `ko.bindingHandlers`.\n*/\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define(['knockout'], factory);\n } else if (typeof exports === 'object') {\n module.exports = factory(require('knockout'));\n } else {\n root.KnockoutFastForeach = factory(root.ko);\n }\n}(this, function (ko) {\n \"use strict\";\n// index.js\n// --------\n// Fast For Each\n//\n// Employing sound techniques to make a faster Knockout foreach binding.\n// --------\n\n// Utilities\n\n// from https://github.com/jonschlinkert/is-plain-object\nfunction isPlainObject(o) {\n return !!o && typeof o === 'object' && o.constructor === Object;\n}\n\n// From knockout/src/virtualElements.js\nvar commentNodesHaveTextProperty = document && document.createComment(\"test\").text === \"<!--test-->\";\nvar startCommentRegex = commentNodesHaveTextProperty ? /^<!--\\s*ko(?:\\s+([\\s\\S]+))?\\s*-->$/ : /^\\s*ko(?:\\s+([\\s\\S]+))?\\s*$/;\nvar supportsDocumentFragment = document && typeof document.createDocumentFragment === \"function\";\nfunction isVirtualNode(node) {\n return (node.nodeType === 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);\n}\n\n\n// Get a copy of the (possibly virtual) child nodes of the given element,\n// put them into a container, then empty the given node.\nfunction makeTemplateNode(sourceNode) {\n var container = document.createElement(\"div\");\n var parentNode;\n if (sourceNode.content) {\n // For e.g. <template> tags\n parentNode = sourceNode.content;\n } else if (sourceNode.tagName === 'SCRIPT') {\n parentNode = document.createElement(\"div\");\n parentNode.innerHTML = sourceNode.text;\n } else {\n // Anything else e.g. <div>\n parentNode = sourceNode;\n }\n ko.utils.arrayForEach(ko.virtualElements.childNodes(parentNode), function (child) {\n // FIXME - This cloneNode could be expensive; we may prefer to iterate over the\n // parentNode children in reverse (so as not to foul the indexes as childNodes are\n // removed from parentNode when inserted into the container)\n if (child) {\n container.insertBefore(child.cloneNode(true), null);\n }\n });\n return container;\n}\n\nfunction insertAllAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode) {\n var frag, len, i;\n // poor man's node and array check, should be enough for this\n if (typeof nodeOrNodeArrayToInsert.nodeType !== \"undefined\" && typeof nodeOrNodeArrayToInsert.length === \"undefined\") {\n throw new Error(\"Expected a single node or a node array\");\n }\n\n if (typeof nodeOrNodeArrayToInsert.nodeType !== \"undefined\") {\n ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert, insertAfterNode);\n return;\n }\n\n if (nodeOrNodeArrayToInsert.length === 1) {\n ko.virtualElements.insertAfter(containerNode, nodeOrNodeArrayToInsert[0], insertAfterNode);\n return;\n }\n\n if (supportsDocumentFragment) {\n frag = document.createDocumentFragment();\n\n for (i = 0, len = nodeOrNodeArrayToInsert.length; i !== len; ++i) {\n frag.appendChild(nodeOrNodeArrayToInsert[i]);\n }\n ko.virtualElements.insertAfter(containerNode, frag, insertAfterNode);\n } else {\n // Nodes are inserted in reverse order - pushed down immediately after\n // the last node for the previous item or as the first node of element.\n for (i = nodeOrNodeArrayToInsert.length - 1; i >= 0; --i) {\n var child = nodeOrNodeArrayToInsert[i];\n if (!child) {\n return;\n }\n ko.virtualElements.insertAfter(containerNode, child, insertAfterNode);\n }\n }\n}\n\n// Mimic a KO change item 'add'\nfunction valueToChangeAddItem(value, index) {\n return {\n status: 'added',\n value: value,\n index: index\n };\n}\n\nfunction isAdditionAdjacentToLast(changeIndex, arrayChanges) {\n return changeIndex > 0 &&\n changeIndex < arrayChanges.length &&\n arrayChanges[changeIndex].status === \"added\" &&\n arrayChanges[changeIndex - 1].status === \"added\" &&\n arrayChanges[changeIndex - 1].index === arrayChanges[changeIndex].index - 1;\n}\n\nfunction FastForEach(spec) {\n this.element = spec.element;\n this.container = isVirtualNode(this.element) ?\n this.element.parentNode : this.element;\n this.$context = spec.$context;\n this.data = spec.data;\n this.as = spec.as;\n this.noContext = spec.noContext;\n this.templateNode = makeTemplateNode(\n spec.name ? document.getElementById(spec.name).cloneNode(true) : spec.element\n );\n this.afterQueueFlush = spec.afterQueueFlush;\n this.beforeQueueFlush = spec.beforeQueueFlush;\n this.changeQueue = [];\n this.lastNodesList = [];\n this.indexesToDelete = [];\n this.rendering_queued = false;\n\n // Remove existing content.\n ko.virtualElements.emptyNode(this.element);\n\n // Prime content\n var primeData = ko.unwrap(this.data);\n if (primeData.map) {\n this.onArrayChange(primeData.map(valueToChangeAddItem));\n }\n\n // Watch for changes\n if (ko.isObservable(this.data)) {\n if (!this.data.indexOf) {\n // Make sure the observable is trackable.\n this.data = this.data.extend({trackArrayChanges: true});\n }\n this.changeSubs = this.data.subscribe(this.onArrayChange, this, 'arrayChange');\n }\n}\n\n\nFastForEach.animateFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame ||\n window.mozRequestAnimationFrame || window.msRequestAnimationFrame ||\n function(cb) { return window.setTimeout(cb, 1000 / 60); };\n\n\nFastForEach.prototype.dispose = function () {\n if (this.changeSubs) {\n this.changeSubs.dispose();\n }\n};\n\n\n// If the array changes we register the change.\nFastForEach.prototype.onArrayChange = function (changeSet) {\n var self = this;\n var changeMap = {\n added: [],\n deleted: []\n };\n for (var i = 0, len = changeSet.length; i < len; i++) {\n // the change is appended to a last change info object when both are 'added' and have indexes next to each other\n // here I presume that ko is sending changes in monotonic order (in index variable) which happens to be true, tested with push and splice with multiple pushed values\n if (isAdditionAdjacentToLast(i, changeSet)) {\n var batchValues = changeMap.added[changeMap.added.length - 1].values;\n if (!batchValues) {\n // transform the last addition into a batch addition object\n var lastAddition = changeMap.added.pop();\n var batchAddition = {\n isBatch: true,\n status: 'added',\n index: lastAddition.index,\n values: [lastAddition.value]\n };\n batchValues = batchAddition.values;\n changeMap.added.push(batchAddition);\n }\n batchValues.push(changeSet[i].value);\n } else {\n changeMap[changeSet[i].status].push(changeSet[i]);\n }\n }\n if (changeMap.deleted.length > 0) {\n this.changeQueue.push.apply(this.changeQueue, changeMap.deleted);\n this.changeQueue.push({status: 'clearDeletedIndexes'});\n }\n this.changeQueue.push.apply(this.changeQueue, changeMap.added);\n // Once a change is registered, the ticking count-down starts for the processQueue.\n if (this.changeQueue.length > 0 && !this.rendering_queued) {\n this.rendering_queued = true;\n FastForEach.animateFrame.call(window, function () { self.processQueue(); });\n }\n};\n\n\n// Reflect all the changes in the queue in the DOM, then wipe the queue.\nFastForEach.prototype.processQueue = function () {\n var self = this;\n\n // Callback so folks can do things before the queue flush.\n if (typeof this.beforeQueueFlush === 'function') {\n this.beforeQueueFlush(this.changeQueue);\n }\n\n ko.utils.arrayForEach(this.changeQueue, function (changeItem) {\n // console.log(self.data(), \"CI\", JSON.stringify(changeItem, null, 2), JSON.stringify($(self.element).text()))\n self[changeItem.status](changeItem);\n // console.log(\" ==> \", JSON.stringify($(self.element).text()))\n });\n this.rendering_queued = false;\n // Callback so folks can do things.\n if (typeof this.afterQueueFlush === 'function') {\n this.afterQueueFlush(this.changeQueue);\n }\n this.changeQueue = [];\n};\n\n\n// Process a changeItem with {status: 'added', ...}\nFastForEach.prototype.added = function (changeItem) {\n var index = changeItem.index;\n var valuesToAdd = changeItem.isBatch ? changeItem.values : [changeItem.value];\n var referenceElement = this.lastNodesList[index - 1] || null;\n // gather all childnodes for a possible batch insertion\n var allChildNodes = [];\n\n for (var i = 0, len = valuesToAdd.length; i < len; ++i) {\n var templateClone = this.templateNode.cloneNode(true);\n var childContext;\n\n if (this.noContext) {\n childContext = this.$context.extend({\n '$item': valuesToAdd[i]\n });\n } else {\n childContext = this.$context.createChildContext(valuesToAdd[i], this.as || null);\n }\n\n // apply bindings first, and then process child nodes, because bindings can add childnodes\n ko.applyBindingsToDescendants(childContext, templateClone);\n\n var childNodes = ko.virtualElements.childNodes(templateClone);\n // Note discussion at https://github.com/angular/angular.js/issues/7851\n allChildNodes.push.apply(allChildNodes, Array.prototype.slice.call(childNodes));\n this.lastNodesList.splice(index + i, 0, childNodes[childNodes.length - 1]);\n }\n\n insertAllAfter(this.element, allChildNodes, referenceElement);\n};\n\n\n// Process a changeItem with {status: 'deleted', ...}\nFastForEach.prototype.deleted = function (changeItem) {\n var index = changeItem.index;\n var ptr = this.lastNodesList[index],\n // We use this.element because that will be the last previous node\n // for virtual element lists.\n lastNode = this.lastNodesList[index - 1] || this.element;\n do {\n ptr = ptr.previousSibling;\n ko.removeNode((ptr && ptr.nextSibling) || ko.virtualElements.firstChild(this.element));\n } while (ptr && ptr !== lastNode);\n // The \"last node\" in the DOM from which we begin our delets of the next adjacent node is\n // now the sibling that preceded the first node of this item.\n this.lastNodesList[index] = this.lastNodesList[index - 1];\n this.indexesToDelete.push(index);\n};\n\n\n// We batch our deletion of item indexes in our parallel array.\n// See brianmhunt/knockout-fast-foreach#6/#8\nFastForEach.prototype.clearDeletedIndexes = function () {\n // We iterate in reverse on the presumption (following the unit tests) that KO's diff engine\n // processes diffs (esp. deletes) monotonically ascending i.e. from index 0 -> N.\n for (var i = this.indexesToDelete.length - 1; i >= 0; --i) {\n this.lastNodesList.splice(this.indexesToDelete[i], 1);\n }\n this.indexesToDelete = [];\n};\n\n\nko.bindingHandlers.fastForEach = {\n // Valid valueAccessors:\n // []\n // ko.observable([])\n // ko.observableArray([])\n // ko.computed\n // {data: array, name: string, as: string}\n init: function init(element, valueAccessor, bindings, vm, context) {\n var value = valueAccessor(),\n ffe;\n if (isPlainObject(value)) {\n value.element = value.element || element;\n value.$context = context;\n ffe = new FastForEach(value);\n } else {\n ffe = new FastForEach({\n element: element,\n data: ko.unwrap(context.$rawData) === value ? context.$rawData : value,\n $context: context\n });\n }\n ko.utils.domNodeDisposal.addDisposeCallback(element, function () {\n ffe.dispose();\n });\n return {controlsDescendantBindings: true};\n },\n\n // Export for testing, debugging, and overloading.\n FastForEach: FastForEach\n};\n\nko.virtualElements.allowedBindings.fastForEach = true;\n}));","knockoutjs/knockout-es5.js":"/*!\n * Knockout ES5 plugin - https://github.com/SteveSanderson/knockout-es5\n * Copyright (c) Steve Sanderson\n * MIT license\n */\n\n(function(global, undefined) {\n 'use strict';\n\n var ko;\n\n // Model tracking\n // --------------\n //\n // This is the central feature of Knockout-ES5. We augment model objects by converting properties\n // into ES5 getter/setter pairs that read/write an underlying Knockout observable. This means you can\n // use plain JavaScript syntax to read/write the property while still getting the full benefits of\n // Knockout's automatic dependency detection and notification triggering.\n //\n // For comparison, here's Knockout ES3-compatible syntax:\n //\n // var firstNameLength = myModel.user().firstName().length; // Read\n // myModel.user().firstName('Bert'); // Write\n //\n // ... versus Knockout-ES5 syntax:\n //\n // var firstNameLength = myModel.user.firstName.length; // Read\n // myModel.user.firstName = 'Bert'; // Write\n\n // `ko.track(model)` converts each property on the given model object into a getter/setter pair that\n // wraps a Knockout observable. Optionally specify an array of property names to wrap; otherwise we\n // wrap all properties. If any of the properties are already observables, we replace them with\n // ES5 getter/setter pairs that wrap your original observable instances. In the case of readonly\n // ko.computed properties, we simply do not define a setter (so attempted writes will be ignored,\n // which is how ES5 readonly properties normally behave).\n //\n // By design, this does *not* recursively walk child object properties, because making literally\n // everything everywhere independently observable is usually unhelpful. When you do want to track\n // child object properties independently, define your own class for those child objects and put\n // a separate ko.track call into its constructor --- this gives you far more control.\n /**\n * @param {object} obj\n * @param {object|array.<string>} propertyNamesOrSettings\n * @param {boolean} propertyNamesOrSettings.deep Use deep track.\n * @param {array.<string>} propertyNamesOrSettings.fields Array of property names to wrap.\n * todo: @param {array.<string>} propertyNamesOrSettings.exclude Array of exclude property names to wrap.\n * todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property \n * names to wrap. A function that takes ... params\n * @return {object}\n */\n function track(obj, propertyNamesOrSettings) {\n if (!obj || typeof obj !== 'object') {\n throw new Error('When calling ko.track, you must pass an object as the first parameter.');\n }\n\n var propertyNames;\n\n if ( isPlainObject(propertyNamesOrSettings) ) {\n // defaults\n propertyNamesOrSettings.deep = propertyNamesOrSettings.deep || false;\n propertyNamesOrSettings.fields = propertyNamesOrSettings.fields || Object.getOwnPropertyNames(obj);\n propertyNamesOrSettings.lazy = propertyNamesOrSettings.lazy || false;\n\n wrap(obj, propertyNamesOrSettings.fields, propertyNamesOrSettings);\n } else {\n propertyNames = propertyNamesOrSettings || Object.getOwnPropertyNames(obj);\n wrap(obj, propertyNames, {});\n }\n\n return obj;\n }\n\n // fix for ie\n var rFunctionName = /^function\\s*([^\\s(]+)/;\n function getFunctionName( ctor ){\n if (ctor.name) {\n return ctor.name;\n }\n return (ctor.toString().trim().match( rFunctionName ) || [])[1];\n }\n\n function canTrack(obj) {\n return obj && typeof obj === 'object' && getFunctionName(obj.constructor) === 'Object';\n }\n\n function createPropertyDescriptor(originalValue, prop, map) {\n var isObservable = ko.isObservable(originalValue);\n var isArray = !isObservable && Array.isArray(originalValue);\n var observable = isObservable ? originalValue\n : isArray ? ko.observableArray(originalValue)\n : ko.observable(originalValue);\n\n map[prop] = function () { return observable; };\n\n // add check in case the object is already an observable array\n if (isArray || (isObservable && 'push' in observable)) {\n notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);\n }\n\n return {\n configurable: true,\n enumerable: true,\n get: observable,\n set: ko.isWriteableObservable(observable) ? observable : undefined\n };\n }\n\n function createLazyPropertyDescriptor(originalValue, prop, map) {\n if (ko.isObservable(originalValue)) {\n // no need to be lazy if we already have an observable\n return createPropertyDescriptor(originalValue, prop, map);\n }\n\n var observable;\n\n function getOrCreateObservable(value, writing) {\n if (observable) {\n return writing ? observable(value) : observable;\n }\n\n if (Array.isArray(value)) {\n observable = ko.observableArray(value);\n notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);\n return observable;\n }\n\n return (observable = ko.observable(value));\n }\n\n map[prop] = function () { return getOrCreateObservable(originalValue); };\n return {\n configurable: true,\n enumerable: true,\n get: function () { return getOrCreateObservable(originalValue)(); },\n set: function (value) { getOrCreateObservable(value, true); }\n };\n }\n\n function wrap(obj, props, options) {\n if (!props.length) {\n return;\n }\n\n var allObservablesForObject = getAllObservablesForObject(obj, true);\n var descriptors = {};\n\n props.forEach(function (prop) {\n // Skip properties that are already tracked\n if (prop in allObservablesForObject) {\n return;\n }\n\n // Skip properties where descriptor can't be redefined\n if (Object.getOwnPropertyDescriptor(obj, prop).configurable === false){\n return;\n }\n\n var originalValue = obj[prop];\n descriptors[prop] = (options.lazy ? createLazyPropertyDescriptor : createPropertyDescriptor)\n (originalValue, prop, allObservablesForObject);\n\n if (options.deep && canTrack(originalValue)) {\n wrap(originalValue, Object.keys(originalValue), options);\n }\n });\n\n Object.defineProperties(obj, descriptors);\n }\n\n function isPlainObject( obj ){\n return !!obj && typeof obj === 'object' && obj.constructor === Object;\n }\n\n // Lazily created by `getAllObservablesForObject` below. Has to be created lazily because the\n // WeakMap factory isn't available until the module has finished loading (may be async).\n var objectToObservableMap;\n\n // Gets or creates the hidden internal key-value collection of observables corresponding to\n // properties on the model object.\n function getAllObservablesForObject(obj, createIfNotDefined) {\n if (!objectToObservableMap) {\n objectToObservableMap = weakMapFactory();\n }\n\n var result = objectToObservableMap.get(obj);\n if (!result && createIfNotDefined) {\n result = {};\n objectToObservableMap.set(obj, result);\n }\n return result;\n }\n\n // Removes the internal references to observables mapped to the specified properties\n // or the entire object reference if no properties are passed in. This allows the\n // observables to be replaced and tracked again.\n function untrack(obj, propertyNames) {\n if (!objectToObservableMap) {\n return;\n }\n\n if (arguments.length === 1) {\n objectToObservableMap['delete'](obj);\n } else {\n var allObservablesForObject = getAllObservablesForObject(obj, false);\n if (allObservablesForObject) {\n propertyNames.forEach(function(propertyName) {\n delete allObservablesForObject[propertyName];\n });\n }\n }\n }\n\n // Computed properties\n // -------------------\n //\n // The preceding code is already sufficient to upgrade ko.computed model properties to ES5\n // getter/setter pairs (or in the case of readonly ko.computed properties, just a getter).\n // These then behave like a regular property with a getter function, except they are smarter:\n // your evaluator is only invoked when one of its dependencies changes. The result is cached\n // and used for all evaluations until the next time a dependency changes).\n //\n // However, instead of forcing developers to declare a ko.computed property explicitly, it's\n // nice to offer a utility function that declares a computed getter directly.\n\n // Implements `ko.defineProperty`\n function defineComputedProperty(obj, propertyName, evaluatorOrOptions) {\n var ko = this,\n computedOptions = { owner: obj, deferEvaluation: true };\n\n if (typeof evaluatorOrOptions === 'function') {\n computedOptions.read = evaluatorOrOptions;\n } else {\n if ('value' in evaluatorOrOptions) {\n throw new Error('For ko.defineProperty, you must not specify a \"value\" for the property. ' +\n 'You must provide a \"get\" function.');\n }\n\n if (typeof evaluatorOrOptions.get !== 'function') {\n throw new Error('For ko.defineProperty, the third parameter must be either an evaluator function, ' +\n 'or an options object containing a function called \"get\".');\n }\n\n computedOptions.read = evaluatorOrOptions.get;\n computedOptions.write = evaluatorOrOptions.set;\n }\n\n obj[propertyName] = ko.computed(computedOptions);\n track.call(ko, obj, [propertyName]);\n return obj;\n }\n\n // Array handling\n // --------------\n //\n // Arrays are special, because unlike other property types, they have standard mutator functions\n // (`push`/`pop`/`splice`/etc.) and it's desirable to trigger a change notification whenever one of\n // those mutator functions is invoked.\n //\n // Traditionally, Knockout handles this by putting special versions of `push`/`pop`/etc. on observable\n // arrays that mutate the underlying array and then trigger a notification. That approach doesn't\n // work for Knockout-ES5 because properties now return the underlying arrays, so the mutator runs\n // in the context of the underlying array, not any particular observable:\n //\n // // Operates on the underlying array value\n // myModel.someCollection.push('New value');\n //\n // To solve this, Knockout-ES5 detects array values, and modifies them as follows:\n // 1. Associates a hidden subscribable with each array instance that it encounters\n // 2. Intercepts standard mutators (`push`/`pop`/etc.) and makes them trigger the subscribable\n // Then, for model properties whose values are arrays, the property's underlying observable\n // subscribes to the array subscribable, so it can trigger a change notification after mutation.\n\n // Given an observable that underlies a model property, watch for any array value that might\n // be assigned as the property value, and hook into its change events\n function notifyWhenPresentOrFutureArrayValuesMutate(ko, observable) {\n var watchingArraySubscription = null;\n ko.computed(function () {\n // Unsubscribe to any earlier array instance\n if (watchingArraySubscription) {\n watchingArraySubscription.dispose();\n watchingArraySubscription = null;\n }\n\n // Subscribe to the new array instance\n var newArrayInstance = observable();\n if (newArrayInstance instanceof Array) {\n watchingArraySubscription = startWatchingArrayInstance(ko, observable, newArrayInstance);\n }\n });\n }\n\n // Listens for array mutations, and when they happen, cause the observable to fire notifications.\n // This is used to make model properties of type array fire notifications when the array changes.\n // Returns a subscribable that can later be disposed.\n function startWatchingArrayInstance(ko, observable, arrayInstance) {\n var subscribable = getSubscribableForArray(ko, arrayInstance);\n return subscribable.subscribe(observable);\n }\n\n // Lazily created by `getSubscribableForArray` below. Has to be created lazily because the\n // WeakMap factory isn't available until the module has finished loading (may be async).\n var arraySubscribablesMap;\n\n // Gets or creates a subscribable that fires after each array mutation\n function getSubscribableForArray(ko, arrayInstance) {\n if (!arraySubscribablesMap) {\n arraySubscribablesMap = weakMapFactory();\n }\n\n var subscribable = arraySubscribablesMap.get(arrayInstance);\n if (!subscribable) {\n subscribable = new ko.subscribable();\n arraySubscribablesMap.set(arrayInstance, subscribable);\n\n var notificationPauseSignal = {};\n wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal);\n addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal);\n }\n\n return subscribable;\n }\n\n // After each array mutation, fires a notification on the given subscribable\n function wrapStandardArrayMutators(arrayInstance, subscribable, notificationPauseSignal) {\n ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'].forEach(function(fnName) {\n var origMutator = arrayInstance[fnName];\n arrayInstance[fnName] = function() {\n var result = origMutator.apply(this, arguments);\n if (notificationPauseSignal.pause !== true) {\n subscribable.notifySubscribers(this);\n }\n return result;\n };\n });\n }\n\n // Adds Knockout's additional array mutation functions to the array\n function addKnockoutArrayMutators(ko, arrayInstance, subscribable, notificationPauseSignal) {\n ['remove', 'removeAll', 'destroy', 'destroyAll', 'replace'].forEach(function(fnName) {\n // Make it a non-enumerable property for consistency with standard Array functions\n Object.defineProperty(arrayInstance, fnName, {\n enumerable: false,\n value: function() {\n var result;\n\n // These additional array mutators are built using the underlying push/pop/etc.\n // mutators, which are wrapped to trigger notifications. But we don't want to\n // trigger multiple notifications, so pause the push/pop/etc. wrappers and\n // delivery only one notification at the end of the process.\n notificationPauseSignal.pause = true;\n try {\n // Creates a temporary observableArray that can perform the operation.\n result = ko.observableArray.fn[fnName].apply(ko.observableArray(arrayInstance), arguments);\n }\n finally {\n notificationPauseSignal.pause = false;\n }\n subscribable.notifySubscribers(arrayInstance);\n return result;\n }\n });\n });\n }\n\n // Static utility functions\n // ------------------------\n //\n // Since Knockout-ES5 sets up properties that return values, not observables, you can't\n // trivially subscribe to the underlying observables (e.g., `someProperty.subscribe(...)`),\n // or tell them that object values have mutated, etc. To handle this, we set up some\n // extra utility functions that can return or work with the underlying observables.\n\n // Returns the underlying observable associated with a model property (or `null` if the\n // model or property doesn't exist, or isn't associated with an observable). This means\n // you can subscribe to the property, e.g.:\n //\n // ko.getObservable(model, 'propertyName')\n // .subscribe(function(newValue) { ... });\n function getObservable(obj, propertyName) {\n if (!obj || typeof obj !== 'object') {\n return null;\n }\n\n var allObservablesForObject = getAllObservablesForObject(obj, false);\n if (allObservablesForObject && propertyName in allObservablesForObject) {\n return allObservablesForObject[propertyName]();\n }\n\n return null;\n }\n \n // Returns a boolean indicating whether the property on the object has an underlying\n // observables. This does the check in a way not to create an observable if the\n // object was created with lazily created observables\n function isTracked(obj, propertyName) {\n if (!obj || typeof obj !== 'object') {\n return false;\n }\n \n var allObservablesForObject = getAllObservablesForObject(obj, false);\n return !!allObservablesForObject && propertyName in allObservablesForObject;\n }\n\n // Causes a property's associated observable to fire a change notification. Useful when\n // the property value is a complex object and you've modified a child property.\n function valueHasMutated(obj, propertyName) {\n var observable = getObservable(obj, propertyName);\n\n if (observable) {\n observable.valueHasMutated();\n }\n }\n\n // Module initialisation\n // ---------------------\n //\n // When this script is first evaluated, it works out what kind of module loading scenario\n // it is in (Node.js or a browser `<script>` tag), stashes a reference to its dependencies\n // (currently that's just the WeakMap shim), and then finally attaches itself to whichever\n // instance of Knockout.js it can find.\n\n // A function that returns a new ES6-compatible WeakMap instance (using ES5 shim if needed).\n // Instantiated by prepareExports, accounting for which module loader is being used.\n var weakMapFactory;\n\n // Extends a Knockout instance with Knockout-ES5 functionality\n function attachToKo(ko) {\n ko.track = track;\n ko.untrack = untrack;\n ko.getObservable = getObservable;\n ko.valueHasMutated = valueHasMutated;\n ko.defineProperty = defineComputedProperty;\n\n // todo: test it, maybe added it to ko. directly\n ko.es5 = {\n getAllObservablesForObject: getAllObservablesForObject,\n notifyWhenPresentOrFutureArrayValuesMutate: notifyWhenPresentOrFutureArrayValuesMutate,\n isTracked: isTracked\n };\n }\n\n // Determines which module loading scenario we're in, grabs dependencies, and attaches to KO\n function prepareExports() {\n if (typeof exports === 'object' && typeof module === 'object') {\n // Node.js case - load KO and WeakMap modules synchronously\n ko = require('knockout');\n var WM = require('../lib/weakmap');\n attachToKo(ko);\n weakMapFactory = function() { return new WM(); };\n module.exports = ko;\n } else if (typeof define === 'function' && define.amd) {\n define(['knockout'], function(koModule) {\n ko = koModule;\n attachToKo(koModule);\n weakMapFactory = function() { return new global.WeakMap(); };\n return koModule;\n });\n } else if ('ko' in global) {\n // Non-module case - attach to the global instance, and assume a global WeakMap constructor\n ko = global.ko;\n attachToKo(global.ko);\n weakMapFactory = function() { return new global.WeakMap(); };\n }\n }\n\n prepareExports();\n\n})(this);","Magento_AuthorizenetAcceptjs/js/view/payment/validator-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'Magento_AuthorizenetAcceptjs/js/view/payment/response-validator'\n], function ($, responseValidator) {\n 'use strict';\n\n return {\n validators: [],\n\n /**\n * Init list of validators\n */\n initialize: function () {\n this.add(responseValidator);\n },\n\n /**\n * Add new validator\n * @param {Object} validator\n */\n add: function (validator) {\n this.validators.push(validator);\n },\n\n /**\n * Run pull of validators\n * @param {Object} context\n * @param {Function} callback\n */\n validate: function (context, callback) {\n var self = this,\n deferred;\n\n // no available validators\n if (!self.validators.length) {\n callback(true);\n\n return;\n }\n\n // get list of deferred validators\n deferred = $.map(self.validators, function (current) {\n return current.validate(context);\n });\n\n $.when.apply($, deferred)\n .done(function () {\n callback(true);\n }).fail(function (error) {\n callback(false, error);\n });\n }\n };\n});\n","Magento_AuthorizenetAcceptjs/js/view/payment/response-validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/translate'\n], function ($, $t) {\n 'use strict';\n\n return {\n /**\n * Validate Authorizenet-Acceptjs response\n *\n * @param {Object} context\n * @returns {jQuery.Deferred}\n */\n validate: function (context) {\n var state = $.Deferred(),\n messages = [];\n\n if (context.messages.resultCode === 'Ok') {\n state.resolve();\n } else {\n if (context.messages.message.length > 0) {\n $.each(context.messages.message, function (index, element) {\n messages.push($t(element.text));\n });\n }\n state.reject(messages);\n }\n\n return state.promise();\n }\n };\n});\n\n","Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'uiClass',\n 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-factory',\n 'Magento_AuthorizenetAcceptjs/js/view/payment/validator-handler'\n], function ($, Class, acceptjsFactory, validatorHandler) {\n 'use strict';\n\n return Class.extend({\n defaults: {\n environment: 'production'\n },\n\n /**\n * @{inheritdoc}\n */\n initialize: function () {\n validatorHandler.initialize();\n\n this._super();\n\n return this;\n },\n\n /**\n * Creates the token pair with the provided data\n *\n * @param {Object} data\n * @return {jQuery.Deferred}\n */\n createTokens: function (data) {\n var deferred = $.Deferred();\n\n if (this.acceptjsClient) {\n this._createTokens(deferred, data);\n } else {\n acceptjsFactory(this.environment)\n .done(function (client) {\n this.acceptjsClient = client;\n this._createTokens(deferred, data);\n }.bind(this));\n }\n\n return deferred.promise();\n },\n\n /**\n * Creates a token from the payment information in the form\n *\n * @param {jQuery.Deferred} deferred\n * @param {Object} data\n */\n _createTokens: function (deferred, data) {\n this.acceptjsClient.dispatchData(data, function (response) {\n validatorHandler.validate(response, function (valid, messages) {\n if (valid) {\n deferred.resolve({\n opaqueDataDescriptor: response.opaqueData.dataDescriptor,\n opaqueDataValue: response.opaqueData.dataValue\n });\n } else {\n deferred.reject(messages);\n }\n });\n });\n }\n });\n});\n","Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-factory.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 (environment) {\n var deferred = $.Deferred(),\n dependency = 'acceptjs';\n\n if (environment === 'sandbox') {\n dependency = 'acceptjssandbox';\n }\n\n require([dependency], function (accept) {\n var $body = $('body');\n\n /*\n * Acceptjs doesn't safely load dependent files which leads to a race condition when trying to use\n * the sdk right away.\n * @see https://community.developer.authorize.net/t5/Integration-and-Testing/\n * Dynamically-loading-Accept-js-E-WC-03-Accept-js-is-not-loaded/td-p/63283\n */\n $body.on('handshake.acceptjs', function () {\n deferred.resolve(accept);\n $body.off('handshake.acceptjs');\n });\n },\n deferred.reject\n );\n\n return deferred.promise();\n };\n});\n","Magento_AuthorizenetAcceptjs/js/view/payment/authorizenet.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'uiComponent',\n 'Magento_Checkout/js/model/payment/renderer-list'\n],\nfunction (Component, rendererList) {\n 'use strict';\n\n rendererList.push({\n type: 'authorizenet_acceptjs',\n component: 'Magento_AuthorizenetAcceptjs/js/view/payment/method-renderer/authorizenet-accept'\n });\n\n /** Add view logic here if needed */\n return Component.extend({});\n});\n","Magento_AuthorizenetAcceptjs/js/view/payment/method-renderer/authorizenet-accept.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'Magento_Payment/js/view/payment/cc-form',\n 'Magento_AuthorizenetAcceptjs/js/view/payment/acceptjs-client',\n 'Magento_Checkout/js/model/full-screen-loader',\n 'Magento_Ui/js/model/messageList',\n 'Magento_Payment/js/model/credit-card-validation/validator'\n], function ($, Component, AcceptjsClient, fullScreenLoader, globalMessageList) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n active: false,\n template: 'Magento_AuthorizenetAcceptjs/payment/authorizenet-acceptjs',\n tokens: null,\n ccForm: 'Magento_Payment/payment/cc-form',\n acceptjsClient: null\n },\n\n /**\n * Set list of observable attributes\n *\n * @returns {exports.initObservable}\n */\n initObservable: function () {\n this._super()\n .observe(['active']);\n\n return this;\n },\n\n /**\n * @returns {String}\n */\n getCode: function () {\n return 'authorizenet_acceptjs';\n },\n\n /**\n * Initialize form elements for validation\n */\n initFormElement: function (element) {\n this.formElement = element;\n this.acceptjsClient = AcceptjsClient({\n environment: window.checkoutConfig.payment[this.getCode()].environment\n });\n $(this.formElement).validation();\n },\n\n /**\n * @returns {Object}\n */\n getData: function () {\n return {\n method: this.getCode(),\n 'additional_data': {\n opaqueDataDescriptor: this.tokens ? this.tokens.opaqueDataDescriptor : null,\n opaqueDataValue: this.tokens ? this.tokens.opaqueDataValue : null,\n ccLast4: this.creditCardNumber().substr(-4)\n }\n };\n },\n\n /**\n * Check if payment is active\n *\n * @returns {Boolean}\n */\n isActive: function () {\n var active = this.getCode() === this.isChecked();\n\n this.active(active);\n\n return active;\n },\n\n /**\n * Prepare data to place order\n */\n beforePlaceOrder: function () {\n var authData = {},\n cardData = {},\n secureData = {};\n\n if (!$(this.formElement).valid()) {\n return;\n }\n\n authData.clientKey = window.checkoutConfig.payment[this.getCode()].clientKey;\n authData.apiLoginID = window.checkoutConfig.payment[this.getCode()].apiLoginID;\n\n cardData.cardNumber = this.creditCardNumber();\n cardData.month = this.creditCardExpMonth();\n cardData.year = this.creditCardExpYear();\n\n if (this.hasVerification()) {\n cardData.cardCode = this.creditCardVerificationNumber();\n }\n\n secureData.authData = authData;\n secureData.cardData = cardData;\n\n fullScreenLoader.startLoader();\n\n this.acceptjsClient.createTokens(secureData)\n .always(function () {\n fullScreenLoader.stopLoader();\n })\n .done(function (tokens) {\n this.tokens = tokens;\n this.placeOrder();\n }.bind(this))\n .fail(function (messages) {\n this.tokens = null;\n this._showErrors(messages);\n }.bind(this));\n },\n\n /**\n * Should the cvv field be used\n *\n * @return {Boolean}\n */\n hasVerification: function () {\n return window.checkoutConfig.payment[this.getCode()].useCvv;\n },\n\n /**\n * Show error messages\n *\n * @param {String[]} errorMessages\n */\n _showErrors: function (errorMessages) {\n $.each(errorMessages, function (index, message) {\n globalMessageList.addErrorMessage({\n message: message\n });\n });\n }\n });\n});\n","Magento_Braintree/js/validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine([\n 'underscore'\n], function (_) {\n 'use strict';\n\n return {\n config: {},\n\n /**\n * Set configuration\n * @param {Object} config\n */\n setConfig: function (config) {\n this.config = config;\n },\n\n /**\n * Get List of available card types\n * @returns {*|exports.defaults.availableCardTypes|{}}\n */\n getAvailableCardTypes: function () {\n return this.config.availableCardTypes;\n },\n\n /**\n * Get list of card types\n * @returns {Object}\n */\n getCcTypesMapper: function () {\n return this.config.ccTypesMapper;\n },\n\n /**\n * Find mage card type by Braintree type\n * @param {String} type\n * @param {Object} availableTypes\n * @returns {*}\n */\n getMageCardType: function (type, availableTypes) {\n var storedCardType = null,\n mapper = this.getCcTypesMapper();\n\n if (type && typeof mapper[type] !== 'undefined') {\n storedCardType = mapper[type];\n\n if (_.indexOf(availableTypes, storedCardType) !== -1) {\n return storedCardType;\n }\n }\n\n return null;\n },\n\n /**\n * Filter list of available card types\n * @param {Object} availableTypes\n * @param {Object} countrySpecificCardTypes\n * @returns {Object}\n */\n collectTypes: function (availableTypes, countrySpecificCardTypes) {\n var key,\n filteredTypes = [];\n\n for (key in availableTypes) {\n if (_.indexOf(countrySpecificCardTypes, availableTypes[key]) !== -1) {\n filteredTypes.push(availableTypes[key]);\n }\n }\n\n return filteredTypes;\n },\n\n /**\n * Get list of card types for country\n * @param {String} countryId\n * @returns {*}\n */\n getCountrySpecificCardTypes: function (countryId) {\n if (typeof this.config.countrySpecificCardTypes[countryId] !== 'undefined') {\n return this.config.countrySpecificCardTypes[countryId];\n }\n\n return false;\n }\n };\n});\n","Magento_Braintree/js/paypal/form-builder.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(\n [\n 'jquery',\n 'underscore',\n 'mage/template'\n ],\n function ($, _, mageTemplate) {\n 'use strict';\n\n return {\n\n /**\n * @param {Object} formData\n * @returns {*|jQuery}\n */\n build: function (formData) {\n var formTmpl = mageTemplate('<form action=\"<%= data.action %>\"' +\n ' method=\"POST\" hidden enctype=\"application/x-www-form-urlencoded\">' +\n '<% _.each(data.fields, function(val, key){ %>' +\n '<input value=\\'<%= val %>\\' name=\"<%= key %>\" type=\"hidden\">' +\n '<% }); %>' +\n '</form>');\n\n return $(formTmpl({\n data: {\n action: formData.action,\n fields: formData.fields\n }\n })).appendTo($('[data-container=\"body\"]'));\n }\n };\n }\n);\n","Magento_Braintree/js/paypal/button.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(\n [\n 'rjsResolver',\n 'uiRegistry',\n 'uiComponent',\n 'underscore',\n 'jquery',\n 'braintree',\n 'Magento_Braintree/js/paypal/form-builder',\n 'domReady!'\n ],\n function (\n resolver,\n registry,\n Component,\n _,\n $,\n braintree,\n formBuilder\n ) {\n 'use strict';\n\n return Component.extend({\n\n defaults: {\n\n integrationName: 'braintreePaypal.currentIntegration',\n\n /**\n * {String}\n */\n displayName: null,\n\n /**\n * {String}\n */\n clientToken: null,\n\n /**\n * {Object}\n */\n clientConfig: {\n\n /**\n * @param {Object} integration\n */\n onReady: function (integration) {\n resolver(function () {\n registry.set(this.integrationName, integration);\n $('#' + this.id).removeAttr('disabled');\n }, this);\n },\n\n /**\n * @param {Object} payload\n */\n onPaymentMethodReceived: function (payload) {\n $('body').trigger('processStart');\n\n formBuilder.build(\n {\n action: this.actionSuccess,\n fields: {\n result: JSON.stringify(payload)\n }\n }\n ).submit();\n }\n }\n },\n\n /**\n * @returns {Object}\n */\n initialize: function () {\n this._super()\n .initComponent();\n\n return this;\n },\n\n /**\n * @returns {Object}\n */\n initComponent: function () {\n var currentIntegration = registry.get(this.integrationName),\n $this = $('#' + this.id),\n self = this,\n data = {\n amount: $this.data('amount'),\n locale: $this.data('locale'),\n currency: $this.data('currency')\n },\n initCallback = function () {\n $this.attr('disabled', 'disabled');\n registry.remove(this.integrationName);\n braintree.setup(this.clientToken, 'custom', this.getClientConfig(data));\n\n $this.off('click')\n .on('click', function (event) {\n event.preventDefault();\n\n registry.get(self.integrationName, function (integration) {\n try {\n integration.paypal.initAuthFlow();\n } catch (e) {\n $this.attr('disabled', 'disabled');\n }\n });\n });\n }.bind(this);\n\n currentIntegration ?\n currentIntegration.teardown(initCallback) :\n initCallback();\n\n return this;\n },\n\n /**\n * @returns {Object}\n */\n getClientConfig: function (data) {\n this.clientConfig.paypal = {\n singleUse: true,\n amount: data.amount,\n currency: data.currency,\n locale: data.locale,\n enableShippingAddress: true,\n headless: true\n };\n\n if (this.displayName) {\n this.clientConfig.paypal.displayName = this.displayName;\n }\n\n _.each(this.clientConfig, function (fn, name) {\n if (typeof fn === 'function') {\n this.clientConfig[name] = fn.bind(this);\n }\n }, this);\n\n return this.clientConfig;\n }\n });\n }\n);\n","Magento_Braintree/js/view/payment/3d-secure.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\n\ndefine([\n 'jquery',\n 'Magento_Braintree/js/view/payment/adapter',\n 'Magento_Checkout/js/model/quote',\n 'mage/translate'\n], function ($, braintree, quote, $t) {\n 'use strict';\n\n return {\n config: null,\n\n /**\n * Set 3d secure config\n * @param {Object} config\n */\n setConfig: function (config) {\n this.config = config;\n this.config.thresholdAmount = parseFloat(config.thresholdAmount);\n },\n\n /**\n * Get code\n * @returns {String}\n */\n getCode: function () {\n return 'three_d_secure';\n },\n\n /**\n * Validate Braintree payment nonce\n * @param {Object} context\n * @returns {Object}\n */\n validate: function (context) {\n var client = braintree.getApiClient(),\n state = $.Deferred(),\n totalAmount = quote.totals()['base_grand_total'],\n billingAddress = quote.billingAddress();\n\n if (!this.isAmountAvailable(totalAmount) || !this.isCountryAvailable(billingAddress.countryId)) {\n state.resolve();\n\n return state.promise();\n }\n\n client.verify3DS({\n amount: totalAmount,\n creditCard: context.paymentMethodNonce\n }, function (error, response) {\n var liability;\n\n if (error) {\n state.reject(error.message);\n\n return;\n }\n\n liability = {\n shifted: response.verificationDetails.liabilityShifted,\n shiftPossible: response.verificationDetails.liabilityShiftPossible\n };\n\n if (liability.shifted || !liability.shifted && !liability.shiftPossible) {\n context.paymentMethodNonce = response.nonce;\n state.resolve();\n } else {\n state.reject($t('Please try again with another form of payment.'));\n }\n });\n\n return state.promise();\n },\n\n /**\n * Check minimal amount for 3d secure activation\n * @param {Number} amount\n * @returns {Boolean}\n */\n isAmountAvailable: function (amount) {\n amount = parseFloat(amount);\n\n return amount >= this.config.thresholdAmount;\n },\n\n /**\n * Check if current country is available for 3d secure\n * @param {String} countryId\n * @returns {Boolean}\n */\n isCountryAvailable: function (countryId) {\n var key,\n specificCountries = this.config.specificCountries;\n\n // all countries are available\n if (!specificCountries.length) {\n return true;\n }\n\n for (key in specificCountries) {\n if (countryId === specificCountries[key]) {\n return true;\n }\n }\n\n return false;\n }\n };\n});\n","Magento_Braintree/js/view/payment/validator-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\n\ndefine([\n 'jquery',\n 'Magento_Ui/js/model/messageList',\n 'Magento_Braintree/js/view/payment/3d-secure'\n], function ($, globalMessageList, verify3DSecure) {\n 'use strict';\n\n return {\n validators: [],\n\n /**\n * Get payment config\n * @returns {Object}\n */\n getConfig: function () {\n return window.checkoutConfig.payment;\n },\n\n /**\n * Init list of validators\n */\n initialize: function () {\n var config = this.getConfig();\n\n if (config[verify3DSecure.getCode()].enabled) {\n verify3DSecure.setConfig(config[verify3DSecure.getCode()]);\n this.add(verify3DSecure);\n }\n },\n\n /**\n * Add new validator\n * @param {Object} validator\n */\n add: function (validator) {\n this.validators.push(validator);\n },\n\n /**\n * Run pull of validators\n * @param {Object} context\n * @param {Function} callback\n */\n validate: function (context, callback) {\n var self = this,\n deferred;\n\n // no available validators\n if (!self.validators.length) {\n callback();\n\n return;\n }\n\n // get list of deferred validators\n deferred = $.map(self.validators, function (current) {\n return current.validate(context);\n });\n\n $.when.apply($, deferred)\n .done(function () {\n callback();\n }).fail(function (error) {\n self.showError(error);\n });\n },\n\n /**\n * Show error message\n * @param {String} errorMessage\n */\n showError: function (errorMessage) {\n globalMessageList.addErrorMessage({\n message: errorMessage\n });\n }\n };\n});\n","Magento_Braintree/js/view/payment/adapter.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine([\n 'jquery',\n 'braintree',\n 'Magento_Ui/js/model/messageList',\n 'mage/translate'\n], function ($, braintree, globalMessageList, $t) {\n 'use strict';\n\n return {\n apiClient: null,\n config: {},\n checkout: null,\n\n /**\n * Get Braintree api client\n * @returns {Object}\n */\n getApiClient: function () {\n if (!this.apiClient) {\n this.apiClient = new braintree.api.Client({\n clientToken: this.getClientToken()\n });\n }\n\n return this.apiClient;\n },\n\n /**\n * Set configuration\n * @param {Object} config\n */\n setConfig: function (config) {\n this.config = config;\n },\n\n /**\n * Setup Braintree SDK\n */\n setup: function () {\n if (!this.getClientToken()) {\n this.showError($t('Sorry, but something went wrong.'));\n }\n\n braintree.setup(this.getClientToken(), 'custom', this.config);\n },\n\n /**\n * Get payment name\n * @returns {String}\n */\n getCode: function () {\n return 'braintree';\n },\n\n /**\n * Get client token\n * @returns {String|*}\n */\n getClientToken: function () {\n\n return window.checkoutConfig.payment[this.getCode()].clientToken;\n },\n\n /**\n * Show error message\n *\n * @param {String} errorMessage\n */\n showError: function (errorMessage) {\n globalMessageList.addErrorMessage({\n message: errorMessage\n });\n },\n\n /**\n * May be triggered on Braintree SDK setup\n */\n onReady: function () {}\n };\n});\n","Magento_Braintree/js/view/payment/braintree.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine(\n [\n 'uiComponent',\n 'Magento_Checkout/js/model/payment/renderer-list'\n ],\n function (\n Component,\n rendererList\n ) {\n 'use strict';\n\n var config = window.checkoutConfig.payment,\n braintreeType = 'braintree',\n payPalType = 'braintree_paypal';\n\n if (config[braintreeType].isActive) {\n rendererList.push(\n {\n type: braintreeType,\n component: 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields'\n }\n );\n }\n\n if (config[payPalType].isActive) {\n rendererList.push(\n {\n type: payPalType,\n component: 'Magento_Braintree/js/view/payment/method-renderer/paypal'\n }\n );\n }\n\n /** Add view logic here if needed */\n return Component.extend({});\n }\n);\n","Magento_Braintree/js/view/payment/method-renderer/cc-form.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine(\n [\n 'underscore',\n 'jquery',\n 'Magento_Payment/js/view/payment/cc-form',\n 'Magento_Checkout/js/model/quote',\n 'Magento_Braintree/js/view/payment/adapter',\n 'mage/translate',\n 'Magento_Braintree/js/validator',\n 'Magento_Braintree/js/view/payment/validator-handler',\n 'Magento_Checkout/js/model/full-screen-loader'\n ],\n function (\n _,\n $,\n Component,\n quote,\n braintree,\n $t,\n validator,\n validatorManager,\n fullScreenLoader\n ) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n active: false,\n braintreeClient: null,\n braintreeDeviceData: null,\n paymentMethodNonce: null,\n lastBillingAddress: null,\n ccCode: null,\n ccMessageContainer: null,\n validatorManager: validatorManager,\n code: 'braintree',\n\n /**\n * Additional payment data\n *\n * {Object}\n */\n additionalData: {},\n\n /**\n * Braintree client configuration\n *\n * {Object}\n */\n clientConfig: {\n\n /**\n * Triggers on payment nonce receive\n * @param {Object} response\n */\n onPaymentMethodReceived: function (response) {\n this.beforePlaceOrder(response);\n },\n\n /**\n * Device data initialization\n *\n * @param {Object} checkout\n */\n onReady: function (checkout) {\n braintree.checkout = checkout;\n braintree.onReady();\n },\n\n /**\n * Triggers on any Braintree error\n * @param {Object} response\n */\n onError: function (response) {\n braintree.showError($t('Payment ' + this.getTitle() + ' can\\'t be initialized'));\n this.isPlaceOrderActionAllowed(true);\n throw response.message;\n },\n\n /**\n * Triggers when customer click \"Cancel\"\n */\n onCancelled: function () {\n this.paymentMethodNonce = null;\n }\n },\n imports: {\n onActiveChange: 'active'\n }\n },\n\n /**\n * Set list of observable attributes\n *\n * @returns {exports.initObservable}\n */\n initObservable: function () {\n validator.setConfig(window.checkoutConfig.payment[this.getCode()]);\n this._super()\n .observe(['active']);\n this.validatorManager.initialize();\n this.initClientConfig();\n\n return this;\n },\n\n /**\n * Get payment name\n *\n * @returns {String}\n */\n getCode: function () {\n return this.code;\n },\n\n /**\n * Check if payment is active\n *\n * @returns {Boolean}\n */\n isActive: function () {\n var active = this.getCode() === this.isChecked();\n\n this.active(active);\n\n return active;\n },\n\n /**\n * Triggers when payment method change\n * @param {Boolean} isActive\n */\n onActiveChange: function (isActive) {\n if (!isActive) {\n return;\n }\n\n this.restoreMessageContainer();\n this.restoreCode();\n\n /**\n * Define onReady callback\n */\n braintree.onReady = function () {};\n this.initBraintree();\n },\n\n /**\n * Restore original message container for cc-form component\n */\n restoreMessageContainer: function () {\n this.messageContainer = this.ccMessageContainer;\n },\n\n /**\n * Restore original code for cc-form component\n */\n restoreCode: function () {\n this.code = this.ccCode;\n },\n\n /** @inheritdoc */\n initChildren: function () {\n this._super();\n this.ccMessageContainer = this.messageContainer;\n this.ccCode = this.code;\n\n return this;\n },\n\n /**\n * Init config\n */\n initClientConfig: function () {\n // Advanced fraud tools settings\n if (this.hasFraudProtection()) {\n this.clientConfig = _.extend(this.clientConfig, this.kountConfig());\n }\n\n _.each(this.clientConfig, function (fn, name) {\n if (typeof fn === 'function') {\n this.clientConfig[name] = fn.bind(this);\n }\n }, this);\n },\n\n /**\n * Init Braintree configuration\n */\n initBraintree: function () {\n var intervalId = setInterval(function () {\n // stop loader when frame will be loaded\n if ($('#braintree-hosted-field-number').length) {\n clearInterval(intervalId);\n fullScreenLoader.stopLoader();\n }\n }, 500);\n\n if (braintree.checkout) {\n braintree.checkout.teardown(function () {\n braintree.checkout = null;\n });\n }\n\n fullScreenLoader.startLoader();\n braintree.setConfig(this.clientConfig);\n braintree.setup();\n },\n\n /**\n * @returns {Object}\n */\n kountConfig: function () {\n var config = {\n dataCollector: {\n kount: {\n environment: this.getEnvironment()\n }\n },\n\n /**\n * Device data initialization\n *\n * @param {Object} checkout\n */\n onReady: function (checkout) {\n braintree.checkout = checkout;\n this.additionalData['device_data'] = checkout.deviceData;\n braintree.onReady();\n }\n };\n\n if (this.getKountMerchantId()) {\n config.dataCollector.kount.merchantId = this.getKountMerchantId();\n }\n\n return config;\n },\n\n /**\n * Get full selector name\n *\n * @param {String} field\n * @returns {String}\n */\n getSelector: function (field) {\n return '#' + this.getCode() + '_' + field;\n },\n\n /**\n * Get list of available CC types\n *\n * @returns {Object}\n */\n getCcAvailableTypes: function () {\n var availableTypes = validator.getAvailableCardTypes(),\n billingAddress = quote.billingAddress(),\n billingCountryId;\n\n this.lastBillingAddress = quote.shippingAddress();\n\n if (!billingAddress) {\n billingAddress = this.lastBillingAddress;\n }\n\n billingCountryId = billingAddress.countryId;\n\n if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) {\n return validator.collectTypes(\n availableTypes, validator.getCountrySpecificCardTypes(billingCountryId)\n );\n }\n\n return availableTypes;\n },\n\n /**\n * @returns {Boolean}\n */\n hasFraudProtection: function () {\n return window.checkoutConfig.payment[this.getCode()].hasFraudProtection;\n },\n\n /**\n * @returns {String}\n */\n getEnvironment: function () {\n return window.checkoutConfig.payment[this.getCode()].environment;\n },\n\n /**\n * @returns {String}\n */\n getKountMerchantId: function () {\n return window.checkoutConfig.payment[this.getCode()].kountMerchantId;\n },\n\n /**\n * Get data\n *\n * @returns {Object}\n */\n getData: function () {\n var data = {\n 'method': this.getCode(),\n 'additional_data': {\n 'payment_method_nonce': this.paymentMethodNonce\n }\n };\n\n data['additional_data'] = _.extend(data['additional_data'], this.additionalData);\n\n return data;\n },\n\n /**\n * Set payment nonce\n * @param {String} paymentMethodNonce\n */\n setPaymentMethodNonce: function (paymentMethodNonce) {\n this.paymentMethodNonce = paymentMethodNonce;\n },\n\n /**\n * Prepare data to place order\n * @param {Object} data\n */\n beforePlaceOrder: function (data) {\n this.setPaymentMethodNonce(data.nonce);\n this.placeOrder();\n },\n\n /**\n * Action to place order\n * @param {String} key\n */\n placeOrder: function (key) {\n var self = this;\n\n if (key) {\n return self._super();\n }\n // place order on success validation\n self.validatorManager.validate(self, function () {\n return self.placeOrder('parent');\n });\n\n return false;\n }\n });\n }\n);\n","Magento_Braintree/js/view/payment/method-renderer/paypal.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine([\n 'jquery',\n 'underscore',\n 'Magento_Checkout/js/view/payment/default',\n 'Magento_Braintree/js/view/payment/adapter',\n 'Magento_Checkout/js/model/quote',\n 'Magento_Checkout/js/model/full-screen-loader',\n 'Magento_Checkout/js/model/payment/additional-validators',\n 'Magento_Vault/js/view/payment/vault-enabler',\n 'Magento_Checkout/js/action/create-billing-address',\n 'mage/translate'\n], function (\n $,\n _,\n Component,\n Braintree,\n quote,\n fullScreenLoader,\n additionalValidators,\n VaultEnabler,\n createBillingAddress,\n $t\n) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Magento_Braintree/payment/paypal',\n code: 'braintree_paypal',\n active: false,\n paymentMethodNonce: null,\n grandTotalAmount: null,\n isReviewRequired: false,\n customerEmail: null,\n\n /**\n * Additional payment data\n *\n * {Object}\n */\n additionalData: {},\n\n /**\n * PayPal client configuration\n * {Object}\n */\n clientConfig: {\n dataCollector: {\n paypal: true\n },\n\n /**\n * Triggers when widget is loaded\n * @param {Object} checkout\n */\n onReady: function (checkout) {\n Braintree.checkout = checkout;\n this.additionalData['device_data'] = checkout.deviceData;\n this.enableButton();\n Braintree.onReady();\n },\n\n /**\n * Triggers on payment nonce receive\n * @param {Object} response\n */\n onPaymentMethodReceived: function (response) {\n this.beforePlaceOrder(response);\n }\n },\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 this._super()\n .observe(['active', 'isReviewRequired', 'customerEmail']);\n\n this.vaultEnabler = new VaultEnabler();\n this.vaultEnabler.setPaymentCode(this.getVaultCode());\n this.vaultEnabler.isActivePaymentTokenEnabler.subscribe(function () {\n self.onVaultPaymentTokenEnablerChange();\n });\n\n this.grandTotalAmount = quote.totals()['base_grand_total'];\n\n quote.totals.subscribe(function () {\n if (self.grandTotalAmount !== quote.totals()['base_grand_total']) {\n self.grandTotalAmount = quote.totals()['base_grand_total'];\n }\n });\n\n quote.shippingAddress.subscribe(function () {\n if (self.isActive()) {\n self.reInitPayPal();\n }\n });\n\n // for each component initialization need update property\n this.isReviewRequired(false);\n this.initClientConfig();\n\n return this;\n },\n\n /**\n * Get payment name\n *\n * @returns {String}\n */\n getCode: function () {\n return this.code;\n },\n\n /**\n * Get payment title\n *\n * @returns {String}\n */\n getTitle: function () {\n return window.checkoutConfig.payment[this.getCode()].title;\n },\n\n /**\n * Check if payment is active\n *\n * @returns {Boolean}\n */\n isActive: function () {\n var active = this.getCode() === this.isChecked();\n\n this.active(active);\n\n return active;\n },\n\n /**\n * Triggers when payment method change\n * @param {Boolean} isActive\n */\n onActiveChange: function (isActive) {\n if (!isActive) {\n return;\n }\n\n // need always re-init Braintree with PayPal configuration\n this.reInitPayPal();\n },\n\n /**\n * Init config\n */\n initClientConfig: function () {\n this.clientConfig = _.extend(this.clientConfig, this.getPayPalConfig());\n\n _.each(this.clientConfig, function (fn, name) {\n if (typeof fn === 'function') {\n this.clientConfig[name] = fn.bind(this);\n }\n }, this);\n },\n\n /**\n * Set payment nonce\n * @param {String} paymentMethodNonce\n */\n setPaymentMethodNonce: function (paymentMethodNonce) {\n this.paymentMethodNonce = paymentMethodNonce;\n },\n\n /**\n * Update quote billing address\n * @param {Object}customer\n * @param {Object}address\n */\n setBillingAddress: function (customer, address) {\n var billingAddress = {\n street: [address.streetAddress],\n city: address.locality,\n postcode: address.postalCode,\n countryId: address.countryCodeAlpha2,\n email: customer.email,\n firstname: customer.firstName,\n lastname: customer.lastName,\n telephone: customer.phone\n };\n\n billingAddress['region_code'] = address.region;\n billingAddress = createBillingAddress(billingAddress);\n quote.billingAddress(billingAddress);\n },\n\n /**\n * Prepare data to place order\n * @param {Object} data\n */\n beforePlaceOrder: function (data) {\n this.setPaymentMethodNonce(data.nonce);\n\n if ((this.isRequiredBillingAddress() || quote.billingAddress() === null) &&\n typeof data.details.billingAddress !== 'undefined'\n ) {\n this.setBillingAddress(data.details, data.details.billingAddress);\n }\n\n if (this.isSkipOrderReview()) {\n this.placeOrder();\n } else {\n this.customerEmail(data.details.email);\n this.isReviewRequired(true);\n }\n },\n\n /**\n * Re-init PayPal Auth Flow\n */\n reInitPayPal: function () {\n if (Braintree.checkout) {\n Braintree.checkout.teardown(function () {\n Braintree.checkout = null;\n });\n }\n\n this.disableButton();\n this.clientConfig.paypal.amount = this.grandTotalAmount;\n this.clientConfig.paypal.shippingAddressOverride = this.getShippingAddress();\n\n Braintree.setConfig(this.clientConfig);\n Braintree.setup();\n },\n\n /**\n * Get locale\n * @returns {String}\n */\n getLocale: function () {\n return window.checkoutConfig.payment[this.getCode()].locale;\n },\n\n /**\n * Is shipping address can be editable on PayPal side\n * @returns {Boolean}\n */\n isAllowOverrideShippingAddress: function () {\n return window.checkoutConfig.payment[this.getCode()].isAllowShippingAddressOverride;\n },\n\n /**\n * Is billing address required from PayPal side\n * @returns {Boolean}\n */\n isRequiredBillingAddress: function () {\n return window.checkoutConfig.payment[this.getCode()].isRequiredBillingAddress;\n },\n\n /**\n * Get configuration for PayPal\n * @returns {Object}\n */\n getPayPalConfig: function () {\n var totals = quote.totals(),\n config = {},\n isActiveVaultEnabler = this.isActiveVault();\n\n config.paypal = {\n container: 'paypal-container',\n singleUse: !isActiveVaultEnabler,\n headless: true,\n amount: this.grandTotalAmount,\n currency: totals['base_currency_code'],\n locale: this.getLocale(),\n enableShippingAddress: true,\n\n /**\n * Triggers on any Braintree error\n */\n onError: function () {\n this.paymentMethodNonce = null;\n },\n\n /**\n * Triggers if browser doesn't support PayPal Checkout\n */\n onUnsupported: function () {\n this.paymentMethodNonce = null;\n }\n };\n\n config.paypal.shippingAddressOverride = this.getShippingAddress();\n\n if (this.getMerchantName()) {\n config.paypal.displayName = this.getMerchantName();\n }\n\n return config;\n },\n\n /**\n * Get shipping address\n * @returns {Object}\n */\n getShippingAddress: function () {\n var address = quote.shippingAddress();\n\n if (_.isNull(address.postcode) || _.isUndefined(address.postcode)) {\n return {};\n }\n\n return {\n recipientName: address.firstname + ' ' + address.lastname,\n streetAddress: address.street[0],\n locality: address.city,\n countryCodeAlpha2: address.countryId,\n postalCode: address.postcode,\n region: address.regionCode,\n phone: address.telephone,\n editable: this.isAllowOverrideShippingAddress()\n };\n },\n\n /**\n * Get merchant name\n * @returns {String}\n */\n getMerchantName: function () {\n return window.checkoutConfig.payment[this.getCode()].merchantName;\n },\n\n /**\n * Get data\n * @returns {Object}\n */\n getData: function () {\n var data = {\n 'method': this.getCode(),\n 'additional_data': {\n 'payment_method_nonce': this.paymentMethodNonce\n }\n };\n\n data['additional_data'] = _.extend(data['additional_data'], this.additionalData);\n\n this.vaultEnabler.visitAdditionalData(data);\n\n return data;\n },\n\n /**\n * Returns payment acceptance mark image path\n * @returns {String}\n */\n getPaymentAcceptanceMarkSrc: function () {\n\n return window.checkoutConfig.payment[this.getCode()].paymentAcceptanceMarkSrc;\n },\n\n /**\n * @returns {String}\n */\n getVaultCode: function () {\n return window.checkoutConfig.payment[this.getCode()].vaultCode;\n },\n\n /**\n * Check if need to skip order review\n * @returns {Boolean}\n */\n isSkipOrderReview: function () {\n return window.checkoutConfig.payment[this.getCode()].skipOrderReview;\n },\n\n /**\n * Checks if vault is active\n * @returns {Boolean}\n */\n isActiveVault: function () {\n return this.vaultEnabler.isVaultEnabled() && this.vaultEnabler.isActivePaymentTokenEnabler();\n },\n\n /**\n * Re-init PayPal Auth flow to use Vault\n */\n onVaultPaymentTokenEnablerChange: function () {\n this.clientConfig.paypal.singleUse = !this.isActiveVault();\n this.reInitPayPal();\n },\n\n /**\n * Disable submit button\n */\n disableButton: function () {\n // stop any previous shown loaders\n fullScreenLoader.stopLoader(true);\n fullScreenLoader.startLoader();\n $('[data-button=\"place\"]').attr('disabled', 'disabled');\n },\n\n /**\n * Enable submit button\n */\n enableButton: function () {\n $('[data-button=\"place\"]').removeAttr('disabled');\n fullScreenLoader.stopLoader();\n },\n\n /**\n * Triggers when customer click \"Continue to PayPal\" button\n */\n payWithPayPal: function () {\n if (!additionalValidators.validate()) {\n return;\n }\n\n try {\n Braintree.checkout.paypal.initAuthFlow();\n } catch (e) {\n this.messageContainer.addErrorMessage({\n message: $t('Payment ' + this.getTitle() + ' can\\'t be initialized.')\n });\n }\n },\n\n /**\n * Get button title\n * @returns {String}\n */\n getButtonTitle: function () {\n return this.isSkipOrderReview() ? 'Pay with PayPal' : 'Continue to PayPal';\n },\n\n /**\n * Get button id\n * @returns {String}\n */\n getButtonId: function () {\n return this.getCode() + (this.isSkipOrderReview() ? '_pay_with' : '_continue_to');\n }\n });\n});\n","Magento_Braintree/js/view/payment/method-renderer/hosted-fields.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\n\ndefine([\n 'jquery',\n 'Magento_Braintree/js/view/payment/method-renderer/cc-form',\n 'Magento_Braintree/js/validator',\n 'Magento_Vault/js/view/payment/vault-enabler',\n 'mage/translate'\n], function ($, Component, validator, VaultEnabler, $t) {\n 'use strict';\n\n return Component.extend({\n\n defaults: {\n template: 'Magento_Braintree/payment/form',\n clientConfig: {\n\n /**\n * {String}\n */\n id: 'co-transparent-form-braintree'\n },\n isValidCardNumber: false\n },\n\n /**\n * @returns {exports.initialize}\n */\n initialize: function () {\n this._super();\n this.vaultEnabler = new VaultEnabler();\n this.vaultEnabler.setPaymentCode(this.getVaultCode());\n\n return this;\n },\n\n /**\n * Init config\n */\n initClientConfig: function () {\n this._super();\n\n // Hosted fields settings\n this.clientConfig.hostedFields = this.getHostedFields();\n },\n\n /**\n * @returns {Object}\n */\n getData: function () {\n var data = this._super();\n\n this.vaultEnabler.visitAdditionalData(data);\n\n return data;\n },\n\n /**\n * @returns {Boolean}\n */\n isVaultEnabled: function () {\n return this.vaultEnabler.isVaultEnabled();\n },\n\n /**\n * Get Braintree Hosted Fields\n * @returns {Object}\n */\n getHostedFields: function () {\n var self = this,\n fields = {\n number: {\n selector: self.getSelector('cc_number')\n },\n expirationMonth: {\n selector: self.getSelector('expirationMonth'),\n placeholder: $t('MM')\n },\n expirationYear: {\n selector: self.getSelector('expirationYear'),\n placeholder: $t('YY')\n }\n };\n\n if (self.hasVerification()) {\n fields.cvv = {\n selector: self.getSelector('cc_cid')\n };\n }\n\n /**\n * Triggers on Hosted Field changes\n * @param {Object} event\n * @returns {Boolean}\n */\n fields.onFieldEvent = function (event) {\n if (event.isEmpty === false) {\n self.validateCardType();\n }\n\n if (event.type !== 'fieldStateChange') {\n return false;\n }\n\n // Handle a change in validation or card type\n if (event.target.fieldKey === 'number') {\n self.selectedCardType(null);\n }\n\n if (event.target.fieldKey === 'number' && event.card) {\n self.isValidCardNumber = event.isValid;\n self.selectedCardType(\n validator.getMageCardType(event.card.type, self.getCcAvailableTypes())\n );\n }\n };\n\n return fields;\n },\n\n /**\n * Validate current credit card type\n * @returns {Boolean}\n */\n validateCardType: function () {\n var $selector = $(this.getSelector('cc_number')),\n invalidClass = 'braintree-hosted-fields-invalid';\n\n $selector.removeClass(invalidClass);\n\n if (this.selectedCardType() === null || !this.isValidCardNumber) {\n $(this.getSelector('cc_number')).addClass(invalidClass);\n\n return false;\n }\n\n return true;\n },\n\n /**\n * Returns state of place order button\n * @returns {Boolean}\n */\n isButtonActive: function () {\n return this.isActive() && this.isPlaceOrderActionAllowed();\n },\n\n /**\n * Trigger order placing\n */\n placeOrderClick: function () {\n if (this.validateCardType()) {\n this.isPlaceOrderActionAllowed(false);\n $(this.getSelector('submit')).trigger('click');\n }\n },\n\n /**\n * @returns {String}\n */\n getVaultCode: function () {\n return window.checkoutConfig.payment[this.getCode()].ccVaultCode;\n }\n });\n});\n","Magento_Braintree/js/view/payment/method-renderer/vault.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine([\n 'jquery',\n 'Magento_Vault/js/view/payment/method-renderer/vault',\n 'Magento_Braintree/js/view/payment/adapter',\n 'Magento_Ui/js/model/messageList',\n 'Magento_Checkout/js/model/full-screen-loader'\n], function ($, VaultComponent, Braintree, globalMessageList, fullScreenLoader) {\n 'use strict';\n\n return VaultComponent.extend({\n defaults: {\n template: 'Magento_Vault/payment/form',\n modules: {\n hostedFields: '${ $.parentName }.braintree'\n }\n },\n\n /**\n * Get last 4 digits of card\n * @returns {String}\n */\n getMaskedCard: function () {\n return this.details.maskedCC;\n },\n\n /**\n * Get expiration date\n * @returns {String}\n */\n getExpirationDate: function () {\n return this.details.expirationDate;\n },\n\n /**\n * Get card type\n * @returns {String}\n */\n getCardType: function () {\n return this.details.type;\n },\n\n /**\n * Place order\n */\n placeOrder: function () {\n var self = this;\n\n /**\n * Define onReady callback\n */\n Braintree.onReady = function () {\n self.getPaymentMethodNonce();\n };\n self.hostedFields(function (formComponent) {\n formComponent.initBraintree();\n });\n },\n\n /**\n * Send request to get payment method nonce\n */\n getPaymentMethodNonce: function () {\n var self = this;\n\n fullScreenLoader.startLoader();\n $.getJSON(self.nonceUrl, {\n 'public_hash': self.publicHash\n })\n .done(function (response) {\n fullScreenLoader.stopLoader();\n self.hostedFields(function (formComponent) {\n formComponent.setPaymentMethodNonce(response.paymentMethodNonce);\n formComponent.additionalData['public_hash'] = self.publicHash;\n formComponent.code = self.code;\n formComponent.messageContainer = self.messageContainer;\n formComponent.placeOrder();\n });\n })\n .fail(function (response) {\n var error = JSON.parse(response.responseText);\n\n fullScreenLoader.stopLoader();\n globalMessageList.addErrorMessage({\n message: error.message\n });\n });\n }\n });\n});\n","Magento_Braintree/js/view/payment/method-renderer/paypal-vault.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*browser:true*/\n/*global define*/\ndefine([\n 'jquery',\n 'underscore',\n 'Magento_Vault/js/view/payment/method-renderer/vault',\n 'Magento_Ui/js/model/messageList',\n 'Magento_Checkout/js/model/full-screen-loader'\n], function ($, _, VaultComponent, globalMessageList, fullScreenLoader) {\n 'use strict';\n\n return VaultComponent.extend({\n defaults: {\n template: 'Magento_Braintree/payment/paypal/vault',\n additionalData: {}\n },\n\n /**\n * Get PayPal payer email\n * @returns {String}\n */\n getPayerEmail: function () {\n return this.details.payerEmail;\n },\n\n /**\n * Get type of payment\n * @returns {String}\n */\n getPaymentIcon: function () {\n return window.checkoutConfig.payment['braintree_paypal'].paymentIcon;\n },\n\n /**\n * Place order\n */\n beforePlaceOrder: function () {\n this.getPaymentMethodNonce();\n },\n\n /**\n * Send request to get payment method nonce\n */\n getPaymentMethodNonce: function () {\n var self = this;\n\n fullScreenLoader.startLoader();\n $.getJSON(self.nonceUrl, {\n 'public_hash': self.publicHash\n })\n .done(function (response) {\n fullScreenLoader.stopLoader();\n self.additionalData['payment_method_nonce'] = response.paymentMethodNonce;\n self.placeOrder();\n })\n .fail(function (response) {\n var error = JSON.parse(response.responseText);\n\n fullScreenLoader.stopLoader();\n globalMessageList.addErrorMessage({\n message: error.message\n });\n });\n },\n\n /**\n * Get payment method data\n * @returns {Object}\n */\n getData: function () {\n var data = {\n 'method': this.code,\n 'additional_data': {\n 'public_hash': this.publicHash\n }\n };\n\n data['additional_data'] = _.extend(data['additional_data'], this.additionalData);\n\n return data;\n }\n });\n});\n","Magento_Theme/menu.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/**\n * Simple Navigation with replacing old handlers.\n *\n * @param {String} id - id of ul element with navigation lists\n * @param {Object} settings - object with settings\n */\n/**\n * global mainNav\n *\n * @deprecated\n * @see lib/web/mage/menu.js\n */\ndefine([\n 'prototype'\n], function () {\n 'use strict';\n\n /**\n * Main nav.\n */\n window.mainNav = function () {\n var main = {\n 'obj_nav': $(arguments[0]) || $('nav'),\n settings: {\n 'show_delay': 0,\n 'hide_delay': 0,\n _ie6: /MSIE 6.+Win/.test(navigator.userAgent),\n _ie7: /MSIE 7.+Win/.test(navigator.userAgent)\n },\n\n /**\n * @param {Object} obj\n * @param {*} level\n */\n init: function (obj, level) {\n obj.lists = obj.childElements();\n obj.lists.each(function (el, ind) {\n main.handlNavElement(el);\n\n if ((main.settings._ie6 || main.settings._ie7) && level) {\n main.ieFixZIndex(el, ind, obj.lists.size());\n }\n });\n\n if (main.settings._ie6 && !level) {\n document.execCommand('BackgroundImageCache', false, true);\n }\n },\n\n /**\n * @param {Object} list\n */\n handlNavElement: function (list) {\n if (list !== undefined) {\n\n /**\n * On mouse over.\n */\n list.onmouseover = function () {\n main.fireNavEvent(this, true);\n };\n\n /**\n * On mouse out.\n */\n list.onmouseout = function () {\n main.fireNavEvent(this, false);\n };\n\n if (list.down('ul')) {\n main.init(list.down('ul'), true);\n }\n }\n },\n\n /**\n * @param {HTMLElement} el\n * @param {*} i\n * @param {*} l\n */\n ieFixZIndex: function (el, i, l) {\n if (el.tagName.toString().toLowerCase().indexOf('iframe') == -1) { //eslint-disable-line eqeqeq\n el.style.zIndex = l - i;\n } else {\n el.onmouseover = 'null';\n el.onmouseout = 'null';\n }\n },\n\n /**\n * @param {Object} elm\n * @param {*} ev\n */\n fireNavEvent: function (elm, ev) {\n if (ev) {\n elm.addClassName('over');\n elm.down('a').addClassName('over');\n\n if (elm.childElements()[1]) {\n main.show(elm.childElements()[1]);\n }\n } else {\n elm.removeClassName('over');\n elm.down('a').removeClassName('over');\n\n if (elm.childElements()[1]) {\n main.hide(elm.childElements()[1]);\n }\n }\n },\n\n /**\n * @param {Object} subElm\n */\n show: function (subElm) {\n if (subElm['hide_time_id']) {\n clearTimeout(subElm['hide_time_id']);\n }\n subElm['show_time_id'] = setTimeout(function () {\n if (!subElm.hasClassName('shown-sub')) {\n subElm.addClassName('shown-sub');\n }\n }, main.settings['show_delay']);\n },\n\n /**\n * @param {Object} subElm\n */\n hide: function (subElm) {\n if (subElm['show_time_id']) {\n clearTimeout(subElm['show_time_id']);\n }\n subElm['hide_time_id'] = setTimeout(function () {\n if (subElm.hasClassName('shown-sub')) {\n subElm.removeClassName('shown-sub');\n }\n }, main.settings['hide_delay']);\n }\n\n };\n\n if (arguments[1]) {\n main.settings = Object.extend(main.settings, arguments[1]);\n }\n\n if (main['obj_nav']) {\n main.init(main['obj_nav'], false);\n }\n };\n\n document.observe('dom:loaded', function () {\n //run navigation without delays and with default id=\"#nav\"\n //mainNav();\n\n //run navigation with delays\n window.mainNav('nav', {\n 'show_delay': '100',\n 'hide_delay': '100'\n });\n });\n});\n","Magento_Theme/js/responsive.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'matchMedia',\n 'mage/tabs',\n 'domReady!'\n], function ($, mediaCheck) {\n 'use strict';\n\n mediaCheck({\n media: '(min-width: 768px)',\n\n /**\n * Switch to Desktop Version.\n */\n entry: function () {\n var galleryElement;\n\n (function () {\n\n var productInfoMain = $('.product-info-main'),\n productInfoAdditional = $('#product-info-additional');\n\n if (productInfoAdditional.length) {\n productInfoAdditional.addClass('hidden');\n productInfoMain.removeClass('responsive');\n }\n\n })();\n\n galleryElement = $('[data-role=media-gallery]');\n\n if (galleryElement.length && galleryElement.data('mageZoom')) {\n galleryElement.zoom('enable');\n }\n\n if (galleryElement.length && galleryElement.data('mageGallery')) {\n galleryElement.gallery('option', 'disableLinks', true);\n galleryElement.gallery('option', 'showNav', false);\n galleryElement.gallery('option', 'showThumbs', true);\n }\n },\n\n /**\n * Switch to Mobile Version.\n */\n exit: function () {\n var galleryElement;\n\n $('.action.toggle.checkout.progress').on('click.gotoCheckoutProgress', function () {\n var myWrapper = '#checkout-progress-wrapper';\n\n scrollTo(myWrapper + ' .title');\n $(myWrapper + ' .title').addClass('active');\n $(myWrapper + ' .content').show();\n });\n\n $('body').on('click.checkoutProgress', '#checkout-progress-wrapper .title', function () {\n $(this).toggleClass('active');\n $('#checkout-progress-wrapper .content').toggle();\n });\n\n galleryElement = $('[data-role=media-gallery]');\n\n setTimeout(function () {\n if (galleryElement.length && galleryElement.data('mageZoom')) {\n galleryElement.zoom('disable');\n }\n\n if (galleryElement.length && galleryElement.data('mageGallery')) {\n galleryElement.gallery('option', 'disableLinks', false);\n galleryElement.gallery('option', 'showNav', true);\n galleryElement.gallery('option', 'showThumbs', false);\n }\n }, 2000);\n }\n });\n});\n","Magento_Theme/js/truncate.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * JQuery UI Widget declaration: 'mage.truncateOptions'\n *\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.truncateOptions', {\n options: {\n detailsLink: 'a.details',\n mouseEvents: 'mouseover mouseout',\n truncatedFullValue: 'div.truncated.full.value'\n },\n\n /**\n * Establish the event handler for mouse events on the appropriate elements.\n *\n * @private\n */\n _create: function () {\n this.element.on(this.options.mouseEvents, $.proxy(this._toggleShow, this))\n .find(this.options.detailsLink).on(this.options.mouseEvents, $.proxy(this._toggleShow, this));\n },\n\n /**\n * Toggle the \"show\" class on the associated element.\n *\n * @private\n * @param {jQuery.Event} event - Mouse over/out event.\n */\n _toggleShow: function (event) {\n $(event.currentTarget).find(this.options.truncatedFullValue).toggleClass('show');\n }\n });\n});\n","Magento_Theme/js/theme.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/smart-keyboard-handler',\n 'mage/mage',\n 'mage/ie-class-fixer',\n 'domReady!'\n], function ($, keyboardHandler) {\n 'use strict';\n\n if ($('body').hasClass('checkout-cart-index')) {\n if ($('#co-shipping-method-form .fieldset.rates').length > 0 &&\n $('#co-shipping-method-form .fieldset.rates :checked').length === 0\n ) {\n $('#block-shipping').on('collapsiblecreate', function () {\n $('#block-shipping').collapsible('forceActivate');\n });\n }\n }\n\n $('.cart-summary').mage('sticky', {\n container: '#maincontent'\n });\n\n $('.panel.header > .header.links').clone().appendTo('#store\\\\.links');\n\n keyboardHandler.apply();\n});\n","Magento_Theme/js/row-builder.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * JQuery UI Widget declaration: 'mage.rowBuilder'\n *\n * @api\n */\ndefine([\n 'jquery',\n 'mage/template',\n 'jquery/ui'\n], function ($, mageTemplate) {\n 'use strict';\n\n $.widget('mage.rowBuilder', {\n\n /**\n * options with default values for setting up the template\n */\n options: {\n //Default template options\n rowTemplate: '#template-registrant',\n rowContainer: '#registrant-container',\n //Row index used by the template rows.\n rowIndex: 0,\n //Row count: Should not be set externally\n rowCount: 0,\n rowParentElem: '<li></li>',\n rowContainerClass: 'fields',\n addRowBtn: '#add-registrant-button',\n btnRemoveIdPrefix: 'btn-remove',\n btnRemoveSelector: '.btn-remove',\n rowIdPrefix: 'row',\n //This class is added to rows added after the first one. Adds the dotted separator\n additionalRowClass: 'add-row',\n\n /*\n This is provided during widget instantiation. eg :\n formDataPost : {\"formData\":formData,\"templateFields\":['field1-name','field2-name'] }\n -\"formData\" is the multi-dimensional array of form field values : [['a','b'],['c','b']]\n received from the server and encoded\n -\"templateFields\" are the input fields in the template with index suffixed after the field name\n eg field1-name{index}\n */\n formDataPost: null,\n //Default selectors for add element of a template\n addEventSelector: 'button',\n //Default selectors for remove markup elements of a template\n remEventSelector: 'a',\n //This option allows adding first row delete option and a row separator\n hideFirstRowAddSeparator: true,\n //Max rows - This option should be set when instantiating the widget\n maxRows: 1000,\n maxRowsMsg: '#max-registrant-message'\n },\n\n /**\n * Initialize create\n * @private\n */\n _create: function () {\n this.rowTemplate = mageTemplate(this.options.rowTemplate);\n\n this.options.rowCount = this.options.rowIndex = 0;\n\n //On document ready related tasks\n $($.proxy(this.ready, this));\n\n //Binding template-wide events handlers for adding and removing rows\n this.element.on(\n 'click',\n this.options.addEventSelector + this.options.addRowBtn,\n $.proxy(this.handleAdd, this)\n );\n this.element.on(\n 'click',\n this.options.remEventSelector + this.options.btnRemoveSelector,\n $.proxy(this.handleRemove, this)\n );\n },\n\n /**\n * Initialize template\n * @public\n */\n ready: function () {\n if (this.options.formDataPost &&\n this.options.formDataPost.formData &&\n this.options.formDataPost.formData.length\n ) {\n this.processFormDataArr(this.options.formDataPost);\n } else if (this.options.rowIndex === 0 && this.options.maxRows !== 0) {\n //If no form data , then add default row\n this.addRow(0);\n }\n },\n\n /**\n * Process and loop through all row data to create preselected values. This is used for any error on submit.\n * For complex implementations the inheriting widget can override this behavior\n * @public\n * @param {Object} formDataArr\n */\n processFormDataArr: function (formDataArr) {\n var formData = formDataArr.formData,\n templateFields = formDataArr.templateFields,\n formRow,\n i, j;\n\n for (i = this.options.rowIndex = 0; i < formData.length; this.options.rowIndex = i++) {\n this.addRow(i);\n\n formRow = formData[i];\n\n for (j = 0; j < formRow.length; j++) {\n this.setFieldById(templateFields[j] + i, formRow[j]);\n }\n }\n\n },\n\n /**\n * Initialize and create markup for template row. Add it to the parent container.\n * The template processing will substitute row index at all places marked with _index_ in the template\n * using the template\n * @public\n * @param {Number} index - current index/count of the created template. This will be used as the id\n * @return {*}\n */\n addRow: function (index) {\n var row = $(this.options.rowParentElem),\n tmpl;\n\n row.addClass(this.options.rowContainerClass).attr('id', this.options.rowIdPrefix + index);\n\n tmpl = this.rowTemplate({\n data: {\n _index_: index\n }\n });\n\n $(tmpl).appendTo(row);\n\n $(this.options.rowContainer).append(row).trigger('contentUpdated');\n\n row.addClass(this.options.additionalRowClass);\n\n //Remove 'delete' link and additionalRowClass for first row\n if (this.options.rowIndex === 0 && this.options.hideFirstRowAddSeparator) {\n $('#' + this._esc(this.options.btnRemoveIdPrefix) + '0').remove();\n $('#' + this._esc(this.options.rowIdPrefix) + '0').removeClass(this.options.additionalRowClass);\n }\n\n this.maxRowCheck(++this.options.rowCount);\n\n return row;\n },\n\n /**\n * Remove return item information row\n * @public\n * @param {*} rowIndex - return item information row index\n * @return {Boolean}\n */\n removeRow: function (rowIndex) {\n $('#' + this._esc(this.options.rowIdPrefix) + rowIndex).remove();\n this.maxRowCheck(--this.options.rowCount);\n\n return false;\n },\n\n /**\n * Function to check if maximum rows are exceeded and render/hide maxMsg and Add btn\n * @public\n * @param {Number} rowIndex\n */\n maxRowCheck: function (rowIndex) {\n var addRowBtn = $(this.options.addRowBtn),\n maxRowMsg = $(this.options.maxRowsMsg);\n\n //liIndex starts from 0\n if (rowIndex >= this.options.maxRows) {\n addRowBtn.hide();\n maxRowMsg.show();\n } else if (addRowBtn.is(':hidden')) {\n addRowBtn.show();\n maxRowMsg.hide();\n }\n },\n\n /**\n * Set the value on given element\n * @public\n * @param {String} domId\n * @param {String} value\n */\n setFieldById: function (domId, value) {\n var x = $('#' + this._esc(domId));\n\n if (x.length) {\n\n if (x.is(':checkbox')) {\n x.attr('checked', true);\n } else if (x.is('option')) {\n x.attr('selected', 'selected');\n } else {\n x.val(value);\n }\n }\n },\n\n /**\n * Delegated handler for adding a row\n * @public\n * @return {Boolean}\n */\n handleAdd: function () {\n this.addRow(++this.options.rowIndex);\n\n return false;\n },\n\n /**\n * Delegated handler for removing a selected row\n * @public\n * @param {Object} e - Native event object\n * @return {Boolean}\n */\n handleRemove: function (e) {\n this.removeRow($(e.currentTarget).closest('[id^=\"' + this.options.btnRemoveIdPrefix + '\"]')\n .attr('id').replace(this.options.btnRemoveIdPrefix, ''));\n\n return false;\n },\n\n /**\n * Utility function to add escape chars for jquery selector strings\n * @private\n * @param {String} str - String to be processed\n * @return {String}\n */\n _esc: function (str) {\n return str ? str.replace(/([ ;&,.+*~\\':\"!\\^$\\[\\]()=>|\\/@])/g, '\\\\$1') : str;\n }\n });\n\n return $.mage.rowBuilder;\n});\n","Magento_Theme/js/model/breadcrumb-list.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([], function () {\n 'use strict';\n\n return [];\n});\n","Magento_Theme/js/view/add-home-breadcrumb.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/* eslint-disable max-nested-callbacks, no-undef */\ndefine([\n 'jquery',\n 'Magento_Theme/js/model/breadcrumb-list',\n 'mage/translate'\n], function ($, breadcrumbList) {\n 'use strict';\n\n /**\n * @return {Object}\n */\n var homeCrumb = function () {\n return {\n name: 'home',\n label: $.mage.__('Home'),\n title: $.mage.__('Go to Home Page'),\n link: BASE_URL || ''\n };\n };\n\n return function (breadcrumb) {\n\n breadcrumbList.unshift(homeCrumb());\n\n return breadcrumb;\n };\n});\n","Magento_Theme/js/view/breadcrumbs.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_Theme/js/model/breadcrumb-list',\n 'text!Magento_Theme/templates/breadcrumbs.html',\n 'jquery/ui'\n], function ($, mageTemplate, breadcrumbList, tpl) {\n 'use strict';\n\n /**\n * Breadcrumb Widget.\n */\n $.widget('mage.breadcrumbs', {\n\n /** @inheritdoc */\n _init: function () {\n this._super();\n this._render();\n },\n\n /**\n * Render breadcrumb.\n *\n * @private\n */\n _render: function () {\n var html,\n crumbs = breadcrumbList,\n template = mageTemplate(tpl);\n\n this._decorate(crumbs);\n\n html = template({\n 'breadcrumbs': crumbs\n });\n\n if (html.length) {\n $(this.element).html(html);\n }\n },\n\n /**\n * Decorate list.\n *\n * @param {Array} list\n * @private\n */\n _decorate: function (list) {\n\n if (list.length) {\n list[0].first = true;\n }\n\n if (list.length > 1) {\n list[list.length - 1].last = true;\n }\n }\n });\n\n return $.mage.breadcrumbs;\n});\n","Magento_Theme/js/view/messages.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 'uiComponent',\n 'Magento_Customer/js/customer-data',\n 'underscore',\n 'jquery/jquery-storageapi'\n], function ($, Component, customerData, _) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n cookieMessages: [],\n messages: []\n },\n\n /**\n * Extends Component object by storage observable messages.\n */\n initialize: function () {\n this._super();\n\n this.cookieMessages = _.unique($.cookieStorage.get('mage-messages'), 'text');\n this.messages = customerData.get('messages').extend({\n disposableCustomerData: 'messages'\n });\n\n // Force to clean obsolete messages\n if (!_.isEmpty(this.messages().messages)) {\n customerData.set('messages', {});\n }\n\n $.cookieStorage.set('mage-messages', '');\n }\n });\n});\n","mage/decorate.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable strict */\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/translate'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($) {\n var methods = {\n /**\n * Decorate a list (e.g. a <ul> containing <li>) recursively if specified.\n * @param {Boolean} isRecursive\n */\n list: function (isRecursive) {\n return this.each(function () {\n var list = $(this),\n items;\n\n if (list.length > 0) {\n items = typeof isRecursive === undefined || isRecursive ?\n list.find('li') :\n list.children();\n items.decorate('generic', ['odd', 'even', 'last']);\n }\n });\n },\n\n /**\n * Annotate a set of DOM elements with decorator classes.\n * @param {Array} decoratorParams\n */\n generic: function (decoratorParams) {\n var elements = $(this),\n allSupportedParams;\n\n if (elements) {\n allSupportedParams = {\n even: 'odd', // Flip jQuery odd/even so that index 0 is odd.\n odd: 'even',\n last: 'last',\n first: 'first'\n };\n\n decoratorParams = decoratorParams || allSupportedParams;\n\n $.each(decoratorParams, function (index, param) {\n if (param === 'even' || param === 'odd') {\n elements.filter(':' + param).removeClass('odd even').addClass(allSupportedParams[param]);\n } else {\n elements.filter(':' + param).addClass(allSupportedParams[param]);\n }\n });\n }\n\n return this;\n },\n\n /**\n * Decorate DOM elements in an HTML table with specified classes.\n * @param {Object} instanceOptions\n */\n table: function (instanceOptions) {\n return this.each(function () {\n var table = $(this),\n options;\n\n if (table.length > 0) {\n options = {\n 'tbody': false,\n 'tbody tr': ['odd', 'even', 'first', 'last'],\n 'thead tr': ['first', 'last'],\n 'tfoot tr': ['first', 'last'],\n 'tr td': ['last']\n };\n\n $.extend(options, instanceOptions || {});\n\n $.each(options, function (key, value) {\n if (options[key]) {\n if (key === 'tr td') {\n $.each(table.find('tr'), function () {\n $(this).find('td').decorate('generic', options['tr td']);\n });\n } else {\n table.find(key).decorate('generic', value);\n }\n }\n });\n }\n });\n },\n\n /**\n * Annotate data list elements with CSS classes.\n */\n dataList: function () {\n return this.each(function () {\n var list = $(this);\n\n if (list) {\n list.find('dt').decorate('generic', ['odd', 'even', 'last']);\n list.find('dd').decorate('generic', ['odd', 'even', 'last']);\n }\n });\n }\n };\n\n /**\n * @param {String} method\n * @return {*}\n */\n $.fn.decorate = function (method) {\n var message;\n\n if (methods[method]) {\n return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n } else if (typeof method === 'object' || !method) {\n return methods.init.apply(this, arguments);\n }\n\n message = $.mage.__('Method %s does not exist on jQuery.decorate');\n $.error(message.replace('%s', method));\n };\n}));\n","mage/accordion.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/tabs'\n], function ($, tabs) {\n 'use strict';\n\n $.widget('mage.accordion', tabs, {\n options: {\n active: [0],\n multipleCollapsible: false,\n openOnFocus: false\n },\n\n /**\n * @private\n */\n _callCollapsible: function () {\n var self = this,\n disabled = false,\n active = false;\n\n if (typeof this.options.active === 'string') {\n this.options.active = this.options.active.split(' ').map(function (item) {\n return parseInt(item, 10);\n });\n }\n\n $.each(this.collapsibles, function (i) {\n disabled = active = false;\n\n if ($.inArray(i, self.options.disabled) !== -1) {\n disabled = true;\n }\n\n if ($.inArray(i, self.options.active) !== -1) {\n active = true;\n }\n self._instantiateCollapsible(this, i, active, disabled);\n });\n },\n\n /**\n * Overwrites default functionality to provide the option to activate/deactivate multiple sections simultaneous\n * @param {*} action\n * @param {*} index\n * @private\n */\n _toggleActivate: function (action, index) {\n var self = this;\n\n if ($.isArray(index && this.options.multipleCollapsible)) {\n $.each(index, function () {\n self.collapsibles.eq(this).collapsible(action);\n });\n } else if (index === undefined && this.options.multipleCollapsible) {\n this.collapsibles.collapsible(action);\n } else {\n this._super(action, index);\n }\n },\n\n /**\n * If the Accordion allows multiple section to be active at the same time, if deep linking is used\n * sections that don't contain the id from anchor shouldn't be closed, otherwise the accordion uses the\n * tabs behavior\n * @private\n */\n _handleDeepLinking: function () {\n if (!this.options.multipleCollapsible) {\n this._super();\n }\n },\n\n /**\n * Prevent default behavior that closes the other sections when one gets activated if the Accordion allows\n * multiple sections simultaneous\n * @private\n */\n _closeOthers: function () {\n if (!this.options.multipleCollapsible) {\n this._super();\n }\n $.each(this.collapsibles, function () {\n $(this).on('beforeOpen', function () {\n var section = $(this);\n\n section.addClass('allow').prevAll().addClass('allow');\n section.nextAll().removeClass('allow');\n });\n });\n }\n });\n\n return $.mage.accordion;\n});\n","mage/multiselect.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'jquery',\n 'text!mage/multiselect.html',\n 'Magento_Ui/js/modal/alert',\n 'jquery/ui',\n 'jquery/editableMultiselect/js/jquery.multiselect'\n], function (_, $, searchTemplate, alert) {\n 'use strict';\n\n $.widget('mage.multiselect2', {\n options: {\n mselectContainer: 'section.mselect-list',\n mselectItemsWrapperClass: 'mselect-items-wrapper',\n mselectCheckedClass: 'mselect-checked',\n containerClass: 'paginated',\n searchInputClass: 'admin__action-multiselect-search',\n selectedItemsCountClass: 'admin__action-multiselect-items-selected',\n currentPage: 1,\n lastAppendValue: 0,\n updateDelay: 1000,\n optionsLoaded: false\n },\n\n /** @inheritdoc */\n _create: function () {\n $.fn.multiselect.call(this.element, this.options);\n },\n\n /** @inheritdoc */\n _init: function () {\n this.domElement = this.element.get(0);\n\n this.$container = $(this.options.mselectContainer);\n this.$wrapper = this.$container.find('.' + this.options.mselectItemsWrapperClass);\n this.$item = this.$wrapper.find('div').first();\n this.selectedValues = [];\n this.values = {};\n\n this.$container.addClass(this.options.containerClass).prepend(searchTemplate);\n this.$input = this.$container.find('.' + this.options.searchInputClass);\n this.$selectedCounter = this.$container.find('.' + this.options.selectedItemsCountClass);\n this.filter = '';\n\n if (this.domElement.options.length) {\n this._setLastAppendOption(this.domElement.options[this.domElement.options.length - 1].value);\n }\n\n this._initElement();\n this._events();\n },\n\n /**\n * Leave only saved/selected options in select element.\n *\n * @private\n */\n _initElement: function () {\n this.element.empty();\n _.each(this.options.selectedValues, function (value) {\n this._createSelectedOption({\n value: value,\n label: value\n });\n }, this);\n },\n\n /**\n * Attach required events.\n *\n * @private\n */\n _events: function () {\n var onKeyUp = _.debounce(this.onKeyUp, this.options.updateDelay);\n\n _.bindAll(this, 'onScroll', 'onCheck', 'onOptionsChange');\n\n this.$wrapper.on('scroll', this.onScroll);\n this.$wrapper.on('change.mselectCheck', '[type=checkbox]', this.onCheck);\n this.$input.on('keyup', _.bind(onKeyUp, this));\n this.element.on('change.hiddenSelect', this.onOptionsChange);\n },\n\n /**\n * Behaves multiselect scroll.\n */\n onScroll: function () {\n var height = this.$wrapper.height(),\n scrollHeight = this.$wrapper.prop('scrollHeight'),\n scrollTop = Math.ceil(this.$wrapper.prop('scrollTop'));\n\n if (!this.options.optionsLoaded && scrollHeight - height <= scrollTop) {\n this.loadOptions();\n }\n },\n\n /**\n * Behaves keyup event on input search\n */\n onKeyUp: function () {\n if (this.getSearchCriteria() === this.filter) {\n return false;\n }\n\n this.setFilter();\n this.clearMultiselectOptions();\n this.setCurrentPage(0);\n this.loadOptions();\n },\n\n /**\n * Callback for select change event\n */\n onOptionsChange: function () {\n this.selectedValues = _.map(this.domElement.options, function (option) {\n this.values[option.value] = true;\n\n return option.value;\n }, this);\n\n this._updateSelectedCounter();\n },\n\n /**\n * Overrides native check behaviour.\n *\n * @param {Event} event\n */\n onCheck: function (event) {\n var checkbox = event.target,\n option = {\n value: checkbox.value,\n label: $(checkbox).parent('label').text()\n };\n\n checkbox.checked ? this._createSelectedOption(option) : this._removeSelectedOption(option);\n event.stopPropagation();\n },\n\n /**\n * Show error message.\n *\n * @param {String} message\n */\n onError: function (message) {\n alert({\n content: message\n });\n },\n\n /**\n * Updates current filter state.\n */\n setFilter: function () {\n this.filter = this.getSearchCriteria() || '';\n },\n\n /**\n * Reads search input value.\n *\n * @return {String}\n */\n getSearchCriteria: function () {\n return $.trim(this.$input.val());\n },\n\n /**\n * Load options data.\n */\n loadOptions: function () {\n var nextPage = this.getCurrentPage() + 1;\n\n this.$wrapper.trigger('processStart');\n this.$input.prop('disabled', true);\n\n $.get(this.options.nextPageUrl, {\n p: nextPage,\n s: this.filter\n })\n .done(function (response) {\n if (response.success) {\n this.appendOptions(response.result);\n this.setCurrentPage(nextPage);\n } else {\n this.onError(response.errorMessage);\n }\n }.bind(this))\n .always(function () {\n this.$wrapper.trigger('processStop');\n this.$input.prop('disabled', false);\n\n if (this.filter) {\n this.$input.focus();\n }\n }.bind(this));\n },\n\n /**\n * Append loaded options\n *\n * @param {Array} options\n */\n appendOptions: function (options) {\n var divOptions = [];\n\n if (!options.length) {\n return false;\n }\n\n if (this.isOptionsLoaded(options)) {\n return;\n }\n\n options.forEach(function (option) {\n if (!this.values[option.value]) {\n this.values[option.value] = true;\n option.selected = this._isOptionSelected(option);\n divOptions.push(this._createMultiSelectOption(option));\n this._setLastAppendOption(option.value);\n }\n }, this);\n\n this.$wrapper.append(divOptions);\n },\n\n /**\n * Clear multiselect options\n */\n clearMultiselectOptions: function () {\n this._setLastAppendOption(0);\n this.values = {};\n this.$wrapper.empty();\n },\n\n /**\n * Checks if all options are already loaded\n *\n * @return {Boolean}\n */\n isOptionsLoaded: function (options) {\n this.options.optionsLoaded = this.options.lastAppendValue === options[options.length - 1].value;\n\n return this.options.optionsLoaded;\n },\n\n /**\n * Setter for current page.\n *\n * @param {Number} page\n */\n setCurrentPage: function (page) {\n this.options.currentPage = page;\n },\n\n /**\n * Getter for current page.\n *\n * @return {Number}\n */\n getCurrentPage: function () {\n return this.options.currentPage;\n },\n\n /**\n * Creates new selected option for select element\n *\n * @param {Object} option - option object\n * @param {String} option.value - option value\n * @param {String} option.label - option label\n * @private\n */\n _createSelectedOption: function (option) {\n var selectOption = new Option(option.label, option.value, false, true);\n\n this.element.append(selectOption);\n this.selectedValues.push(option.value);\n this._updateSelectedCounter();\n\n return selectOption;\n },\n\n /**\n * Remove passed option from select element\n *\n * @param {Object} option - option object\n * @param {String} option.value - option value\n * @param {String} option.label - option label\n * @return {Object} option\n * @private\n */\n _removeSelectedOption: function (option) {\n var unselectedOption = _.findWhere(this.domElement.options, {\n value: option.value\n });\n\n if (!_.isUndefined(unselectedOption)) {\n this.domElement.remove(unselectedOption.index);\n this.selectedValues.splice(_.indexOf(this.selectedValues, option.value), 1);\n this._updateSelectedCounter();\n }\n\n return unselectedOption;\n },\n\n /**\n * Creates new DIV option for multiselect widget\n *\n * @param {Object} option - option object\n * @param {String} option.value - option value\n * @param {String} option.label - option label\n * @param {Boolean} option.selected - is option selected\n * @private\n */\n _createMultiSelectOption: function (option) {\n var item = this.$item.clone(),\n checkbox = item.find('input'),\n isSelected = !!option.selected;\n\n checkbox.val(option.value)\n .prop('checked', isSelected)\n .toggleClass(this.options.mselectCheckedClass, isSelected);\n\n item.find('label > span').text(option.label);\n\n return item;\n },\n\n /**\n * Checks if passed option should be selected\n *\n * @param {Object} option - option object\n * @param {String} option.value - option value\n * @param {String} option.label - option label\n * @param {Boolean} option.selected - is option selected\n * @return {Boolean}\n * @private\n */\n _isOptionSelected: function (option) {\n return !!~this.selectedValues.indexOf(option.value);\n },\n\n /**\n * Saves last added option value.\n *\n * @param {Number} value\n * @private\n */\n _setLastAppendOption: function (value) {\n this.options.lastAppendValue = value;\n },\n\n /**\n * Updates counter of selected items.\n *\n * @private\n */\n _updateSelectedCounter: function () {\n this.$selectedCounter.text(this.selectedValues.length);\n }\n });\n\n return $.mage.multiselect2;\n});\n","mage/collapsible.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui',\n 'jquery/jquery-storageapi',\n 'mage/mage'\n], function ($) {\n 'use strict';\n\n var hideProps = {},\n showProps = {};\n\n hideProps.height = 'hide';\n showProps.height = 'show';\n\n $.widget('mage.collapsible', {\n options: {\n active: false,\n disabled: false,\n collapsible: true,\n header: '[data-role=title]',\n content: '[data-role=content]',\n trigger: '[data-role=trigger]',\n closedState: null,\n openedState: null,\n disabledState: null,\n ajaxUrlElement: '[data-ajax=true]',\n ajaxContent: false,\n loadingClass: null,\n saveState: false,\n animate: false,\n icons: {\n activeHeader: null,\n header: null\n },\n collateral: {\n element: null,\n openedState: null\n }\n },\n\n /**\n * @private\n */\n _create: function () {\n this.storage = $.localStorage;\n this.icons = false;\n\n if (typeof this.options.icons === 'string') {\n this.options.icons = $.parseJSON(this.options.icons);\n }\n\n this._processPanels();\n this._processState();\n this._refresh();\n\n if (this.options.icons.header && this.options.icons.activeHeader) {\n this._createIcons();\n this.icons = true;\n }\n\n this.element.on('dimensionsChanged', function (e) {\n if (e.target && e.target.classList.contains('active')) {\n this._scrollToTopIfVisible(e.target);\n }\n }.bind(this));\n\n this._bind('click');\n this._trigger('created');\n },\n\n /**\n * @private\n */\n _refresh: function () {\n this.trigger.attr('tabIndex', 0);\n\n if (this.options.active && !this.options.disabled) {\n if (this.options.openedState) {\n this.element.addClass(this.options.openedState);\n }\n\n if (this.options.collateral.element && this.options.collateral.openedState) {\n $(this.options.collateral.element).addClass(this.options.collateral.openedState);\n }\n\n if (this.options.ajaxContent) {\n this._loadContent();\n }\n // ARIA (updates aria attributes)\n this.header.attr({\n 'aria-selected': false\n });\n } else if (this.options.disabled) {\n this.disable();\n } else {\n this.content.hide();\n\n if (this.options.closedState) {\n this.element.addClass(this.options.closedState);\n }\n }\n },\n\n /**\n * Processing the state:\n * If deep linking is used and the anchor is the id of the content or the content contains this id,\n * and the collapsible element is a nested one having collapsible parents, in order to see the content,\n * all the parents must be expanded.\n * @private\n */\n _processState: function () {\n var anchor = window.location.hash,\n isValid = $.mage.isValidSelector(anchor),\n urlPath = window.location.pathname.replace(/\\./g, ''),\n state;\n\n this.stateKey = encodeURIComponent(urlPath + this.element.attr('id'));\n\n if (isValid &&\n ($(this.content.find(anchor)).length > 0 || this.content.attr('id') === anchor.replace('#', ''))\n ) {\n this.element.parents('[data-collapsible=true]').collapsible('forceActivate');\n\n if (!this.options.disabled) {\n this.options.active = true;\n\n if (this.options.saveState) { //eslint-disable-line max-depth\n this.storage.set(this.stateKey, true);\n }\n }\n } else if (this.options.saveState && !this.options.disabled) {\n state = this.storage.get(this.stateKey);\n\n if (typeof state === 'undefined' || state === null) {\n this.storage.set(this.stateKey, this.options.active);\n } else if (state === true) {\n this.options.active = true;\n } else if (state === false) {\n this.options.active = false;\n }\n }\n },\n\n /**\n * @private\n */\n _createIcons: function () {\n var icons = this.options.icons;\n\n if (icons) {\n $('<span>')\n .addClass(icons.header)\n .attr('data-role', 'icons')\n .prependTo(this.header);\n\n if (this.options.active && !this.options.disabled) {\n this.header.children('[data-role=icons]')\n .removeClass(icons.header)\n .addClass(icons.activeHeader);\n }\n }\n },\n\n /**\n * @private\n */\n _destroyIcons: function () {\n this.header\n .children('[data-role=icons]')\n .remove();\n },\n\n /**\n * @private\n */\n _destroy: function () {\n var options = this.options;\n\n this.element.removeAttr('data-collapsible');\n\n this.trigger.removeAttr('tabIndex');\n\n if (options.openedState) {\n this.element.removeClass(options.openedState);\n }\n\n if (this.options.collateral.element && this.options.collateral.openedState) {\n $(this.options.collateral.element).removeClass(this.options.collateral.openedState);\n }\n\n if (options.closedState) {\n this.element.removeClass(options.closedState);\n }\n\n if (options.disabledState) {\n this.element.removeClass(options.disabledState);\n }\n\n if (this.icons) {\n this._destroyIcons();\n }\n },\n\n /**\n * @private\n */\n _processPanels: function () {\n var headers, triggers;\n\n this.element.attr('data-collapsible', 'true');\n\n if (typeof this.options.header === 'object') {\n this.header = this.options.header;\n } else {\n headers = this.element.find(this.options.header);\n\n if (headers.length > 0) {\n this.header = headers.eq(0);\n } else {\n this.header = this.element;\n }\n }\n\n if (typeof this.options.content === 'object') {\n this.content = this.options.content;\n } else {\n this.content = this.header.next(this.options.content).eq(0);\n }\n\n // ARIA (init aria attributes)\n if (this.header.attr('id')) {\n this.content.attr('aria-labelledby', this.header.attr('id'));\n }\n\n if (this.content.attr('id')) {\n this.header.attr('aria-controls', this.content.attr('id'));\n }\n\n this.header\n .attr({\n 'role': 'tab',\n 'aria-selected': this.options.active,\n 'aria-expanded': this.options.active\n });\n\n // For collapsible widget only (not tabs or accordion)\n if (this.header.parent().attr('role') !== 'presentation') {\n this.header\n .parent()\n .attr('role', 'tablist');\n }\n\n this.content.attr({\n 'role': 'tabpanel',\n 'aria-hidden': !this.options.active\n });\n\n if (typeof this.options.trigger === 'object') {\n this.trigger = this.options.trigger;\n } else {\n triggers = this.header.find(this.options.trigger);\n\n if (triggers.length > 0) {\n this.trigger = triggers.eq(0);\n } else {\n this.trigger = this.header;\n }\n }\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _keydown: function (event) {\n var keyCode;\n\n if (event.altKey || event.ctrlKey) {\n return;\n }\n\n keyCode = $.ui.keyCode;\n\n switch (event.keyCode) {\n case keyCode.SPACE:\n case keyCode.ENTER:\n this._eventHandler(event);\n break;\n }\n\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _bind: function (event) {\n var self = this;\n\n this.events = {\n keydown: '_keydown'\n };\n\n if (event) {\n $.each(event.split(' '), function (index, eventName) {\n self.events[ eventName ] = '_eventHandler';\n });\n }\n this._off(this.trigger);\n\n if (!this.options.disabled) {\n this._on(this.trigger, this.events);\n }\n },\n\n /**\n * Disable.\n */\n disable: function () {\n this.options.disabled = true;\n this._off(this.trigger);\n this.forceDeactivate();\n\n if (this.options.disabledState) {\n this.element.addClass(this.options.disabledState);\n }\n this.trigger.attr('tabIndex', -1);\n },\n\n /**\n * Enable.\n */\n enable: function () {\n this.options.disabled = false;\n this._on(this.trigger, this.events);\n this.forceActivate();\n\n if (this.options.disabledState) {\n this.element.removeClass(this.options.disabledState);\n }\n this.trigger.attr('tabIndex', 0);\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _eventHandler: function (event) {\n\n if (this.options.active && this.options.collapsible) {\n this.deactivate();\n } else {\n this.activate();\n\n }\n event.preventDefault();\n\n },\n\n /**\n * @param {*} prop\n * @private\n */\n _animate: function (prop) {\n var duration,\n easing,\n animate = this.options.animate;\n\n if (typeof animate === 'number') {\n duration = animate;\n }\n\n if (typeof animate === 'string') {\n animate = $.parseJSON(animate);\n }\n duration = duration || animate.duration;\n easing = animate.easing;\n this.content.animate(prop, duration, easing);\n },\n\n /**\n * Deactivate.\n */\n deactivate: function () {\n if (this.options.animate) {\n this._animate(hideProps);\n } else {\n this.content.hide();\n }\n this._close();\n },\n\n /**\n * Force deactivate.\n */\n forceDeactivate: function () {\n this.content.hide();\n this._close();\n\n },\n\n /**\n * @private\n */\n _close: function () {\n this.options.active = false;\n\n if (this.options.saveState) {\n this.storage.set(this.stateKey, false);\n }\n\n if (this.options.openedState) {\n this.element.removeClass(this.options.openedState);\n }\n\n if (this.options.collateral.element && this.options.collateral.openedState) {\n $(this.options.collateral.element).removeClass(this.options.collateral.openedState);\n }\n\n if (this.options.closedState) {\n this.element.addClass(this.options.closedState);\n }\n\n if (this.icons) {\n this.header.children('[data-role=icons]')\n .removeClass(this.options.icons.activeHeader)\n .addClass(this.options.icons.header);\n }\n\n // ARIA (updates aria attributes)\n this.header.attr({\n 'aria-selected': 'false',\n 'aria-expanded': 'false'\n });\n this.content.attr({\n 'aria-hidden': 'true'\n });\n\n this.element.trigger('dimensionsChanged', {\n opened: false\n });\n },\n\n /**\n * Activate.\n *\n * @return void;\n */\n activate: function () {\n if (this.options.disabled) {\n return;\n }\n\n if (this.options.animate) {\n this._animate(showProps);\n } else {\n this.content.show();\n }\n this._open();\n },\n\n /**\n * Force activate.\n */\n forceActivate: function () {\n if (!this.options.disabled) {\n this.content.show();\n this._open();\n }\n },\n\n /**\n * @private\n */\n _open: function () {\n this.element.trigger('beforeOpen');\n this.options.active = true;\n\n if (this.options.ajaxContent) {\n this._loadContent();\n }\n\n if (this.options.saveState) {\n this.storage.set(this.stateKey, true);\n }\n\n if (this.options.openedState) {\n this.element.addClass(this.options.openedState);\n }\n\n if (this.options.collateral.element && this.options.collateral.openedState) {\n $(this.options.collateral.element).addClass(this.options.collateral.openedState);\n }\n\n if (this.options.closedState) {\n this.element.removeClass(this.options.closedState);\n }\n\n if (this.icons) {\n this.header.children('[data-role=icons]')\n .removeClass(this.options.icons.header)\n .addClass(this.options.icons.activeHeader);\n }\n\n // ARIA (updates aria attributes)\n this.header.attr({\n 'aria-selected': 'true',\n 'aria-expanded': 'true'\n });\n this.content.attr({\n 'aria-hidden': 'false'\n });\n\n this.element.trigger('dimensionsChanged', {\n opened: true\n });\n },\n\n /**\n * @private\n */\n _loadContent: function () {\n var url = this.element.find(this.options.ajaxUrlElement).attr('href'),\n that = this;\n\n if (url) {\n that.xhr = $.get({\n url: url,\n dataType: 'html'\n }, function () {\n });\n }\n\n if (that.xhr && that.xhr.statusText !== 'canceled') {\n if (that.options.loadingClass) {\n that.element.addClass(that.options.loadingClass);\n }\n that.content.attr('aria-busy', 'true');\n that.xhr.done(function (response) {\n setTimeout(function () {\n that.content.html(response);\n }, 1);\n });\n that.xhr.always(function (jqXHR, status) {\n setTimeout(function () {\n if (status === 'abort') {\n that.content.stop(false, true);\n }\n\n if (that.options.loadingClass) {\n that.element.removeClass(that.options.loadingClass);\n }\n that.content.removeAttr('aria-busy');\n\n if (jqXHR === that.xhr) {\n delete that.xhr;\n }\n }, 1);\n });\n }\n },\n\n /**\n * @param {HTMLElement} elem\n * @private\n */\n _scrollToTopIfVisible: function (elem) {\n if (this._isElementOutOfViewport(elem)) {\n elem.scrollIntoView();\n }\n },\n\n /**\n * @param {HTMLElement} elem\n * @private\n * @return {Boolean}\n */\n _isElementOutOfViewport: function (elem) {\n var rect = elem.getBoundingClientRect();\n\n return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;\n }\n });\n\n return $.mage.collapsible;\n});\n","mage/cookies.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/mage',\n 'jquery/jquery.cookie'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($) {\n 'use strict';\n\n /**\n * Helper for cookies manipulation\n * @returns {CookieHelper}\n * @constructor\n */\n var CookieHelper = function () {\n\n /**\n * Cookie default values.\n * @type {Object}\n */\n this.defaults = {\n expires: null,\n path: '/',\n domain: null,\n secure: false,\n lifetime: null\n };\n\n /**\n * Calculate cookie expiration date based on its lifetime.\n * @param {Object} options - Cookie option values\n * @return {Date|null} Calculated cookie expiration date or null if no lifetime provided.\n * @private\n */\n function lifetimeToExpires(options, defaults) {\n var expires,\n lifetime;\n\n lifetime = options.lifetime || defaults.lifetime;\n\n if (lifetime && lifetime > 0) {\n expires = options.expires || new Date();\n\n return new Date(expires.getTime() + lifetime * 1000);\n }\n\n return null;\n }\n\n /**\n * Set a cookie's value by cookie name based on optional cookie options.\n * @param {String} name - The name of the cookie.\n * @param {String} value - The cookie's value.\n * @param {Object} options - Optional options (e.g. lifetime, expires, path, etc.)\n */\n this.set = function (name, value, options) {\n var expires,\n path,\n domain,\n secure;\n\n options = $.extend({}, this.defaults, options || {});\n expires = lifetimeToExpires(options, this.defaults) || options.expires;\n path = options.path;\n domain = options.domain;\n secure = options.secure;\n\n document.cookie = name + '=' + encodeURIComponent(value) +\n (expires ? '; expires=' + expires.toUTCString() : '') +\n (path ? '; path=' + path : '') +\n (domain ? '; domain=' + domain : '') +\n (secure ? '; secure' : '');\n };\n\n /**\n * Get a cookie's value by cookie name.\n * @param {String} name - The name of the cookie.\n * @return {(null|String)}\n */\n this.get = function (name) {\n var arg = name + '=',\n aLength = arg.length,\n cookie = document.cookie,\n cLength = cookie.length,\n i = 0,\n j = 0;\n\n while (i < cLength) {\n j = i + aLength;\n\n if (cookie.substring(i, j) === arg) {\n return this.getCookieVal(j);\n }\n i = cookie.indexOf(' ', i) + 1;\n\n if (i === 0) {\n break;\n }\n }\n\n return null;\n };\n\n /**\n * Clear a cookie's value by name.\n * @param {String} name - The name of the cookie being cleared.\n */\n this.clear = function (name) {\n if (this.get(name)) {\n this.set(name, '', {\n expires: new Date('Jan 01 1970 00:00:01 GMT')\n });\n }\n };\n\n /**\n * Return URI decoded cookie component value (e.g. expires, path, etc.) based on a\n * numeric offset in the document's cookie value.\n * @param {Number} offset - Offset into the document's cookie value.\n * @return {String}\n */\n this.getCookieVal = function (offset) {\n var cookie = document.cookie,\n endstr = cookie.indexOf(';', offset);\n\n if (endstr === -1) {\n endstr = cookie.length;\n }\n\n return decodeURIComponent(cookie.substring(offset, endstr));\n };\n\n return this;\n };\n\n $.extend(true, $, {\n mage: {\n cookies: new CookieHelper()\n }\n });\n\n return function (pageOptions) {\n $.extend($.mage.cookies.defaults, pageOptions);\n $.extend($.cookie.defaults, $.mage.cookies.defaults);\n };\n}));\n","mage/sticky.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.sticky', {\n options: {\n /**\n * Element selector, who's height will be used to restrict the\n * maximum offsetTop position of the stuck element.\n * Default uses document body.\n * @type {String}\n */\n container: '',\n\n /**\n * Spacing in pixels above the stuck element\n * @type {Number|Function} Number or Function that will return a Number\n */\n spacingTop: 0,\n\n /**\n * Allows postponing sticking, until element will go out of the\n * screen for the number of pixels.\n * @type {Number|Function} Number or Function that will return a Number\n */\n stickAfter: 0,\n\n /**\n * CSS class for active sticky state\n * @type {String}\n */\n stickyClass: '_sticky'\n },\n\n /**\n * Retrieve option value\n * @param {String} option\n * @return {*}\n * @private\n */\n _getOptionValue: function (option) {\n var value = this.options[option] || 0;\n\n if (typeof value === 'function') {\n value = this.options[option]();\n }\n\n return value;\n },\n\n /**\n * Bind handlers to scroll event\n * @private\n */\n _create: function () {\n $(window).on({\n 'scroll': $.proxy(this._stick, this),\n 'resize': $.proxy(this.reset, this)\n });\n\n this.element.on('dimensionsChanged', $.proxy(this.reset, this));\n\n this.reset();\n },\n\n /**\n * float Block on windowScroll\n * @private\n */\n _stick: function () {\n var offset,\n isStatic,\n stuck,\n stickAfter;\n\n isStatic = this.element.css('position') === 'static';\n\n if (!isStatic && this.element.is(':visible')) {\n offset = $(document).scrollTop() -\n this.parentOffset +\n this._getOptionValue('spacingTop');\n\n offset = Math.max(0, Math.min(offset, this.maxOffset));\n\n stuck = this.element.hasClass(this.options.stickyClass);\n stickAfter = this._getOptionValue('stickAfter');\n\n if (offset && !stuck && offset < stickAfter) {\n offset = 0;\n }\n\n this.element\n .toggleClass(this.options.stickyClass, offset > 0)\n .css('top', offset);\n }\n },\n\n /**\n * Defines maximum offset value of the element.\n * @private\n */\n _calculateDimens: function () {\n var $parent = this.element.parent(),\n topMargin = parseInt(this.element.css('margin-top'), 10),\n parentHeight = $parent.height() - topMargin,\n height = this.element.innerHeight(),\n maxScroll = document.body.offsetHeight - window.innerHeight;\n\n if (this.options.container.length > 0) {\n maxScroll = $(this.options.container).height();\n }\n\n this.parentOffset = $parent.offset().top + topMargin;\n this.maxOffset = maxScroll - this.parentOffset;\n\n if (this.maxOffset + height >= parentHeight) {\n this.maxOffset = parentHeight - height;\n }\n\n return this;\n },\n\n /**\n * Facade method that palces sticky element where it should be.\n */\n reset: function () {\n this._calculateDimens()\n ._stick();\n }\n });\n\n return $.mage.sticky;\n});\n","mage/dropdowns.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery'\n], function ($) {\n 'use strict';\n\n /**\n * @param {Object} options\n */\n $.fn.dropdown = function (options) {\n var defaults = {\n parent: null,\n autoclose: true,\n btnArrow: '.arrow',\n menu: '[data-target=\"dropdown\"]',\n activeClass: 'active'\n },\n actionElem = $(this),\n self = this;\n\n options = $.extend(defaults, options);\n actionElem = $(this);\n self = this;\n\n /**\n * @param {HTMLElement} elem\n */\n this.openDropdown = function (elem) {\n elem\n .addClass(options.activeClass)\n .attr('aria-expanded', true)\n .parent()\n .addClass(options.activeClass);\n\n elem.parent()\n .find(options.menu)\n .attr('aria-hidden', false);\n\n $(options.btnArrow, elem).text('-');\n };\n\n /**\n * @param {HTMLElement} elem\n */\n this.closeDropdown = function (elem) {\n elem.removeClass(options.activeClass)\n .attr('aria-expanded', false)\n .parent()\n .removeClass(options.activeClass);\n\n elem.parent()\n .find(options.menu)\n .attr('aria-hidden', true);\n\n $(options.btnArrow, elem).text('+');\n };\n\n /**\n * Reset all dropdowns.\n *\n * @param {Object} param\n */\n this.reset = function (param) {\n var params = param || {},\n dropdowns = params.elems || actionElem;\n\n dropdowns.each(function (index, elem) {\n self.closeDropdown($(elem));\n });\n };\n\n /* document Event bindings */\n if (options.autoclose === true) {\n $(document).on('click.hideDropdown', this.reset);\n $(document).on('keyup.hideDropdown', function (e) {\n var ESC_CODE = '27';\n\n if (e.keyCode == ESC_CODE) { //eslint-disable-line eqeqeq\n self.reset();\n }\n });\n }\n\n if (options.events) {\n $.each(options.events, function (index, event) {\n $(document).on(event.name, event.selector, event.action);\n });\n }\n\n return this.each(function () {\n var elem = $(this),\n parent = $(options.parent).length > 0 ? $(options.parent) : elem.parent(),\n menu = $(options.menu, parent) || $('.dropdown-menu', parent);\n\n // ARIA (adding aria attributes)\n if (menu.length) {\n elem.attr('aria-haspopup', true);\n }\n\n if (!elem.hasClass(options.activeClass)) {\n elem.attr('aria-expanded', false);\n menu.attr('aria-hidden', true);\n } else {\n elem.attr('aria-expanded', true);\n menu.attr('aria-hidden', false);\n }\n\n if (!elem.is('a, button')) {\n elem.attr('role', 'button');\n elem.attr('tabindex', 0);\n }\n\n if (elem.attr('data-trigger-keypress-button')) {\n elem.on('keypress', function (e) {\n var keyCode = e.keyCode || e.which,\n ENTER_CODE = 13;\n\n if (keyCode === ENTER_CODE) {\n e.preventDefault();\n elem.trigger('click.toggleDropdown');\n }\n });\n }\n\n elem.on('click.toggleDropdown', function () {\n var el = actionElem;\n\n if (options.autoclose === true) {\n actionElem = $();\n $(document).trigger('click.hideDropdown');\n actionElem = el;\n }\n\n self[el.hasClass(options.activeClass) ? 'closeDropdown' : 'openDropdown'](elem);\n\n return false;\n });\n });\n };\n\n return function (data, el) {\n $(el).dropdown(data);\n };\n});\n","mage/url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable strict */\ndefine([], function () {\n var baseUrl = '';\n\n return {\n /**\n * @param {String} url\n */\n setBaseUrl: function (url) {\n baseUrl = url;\n },\n\n /**\n * @param {String} path\n * @return {*}\n */\n build: function (path) {\n if (path.indexOf(baseUrl) !== -1) {\n return path;\n }\n\n return baseUrl + path;\n }\n };\n});\n","mage/storage.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine(['jquery', 'mage/url'], function ($, urlBuilder) {\n 'use strict';\n\n return {\n /**\n * Perform asynchronous GET request to server.\n * @param {String} url\n * @param {Boolean} global\n * @param {String} contentType\n * @returns {Deferred}\n */\n get: function (url, global, contentType) {\n global = global === undefined ? true : global;\n contentType = contentType || 'application/json';\n\n return $.ajax({\n url: urlBuilder.build(url),\n type: 'GET',\n global: global,\n contentType: contentType\n });\n },\n\n /**\n * Perform asynchronous POST request to server.\n * @param {String} url\n * @param {String} data\n * @param {Boolean} global\n * @param {String} contentType\n * @returns {Deferred}\n */\n post: function (url, data, global, contentType) {\n global = global === undefined ? true : global;\n contentType = contentType || 'application/json';\n\n return $.ajax({\n url: urlBuilder.build(url),\n type: 'POST',\n data: data,\n global: global,\n contentType: contentType\n });\n },\n\n /**\n * Perform asynchronous PUT request to server.\n * @param {String} url\n * @param {String} data\n * @param {Boolean} global\n * @param {String} contentType\n * @returns {Deferred}\n */\n put: function (url, data, global, contentType) {\n global = global === undefined ? true : global;\n contentType = contentType || 'application/json';\n\n return $.ajax({\n url: urlBuilder.build(url),\n type: 'PUT',\n data: data,\n global: global,\n contentType: contentType\n });\n },\n\n /**\n * Perform asynchronous DELETE request to server.\n * @param {String} url\n * @param {Boolean} global\n * @param {String} contentType\n * @returns {Deferred}\n */\n delete: function (url, global, contentType) {\n global = global === undefined ? true : global;\n contentType = contentType || 'application/json';\n\n return $.ajax({\n url: urlBuilder.build(url),\n type: 'DELETE',\n global: global,\n contentType: contentType\n });\n }\n };\n});\n","mage/bootstrap.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/apply/main',\n 'Magento_Ui/js/lib/knockout/bootstrap'\n], function ($, mage) {\n 'use strict';\n\n $.ajaxSetup({\n cache: false\n });\n\n /**\n * Init all components defined via data-mage-init attribute.\n */\n $(mage.apply);\n});\n","mage/layout.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\n/* eslint-disable strict */\ndefine(['underscore'], function (_) {\n return {\n /**\n * @param {Object} config\n */\n build: function (config) {\n var types = _.map(_.flatten(config), function (item) {\n return item.type;\n });\n\n require(types, function () {});\n }\n };\n});\n","mage/loader.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/template',\n 'jquery/ui',\n 'mage/translate'\n], function ($, mageTemplate) {\n 'use strict';\n\n $.widget('mage.loader', {\n loaderStarted: 0,\n options: {\n icon: '',\n texts: {\n loaderText: $.mage.__('Please wait...'),\n imgAlt: $.mage.__('Loading...')\n },\n template:\n '<div class=\"loading-mask\" data-role=\"loader\">' +\n '<div class=\"loader\">' +\n '<img alt=\"<%- data.texts.imgAlt %>\" src=\"<%- data.icon %>\">' +\n '<p><%- data.texts.loaderText %></p>' +\n '</div>' +\n '</div>'\n\n },\n\n /**\n * Loader creation\n * @protected\n */\n _create: function () {\n this._bind();\n },\n\n /**\n * Bind on ajax events\n * @protected\n */\n _bind: function () {\n this._on({\n 'processStop': 'hide',\n 'processStart': 'show',\n 'show.loader': 'show',\n 'hide.loader': 'hide',\n 'contentUpdated.loader': '_contentUpdated'\n });\n },\n\n /**\n * Verify loader present after content updated\n *\n * This will be cleaned up by the task MAGETWO-11070\n *\n * @param {EventObject} e\n * @private\n */\n _contentUpdated: function (e) {\n this.show(e);\n },\n\n /**\n * Show loader\n */\n show: function (e, ctx) {\n this._render();\n this.loaderStarted++;\n this.spinner.show();\n\n if (ctx) {\n this.spinner\n .css({\n width: ctx.outerWidth(),\n height: ctx.outerHeight(),\n position: 'absolute'\n })\n .position({\n my: 'top left',\n at: 'top left',\n of: ctx\n });\n }\n\n return false;\n },\n\n /**\n * Hide loader\n */\n hide: function () {\n if (this.loaderStarted > 0) {\n this.loaderStarted--;\n\n if (this.loaderStarted === 0) {\n this.spinner.hide();\n }\n }\n\n return false;\n },\n\n /**\n * Render loader\n * @protected\n */\n _render: function () {\n var html;\n\n if (!this.spinnerTemplate) {\n this.spinnerTemplate = mageTemplate(this.options.template);\n\n html = $(this.spinnerTemplate({\n data: this.options\n }));\n\n html.prependTo(this.element);\n\n this.spinner = html;\n }\n },\n\n /**\n * Destroy loader\n */\n _destroy: function () {\n this.spinner.remove();\n }\n });\n\n /**\n * This widget takes care of registering the needed loader listeners on the body\n */\n $.widget('mage.loaderAjax', {\n options: {\n defaultContainer: '[data-container=body]',\n loadingClass: 'ajax-loading'\n },\n\n /**\n * @private\n */\n _create: function () {\n this._bind();\n // There should only be one instance of this widget, and it should be attached\n // to the body only. Having it on the page twice will trigger multiple processStarts.\n if (window.console && !this.element.is(this.options.defaultContainer) && $.mage.isDevMode(undefined)) {\n console.warn('This widget is intended to be attached to the body, not below.');\n }\n },\n\n /**\n * @private\n */\n _bind: function () {\n $(document).on({\n 'ajaxSend': this._onAjaxSend.bind(this),\n 'ajaxComplete': this._onAjaxComplete.bind(this)\n });\n },\n\n /**\n * @param {Object} loaderContext\n * @return {*}\n * @private\n */\n _getJqueryObj: function (loaderContext) {\n var ctx;\n\n // Check to see if context is jQuery object or not.\n if (loaderContext) {\n if (loaderContext.jquery) {\n ctx = loaderContext;\n } else {\n ctx = $(loaderContext);\n }\n } else {\n ctx = $('[data-container=\"body\"]');\n }\n\n return ctx;\n },\n\n /**\n * @param {jQuery.Event} e\n * @param {Object} jqxhr\n * @param {Object} settings\n * @private\n */\n _onAjaxSend: function (e, jqxhr, settings) {\n var ctx;\n\n $(this.options.defaultContainer)\n .addClass(this.options.loadingClass)\n .attr({\n 'aria-busy': true\n });\n\n if (settings && settings.showLoader) {\n ctx = this._getJqueryObj(settings.loaderContext);\n ctx.trigger('processStart');\n\n // Check to make sure the loader is there on the page if not report it on the console.\n // NOTE that this check should be removed before going live. It is just an aid to help\n // in finding the uses of the loader that maybe broken.\n if (window.console && !ctx.parents('[data-role=\"loader\"]').length) {\n console.warn('Expected to start loader but did not find one in the dom');\n }\n }\n },\n\n /**\n * @param {jQuery.Event} e\n * @param {Object} jqxhr\n * @param {Object} settings\n * @private\n */\n _onAjaxComplete: function (e, jqxhr, settings) {\n $(this.options.defaultContainer)\n .removeClass(this.options.loadingClass)\n .attr('aria-busy', false);\n\n if (settings && settings.showLoader) {\n this._getJqueryObj(settings.loaderContext).trigger('processStop');\n }\n }\n\n });\n\n return {\n loader: $.mage.loader,\n loaderAjax: $.mage.loaderAjax\n };\n});\n","mage/deletable-item.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n /**\n * This widget is used to tag a DOM element as deletable. By default, it will use the click event on the item with a\n * data role of delete to trigger the deletion.\n */\n $.widget('mage.deletableItem', {\n options: {\n deleteEvent: 'click',\n deleteSelector: '[data-role=\"delete\"]',\n hiddenClass: 'no-display'\n },\n\n /**\n * This method binds elements found in this widget.\n */\n _bind: function () {\n var handlers = {};\n\n // since the first handler is dynamic, generate the object using array notation\n handlers[this.options.deleteEvent + ' ' + this.options.deleteSelector] = '_onDeleteClicked';\n handlers.hideDelete = '_onHideDelete';\n handlers.showDelete = '_onShowDelete';\n\n this._on(handlers);\n },\n\n /**\n * This method constructs a new widget.\n */\n _create: function () {\n this._bind();\n },\n\n /**\n * This method is to initialize the control\n * @private\n */\n _init: function () {\n this._onHideDelete(); // by default, hide the control\n },\n\n /**\n * This method removes the entity from the DOM.\n * @private\n */\n _onDeleteClicked: function (e) {\n e.stopPropagation();\n this.element.trigger('deleteItem');\n },\n\n /**\n * This method hides the delete capability of this item (i.e. making it not deletable)\n * @private\n */\n _onHideDelete: function () {\n this.element.find(this.options.deleteSelector).addClass(this.options.hiddenClass);\n },\n\n /**\n * This method shows the delete capability of this item (i.e. making it deletable)\n * @private\n */\n _onShowDelete: function () {\n this.element.find(this.options.deleteSelector).removeClass(this.options.hiddenClass);\n }\n });\n\n return $.mage.deletableItem;\n});\n","mage/popup-window.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.popupWindow', {\n options: {\n centerBrowser: 0, // center window over browser window? {1 (YES) or 0 (NO)}. overrides top and left\n centerScreen: 0, // center window over entire screen? {1 (YES) or 0 (NO)}. overrides top and left\n height: 500, // sets the height in pixels of the window.\n left: 0, // left position when the window appears.\n location: 0, // determines whether the address bar is displayed {1 (YES) or 0 (NO)}.\n menubar: 0, // determines whether the menu bar is displayed {1 (YES) or 0 (NO)}.\n resizable: 0, // whether the window can be resized {1 (YES) or 0 (NO)}.\n scrollbars: 0, // determines whether scrollbars appear on the window {1 (YES) or 0 (NO)}.\n status: 0, // whether a status line appears at the bottom of the window {1 (YES) or 0 (NO)}.\n width: 500, // sets the width in pixels of the window.\n windowName: null, // name of window set from the name attribute of the element that invokes the click\n windowURL: null, // url used for the popup\n top: 0, // top position when the window appears.\n toolbar: 0 // determines whether a toolbar is displayed {1 (YES) or 0 (NO)}.\n },\n\n /**\n * @private\n */\n _create: function () {\n this.element.on('click', $.proxy(this._openPopupWindow, this));\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _openPopupWindow: function (event) {\n var element = $(event.target),\n settings = this.options,\n windowFeatures =\n 'height=' + settings.height +\n ',width=' + settings.width +\n ',toolbar=' + settings.toolbar +\n ',scrollbars=' + settings.scrollbars +\n ',status=' + settings.status +\n ',resizable=' + settings.resizable +\n ',location=' + settings.location +\n ',menuBar=' + settings.menubar,\n centeredX,\n centeredY;\n\n settings.windowName = settings.windowName || element.attr('name');\n settings.windowURL = settings.windowURL || element.attr('href');\n\n if (settings.centerBrowser) {\n centeredY = window.screenY + ((window.outerHeight / 2 - settings.height / 2));\n centeredX = window.screenX + ((window.outerWidth / 2 - settings.width / 2));\n windowFeatures += ',left=' + centeredX + ',top=' + centeredY;\n } else if (settings.centerScreen) {\n centeredY = (screen.height - settings.height) / 2;\n centeredX = (screen.width - settings.width) / 2;\n windowFeatures += ',left=' + centeredX + ',top=' + centeredY;\n } else {\n windowFeatures += ',left=' + settings.left + ',top=' + settings.top;\n }\n\n window.open(settings.windowURL, settings.windowName, windowFeatures).focus();\n event.preventDefault();\n }\n });\n\n return $.mage.popupWindow;\n});\n","mage/smart-keyboard-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'jquery'\n], function ($) {\n 'use strict';\n\n /**\n * @return {Object}\n * @constructor\n */\n function KeyboardHandler() {\n var body = $('body'),\n focusState = false,\n tabFocusClass = '_keyfocus',\n productsGrid = '[data-container=\"product-grid\"]',\n catalogProductsGrid = $(productsGrid),\n CODE_TAB = 9;\n\n /**\n * Handle logic, when onTabKeyPress fired at first.\n * Then it changes state.\n */\n function onFocusInHandler() {\n focusState = true;\n body.addClass(tabFocusClass)\n .off('focusin.keyboardHandler', onFocusInHandler);\n }\n\n /**\n * Handle logic to remove state after onTabKeyPress to normal.\n */\n function onClickHandler() {\n focusState = false;\n body.removeClass(tabFocusClass)\n .off('click', onClickHandler);\n }\n\n /**\n * Tab key onKeypress handler. Apply main logic:\n * - call differ actions onTabKeyPress and onClick\n */\n function smartKeyboardFocus() {\n $(document).on('keydown keypress', function (event) {\n if (event.which === CODE_TAB && !focusState) {\n body\n .on('focusin.keyboardHandler', onFocusInHandler)\n .on('click', onClickHandler);\n }\n });\n\n // ARIA support for catalog grid products\n if (catalogProductsGrid.length) {\n body.on('focusin.gridProducts', productsGrid, function () {\n if (body.hasClass(tabFocusClass)) {\n $(this).addClass('active');\n }\n });\n body.on('focusout.gridProducts', productsGrid, function () {\n $(this).removeClass('active');\n });\n }\n }\n\n /**\n * Attach smart focus on specific element.\n * @param {jQuery} element\n */\n function handleFocus(element) {\n element.on('focusin.emulateTabFocus', function () {\n focusState = true;\n body.addClass(tabFocusClass);\n element.off();\n });\n\n element.on('focusout.emulateTabFocus', function () {\n focusState = false;\n body.removeClass(tabFocusClass);\n element.off();\n });\n }\n\n return {\n apply: smartKeyboardFocus,\n focus: handleFocus\n };\n }\n\n return new KeyboardHandler;\n});\n","mage/validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'moment',\n 'jquery/ui',\n 'jquery/validate',\n 'mage/translate'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($, moment) {\n 'use strict';\n\n var creditCartTypes, rules, showLabel, originValidateDelegate;\n\n $.extend(true, $, {\n // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions\n mage: {\n /**\n * Check if string is empty with trim\n * @param {String} value\n */\n isEmpty: function (value) {\n return value === '' || value === undefined ||\n value == null || value.length === 0 || /^\\s+$/.test(value);\n },\n\n /**\n * Check if string is empty no trim\n * @param {String} value\n */\n isEmptyNoTrim: function (value) {\n return value === '' || value == null || value.length === 0;\n },\n\n /**\n * Checks if {value} is between numbers {from} and {to}\n * @param {String} value\n * @param {String} from\n * @param {String} to\n * @returns {Boolean}\n */\n isBetween: function (value, from, to) {\n return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&\n ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));\n },\n\n /**\n * Parse price string\n * @param {String} value\n */\n parseNumber: function (value) {\n var isDot, isComa;\n\n if (typeof value !== 'string') {\n return parseFloat(value);\n }\n isDot = value.indexOf('.');\n isComa = value.indexOf(',');\n\n if (isDot !== -1 && isComa !== -1) {\n if (isComa > isDot) {\n value = value.replace('.', '').replace(',', '.');\n } else {\n value = value.replace(',', '');\n }\n } else if (isComa !== -1) {\n value = value.replace(',', '.');\n }\n\n return parseFloat(value);\n },\n\n /**\n * Removes HTML tags and space characters, numbers and punctuation.\n *\n * @param {String} value - Value being stripped.\n * @return {String}\n */\n stripHtml: function (value) {\n return value.replace(/<.[^<>]*?>/g, ' ').replace(/ | /gi, ' ')\n .replace(/[0-9.(),;:!?%#$'\"_+=\\/-]*/g, '');\n }\n }\n });\n\n /**\n * @param {String} name\n * @param {*} method\n * @param {*} message\n * @param {*} dontSkip\n */\n $.validator.addMethod = function (name, method, message, dontSkip) {\n $.validator.methods[name] = method;\n $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];\n\n if (method.length < 3 || dontSkip) {\n $.validator.addClassRules(name, $.validator.normalizeRule(name));\n }\n };\n\n /**\n * Javascript object with credit card types\n * 0 - regexp for card number\n * 1 - regexp for cvn\n * 2 - check or not credit card number trough Luhn algorithm by\n */\n creditCartTypes = {\n 'SO': [\n new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'),\n new RegExp('^([0-9]{3}|[0-9]{4})?$'),\n true\n ],\n 'SM': [\n new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|' +\n '(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|' +\n '(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|' +\n '(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|' +\n '(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|' +\n '(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'),\n true\n ],\n 'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],\n 'MC': [\n new RegExp('^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$'),\n new RegExp('^[0-9]{3}$'),\n true\n ],\n 'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],\n 'DI': [new RegExp('^(6011(0|[2-4]|74|7[7-9]|8[6-9]|9)|6(4[4-9]|5))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n 'JCB': [new RegExp('^35(2[8-9]|[3-8])\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n 'DN': [new RegExp('^(3(0[0-5]|095|6|[8-9]))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n 'UN': [\n new RegExp('^(622(1(2[6-9]|[3-9])|[3-8]|9([[0-1]|2[0-5]))|62[4-6]|628([2-8]))\\\\d*?$'),\n new RegExp('^[0-9]{3}$'),\n true\n ],\n 'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\\\d*$'), new RegExp('^[0-9]{3}$'), true],\n 'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\\\d*$'), new RegExp('^[0-9]{3}$'), true]\n };\n\n /**\n * validate credit card number using mod10\n * @param {String} s\n * @return {Boolean}\n */\n function validateCreditCard(s) {\n // remove non-numerics\n var v = '0123456789',\n w = '',\n i, j, k, m, c, a, x;\n\n for (i = 0; i < s.length; i++) {\n x = s.charAt(i);\n\n if (v.indexOf(x, 0) !== -1) {\n w += x;\n }\n }\n // validate number\n j = w.length / 2;\n k = Math.floor(j);\n m = Math.ceil(j) - k;\n c = 0;\n\n for (i = 0; i < k; i++) {\n a = w.charAt(i * 2 + m) * 2;\n c += a > 9 ? Math.floor(a / 10 + a % 10) : a;\n }\n\n for (i = 0; i < k + m; i++) {\n c += w.charAt(i * 2 + 1 - m) * 1;\n }\n\n return c % 10 === 0;\n }\n\n /**\n * validate all table required inputs at once, using single hidden input\n * @param {String} value\n * @param {HTMLElement} element\n *\n * @return {Boolean}\n */\n function tableSingleValidation(value, element) {\n var empty = $(element).closest('table')\n .find('input.required-option:visible')\n .filter(function (i, el) {\n if ($(el).is('disabled')) {\n return $.mage.isEmpty(el.value);\n }\n })\n .length;\n\n return empty === 0;\n }\n\n /**\n * Collection of validation rules including rules from additional-methods.js\n * @type {Object}\n */\n rules = {\n 'max-words': [\n function (value, element, params) {\n return this.optional(element) || $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length < params;\n },\n $.mage.__('Please enter {0} words or less.')\n ],\n 'min-words': [\n function (value, element, params) {\n return this.optional(element) || $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length >= params;\n },\n $.mage.__('Please enter at least {0} words.')\n ],\n 'range-words': [\n function (value, element, params) {\n return this.optional(element) ||\n $.mage.stripHtml(value).match(/\\b\\w+\\b/g).length >= params[0] &&\n value.match(/bw+b/g).length < params[1];\n },\n $.mage.__('Please enter between {0} and {1} words.')\n ],\n 'letters-with-basic-punc': [\n function (value, element) {\n return this.optional(element) || /^[a-z\\-.,()'\\\"\\s]+$/i.test(value);\n },\n $.mage.__('Letters or punctuation only please')\n ],\n 'alphanumeric': [\n function (value, element) {\n return this.optional(element) || /^\\w+$/i.test(value);\n },\n $.mage.__('Letters, numbers, spaces or underscores only please')\n ],\n 'letters-only': [\n function (value, element) {\n return this.optional(element) || /^[a-z]+$/i.test(value);\n },\n $.mage.__('Letters only please')\n ],\n 'no-whitespace': [\n function (value, element) {\n return this.optional(element) || /^\\S+$/i.test(value);\n },\n $.mage.__('No white space please')\n ],\n 'no-marginal-whitespace': [\n function (value, element) {\n return this.optional(element) || !/^\\s+|\\s+$/i.test(value);\n },\n $.mage.__('No marginal white space please')\n ],\n 'zip-range': [\n function (value, element) {\n return this.optional(element) || /^90[2-5]-\\d{2}-\\d{4}$/.test(value);\n },\n $.mage.__('Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx')\n ],\n 'integer': [\n function (value, element) {\n return this.optional(element) || /^-?\\d+$/.test(value);\n },\n $.mage.__('A positive or negative non-decimal number please')\n ],\n 'vinUS': [\n function (v) {\n var i, n, d, f, cd, cdv, LL, VL, FL, rs;\n\n /* eslint-disable max-depth */\n if (v.length !== 17) {\n return false;\n }\n\n LL = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L',\n 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];\n VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];\n FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];\n rs = 0;\n\n for (i = 0; i < 17; i++) {\n f = FL[i];\n d = v.slice(i, i + 1);\n\n if (i === 8) {\n cdv = d;\n }\n\n if (!isNaN(d)) {\n d *= f;\n } else {\n for (n = 0; n < LL.length; n++) {\n if (d.toUpperCase() === LL[n]) {\n d = VL[n];\n d *= f;\n\n if (isNaN(cdv) && n === 8) {\n cdv = LL[n];\n }\n break;\n }\n }\n }\n rs += d;\n }\n\n /* eslint-enable max-depth */\n cd = rs % 11;\n\n if (cd === 10) {\n cd = 'X';\n }\n\n if (cd === cdv) {\n return true;\n }\n\n return false;\n },\n $.mage.__('The specified vehicle identification number (VIN) is invalid.')\n ],\n 'dateITA': [\n function (value, element) {\n var check = false,\n re = /^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/,\n adata, gg, mm, aaaa, xdata;\n\n if (re.test(value)) {\n adata = value.split('/');\n gg = parseInt(adata[0], 10);\n mm = parseInt(adata[1], 10);\n aaaa = parseInt(adata[2], 10);\n xdata = new Date(aaaa, mm - 1, gg);\n\n if (xdata.getFullYear() === aaaa &&\n xdata.getMonth() === mm - 1 &&\n xdata.getDate() === gg\n ) {\n check = true;\n } else {\n check = false;\n }\n } else {\n check = false;\n }\n\n return this.optional(element) || check;\n },\n $.mage.__('Please enter a correct date')\n ],\n 'dateNL': [\n function (value, element) {\n return this.optional(element) || /^\\d\\d?[\\.\\/-]\\d\\d?[\\.\\/-]\\d\\d\\d?\\d?$/.test(value);\n },\n 'Vul hier een geldige datum in.'\n ],\n 'time': [\n function (value, element) {\n return this.optional(element) || /^([01]\\d|2[0-3])(:[0-5]\\d){0,2}$/.test(value);\n },\n $.mage.__('Please enter a valid time, between 00:00 and 23:59')\n ],\n 'time12h': [\n function (value, element) {\n return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\\d){0,2}(\\s[AP]M))$/i.test(value);\n },\n $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm')\n ],\n 'phoneUS': [\n function (phoneNumber, element) {\n phoneNumber = phoneNumber.replace(/\\s+/g, '');\n\n return this.optional(element) || phoneNumber.length > 9 &&\n phoneNumber.match(/^(1-?)?(\\([2-9]\\d{2}\\)|[2-9]\\d{2})-?[2-9]\\d{2}-?\\d{4}$/);\n },\n $.mage.__('Please specify a valid phone number')\n ],\n 'phoneUK': [\n function (phoneNumber, element) {\n return this.optional(element) || phoneNumber.length > 9 &&\n phoneNumber.match(/^(\\(?(0|\\+44)[1-9]{1}\\d{1,4}?\\)?\\s?\\d{3,4}\\s?\\d{3,4})$/);\n },\n $.mage.__('Please specify a valid phone number')\n ],\n 'mobileUK': [\n function (phoneNumber, element) {\n return this.optional(element) || phoneNumber.length > 9 &&\n phoneNumber.match(/^((0|\\+44)7\\d{3}\\s?\\d{6})$/);\n },\n $.mage.__('Please specify a valid mobile number')\n ],\n 'stripped-min-length': [\n function (value, element, param) {\n return value.length >= param;\n },\n $.mage.__('Please enter at least {0} characters')\n ],\n\n /* detect chars that would require more than 3 bytes */\n 'validate-no-utf8mb4-characters': [\n function (value) {\n var validator = this,\n message = $.mage.__('Please remove invalid characters: {0}.'),\n matches = value.match(/(?:[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])/g),\n result = matches === null;\n\n if (!result) {\n validator.charErrorMessage = message.replace('{0}', matches.join());\n }\n\n return result;\n }, function () {\n return this.charErrorMessage;\n }\n ],\n\n /* eslint-disable max-len */\n 'email2': [\n function (value, element) {\n return this.optional(element) ||\n /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)*(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$/i.test(value);\n },\n $.validator.messages.email\n ],\n 'url2': [\n function (value, element) {\n return this.optional(element) || /^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)*(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(\\#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i.test(value);\n },\n $.validator.messages.url\n ],\n\n /* eslint-enable max-len */\n 'credit-card-types': [\n function (value, element, param) {\n var validTypes;\n\n if (/[^0-9-]+/.test(value)) {\n return false;\n }\n value = value.replace(/\\D/g, '');\n\n validTypes = 0x0000;\n\n if (param.mastercard) {\n validTypes |= 0x0001;\n }\n\n if (param.visa) {\n validTypes |= 0x0002;\n }\n\n if (param.amex) {\n validTypes |= 0x0004;\n }\n\n if (param.dinersclub) {\n validTypes |= 0x0008;\n }\n\n if (param.enroute) {\n validTypes |= 0x0010;\n }\n\n if (param.discover) {\n validTypes |= 0x0020;\n }\n\n if (param.jcb) {\n validTypes |= 0x0040;\n }\n\n if (param.unknown) {\n validTypes |= 0x0080;\n }\n\n if (param.all) {\n validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;\n }\n\n if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard\n return value.length === 16;\n }\n\n if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa\n return value.length === 16;\n }\n\n if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex\n return value.length === 15;\n }\n\n if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub\n return value.length === 14;\n }\n\n if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute\n return value.length === 15;\n }\n\n if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover\n return value.length === 16;\n }\n\n if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb\n return value.length === 16;\n }\n\n if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb\n return value.length === 15;\n }\n\n if (validTypes & 0x0080) { //unknown\n return true;\n }\n\n return false;\n },\n $.mage.__('Please enter a valid credit card number.')\n ],\n\n /* eslint-disable max-len */\n 'ipv4': [\n function (value, element) {\n return this.optional(element) ||\n /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);\n },\n $.mage.__('Please enter a valid IP v4 address.')\n ],\n 'ipv6': [\n function (value, element) {\n return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);\n },\n $.mage.__('Please enter a valid IP v6 address.')\n ],\n\n /* eslint-enable max-len */\n 'pattern': [\n function (value, element, param) {\n return this.optional(element) || param.test(value);\n },\n $.mage.__('Invalid format.')\n ],\n 'allow-container-className': [\n function (element) {\n if (element.type === 'radio' || element.type === 'checkbox') {\n return $(element).hasClass('change-container-classname');\n }\n },\n ''\n ],\n 'validate-no-html-tags': [\n function (value) {\n return !/<(\\/)?\\w+/.test(value);\n },\n $.mage.__('HTML tags are not allowed.')\n ],\n 'validate-select': [\n function (value) {\n return value !== 'none' && value != null && value.length !== 0;\n },\n $.mage.__('Please select an option.')\n ],\n 'validate-no-empty': [\n function (value) {\n return !$.mage.isEmpty(value);\n },\n $.mage.__('Empty Value.')\n ],\n 'validate-alphanum-with-spaces': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);\n },\n $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.')\n ],\n 'validate-data': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);\n },\n $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len\n ],\n 'validate-street': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[ \\w]{3,}([A-Za-z]\\.)?([ \\w]*\\#\\d+)?(\\r\\n| )[ \\w]{3,}/.test(v);\n },\n $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), spaces and \"#\" in this field.')\n ],\n 'validate-phoneStrict': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(v);\n },\n $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n ],\n 'validate-phoneLax': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) ||\n /^((\\d[\\-. ]?)?((\\(\\d{3}\\))|\\d{3}))?[\\-. ]?\\d{3}[\\-. ]?\\d{4}$/.test(v);\n },\n $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')\n ],\n 'validate-fax': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^(\\()?\\d{3}(\\))?(-|\\s)?\\d{3}(-|\\s)\\d{4}$/.test(v);\n },\n $.mage.__('Please enter a valid fax number (Ex: 123-456-7890).')\n ],\n 'validate-email': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*@([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*\\.(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]){2,})$/i.test(v); //eslint-disable-line max-len\n },\n $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')\n ],\n 'validate-emailSender': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[\\S ]+$/.test(v);\n },\n $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')\n ],\n 'validate-password': [\n function (v) {\n var pass;\n\n if (v == null) {\n return false;\n }\n //strip leading and trailing spaces\n pass = $.trim(v);\n\n if (!pass.length) {\n return true;\n }\n\n return !(pass.length > 0 && pass.length < 6);\n },\n $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')\n ],\n 'validate-admin-password': [\n function (v) {\n var pass;\n\n if (v == null) {\n return false;\n }\n pass = $.trim(v);\n // strip leading and trailing spaces\n if (pass.length === 0) {\n return true;\n }\n\n if (!/[a-z]/i.test(v) || !/[0-9]/.test(v)) {\n return false;\n }\n\n if (pass.length < 7) {\n return false;\n }\n\n return true;\n },\n $.mage.__('Please enter 7 or more characters, using both numeric and alphabetic.')\n ],\n 'validate-customer-password': [\n function (v, elm) {\n var validator = this,\n counter = 0,\n passwordMinLength = $(elm).data('password-min-length'),\n passwordMinCharacterSets = $(elm).data('password-min-character-sets'),\n pass = $.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 (v) {\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n v = (v || '').replace(/^\\s+/, '').replace(/\\s+$/, '');\n\n return (/^(http|https|ftp):\\/\\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\\d+))?(\\/[A-Z0-9~](([A-Z0-9_~-]|\\.)*[A-Z0-9~]|))*\\/?(.*)?$/i).test(v); //eslint-disable-line max-len\n\n },\n $.mage.__('Please enter a valid URL. Protocol is required (http://, https:// or ftp://).')\n ],\n 'validate-clean-url': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\\/\\/(([A-Z0-9][A-Z0-9_-]*)(\\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\\d+))?\\/?/i.test(v) || /^(www)((\\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\\d+))?\\/?/i.test(v); //eslint-disable-line max-len\n\n },\n $.mage.__('Please enter a valid URL. For example http://www.example.com or www.example.com.')\n ],\n 'validate-xml-identifier': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\\/-]*$/i.test(v);\n\n },\n $.mage.__('Please enter a valid XML-identifier (Ex: something_1, block5, id-4).')\n ],\n 'validate-ssn': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^\\d{3}-?\\d{2}-?\\d{4}$/.test(v);\n\n },\n $.mage.__('Please enter a valid social security number (Ex: 123-45-6789).')\n ],\n 'validate-zip-us': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /(^\\d{5}$)|(^\\d{5}-\\d{4}$)/.test(v);\n\n },\n $.mage.__('Please enter a valid zip code (Ex: 90602 or 90602-1234).')\n ],\n 'validate-date-au': [\n function (v) {\n var regex, d;\n\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n regex = /^(\\d{2})\\/(\\d{2})\\/(\\d{4})$/;\n\n if ($.mage.isEmpty(v) || !regex.test(v)) {\n return false;\n }\n d = new Date(v.replace(regex, '$2/$1/$3'));\n\n return parseInt(RegExp.$2, 10) === 1 + d.getMonth() &&\n parseInt(RegExp.$1, 10) === d.getDate() &&\n parseInt(RegExp.$3, 10) === d.getFullYear();\n\n },\n $.mage.__('Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.')\n ],\n 'validate-currency-dollar': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^\\$?\\-?([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}\\d*(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$/.test(v); //eslint-disable-line max-len\n\n },\n $.mage.__('Please enter a valid $ amount. For example $100.00.')\n ],\n 'validate-not-negative-number': [\n function (v) {\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n v = $.mage.parseNumber(v);\n\n return !isNaN(v) && v >= 0;\n\n },\n $.mage.__('Please enter a number 0 or greater in this field.')\n ],\n // validate-not-negative-number should be replaced in all places with this one and then removed\n 'validate-zero-or-greater': [\n function (v) {\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n v = $.mage.parseNumber(v);\n\n return !isNaN(v) && v >= 0;\n\n },\n $.mage.__('Please enter a number 0 or greater in this field.')\n ],\n 'validate-greater-than-zero': [\n function (v) {\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n v = $.mage.parseNumber(v);\n\n return !isNaN(v) && v > 0;\n },\n $.mage.__('Please enter a number greater than 0 in this field.')\n ],\n 'validate-css-length': [\n function (v) {\n if (v !== '') {\n return (/^[0-9]*\\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);\n }\n\n return true;\n },\n $.mage.__('Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).')\n ],\n // Additional methods\n 'validate-number': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || !isNaN($.mage.parseNumber(v)) && /^\\s*-?\\d*(\\.\\d*)?\\s*$/.test(v);\n },\n $.mage.__('Please enter a valid number in this field.')\n ],\n 'required-number': [\n function (v) {\n return !!v.length;\n },\n $.mage.__('Please enter a valid number in this field.')\n ],\n 'validate-number-range': [\n function (v, elm, param) {\n var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;\n\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n\n numValue = $.mage.parseNumber(v);\n\n if (isNaN(numValue)) {\n return false;\n }\n\n dataAttrRange = /^(-?[\\d.,]+)?-(-?[\\d.,]+)?$/;\n classNameRange = /^number-range-(-?[\\d.,]+)?-(-?[\\d.,]+)?$/;\n result = true;\n range = param;\n\n if (typeof range === 'string') {\n m = dataAttrRange.exec(range);\n\n if (m) {\n result = result && $.mage.isBetween(numValue, m[1], m[2]);\n } else {\n result = false;\n }\n } else if (elm && elm.className) {\n classes = elm.className.split(' ');\n ii = classes.length;\n\n while (ii--) {\n range = classes[ii];\n m = classNameRange.exec(range);\n\n if (m) { //eslint-disable-line max-depth\n result = result && $.mage.isBetween(numValue, m[1], m[2]);\n break;\n }\n }\n }\n\n return result;\n },\n $.mage.__('The value is not within the specified range.'),\n true\n ],\n 'validate-digits': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || !/[^\\d]/.test(v);\n },\n $.mage.__('Please enter a valid number in this field.')\n ],\n 'validate-forbidden-extensions': [\n function (v, elem) {\n var forbiddenExtensions = $(elem).attr('data-validation-params'),\n forbiddenExtensionsArray = forbiddenExtensions.split(','),\n extensionsArray = v.split(','),\n result = true;\n\n this.validateExtensionsMessage = $.mage.__('Forbidden extensions has been used. Avoid usage of ') +\n forbiddenExtensions;\n\n $.each(extensionsArray, function (key, extension) {\n if (forbiddenExtensionsArray.indexOf(extension) !== -1) {\n result = false;\n }\n });\n\n return result;\n }, function () {\n return this.validateExtensionsMessage;\n }\n ],\n 'validate-digits-range': [\n function (v, elm, param) {\n var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;\n\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n }\n\n numValue = $.mage.parseNumber(v);\n\n if (isNaN(numValue)) {\n return false;\n }\n\n dataAttrRange = /^(-?\\d+)?-(-?\\d+)?$/;\n classNameRange = /^digits-range-(-?\\d+)?-(-?\\d+)?$/;\n result = true;\n range = param;\n\n if (typeof range === 'string') {\n m = dataAttrRange.exec(range);\n\n if (m) {\n result = result && $.mage.isBetween(numValue, m[1], m[2]);\n } else {\n result = false;\n }\n } else if (elm && elm.className) {\n classes = elm.className.split(' ');\n ii = classes.length;\n\n while (ii--) {\n range = classes[ii];\n m = classNameRange.exec(range);\n\n if (m) { //eslint-disable-line max-depth\n result = result && $.mage.isBetween(numValue, m[1], m[2]);\n break;\n }\n }\n }\n\n return result;\n },\n $.mage.__('The value is not within the specified range.'),\n true\n ],\n 'validate-range': [\n function (v, elm) {\n var minValue, maxValue, ranges, reRange, result, values,\n i, name, validRange, minValidRange, maxValidRange;\n\n if ($.mage.isEmptyNoTrim(v)) {\n return true;\n } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {\n minValue = maxValue = $.mage.parseNumber(v);\n } else {\n ranges = /^(-?\\d+)?-(-?\\d+)?$/.exec(v);\n\n if (ranges) {\n minValue = $.mage.parseNumber(ranges[1]);\n maxValue = $.mage.parseNumber(ranges[2]);\n\n if (minValue > maxValue) { //eslint-disable-line max-depth\n return false;\n }\n } else {\n return false;\n }\n }\n reRange = /^range-(-?\\d+)?-(-?\\d+)?$/;\n result = true;\n values = $(elm).prop('class').split(' ');\n\n for (i = values.length - 1; i >= 0; i--) {\n name = values[i];\n validRange = reRange.exec(name);\n\n if (validRange) {\n minValidRange = $.mage.parseNumber(validRange[1]);\n maxValidRange = $.mage.parseNumber(validRange[2]);\n result = result &&\n (isNaN(minValidRange) || minValue >= minValidRange) &&\n (isNaN(maxValidRange) || maxValue <= maxValidRange);\n }\n }\n\n return result;\n },\n $.mage.__('The value is not within the specified range.')\n ],\n 'validate-alpha': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);\n },\n $.mage.__('Please use letters only (a-z or A-Z) in this field.')\n ],\n 'validate-code': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+[a-zA-Z0-9_]+$/.test(v);\n },\n $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len\n ],\n 'validate-alphanum': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);\n },\n $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') //eslint-disable-line max-len\n ],\n 'validate-not-number-first': [\n function (value) {\n return $.mage.isEmptyNoTrim(value) || /^[^0-9-\\.].*$/.test(value.trim());\n },\n $.mage.__('First character must be letter.')\n ],\n 'validate-date': [\n function (value, params, additionalParams) {\n var test = moment(value, additionalParams.dateFormat);\n\n return $.mage.isEmptyNoTrim(value) || test.isValid();\n },\n $.mage.__('Please enter a valid date.')\n\n ],\n 'validate-date-range': [\n function (v, elm) {\n var m = /\\bdate-range-(\\w+)-(\\w+)\\b/.exec(elm.className),\n currentYear, normalizedTime, dependentElements;\n\n if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {\n return true;\n }\n\n currentYear = new Date().getFullYear() + '';\n\n /**\n * @param {String} vd\n * @return {Number}\n */\n normalizedTime = function (vd) {\n vd = vd.split(/[.\\/]/);\n\n if (vd[2] && vd[2].length < 4) {\n vd[2] = currentYear.substr(0, vd[2].length) + vd[2];\n }\n\n return new Date(vd.join('/')).getTime();\n };\n\n dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');\n\n return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||\n normalizedTime(v) <= normalizedTime(dependentElements[0].value);\n },\n $.mage.__('Make sure the To Date is later than or the same as the From Date.')\n ],\n 'validate-cpassword': [\n function () {\n var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]),\n pass = false,\n passwordElements, i, passwordElement;\n\n if ($('#password')) {\n pass = $('#password');\n }\n passwordElements = $('.validate-password');\n\n for (i = 0; i < passwordElements.length; i++) {\n passwordElement = $(passwordElements[i]);\n\n if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {\n pass = passwordElement;\n }\n }\n\n if ($('.validate-admin-password').length) {\n pass = $($('.validate-admin-password')[0]);\n }\n\n return pass.val() === conf.val();\n },\n $.mage.__('Please make sure your passwords match.')\n ],\n 'validate-identifier': [\n function (v) {\n return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\\/-]+(\\.[a-z0-9_-]+)?$/.test(v);\n },\n $.mage.__('Please enter a valid URL Key (Ex: \"example-page\", \"example-page.html\" or \"anotherlevel/example-page\").') //eslint-disable-line max-len\n ],\n 'validate-zip-international': [\n\n /*function(v) {\n // @TODO: Cleanup\n return Validation.get('IsEmpty').test(v) ||\n /(^[A-z0-9]{2,10}([\\s]{0,1}|[\\-]{0,1})[A-z0-9]{2,10}$)/.test(v);\n }*/\n function () {\n return true;\n },\n $.mage.__('Please enter a valid zip code.')\n ],\n 'validate-one-required': [\n function (v, elm) {\n var p = $(elm).parent(),\n options = p.find('input');\n\n return options.map(function (el) {\n return $(el).val();\n }).length > 0;\n },\n $.mage.__('Please select one of the options above.')\n ],\n 'validate-state': [\n function (v) {\n return v !== 0 || v === '';\n },\n $.mage.__('Please select State/Province.')\n ],\n 'required-file': [\n function (v, elm) {\n var result = !$.mage.isEmptyNoTrim(v),\n ovId;\n\n if (!result) {\n ovId = $('#' + $(elm).attr('id') + '_value');\n\n if (ovId.length > 0) {\n result = !$.mage.isEmptyNoTrim(ovId.val());\n }\n }\n\n return result;\n },\n $.mage.__('Please select a file.')\n ],\n 'validate-ajax-error': [\n function (v, element) {\n element = $(element);\n element.on('change.ajaxError', function () {\n element.removeClass('validate-ajax-error');\n element.off('change.ajaxError');\n });\n\n return !element.hasClass('validate-ajax-error');\n },\n ''\n ],\n 'validate-optional-datetime': [\n function (v, elm, param) {\n var dateTimeParts = $('.datetime-picker[id^=\"options_' + param + '\"]'),\n hasWithValue = false,\n hasWithNoValue = false,\n pattern = /day_part$/i,\n i;\n\n for (i = 0; i < dateTimeParts.length; i++) {\n if (!pattern.test($(dateTimeParts[i]).attr('id'))) {\n if ($(dateTimeParts[i]).val() === 's') { //eslint-disable-line max-depth\n hasWithValue = true;\n } else {\n hasWithNoValue = true;\n }\n }\n }\n\n return hasWithValue ^ hasWithNoValue;\n },\n $.mage.__('The field isn\\'t complete.')\n ],\n 'validate-required-datetime': [\n function (v, elm, param) {\n var dateTimeParts = $('.datetime-picker[id^=\"options_' + param + '\"]'),\n i;\n\n for (i = 0; i < dateTimeParts.length; i++) {\n if (dateTimeParts[i].value === '') {\n return false;\n }\n }\n\n return true;\n },\n $.mage.__('This is a required field.')\n ],\n 'validate-one-required-by-name': [\n function (v, elm, selector) {\n var name = elm.name.replace(/([\\\\\"])/g, '\\\\$1'),\n container = this.currentForm;\n\n selector = selector === true ? 'input[name=\"' + name + '\"]:checked' : selector;\n\n return !!container.querySelectorAll(selector).length;\n },\n $.mage.__('Please select one of the options.')\n ],\n 'less-than-equals-to': [\n function (value, element, params) {\n if ($.isNumeric($(params).val()) && $.isNumeric(value)) {\n this.lteToVal = $(params).val();\n\n return parseFloat(value) <= parseFloat($(params).val());\n }\n\n return true;\n },\n function () {\n var message = $.mage.__('Please enter a value less than or equal to %s.');\n\n return message.replace('%s', this.lteToVal);\n }\n ],\n 'greater-than-equals-to': [\n function (value, element, params) {\n if ($.isNumeric($(params).val()) && $.isNumeric(value)) {\n this.gteToVal = $(params).val();\n\n return parseFloat(value) >= parseFloat($(params).val());\n }\n\n return true;\n },\n function () {\n var message = $.mage.__('Please enter a value greater than or equal to %s.');\n\n return message.replace('%s', this.gteToVal);\n }\n ],\n 'validate-emails': [\n function (value) {\n var validRegexp, emails, i;\n\n if ($.mage.isEmpty(value)) {\n return true;\n }\n validRegexp = /^([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9,!\\#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*@([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z0-9-]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*\\.(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]){2,})$/i; //eslint-disable-line max-len\n emails = value.split(/[\\s\\n\\,]+/g);\n\n for (i = 0; i < emails.length; i++) {\n if (!validRegexp.test(emails[i].trim())) {\n return false;\n }\n }\n\n return true;\n },\n $.mage.__('Please enter valid email addresses, separated by commas. For example, johndoe@domain.com, johnsmith@domain.com.') //eslint-disable-line max-len\n ],\n\n 'validate-cc-type-select': [\n\n /**\n * Validate credit card type matches credit card number\n * @param {*} value - select credit card type\n * @param {*} element - element contains the select box for credit card types\n * @param {*} params - selector for credit card number\n * @return {Boolean}\n */\n function (value, element, params) {\n if (value && params && creditCartTypes[value]) {\n return creditCartTypes[value][0].test($(params).val().replace(/\\s+/g, ''));\n }\n\n return false;\n },\n $.mage.__('Card type does not match credit card number.')\n ],\n 'validate-cc-number': [\n\n /**\n * Validate credit card number based on mod 10.\n *\n * @param {*} value - credit card number\n * @return {Boolean}\n */\n function (value) {\n if (value) {\n return validateCreditCard(value);\n }\n\n return false;\n },\n $.mage.__('Please enter a valid credit card number.')\n ],\n 'validate-cc-type': [\n\n /**\n * Validate credit card number is for the correct credit card type.\n *\n * @param {String} value - credit card number\n * @param {*} element - element contains credit card number\n * @param {*} params - selector for credit card type\n * @return {Boolean}\n */\n function (value, element, params) {\n var ccType;\n\n if (value && params) {\n ccType = $(params).val();\n value = value.replace(/\\s/g, '').replace(/\\-/g, '');\n\n if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {\n return creditCartTypes[ccType][0].test(value);\n } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {\n return true;\n }\n }\n\n return false;\n },\n $.mage.__('Credit card number does not match credit card type.')\n ],\n 'validate-cc-exp': [\n\n /**\n * Validate credit card expiration date, make sure it's within the year and not before current month.\n *\n * @param {*} value - month\n * @param {*} element - element contains month\n * @param {*} params - year selector\n * @return {Boolean}\n */\n function (value, element, params) {\n var isValid = false,\n month, year, currentTime, currentMonth, currentYear;\n\n if (value && params) {\n month = value;\n year = $(params).val();\n currentTime = new Date();\n currentMonth = currentTime.getMonth() + 1;\n currentYear = currentTime.getFullYear();\n\n isValid = !year || year > currentYear || year == currentYear && month >= currentMonth; //eslint-disable-line\n }\n\n return isValid;\n },\n $.mage.__('Incorrect credit card expiration date.')\n ],\n 'validate-cc-cvn': [\n\n /**\n * Validate credit card cvn based on credit card type.\n *\n * @param {*} value - credit card cvn\n * @param {*} element - element contains credit card cvn\n * @param {*} params - credit card type selector\n * @return {*}\n */\n function (value, element, params) {\n var ccType;\n\n if (value && params) {\n ccType = $(params).val();\n\n if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {\n return creditCartTypes[ccType][1].test(value);\n }\n }\n\n return false;\n },\n $.mage.__('Please enter a valid credit card verification number.')\n ],\n 'validate-cc-ukss': [\n\n /**\n * Validate Switch/Solo/Maestro issue number and start date is filled.\n *\n * @param {*} value - input field value\n * @return {*}\n */\n function (value) {\n return value;\n },\n $.mage.__('Please enter issue number or start date for switch/solo card type.')\n ],\n 'validate-length': [\n function (v, elm) {\n var reMax = new RegExp(/^maximum-length-[0-9]+$/),\n reMin = new RegExp(/^minimum-length-[0-9]+$/),\n validator = this,\n result = true,\n length = 0;\n\n $.each(elm.className.split(' '), function (index, name) {\n if (name.match(reMax) && result) {\n length = name.split('-')[2];\n result = v.length <= length;\n validator.validateMessage =\n $.mage.__('Please enter less or equal than %1 symbols.').replace('%1', length);\n }\n\n if (name.match(reMin) && result && !$.mage.isEmpty(v)) {\n length = name.split('-')[2];\n result = v.length >= length;\n validator.validateMessage =\n $.mage.__('Please enter more or equal than %1 symbols.').replace('%1', length);\n }\n });\n\n return result;\n }, function () {\n return this.validateMessage;\n }\n ],\n 'required-entry': [\n function (value) {\n return !$.mage.isEmpty(value);\n }, $.mage.__('This is a required field.')\n ],\n 'not-negative-amount': [\n function (v) {\n if (v.length) {\n return (/^\\s*\\d+([,.]\\d+)*\\s*%?\\s*$/).test(v);\n }\n\n return true;\n },\n $.mage.__('Please enter positive number in this field.')\n ],\n 'validate-per-page-value-list': [\n function (v) {\n var isValid = !$.mage.isEmpty(v),\n values = v.split(','),\n i;\n\n for (i = 0; i < values.length; i++) {\n if (!/^[0-9]+$/.test(values[i])) {\n isValid = false;\n }\n }\n\n return isValid;\n },\n $.mage.__('Please enter a valid value, ex: 10,20,30')\n ],\n 'validate-per-page-value': [\n function (v, elm) {\n var values;\n\n if ($.mage.isEmpty(v)) {\n return false;\n }\n values = $('#' + elm.id + '_values').val().split(',');\n\n return values.indexOf(v) !== -1;\n },\n $.mage.__('Please enter a valid value from list')\n ],\n 'validate-new-password': [\n function (v) {\n if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {\n return false;\n }\n\n if ($.mage.isEmpty(v) && v !== '') {\n return false;\n }\n\n return true;\n },\n $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')\n ],\n 'required-if-not-specified': [\n function (value, element, params) {\n var valid = false,\n alternate = $(params),\n alternateValue;\n\n if (alternate.length > 0) {\n valid = this.check(alternate);\n // if valid, it may be blank, so check for that\n if (valid) {\n alternateValue = alternate.val();\n\n if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line\n valid = false;\n }\n }\n }\n\n if (!valid) {\n valid = !this.optional(element);\n }\n\n return valid;\n },\n $.mage.__('This is a required field.')\n ],\n 'required-if-all-sku-empty-and-file-not-loaded': [\n function (value, element, params) {\n var valid = false,\n alternate = $(params.specifiedId),\n alternateValue;\n\n if (alternate.length > 0) {\n valid = this.check(alternate);\n // if valid, it may be blank, so check for that\n if (valid) {\n alternateValue = alternate.val();\n\n if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line\n valid = false;\n }\n }\n }\n\n if (!valid) {\n valid = !this.optional(element);\n }\n\n $('input[' + params.dataSku + '=true]').each(function () {\n if ($(this).val() !== '') {\n valid = true;\n }\n });\n\n return valid;\n },\n $.mage.__('Please enter valid SKU key.')\n ],\n 'required-if-specified': [\n function (value, element, params) {\n var valid = true,\n dependent = $(params),\n dependentValue;\n\n if (dependent.length > 0) {\n valid = this.check(dependent);\n // if valid, it may be blank, so check for that\n if (valid) {\n dependentValue = dependent.val();\n valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;\n }\n }\n\n if (valid) {\n valid = !this.optional(element);\n } else {\n valid = true; // dependent was not valid, so don't even check\n }\n\n return valid;\n },\n $.mage.__('This is a required field.')\n ],\n 'required-number-if-specified': [\n function (value, element, params) {\n var valid = true,\n dependent = $(params),\n depeValue;\n\n if (dependent.length) {\n valid = this.check(dependent);\n\n if (valid) {\n depeValue = dependent[0].value;\n valid = !!(depeValue && depeValue.length);\n }\n }\n\n return valid ? !!value.length : true;\n },\n $.mage.__('Please enter a valid number.')\n ],\n 'datetime-validation': [\n function (value, element) {\n var isValid = true;\n\n if ($(element).val().length === 0) {\n isValid = false;\n $(element).addClass('mage-error');\n }\n\n return isValid;\n },\n $.mage.__('This is required field')\n ],\n 'required-text-swatch-entry': [\n tableSingleValidation,\n $.mage.__('Admin is a required field in each row.')\n ],\n 'required-visual-swatch-entry': [\n tableSingleValidation,\n $.mage.__('Admin is a required field in each row.')\n ],\n 'required-dropdown-attribute-entry': [\n tableSingleValidation,\n $.mage.__('Admin is a required field in each row.')\n ],\n 'validate-item-quantity': [\n function (value, element, params) {\n var validator = this,\n result = false,\n // obtain values for validation\n qty = $.mage.parseNumber(value),\n isMinAllowedValid = typeof params.minAllowed === 'undefined' ||\n qty >= $.mage.parseNumber(params.minAllowed),\n isMaxAllowedValid = typeof params.maxAllowed === 'undefined' ||\n qty <= $.mage.parseNumber(params.maxAllowed),\n isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' ||\n qty % $.mage.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 'password-not-equal-to-user-name': [\n function (value, element, params) {\n if (typeof params === 'string') {\n return value.toLowerCase() !== params.toLowerCase();\n }\n\n return true;\n },\n $.mage.__('The password can\\'t be the same as the email address. Create a new password and try again.')\n ]\n };\n\n $.each(rules, function (i, rule) {\n rule.unshift(i);\n $.validator.addMethod.apply($.validator, rule);\n });\n $.validator.addClassRules({\n 'required-option': {\n required: true\n },\n 'required-options-count': {\n required: true\n },\n 'validate-both-passwords': {\n 'validate-cpassword': true\n }\n });\n $.validator.messages = $.extend($.validator.messages, {\n required: $.mage.__('This is a required field.'),\n remote: $.mage.__('Please fix this field.'),\n email: $.mage.__('Please enter a valid email address.'),\n url: $.mage.__('Please enter a valid URL.'),\n date: $.mage.__('Please enter a valid date.'),\n dateISO: $.mage.__('Please enter a valid date (ISO).'),\n number: $.mage.__('Please enter a valid number.'),\n digits: $.mage.__('Please enter only digits.'),\n creditcard: $.mage.__('Please enter a valid credit card number.'),\n equalTo: $.mage.__('Please enter the same value again.'),\n maxlength: $.validator.format($.mage.__('Please enter no more than {0} characters.')),\n minlength: $.validator.format($.mage.__('Please enter at least {0} characters.')),\n rangelength: $.validator.format($.mage.__('Please enter a value between {0} and {1} characters long.')),\n range: $.validator.format($.mage.__('Please enter a value between {0} and {1}.')),\n max: $.validator.format($.mage.__('Please enter a value less than or equal to {0}.')),\n min: $.validator.format($.mage.__('Please enter a value greater than or equal to {0}.'))\n });\n\n if ($.metadata) {\n // Setting the type as html5 to enable data-validate attribute\n $.metadata.setType('html5');\n }\n\n showLabel = $.validator.prototype.showLabel;\n $.extend(true, $.validator.prototype, {\n /**\n * @param {*} element\n * @param {*} message\n */\n showLabel: function (element, message) {\n var label, elem;\n\n showLabel.call(this, element, message);\n\n // ARIA (adding aria-invalid & aria-describedby)\n label = this.errorsFor(element);\n elem = $(element);\n\n if (!label.attr('id')) {\n label.attr('id', this.idOrName(element) + '-error');\n }\n elem.attr('aria-invalid', 'true')\n .attr('aria-describedby', label.attr('id'));\n }\n });\n\n /**\n * Validate form field without instantiating validate plug-in.\n *\n * @param {Element|String} element - DOM element or selector\n * @return {Boolean} validation result\n */\n $.validator.validateElement = function (element) {\n var form, validator, valid, classes;\n\n element = $(element);\n form = element.get(0).form;\n validator = form ? $(form).data('validator') : null;\n\n if (validator) {\n return validator.element(element.get(0));\n }\n valid = true;\n classes = element.prop('class').split(' ');\n $.each(classes, $.proxy(function (i, className) {\n if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {\n valid = false;\n\n return valid;\n }\n }, this));\n\n return valid;\n };\n\n originValidateDelegate = $.fn.validateDelegate;\n\n /**\n * @return {*}\n */\n $.fn.validateDelegate = function () {\n if (!this[0].form) {\n return this;\n }\n\n return originValidateDelegate.apply(this, arguments);\n };\n\n /**\n * Validate single element.\n *\n * @param {Element} element\n * @param {Object} config\n * @returns {*}\n */\n $.validator.validateSingleElement = function (element, config) {\n var errors = {},\n valid = true,\n validateConfig = {\n errorElement: 'label',\n ignore: '.ignore-validate',\n hideError: false\n },\n form, validator, classes, elementValue;\n\n $.extend(validateConfig, config);\n element = $(element).not(validateConfig.ignore);\n\n if (!element.length) {\n return true;\n }\n\n form = element.get(0).form;\n validator = form ? $(form).data('validator') : null;\n\n if (validator) {\n return validator.element(element.get(0));\n }\n\n classes = element.prop('class').split(' ');\n validator = element.parent().data('validator') ||\n $.mage.validation(validateConfig, element.parent()).validate;\n\n element.removeClass(validator.settings.errorClass);\n validator.toHide = validator.toShow;\n validator.hideErrors();\n validator.toShow = validator.toHide = $([]);\n\n $.each(classes, $.proxy(function (i, className) {\n elementValue = element.val();\n\n if (element.is(':checkbox') || element.is(':radio')) {\n elementValue = element.is(':checked') || null;\n }\n\n if (this.methods[className] && !this.methods[className](elementValue, element.get(0))) {\n valid = false;\n errors[element.get(0).name] = this.messages[className];\n validator.invalid[element.get(0).name] = true;\n\n if (!validateConfig.hideError) {\n validator.showErrors(errors);\n }\n\n return valid;\n }\n }, this));\n\n return valid;\n };\n\n $.widget('mage.validation', {\n options: {\n meta: 'validate',\n onfocusout: false,\n onkeyup: false,\n onclick: false,\n ignoreTitle: true,\n errorClass: 'mage-error',\n errorElement: 'div',\n\n /**\n * @param {*} error\n * @param {*} element\n */\n errorPlacement: function (error, element) {\n var errorPlacement = element,\n fieldWrapper;\n\n // logic for date-picker error placement\n if (element.hasClass('_has-datepicker')) {\n errorPlacement = element.siblings('button');\n }\n // logic for field wrapper\n fieldWrapper = element.closest('.addon');\n\n if (fieldWrapper.length) {\n errorPlacement = fieldWrapper.after(error);\n }\n //logic for checkboxes/radio\n if (element.is(':checkbox') || element.is(':radio')) {\n errorPlacement = element.parents('.control').children().last();\n\n //fallback if group does not have .control parent\n if (!errorPlacement.length) {\n errorPlacement = element.siblings('label').last();\n }\n }\n //logic for control with tooltip\n if (element.siblings('.tooltip').length) {\n errorPlacement = element.siblings('.tooltip');\n }\n errorPlacement.after(error);\n }\n },\n\n /**\n * Check if form pass validation rules without submit.\n *\n * @return boolean\n */\n isValid: function () {\n return this.element.valid();\n },\n\n /**\n * Remove validation error messages\n */\n clearError: function () {\n if (arguments.length) {\n $.each(arguments, $.proxy(function (index, item) {\n this.validate.prepareElement(item);\n this.validate.hideErrors();\n }, this));\n } else {\n this.validate.resetForm();\n }\n },\n\n /**\n * Validation creation.\n *\n * @protected\n */\n _create: function () {\n this.validate = this.element.validate(this.options);\n\n // ARIA (adding aria-required attribute)\n this.element\n .find('.field.required')\n .find('.control')\n .find('input, select, textarea')\n .attr('aria-required', 'true');\n\n this._listenFormValidate();\n },\n\n /**\n * Validation listening.\n * @protected\n */\n _listenFormValidate: function () {\n $('form').on('invalid-form.validate', this.listenFormValidateHandler);\n },\n\n /**\n * Handle form validation. Focus on first invalid form field.\n *\n * @param {jQuery.Event} event\n * @param {Object} validation\n */\n listenFormValidateHandler: function (event, validation) {\n var firstActive = $(validation.errorList[0].element || []),\n lastActive = $(validation.findLastActive() ||\n validation.errorList.length && validation.errorList[0].element || []),\n parent, windowHeight, successList;\n\n if (lastActive.is(':hidden')) {\n parent = lastActive.parent();\n windowHeight = $(window).height();\n $('html, body').animate({\n scrollTop: parent.offset().top - windowHeight / 2\n });\n }\n\n // ARIA (removing aria attributes if success)\n successList = validation.successList;\n\n if (successList.length) {\n $.each(successList, function () {\n $(this)\n .removeAttr('aria-describedby')\n .removeAttr('aria-invalid');\n });\n }\n\n if (firstActive.length) {\n $('html, body').animate({\n scrollTop: firstActive.offset().top\n });\n firstActive.focus();\n }\n }\n });\n\n return $.mage.validation;\n}));\n","mage/tooltip.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n //Widget Wrapper\n $.widget('mage.tooltip', $.ui.tooltip, {});\n\n return $.mage.tooltip;\n});\n","mage/item-table.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'mage/template',\n 'jquery/ui'\n], function ($, mageTemplate) {\n 'use strict';\n\n $.widget('mage.itemTable', {\n options: {\n addBlock: '[data-template=\"add-block\"]',\n addBlockData: {},\n addEvent: 'click',\n addSelector: '[data-role=\"add\"]',\n itemsSelector: '[data-container=\"items\"]',\n keepLastRow: true\n },\n\n /**\n * This method adds a new instance of the block to the items.\n * @private\n */\n _add: function () {\n var hideShowDelete,\n deletableItems,\n addedBlock;\n\n // adding a new row, so increment the count to give each row a unique index\n this.rowIndex++;\n\n // make sure the block data has the rowIndex\n this.options.addBlockData.rowIndex = this.rowIndex;\n\n // render the form\n addedBlock = $(this.addBlockTmpl({\n data: this.options.addBlockData\n }));\n\n // add the row to the item block\n this.element.find(this.options.itemsSelector).append(addedBlock);\n\n // initialize all mage content\n addedBlock.trigger('contentUpdated');\n\n // determine all existing items in the collection\n deletableItems = this._getDeletableItems();\n\n // for the most part, show the delete mechanism, except in the case where there is only one it should not\n // be deleted\n hideShowDelete = 'showDelete';\n\n if (this.options.keepLastRow && deletableItems.length === 1) {\n hideShowDelete = 'hideDelete';\n }\n\n // loop through each control and perform that action on the deletable item\n $.each(deletableItems, function (index) {\n $(deletableItems[index]).trigger(hideShowDelete);\n });\n },\n\n /**\n * This method binds elements found in this widget.\n * @private\n */\n _bind: function () {\n var handlers = {};\n\n // since the first handler is dynamic, generate the object using array notation\n handlers[this.options.addEvent + ' ' + this.options.addSelector] = '_add';\n handlers.deleteItem = '_onDeleteItem';\n\n this._on(handlers);\n },\n\n /**\n * This method constructs a new widget.\n * @private\n */\n _create: function () {\n this._bind();\n\n this.addBlockTmpl = mageTemplate(this.options.addBlock);\n\n // nothing in the table, so indicate that\n this.rowIndex = -1;\n\n // make sure the block data is an object\n if (this.options.addBlockData == null || typeof this.options.addBlockData !== 'object') {\n // reset the block data to an empty object\n this.options.addBlockData = {};\n }\n\n // add the first row to the table\n this._add();\n },\n\n /**\n * This method returns the list of widgets associated with deletable items from the container (direct children\n * only).\n * @private\n */\n _getDeletableItems: function () {\n return this.element.find(this.options.itemsSelector + '> .deletableItem');\n },\n\n /**\n * This method removes the item associated with the message.\n * @private\n */\n _onDeleteItem: function (e) {\n var deletableItems;\n\n // parent elements don't need to see this event\n e.stopPropagation();\n\n // remove the deletable item\n $(e.target).remove();\n\n if (this.options.keepLastRow) {\n // determine if there is only one element remaining, in which case, disable the delete mechanism on it\n deletableItems = this._getDeletableItems();\n\n if (deletableItems.length === 1) {\n $(deletableItems[0]).trigger('hideDelete');\n }\n }\n }\n });\n\n return $.mage.itemTable;\n});\n","mage/terms.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery'\n], function ($) {\n 'use strict';\n\n /**\n * @param {*} args\n */\n $.fn.terms = function (args) {\n\n // default\n var defaults = {\n start: 0,\n wrapper: '',\n showAnchor: '',\n effects: 'slide'\n },\n options = $.extend(defaults, args);\n\n this.each(function () {\n var obj = $(this),\n wrapper = options.wrapper !== '' ? '> ' + options.wrapper : '',\n switches = $(wrapper + '> [data-section=\"title\"] > [data-toggle=\"switch\"]', obj),\n terms = $(wrapper + '> [data-section=\"content\"]', obj),\n t = switches.length,\n marginTop = $(switches[0]).closest('[data-section=\"title\"]').css('position') == 'absolute' ? 0 : null, //eslint-disable-line\n title,\n current,\n\n /**\n * @param {*} item\n */\n showItem = function (item) {\n if (item != current && !$(switches[item]).closest('[data-section=\"title\"]').hasClass('disabled')) { //eslint-disable-line\n $(switches).closest('[data-section=\"title\"]').removeClass('active');\n\n if (options.wrapper !== '') {\n $(switches).parent().parent().removeClass('active');\n }\n $(terms).removeClass('active');\n $(switches[item]).closest('[data-section=\"title\"]').addClass('active');\n\n if (options.wrapper !== '') {\n $(switches[current]).parent().parent().addClass('active');\n }\n $(terms[item]).addClass('active');\n current = item;\n } else if (\n // Check if this is accordion width as criteria for now\n (obj.attr('data-sections') == 'accordion' || $(switches[item]).closest('[data-section=\"title\"]').css('width') == obj.css('width')) && //eslint-disable-line\n item == current && !$(switches[item]).closest('[data-section=\"title\"]').hasClass('disabled') //eslint-disable-line\n ) {\n $(switches).closest('[data-section=\"title\"]').removeClass('active');\n\n if (options.wrapper !== '') {\n $(switches).parent().parent().removeClass('active');\n }\n $(terms).removeClass('active');\n current = -1;\n }\n },\n\n /**\n * Init.\n */\n init = function () {\n var linksList, i, classes, dataSection, itemHref, itemClass, fromUrl;\n\n if (t > 0) {\n if ($(switches[0]).closest('[data-section=\"title\"]').css('display') == 'table-cell') { //eslint-disable-line\n obj.addClass('adjusted');\n\n if (obj[0].tagName == 'DL') { //eslint-disable-line eqeqeq, max-depth\n linksList = $('<dd>');\n } else {\n linksList = $('<div>');\n }\n linksList.addClass('sections-nav');\n obj.prepend(linksList);\n\n for (i = 0; i < t; i++) { //eslint-disable-line max-depth\n title = $(switches[i]).html();\n classes = $(switches[i]).closest('[data-section=\"title\"]').attr('class');\n dataSection = $(switches[i]).closest('[data-section=\"title\"]').attr('data-section');\n itemHref = $(switches[i]).attr('href');\n itemClass = $(switches[i]).attr('class');\n $(switches[i]).parent('[data-section=\"title\"]').hide();\n switches[i] = $('<a/>', {\n href: itemHref,\n 'class': itemClass,\n html: title\n }).appendTo(linksList);\n $(switches[i]).wrap(\n '<strong class=\"' + classes + '\" data-section=\"' + dataSection + '\" />'\n );\n }\n }\n $(switches).each(function (ind, el) {\n $(el).click(function (event) {\n event.preventDefault();\n showItem(ind);\n });\n\n if (marginTop !== null) {\n $(el).closest('[data-section=\"title\"]').css({\n 'top': marginTop + 'px'\n });\n marginTop += $(el).closest('[data-section=\"title\"]').outerHeight(true);\n obj.css({\n 'min-height': marginTop + 'px'\n });\n }\n });\n\n fromUrl = false;\n\n if (window.location.hash.length > 0) {\n $(terms).each(function (ind, el) {\n if ('#info-' + $(el).attr('id') == window.location.hash) { //eslint-disable-line eqeqeq\n showItem(ind);\n $('html, body').animate({\n scrollTop: $(switches[ind]).offset().top\n }, 700);\n fromUrl = true;\n }\n });\n }\n\n if (fromUrl === false) {\n if (options.start % 1 === 0) { //eslint-disable-line max-depth\n current = options.start + 1;\n showItem(options.start);\n } else {\n $(terms).each(function (ind, el) {\n if ($(el).attr('id') == options.start) { //eslint-disable-line eqeqeq\n current = ind + 1;\n showItem(ind);\n $('html, body').animate({\n scrollTop: $(switches[ind]).offset().top\n }, 700);\n }\n });\n }\n }\n }\n };\n\n init();\n });\n };\n\n return function (data, el) {\n $(el).terms(data);\n };\n});\n","mage/dropdown.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui',\n 'mage/translate'\n], function ($) {\n 'use strict';\n\n var timer = null;\n\n /**\n * Dropdown Widget - this widget is a wrapper for the jQuery UI Dialog\n */\n $.widget('mage.dropdownDialog', $.ui.dialog, {\n options: {\n triggerEvent: 'click',\n triggerClass: null,\n parentClass: null,\n triggerTarget: null,\n defaultDialogClass: 'mage-dropdown-dialog',\n dialogContentClass: null,\n shadowHinter: null,\n closeOnMouseLeave: true,\n closeOnClickOutside: true,\n minHeight: null,\n minWidth: null,\n width: null,\n modal: false,\n timeout: null,\n autoOpen: false,\n createTitleBar: false,\n autoPosition: false,\n autoSize: false,\n draggable: false,\n resizable: false,\n bodyClass: '',\n buttons: [\n {\n 'class': 'action close',\n 'text': $.mage.__('Close'),\n\n /**\n * Click action.\n */\n 'click': function () {\n $(this).dropdownDialog('close');\n }\n }\n ]\n },\n\n /**\n * extend default functionality to bind the opener for dropdown\n * @private\n */\n _create: function () {\n var _self = this;\n\n this._super();\n this.uiDialog.addClass(this.options.defaultDialogClass);\n\n if (_self.options.triggerTarget) {\n $(_self.options.triggerTarget).on(_self.options.triggerEvent, function (event) {\n event.preventDefault();\n event.stopPropagation();\n\n if (!_self._isOpen) {\n $('.' + _self.options.defaultDialogClass + ' > .ui-dialog-content').dropdownDialog('close');\n _self.open();\n } else {\n _self.close(event);\n }\n });\n }\n\n if (_self.options.shadowHinter) {\n _self.hinter = $('<div class=\"' + _self.options.shadowHinter + '\"/>');\n _self.element.append(_self.hinter);\n }\n },\n\n /**\n * Extend default functionality to close the dropdown\n * with custom delay on mouse out and also to close when clicking outside\n */\n open: function () {\n var _self = this;\n\n this._super();\n\n if (_self.options.dialogContentClass) {\n _self.element.addClass(_self.options.dialogContentClass);\n }\n\n if (_self.options.closeOnMouseLeave) {\n\n this._mouseEnter(_self.uiDialog);\n this._mouseLeave(_self.uiDialog);\n\n if (_self.options.triggerTarget) {\n this._mouseLeave($(_self.options.triggerTarget));\n }\n }\n\n if (_self.options.closeOnClickOutside) {\n $('body').on('click.outsideDropdown', function (event) {\n if (_self._isOpen && !$(event.target).closest('.ui-dialog').length) {\n if (timer) {\n clearTimeout(timer);\n }\n _self.close(event);\n }\n });\n }\n // adding the class on the opener and parent element for dropdown\n if (_self.options.triggerClass) {\n $(_self.options.triggerTarget).addClass(_self.options.triggerClass);\n }\n\n if (_self.options.parentClass) {\n $(_self.options.appendTo).addClass(_self.options.parentClass);\n }\n\n if (_self.options.bodyClass) {\n $('body').addClass(_self.options.bodyClass);\n }\n\n if (_self.options.shadowHinter) {\n _self._setShadowHinterPosition();\n }\n },\n\n /**\n * extend default functionality to reset the timer and remove the active class for opener\n */\n close: function () {\n this._super();\n\n if (this.options.dialogContentClass) {\n this.element.removeClass(this.options.dialogContentClass);\n }\n\n if (this.options.triggerClass) {\n $(this.options.triggerTarget).removeClass(this.options.triggerClass);\n }\n\n if (this.options.parentClass) {\n $(this.options.appendTo).removeClass(this.options.parentClass);\n }\n\n if (this.options.bodyClass) {\n $('body').removeClass(this.options.bodyClass);\n }\n\n if (timer) {\n clearTimeout(timer);\n }\n\n if (this.options.triggerTarget) {\n $(this.options.triggerTarget).off('mouseleave');\n }\n this.uiDialog.off('mouseenter');\n this.uiDialog.off('mouseleave');\n $('body').off('click.outsideDropdown');\n },\n\n /**\n * _setShadowHinterPosition\n * @private\n */\n _setShadowHinterPosition: function () {\n var _self = this,\n offset;\n\n offset = _self.options.position.of.offset().left -\n _self.element.offset().left +\n _self.options.position.of.outerWidth() / 2;\n offset = isNaN(offset) ? 0 : Math.floor(offset);\n _self.hinter.css('left', offset);\n },\n\n /**\n * @private\n */\n _position: function () {\n if (this.options.autoPosition) {\n this._super();\n }\n },\n\n /**\n * @private\n */\n _createTitlebar: function () {\n if (this.options.createTitleBar) {\n this._super();\n } else {\n // the title bar close button is referenced\n // in _focusTabbable function, so to prevent errors it must be declared\n this.uiDialogTitlebarClose = $('<div>');\n }\n },\n\n /**\n * @private\n */\n _size: function () {\n if (this.options.autoSize) {\n this._super();\n }\n },\n\n /**\n * @param {Object} handler\n * @private\n */\n _mouseLeave: function (handler) {\n var _self = this;\n\n handler.on('mouseleave', function (event) {\n event.stopPropagation();\n\n if (_self._isOpen) {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(function (e) {\n _self.close(e);\n }, _self.options.timeout);\n }\n });\n },\n\n /**\n * @param {Object} handler\n * @private\n */\n _mouseEnter: function (handler) {\n handler.on('mouseenter', function (event) {\n event.stopPropagation();\n\n if (timer) {\n clearTimeout(timer);\n }\n });\n },\n\n /**\n * @param {String} key\n * @param {*} value\n * @private\n */\n _setOption: function (key, value) {\n this._super(key, value);\n\n if (key === 'triggerTarget') {\n this.options.triggerTarget = value;\n }\n }\n });\n\n return $.mage.dropdownDialog;\n});\n","mage/mage.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (root, factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/apply/main'\n ], factory);\n } else {\n factory(root.jQuery);\n }\n}(this, function ($, mage) {\n 'use strict';\n\n /**\n * Main namespace for Magento extensions\n * @type {Object}\n */\n $.mage = $.mage || {};\n\n /**\n * Plugin mage, initialize components on elements\n * @param {String} name - Components' path.\n * @param {Object} config - Components' config.\n * @returns {JQuery} Chainable.\n */\n $.fn.mage = function (name, config) {\n config = config || {};\n\n this.each(function (index, el) {\n mage.applyFor(el, config, name);\n });\n\n return this;\n };\n\n $.extend($.mage, {\n /**\n * Handle all components declared via data attribute\n * @return {Object} $.mage\n */\n init: function () {\n mage.apply();\n\n return this;\n },\n\n /**\n * Method handling redirects and page refresh\n * @param {String} url - redirect URL\n * @param {(undefined|String)} type - 'assign', 'reload', 'replace'\n * @param {(undefined|Number)} timeout - timeout in milliseconds before processing the redirect or reload\n * @param {(undefined|Boolean)} forced - true|false used for 'reload' only\n */\n redirect: function (url, type, timeout, forced) {\n var _redirect;\n\n forced = !!forced;\n timeout = timeout || 0;\n type = type || 'assign';\n\n /**\n * @private\n */\n _redirect = function () {\n window.location[type](type === 'reload' ? forced : url);\n };\n\n timeout ? setTimeout(_redirect, timeout) : _redirect();\n },\n\n /**\n * Checks if provided string is a valid selector.\n * @param {String} selector - Selector to check.\n * @returns {Boolean}\n */\n isValidSelector: function (selector) {\n try {\n document.querySelector(selector);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n });\n\n /**\n * Init components inside of dynamically updated elements\n */\n $('body').on('contentUpdated', function () {\n if (mage) {\n mage.apply();\n }\n });\n\n return $.mage;\n}));\n","mage/translate-inline.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (root, factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/template',\n 'jquery/ui',\n 'mage/translate'\n ], factory);\n } else {\n factory(root.jQuery, root.mageTemplate);\n }\n}(this, function ($, mageTemplate) {\n 'use strict';\n\n $.widget('mage.translateInline', $.ui.dialog, {\n options: {\n translateForm: {\n template: '#translate-form-template',\n data: {\n id: 'translate-inline-form',\n message: 'Please refresh the page to see your changes after submitting this form.'\n }\n },\n autoOpen: false,\n translateArea: null,\n modal: true,\n dialogClass: 'popup-window',\n width: '75%',\n title: $.mage.__('Translate'),\n height: 470,\n position: {\n my: 'left top',\n at: 'center top',\n of: 'body'\n },\n buttons: [{\n text: $.mage.__('Submit'),\n 'class': 'action-primary',\n\n /**\n * Click\n */\n click: function () {\n $(this).translateInline('submit');\n }\n },\n {\n text: $.mage.__('Close'),\n 'class': 'action-close',\n\n /**\n * Click.\n */\n click: function () {\n $(this).translateInline('close');\n }\n }],\n\n /**\n * Open.\n */\n open: function () {\n var topMargin;\n\n $(this).closest('.ui-dialog').addClass('ui-dialog-active');\n topMargin = jQuery(this).closest('.ui-dialog').children('.ui-dialog-titlebar').outerHeight() + 45;\n jQuery(this).closest('.ui-dialog').css('margin-top', topMargin);\n },\n\n /**\n * Close.\n */\n close: function () {\n $(this).closest('.ui-dialog').removeClass('ui-dialog-active');\n }\n },\n\n /**\n * Translate Inline creation\n * @protected\n */\n _create: function () {\n this.tmpl = mageTemplate(this.options.translateForm.template);\n (this.options.translateArea && $(this.options.translateArea).length ?\n $(this.options.translateArea) :\n this.element.closest('body'))\n .on('edit.editTrigger', $.proxy(this._onEdit, this));\n this._super();\n },\n\n /**\n * @param {*} templateData\n * @return {*|jQuery|HTMLElement}\n * @private\n */\n _prepareContent: function (templateData) {\n var data = $.extend({\n items: templateData,\n escape: $.mage.escapeHTML\n }, this.options.translateForm.data);\n\n this.data = data;\n\n return $(this.tmpl({\n data: data\n }));\n },\n\n /**\n * Render translation form and open dialog\n * @param {Object} e - object\n * @protected\n */\n _onEdit: function (e) {\n this.target = e.target;\n this.element.html(this._prepareContent($(e.target).data('translate')));\n this.open(e);\n },\n\n /**\n * Submit.\n */\n submit: function () {\n if (this.formIsSubmitted) {\n return;\n }\n this._formSubmit();\n },\n\n /**\n * Send ajax request on form submit\n * @protected\n */\n _formSubmit: function () {\n var parameters;\n\n this.formIsSubmitted = true;\n parameters = $.param({\n area: this.options.area\n }) + '&' + $('#' + this.options.translateForm.data.id).serialize();\n\n $.ajax({\n url: this.options.ajaxUrl,\n type: 'POST',\n data: parameters,\n loaderContext: this.element,\n showLoader: true\n }).complete($.proxy(this._formSubmitComplete, this));\n },\n\n /**\n * @param {Object} response\n * @private\n */\n _formSubmitComplete: function (response) {\n this.close();\n this.formIsSubmitted = false;\n this._updatePlaceholder(response.responseJSON[this.data.items[0].original]);\n },\n\n /**\n * @param {*} newValue\n * @private\n */\n _updatePlaceholder: function (newValue) {\n var target = jQuery(this.target);\n\n target.data('translate')[0].shown = newValue;\n target.data('translate')[0].translated = newValue;\n target.html(newValue);\n },\n\n /**\n * Destroy translateInline\n */\n destroy: function () {\n this.element.off('.editTrigger');\n this._super();\n }\n });\n // @TODO move the \"escapeHTML\" method into the file with global utility functions\n $.extend(true, $, {\n mage: {\n /**\n * @param {String} str\n * @return {Boolean}\n */\n escapeHTML: function (str) {\n return str ?\n jQuery('<div/>').text(str).html().replace(/\"/g, '"') :\n false;\n }\n }\n });\n\n return $.mage.translateInline;\n}));\n","mage/redirect-url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.redirectUrl', {\n options: {\n event: 'click',\n url: undefined\n },\n\n /**\n * This method binds elements found in this widget.\n * @private\n */\n _bind: function () {\n var handlers = {};\n\n handlers[this.options.event] = '_onEvent';\n this._on(handlers);\n },\n\n /**\n * This method constructs a new widget.\n * @private\n */\n _create: function () {\n this._bind();\n },\n\n /**\n * This method set the url for the redirect.\n * @private\n */\n _onEvent: function () {\n if (this.options.url) {\n location.href = this.options.url;\n } else {\n location.href = this.element.val();\n }\n }\n });\n\n return $.mage.redirectUrl;\n});\n","mage/dataPost.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/template',\n 'Magento_Ui/js/modal/confirm',\n 'jquery/ui'\n], function ($, mageTemplate, uiConfirm) {\n 'use strict';\n\n $.widget('mage.dataPost', {\n options: {\n formTemplate: '<form action=\"<%- data.action %>\" method=\"post\">' +\n '<% _.each(data.data, function(value, index) { %>' +\n '<input name=\"<%- index %>\" value=\"<%- value %>\">' +\n '<% }) %></form>',\n postTrigger: ['a[data-post]', 'button[data-post]', 'span[data-post]'],\n formKeyInputSelector: 'input[name=\"form_key\"]'\n },\n\n /** @inheritdoc */\n _create: function () {\n this._bind();\n },\n\n /** @inheritdoc */\n _bind: function () {\n var events = {};\n\n $.each(this.options.postTrigger, function (index, value) {\n events['click ' + value] = '_postDataAction';\n });\n\n this._on(events);\n },\n\n /**\n * Handler for click.\n *\n * @param {Object} e\n * @private\n */\n _postDataAction: function (e) {\n var params = $(e.currentTarget).data('post');\n\n e.preventDefault();\n this.postData(params);\n },\n\n /**\n * Data post action.\n *\n * @param {Object} params\n */\n postData: function (params) {\n var formKey = $(this.options.formKeyInputSelector).val(),\n $form, input;\n\n if (formKey) {\n params.data['form_key'] = formKey;\n }\n\n $form = $(mageTemplate(this.options.formTemplate, {\n data: params\n }));\n\n if (params.files) {\n $form[0].enctype = 'multipart/form-data';\n $.each(params.files, function (key, files) {\n if (files instanceof FileList) {\n input = document.createElement('input');\n input.type = 'file';\n input.name = key;\n input.files = files;\n $form[0].appendChild(input);\n }\n });\n }\n\n if (params.data.confirmation) {\n uiConfirm({\n content: params.data.confirmationMessage,\n actions: {\n /** @inheritdoc */\n confirm: function () {\n $form.appendTo('body').hide().submit();\n }\n }\n });\n } else {\n $form.appendTo('body').hide().submit();\n }\n }\n });\n\n $(document).dataPost();\n\n return $.mage.dataPost;\n});\n","mage/toggle.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n $.widget('mage.toggleAdvanced', {\n options: {\n baseToggleClass: 'active' // Class used to be toggled on clicked element\n },\n\n /**\n * Toggle creation\n * @private\n */\n _create: function () {\n this.beforeCreate();\n this._bindCore();\n this.afterCreate();\n },\n\n /**\n * Core bound events & setup\n * @protected\n */\n _bindCore: function () {\n var widget = this;\n\n this.element.on('click', $.proxy(function (e) {\n widget._onClick();\n e.preventDefault();\n }, this));\n },\n\n /**\n * Binding Click event\n *\n * @protected\n */\n _onClick: function () {\n this._prepareOptions();\n this._toggleSelectors();\n },\n\n /**\n * Method used to look for data attributes to override default options\n *\n * @protected\n */\n _prepareOptions: function () {\n this.options.baseToggleClass = this.element.data('base-toggle-class') ?\n this.element.data('base-toggle-class') : this.options.baseToggleClass;\n },\n\n /**\n * Method responsible for hiding and revealing specified DOM elements\n * Toggle the class on clicked element\n *\n * @protected\n */\n _toggleSelectors: function () {\n this.element.toggleClass(this.options.baseToggleClass);\n },\n\n /**\n * Method used to inject 3rd party functionality before create\n * @public\n */\n beforeCreate: function () {},\n\n /**\n * Method used to inject 3rd party functionality after create\n * @public\n */\n afterCreate: function () {}\n });\n\n // Extension for mage.toggle - Adding selectors support for other DOM elements we wish to toggle\n $.widget('mage.toggleAdvanced', $.mage.toggleAdvanced, {\n\n options: {\n selectorsToggleClass: 'hidden', // Class used to be toggled on selectors DOM elements\n toggleContainers: null\n },\n\n /**\n * Method responsible for hiding and revealing specified DOM elements\n * If data-toggle-selectors attribute is present - toggle will be done on these selectors\n * Otherwise we toggle the class on clicked element\n *\n * @protected\n * @override\n */\n _toggleSelectors: function () {\n this._super();\n\n if (this.options.toggleContainers) {\n $(this.options.toggleContainers).toggleClass(this.options.selectorsToggleClass);\n } else {\n this.element.toggleClass(this.options.baseToggleClass);\n }\n },\n\n /**\n * Method used to look for data attributes to override default options\n *\n * @protected\n * @override\n */\n _prepareOptions: function () {\n this.options.selectorsToggleClass = this.element.data('selectors-toggle-class') ?\n this.element.data('selectors-toggle-class') : this.options.selectorsToggleClass;\n this.options.toggleContainers = this.element.data('toggle-selectors') ?\n this.element.data('toggle-selectors') : this.options.toggleContainers;\n this._super();\n }\n });\n\n // Extension for mage.toggle - Adding label toggle\n $.widget('mage.toggleAdvanced', $.mage.toggleAdvanced, {\n\n options: {\n newLabel: null, // Text of the new label to be used on toggle\n curLabel: null, // Text of the old label to be used on toggle\n currentLabelElement: null // Current label container\n },\n\n /**\n * Binding Click event\n *\n * @protected\n * @override\n */\n _onClick: function () {\n this._super();\n this._toggleLabel();\n },\n\n /**\n * Method responsible for replacing clicked element labels\n * @protected\n */\n _toggleLabel: function () {\n var cachedLabel, currentLabelSelector;\n\n if (this.options.newLabel) {\n cachedLabel = this.options.newLabel;\n currentLabelSelector = this.options.currentLabelElement ?\n $(this.options.currentLabelElement) : this.element;\n\n this.element.data('toggle-label', this.options.curLabel);\n currentLabelSelector.html(this.options.newLabel);\n\n this.options.curLabel = this.options.newLabel;\n this.options.newLabel = cachedLabel;\n }\n },\n\n /**\n * Method used to look for data attributes to override default options\n *\n * @protected\n * @override\n */\n _prepareOptions: function () {\n this.options.newLabel = this.element.data('toggle-label') ?\n this.element.data('toggle-label') : this.options.newLabel;\n\n this.options.currentLabelElement = this.element.data('current-label-el') ?\n this.element.data('current-label-el') : this.options.currentLabelElement;\n\n if (!this.options.currentLabelElement) {\n this.options.currentLabelElement = this.element;\n }\n\n this.options.curLabel = $(this.options.currentLabelElement).html();\n\n this._super();\n }\n });\n\n return $.mage.toggleAdvanced;\n});\n","mage/trim-input.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 $.widget('mage.trimInput', {\n options: {\n cache: {}\n },\n\n /**\n * Widget initialization\n * @private\n */\n _create: function () {\n this.options.cache.input = $(this.element);\n this._bind();\n },\n\n /**\n * Event binding, will monitor change, keyup and paste events.\n * @private\n */\n _bind: function () {\n if (this.options.cache.input.length) {\n this._on(this.options.cache.input, {\n 'change': this._trimInput,\n 'keyup': this._trimInput,\n 'paste': this._trimInput\n });\n }\n },\n\n /**\n * Trim value\n * @private\n */\n _trimInput: function () {\n var input = this._getInputValue().trim();\n\n this.options.cache.input.val(input);\n },\n\n /**\n * Get input value\n * @returns {*}\n * @private\n */\n _getInputValue: function () {\n return this.options.cache.input.val();\n }\n });\n\n return $.mage.trimInput;\n});\n","mage/menu.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'matchMedia',\n 'jquery/ui',\n 'jquery/jquery.mobile.custom',\n 'mage/translate'\n], function ($, mediaCheck) {\n 'use strict';\n\n /**\n * Menu Widget - this widget is a wrapper for the jQuery UI Menu\n */\n $.widget('mage.menu', $.ui.menu, {\n options: {\n responsive: false,\n expanded: false,\n showDelay: 42,\n hideDelay: 300,\n delay: 0,\n mediaBreakpoint: '(max-width: 768px)'\n },\n\n /**\n * @private\n */\n _create: function () {\n var self = this;\n\n this.delay = this.options.delay;\n\n this._super();\n $(window).on('resize', function () {\n self.element.find('.submenu-reverse').removeClass('submenu-reverse');\n });\n },\n\n /**\n * @private\n */\n _init: function () {\n this._super();\n\n if (this.options.expanded === true) {\n this.isExpanded();\n }\n\n if (this.options.responsive === true) {\n mediaCheck({\n media: this.options.mediaBreakpoint,\n entry: $.proxy(function () {\n this._toggleMobileMode();\n }, this),\n exit: $.proxy(function () {\n this._toggleDesktopMode();\n }, this)\n });\n }\n\n this._assignControls()._listen();\n this._setActiveMenu();\n },\n\n /**\n * @return {Object}\n * @private\n */\n _assignControls: function () {\n this.controls = {\n toggleBtn: $('[data-action=\"toggle-nav\"]'),\n swipeArea: $('.nav-sections')\n };\n\n return this;\n },\n\n /**\n * @private\n */\n _listen: function () {\n var controls = this.controls,\n toggle = this.toggle;\n\n controls.toggleBtn.off('click');\n controls.toggleBtn.on('click', toggle.bind(this));\n controls.swipeArea.off('swipeleft');\n controls.swipeArea.on('swipeleft', toggle.bind(this));\n },\n\n /**\n * Toggle.\n */\n toggle: function () {\n var html = $('html');\n\n if (html.hasClass('nav-open')) {\n html.removeClass('nav-open');\n setTimeout(function () {\n html.removeClass('nav-before-open');\n }, this.options.hideDelay);\n } else {\n html.addClass('nav-before-open');\n setTimeout(function () {\n html.addClass('nav-open');\n }, this.options.showDelay);\n }\n },\n\n /**\n * Tries to figure out the active category for current page and add appropriate classes:\n * - 'active' class for active category\n * - 'has-active' class for all parents of active category\n *\n * First, checks whether current URL is URL of category page,\n * otherwise tries to retrieve category URL in case of current URL is product view page URL\n * which has category tree path in it.\n *\n * @return void\n * @private\n */\n _setActiveMenu: function () {\n var currentUrl = window.location.href.split('?')[0];\n\n if (!this._setActiveMenuForCategory(currentUrl)) {\n this._setActiveMenuForProduct(currentUrl);\n }\n },\n\n /**\n * Looks for category with provided URL and adds 'active' CSS class to it if it was not set before.\n * If menu item has parent categories, sets 'has-active' class to all af them.\n *\n * @param {String} url - possible category URL\n * @returns {Boolean} - true if active category was founded by provided URL, otherwise return false\n * @private\n */\n _setActiveMenuForCategory: function (url) {\n var activeCategoryLink = this.element.find('a[href=\"' + url + '\"]'),\n classes,\n classNav;\n\n if (!activeCategoryLink || !activeCategoryLink.hasClass('ui-corner-all')) {\n\n //category was not found by provided URL\n return false;\n } else if (!activeCategoryLink.parent().hasClass('active')) {\n activeCategoryLink.parent().addClass('active');\n classes = activeCategoryLink.parent().attr('class');\n classNav = classes.match(/(nav\\-)[0-9]+(\\-[0-9]+)+/gi);\n\n if (classNav) {\n this._setActiveParent(classNav[0]);\n }\n }\n\n return true;\n },\n\n /**\n * Sets 'has-active' CSS class to all parent categories which have part of provided class in childClassName\n *\n * @example\n * childClassName - 'nav-1-2-3'\n * CSS class 'has-active' will be added to categories have 'nav-1-2' and 'nav-1' classes\n *\n * @param {String} childClassName - Class name of active category <li> element\n * @return void\n * @private\n */\n _setActiveParent: function (childClassName) {\n var parentElement,\n parentClass = childClassName.substr(0, childClassName.lastIndexOf('-'));\n\n if (parentClass.lastIndexOf('-') !== -1) {\n parentElement = this.element.find('.' + parentClass);\n\n if (parentElement) {\n parentElement.addClass('has-active');\n }\n this._setActiveParent(parentClass);\n }\n },\n\n /**\n * Tries to retrieve category URL from current URL and mark this category as active\n * @see _setActiveMenuForCategory(url)\n *\n * @example\n * currentUrl - http://magento.com/category1/category12/product.html,\n * category URLs has extensions .phtml - http://magento.com/category1.phtml\n * method sets active category which has URL http://magento.com/category1/category12.phtml\n *\n * @param {String} currentUrl - current page URL without parameters\n * @return void\n * @private\n */\n _setActiveMenuForProduct: function (currentUrl) {\n var categoryUrlExtension,\n lastUrlSection,\n possibleCategoryUrl,\n //retrieve first category URL to know what extension is used for category URLs\n firstCategoryUrl = this.element.find('> li a').attr('href');\n\n if (firstCategoryUrl) {\n lastUrlSection = firstCategoryUrl.substr(firstCategoryUrl.lastIndexOf('/'));\n categoryUrlExtension = lastUrlSection.lastIndexOf('.') !== -1 ?\n lastUrlSection.substr(lastUrlSection.lastIndexOf('.')) : '';\n\n possibleCategoryUrl = currentUrl.substr(0, currentUrl.lastIndexOf('/')) + categoryUrlExtension;\n this._setActiveMenuForCategory(possibleCategoryUrl);\n }\n },\n\n /**\n * Add class for expanded option.\n */\n isExpanded: function () {\n var subMenus = this.element.find(this.options.menus),\n expandedMenus = subMenus.find(this.options.menus);\n\n expandedMenus.addClass('expanded');\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _activate: function (event) {\n window.location.href = this.active.find('> a').attr('href');\n this.collapseAll(event);\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _keydown: function (event) {\n var match, prev, character, skip, regex,\n preventDefault = true;\n\n /* eslint-disable max-depth */\n /**\n * @param {String} value\n */\n function escape(value) {\n return value.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, '\\\\$&');\n }\n\n if (this.active.closest(this.options.menus).attr('aria-expanded') != 'true') { //eslint-disable-line eqeqeq\n\n switch (event.keyCode) {\n case $.ui.keyCode.PAGE_UP:\n this.previousPage(event);\n break;\n\n case $.ui.keyCode.PAGE_DOWN:\n this.nextPage(event);\n break;\n\n case $.ui.keyCode.HOME:\n this._move('first', 'first', event);\n break;\n\n case $.ui.keyCode.END:\n this._move('last', 'last', event);\n break;\n\n case $.ui.keyCode.UP:\n this.previous(event);\n break;\n\n case $.ui.keyCode.DOWN:\n if (this.active && !this.active.is('.ui-state-disabled')) {\n this.expand(event);\n }\n break;\n\n case $.ui.keyCode.LEFT:\n this.previous(event);\n break;\n\n case $.ui.keyCode.RIGHT:\n this.next(event);\n break;\n\n case $.ui.keyCode.ENTER:\n case $.ui.keyCode.SPACE:\n this._activate(event);\n break;\n\n case $.ui.keyCode.ESCAPE:\n this.collapse(event);\n break;\n default:\n preventDefault = false;\n prev = this.previousFilter || '';\n character = String.fromCharCode(event.keyCode);\n skip = false;\n\n clearTimeout(this.filterTimer);\n\n if (character === prev) {\n skip = true;\n } else {\n character = prev + character;\n }\n\n regex = new RegExp('^' + escape(character), 'i');\n match = this.activeMenu.children('.ui-menu-item').filter(function () {\n return regex.test($(this).children('a').text());\n });\n match = skip && match.index(this.active.next()) !== -1 ?\n this.active.nextAll('.ui-menu-item') :\n match;\n\n // If no matches on the current filter, reset to the last character pressed\n // to move down the menu to the first item that starts with that character\n if (!match.length) {\n character = String.fromCharCode(event.keyCode);\n regex = new RegExp('^' + escape(character), 'i');\n match = this.activeMenu.children('.ui-menu-item').filter(function () {\n return regex.test($(this).children('a').text());\n });\n }\n\n if (match.length) {\n this.focus(event, match);\n\n if (match.length > 1) {\n this.previousFilter = character;\n this.filterTimer = this._delay(function () {\n delete this.previousFilter;\n }, 1000);\n } else {\n delete this.previousFilter;\n }\n } else {\n delete this.previousFilter;\n }\n }\n } else {\n switch (event.keyCode) {\n case $.ui.keyCode.DOWN:\n this.next(event);\n break;\n\n case $.ui.keyCode.UP:\n this.previous(event);\n break;\n\n case $.ui.keyCode.RIGHT:\n if (this.active && !this.active.is('.ui-state-disabled')) {\n this.expand(event);\n }\n break;\n\n case $.ui.keyCode.ENTER:\n case $.ui.keyCode.SPACE:\n this._activate(event);\n break;\n\n case $.ui.keyCode.LEFT:\n case $.ui.keyCode.ESCAPE:\n this.collapse(event);\n break;\n default:\n preventDefault = false;\n prev = this.previousFilter || '';\n character = String.fromCharCode(event.keyCode);\n skip = false;\n\n clearTimeout(this.filterTimer);\n\n if (character === prev) {\n skip = true;\n } else {\n character = prev + character;\n }\n\n regex = new RegExp('^' + escape(character), 'i');\n match = this.activeMenu.children('.ui-menu-item').filter(function () {\n return regex.test($(this).children('a').text());\n });\n match = skip && match.index(this.active.next()) !== -1 ?\n this.active.nextAll('.ui-menu-item') :\n match;\n\n // If no matches on the current filter, reset to the last character pressed\n // to move down the menu to the first item that starts with that character\n if (!match.length) {\n character = String.fromCharCode(event.keyCode);\n regex = new RegExp('^' + escape(character), 'i');\n match = this.activeMenu.children('.ui-menu-item').filter(function () {\n return regex.test($(this).children('a').text());\n });\n }\n\n if (match.length) {\n this.focus(event, match);\n\n if (match.length > 1) {\n this.previousFilter = character;\n this.filterTimer = this._delay(function () {\n delete this.previousFilter;\n }, 1000);\n } else {\n delete this.previousFilter;\n }\n } else {\n delete this.previousFilter;\n }\n }\n }\n\n /* eslint-enable max-depth */\n if (preventDefault) {\n event.preventDefault();\n }\n },\n\n /**\n * @private\n */\n _toggleMobileMode: function () {\n var subMenus;\n\n $(this.element).off('mouseenter mouseleave');\n this._on({\n\n /**\n * @param {jQuery.Event} event\n */\n 'click .ui-menu-item:has(a)': function (event) {\n var target;\n\n event.preventDefault();\n target = $(event.target).closest('.ui-menu-item');\n target.get(0).scrollIntoView();\n\n if (!target.hasClass('level-top') || !target.has('.ui-menu').length) {\n window.location.href = target.find('> a').attr('href');\n }\n },\n\n /**\n * @param {jQuery.Event} event\n */\n 'click .ui-menu-item:has(.ui-state-active)': function (event) {\n this.collapseAll(event, true);\n }\n });\n\n subMenus = this.element.find('.level-top');\n $.each(subMenus, $.proxy(function (index, item) {\n var category = $(item).find('> a span').not('.ui-menu-icon').text(),\n categoryUrl = $(item).find('> a').attr('href'),\n menu = $(item).find('> .ui-menu');\n\n this.categoryLink = $('<a>')\n .attr('href', categoryUrl)\n .text($.mage.__('All ') + category);\n\n this.categoryParent = $('<li>')\n .addClass('ui-menu-item all-category')\n .html(this.categoryLink);\n\n if (menu.find('.all-category').length === 0) {\n menu.prepend(this.categoryParent);\n }\n\n }, this));\n },\n\n /**\n * @private\n */\n _toggleDesktopMode: function () {\n var categoryParent, html;\n\n $(this.element).off('click mousedown mouseenter mouseleave');\n this._on({\n\n /**\n * Prevent focus from sticking to links inside menu after clicking\n * them (focus should always stay on UL during navigation).\n */\n 'mousedown .ui-menu-item > a': function (event) {\n event.preventDefault();\n },\n\n /**\n * Prevent focus from sticking to links inside menu after clicking\n * them (focus should always stay on UL during navigation).\n */\n 'click .ui-state-disabled > a': function (event) {\n event.preventDefault();\n },\n\n /**\n * @param {jQuer.Event} event\n */\n 'click .ui-menu-item:has(a)': function (event) {\n var target = $(event.target).closest('.ui-menu-item');\n\n if (!this.mouseHandled && target.not('.ui-state-disabled').length) {\n this.select(event);\n\n // Only set the mouseHandled flag if the event will bubble, see #9469.\n if (!event.isPropagationStopped()) {\n this.mouseHandled = true;\n }\n\n // Open submenu on click\n if (target.has('.ui-menu').length) {\n this.expand(event);\n } else if (!this.element.is(':focus') &&\n $(this.document[0].activeElement).closest('.ui-menu').length\n ) {\n // Redirect focus to the menu\n this.element.trigger('focus', [true]);\n\n // If the active item is on the top level, let it stay active.\n // Otherwise, blur the active item since it is no longer visible.\n if (this.active && this.active.parents('.ui-menu').length === 1) { //eslint-disable-line\n clearTimeout(this.timer);\n }\n }\n }\n },\n\n /**\n * @param {jQuery.Event} event\n */\n 'mouseenter .ui-menu-item': function (event) {\n var target = $(event.currentTarget),\n submenu = this.options.menus,\n ulElement,\n ulElementWidth,\n width,\n targetPageX,\n rightBound;\n\n if (target.has(submenu)) {\n ulElement = target.find(submenu);\n ulElementWidth = ulElement.outerWidth(true);\n width = target.outerWidth() * 2;\n targetPageX = target.offset().left;\n rightBound = $(window).width();\n\n if (ulElementWidth + width + targetPageX > rightBound) {\n ulElement.addClass('submenu-reverse');\n }\n\n if (targetPageX - ulElementWidth < 0) {\n ulElement.removeClass('submenu-reverse');\n }\n }\n\n // Remove ui-state-active class from siblings of the newly focused menu item\n // to avoid a jump caused by adjacent elements both having a class with a border\n target.siblings().children('.ui-state-active').removeClass('ui-state-active');\n this.focus(event, target);\n },\n\n /**\n * @param {jQuery.Event} event\n */\n 'mouseleave': function (event) {\n this.collapseAll(event, true);\n },\n\n /**\n * Mouse leave.\n */\n 'mouseleave .ui-menu': 'collapseAll'\n });\n\n categoryParent = this.element.find('.all-category');\n html = $('html');\n\n categoryParent.remove();\n\n if (html.hasClass('nav-open')) {\n html.removeClass('nav-open');\n setTimeout(function () {\n html.removeClass('nav-before-open');\n }, this.options.hideDelay);\n }\n },\n\n /**\n * @param {*} handler\n * @param {Number} delay\n * @return {Number}\n * @private\n */\n _delay: function (handler, delay) {\n var instance = this,\n\n /**\n * @return {*}\n */\n handlerProxy = function () {\n return (typeof handler === 'string' ? instance[handler] : handler).apply(instance, arguments);\n };\n\n return setTimeout(handlerProxy, delay || 0);\n },\n\n /**\n * @param {jQuery.Event} event\n */\n expand: function (event) {\n var newItem = this.active &&\n this.active\n .children('.ui-menu')\n .children('.ui-menu-item')\n .first();\n\n if (newItem && newItem.length) {\n if (newItem.closest('.ui-menu').is(':visible') &&\n newItem.closest('.ui-menu').has('.all-categories')\n ) {\n return;\n }\n\n // remove the active state class from the siblings\n this.active.siblings().children('.ui-state-active').removeClass('ui-state-active');\n\n this._open(newItem.parent());\n\n // Delay so Firefox will not hide activedescendant change in expanding submenu from AT\n this._delay(function () {\n this.focus(event, newItem);\n });\n }\n },\n\n /**\n * @param {jQuery.Event} event\n */\n select: function (event) {\n var ui;\n\n this.active = this.active || $(event.target).closest('.ui-menu-item');\n\n if (this.active.is('.all-category')) {\n this.active = $(event.target).closest('.ui-menu-item');\n }\n ui = {\n item: this.active\n };\n\n if (!this.active.has('.ui-menu').length) {\n this.collapseAll(event, true);\n }\n this._trigger('select', event, ui);\n }\n });\n\n $.widget('mage.navigation', $.mage.menu, {\n options: {\n responsiveAction: 'wrap', //option for responsive handling\n maxItems: null, //option to set max number of menu items\n container: '#menu', //container to check against navigation length\n moreText: $.mage.__('more'),\n breakpoint: 768\n },\n\n /**\n * @private\n */\n _init: function () {\n var that, responsive;\n\n this._super();\n\n that = this;\n responsive = this.options.responsiveAction;\n\n this.element\n .addClass('ui-menu-responsive')\n .attr('responsive', 'main');\n\n this.setupMoreMenu();\n this.setMaxItems();\n\n //check responsive option\n if (responsive == 'onResize') { //eslint-disable-line eqeqeq\n $(window).on('resize', function () {\n if ($(window).width() > that.options.breakpoint) {\n that._responsive();\n $('[responsive=more]').show();\n } else {\n that.element.children().show();\n $('[responsive=more]').hide();\n }\n });\n } else if (responsive == 'onReload') { //eslint-disable-line eqeqeq\n this._responsive();\n }\n },\n\n /**\n * Setup more menu.\n */\n setupMoreMenu: function () {\n var moreListItems = this.element.children().clone(),\n moreLink = $('<a>' + this.options.moreText + '</a>');\n\n moreListItems.hide();\n\n moreLink.attr('href', '#');\n\n this.moreItemsList = $('<ul>')\n .append(moreListItems);\n\n this.moreListContainer = $('<li>')\n .append(moreLink)\n .append(this.moreItemsList);\n\n this.responsiveMenu = $('<ul>')\n .addClass('ui-menu-more')\n .attr('responsive', 'more')\n .append(this.moreListContainer)\n .menu({\n position: {\n my: 'right top',\n at: 'right bottom'\n }\n })\n .insertAfter(this.element);\n },\n\n /**\n * @private\n */\n _responsive: function () {\n var container = $(this.options.container),\n containerSize = container.width(),\n width = 0,\n items = this.element.children('li'),\n more = $('.ui-menu-more > li > ul > li a');\n\n items = items.map(function () {\n var item = {};\n\n item.item = $(this);\n item.itemSize = $(this).outerWidth();\n\n return item;\n });\n\n $.each(items, function (index) {\n var itemText = items[index].item\n .find('a:first')\n .text();\n\n width += parseInt(items[index].itemSize, null); //eslint-disable-line radix\n\n if (width < containerSize) {\n items[index].item.show();\n\n more.each(function () {\n var text = $(this).text();\n\n if (text === itemText) {\n $(this).parent().hide();\n }\n });\n } else if (width > containerSize) {\n items[index].item.hide();\n\n more.each(function () {\n var text = $(this).text();\n\n if (text === itemText) {\n $(this).parent().show();\n }\n });\n }\n });\n },\n\n /**\n * Set max items.\n */\n setMaxItems: function () {\n var items = this.element.children('li'),\n itemsCount = items.length,\n maxItems = this.options.maxItems,\n overflow = itemsCount - maxItems,\n overflowItems = items.slice(overflow);\n\n overflowItems.hide();\n\n overflowItems.each(function () {\n var itemText = $(this).find('a:first').text();\n\n $(this).hide();\n\n $('.ui-menu-more > li > ul > li a').each(function () {\n var text = $(this).text();\n\n if (text === itemText) {\n $(this).parent().show();\n }\n });\n });\n }\n });\n\n return {\n menu: $.mage.menu,\n navigation: $.mage.navigation\n };\n});\n","mage/calendar.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/*eslint max-depth: 0*/\n(function (factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'jquery/ui',\n 'jquery/jquery-ui-timepicker-addon'\n ], factory);\n } else {\n factory(window.jQuery);\n }\n}(function ($) {\n 'use strict';\n\n var calendarBasePrototype,\n datepickerPrototype = $.datepicker.constructor.prototype;\n\n $.datepicker.markerClassName = '_has-datepicker';\n\n /**\n * Extend JQuery date picker prototype with store local time methods\n */\n $.extend(datepickerPrototype, {\n /**\n * Get date/time according to store settings.\n * We use serverTimezoneOffset (in seconds) instead of serverTimezoneSeconds\n * in order to have ability to know actual store time even if page hadn't been reloaded\n * @returns {Date}\n */\n _getTimezoneDate: function (options) {\n // local time in ms\n var ms = Date.now();\n\n options = options || $.calendarConfig || {};\n\n // Adjust milliseconds according to store timezone offset,\n // mind the GMT zero offset\n if (typeof options.serverTimezoneOffset !== 'undefined') {\n // Make UTC time and add store timezone offset in seconds\n ms += new Date().getTimezoneOffset() * 60 * 1000 + options.serverTimezoneOffset * 1000;\n } else if (typeof options.serverTimezoneSeconds !== 'undefined') {\n //Set milliseconds according to client local timezone offset\n ms = (options.serverTimezoneSeconds + new Date().getTimezoneOffset() * 60) * 1000;\n }\n\n return new Date(ms);\n },\n\n /**\n * Set date/time according to store settings.\n * @param {String|Object} target - the target input field or division or span\n */\n _setTimezoneDateDatepicker: function (target) {\n this._setDateDatepicker(target, this._getTimezoneDate());\n }\n });\n\n /**\n * Widget calendar\n */\n $.widget('mage.calendar', {\n options: {\n autoComplete: true\n },\n\n /**\n * Merge global options with options passed to widget invoke\n * @protected\n */\n _create: function () {\n this._enableAMPM();\n this.options = $.extend(\n {},\n $.calendarConfig ? $.calendarConfig : {},\n this.options.showsTime ? {\n showTime: true,\n showHour: true,\n showMinute: true\n } : {},\n this.options\n );\n this._initPicker(this.element);\n this._overwriteGenerateHtml();\n },\n\n /**\n * Get picker name\n * @protected\n */\n _picker: function () {\n return this.options.showsTime ? 'datetimepicker' : 'datepicker';\n },\n\n /**\n * Fix for Timepicker - Set ampm option for Timepicker if timeformat contains string 'tt'\n * @protected\n */\n _enableAMPM: function () {\n if (this.options.timeFormat && this.options.timeFormat.indexOf('tt') >= 0) {\n this.options.ampm = true;\n }\n },\n\n /**\n * Wrapper for overwrite jQuery UI datepicker function.\n */\n _overwriteGenerateHtml: function () {\n /**\n * Overwrite jQuery UI datepicker function.\n * Reason: magento date could be set before calendar show\n * but local date will be styled as current in original _generateHTML\n *\n * @param {Object} inst - instance datepicker.\n * @return {String} html template\n */\n $.datepicker.constructor.prototype._generateHTML = function (inst) {\n var today = this._getTimezoneDate(),\n isRTL = this._get(inst, 'isRTL'),\n showButtonPanel = this._get(inst, 'showButtonPanel'),\n hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext'),\n navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat'),\n numMonths = this._getNumberOfMonths(inst),\n showCurrentAtPos = this._get(inst, 'showCurrentAtPos'),\n stepMonths = this._get(inst, 'stepMonths'),\n isMultiMonth = parseInt(numMonths[0], 10) !== 1 || parseInt(numMonths[1], 10) !== 1,\n currentDate = this._daylightSavingAdjust(!inst.currentDay ? new Date(9999, 9, 9) :\n new Date(inst.currentYear, inst.currentMonth, inst.currentDay)),\n minDate = this._getMinMaxDate(inst, 'min'),\n maxDate = this._getMinMaxDate(inst, 'max'),\n drawMonth = inst.drawMonth - showCurrentAtPos,\n drawYear = inst.drawYear,\n maxDraw,\n prevText = this._get(inst, 'prevText'),\n prev,\n nextText = this._get(inst, 'nextText'),\n next,\n currentText = this._get(inst, 'currentText'),\n gotoDate,\n controls,\n buttonPanel,\n firstDay,\n showWeek = this._get(inst, 'showWeek'),\n dayNames = this._get(inst, 'dayNames'),\n dayNamesMin = this._get(inst, 'dayNamesMin'),\n monthNames = this._get(inst, 'monthNames'),\n monthNamesShort = this._get(inst, 'monthNamesShort'),\n beforeShowDay = this._get(inst, 'beforeShowDay'),\n showOtherMonths = this._get(inst, 'showOtherMonths'),\n selectOtherMonths = this._get(inst, 'selectOtherMonths'),\n defaultDate = this._getDefaultDate(inst),\n html = '',\n row = 0,\n col = 0,\n selectedDate,\n cornerClass = ' ui-corner-all',\n group = '',\n calender = '',\n dow = 0,\n thead,\n day,\n daysInMonth,\n leadDays,\n curRows,\n numRows,\n printDate,\n dRow = 0,\n tbody,\n daySettings,\n otherMonth,\n unselectable;\n\n if (drawMonth < 0) {\n drawMonth += 12;\n drawYear--;\n }\n\n if (maxDate) {\n maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),\n maxDate.getMonth() - numMonths[0] * numMonths[1] + 1, maxDate.getDate()));\n maxDraw = minDate && maxDraw < minDate ? minDate : maxDraw;\n\n while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {\n drawMonth--;\n\n if (drawMonth < 0) {\n drawMonth = 11;\n drawYear--;\n\n }\n }\n }\n inst.drawMonth = drawMonth;\n inst.drawYear = drawYear;\n prevText = !navigationAsDateFormat ? prevText : this.formatDate(prevText,\n this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),\n this._getFormatConfig(inst));\n prev = this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?\n '<a class=\"ui-datepicker-prev ui-corner-all\" data-handler=\"prev\" data-event=\"click\"' +\n ' title=\"' + prevText + '\">' +\n '<span class=\"ui-icon ui-icon-circle-triangle-' + (isRTL ? 'e' : 'w') + '\">' +\n '' + prevText + '</span></a>'\n : hideIfNoPrevNext ? ''\n : '<a class=\"ui-datepicker-prev ui-corner-all ui-state-disabled\" title=\"' +\n '' + prevText + '\"><span class=\"ui-icon ui-icon-circle-triangle-' +\n '' + (isRTL ? 'e' : 'w') + '\">' + prevText + '</span></a>';\n nextText = !navigationAsDateFormat ?\n nextText\n : this.formatDate(nextText,\n this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),\n this._getFormatConfig(inst));\n next = this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?\n '<a class=\"ui-datepicker-next ui-corner-all\" data-handler=\"next\" data-event=\"click\"' +\n 'title=\"' + nextText + '\"><span class=\"ui-icon ui-icon-circle-triangle-' +\n '' + (isRTL ? 'w' : 'e') + '\">' + nextText + '</span></a>'\n : hideIfNoPrevNext ? ''\n : '<a class=\"ui-datepicker-next ui-corner-all ui-state-disabled\" title=\"' + nextText + '\">' +\n '<span class=\"ui-icon ui-icon-circle-triangle-' + (isRTL ? 'w' : 'e') + '\">' + nextText +\n '</span></a>';\n gotoDate = this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today;\n currentText = !navigationAsDateFormat ? currentText :\n this.formatDate(currentText, gotoDate, this._getFormatConfig(inst));\n controls = !inst.inline ?\n '<button type=\"button\" class=\"ui-datepicker-close ui-state-default ui-priority-primary ' +\n 'ui-corner-all\" data-handler=\"hide\" data-event=\"click\">' +\n this._get(inst, 'closeText') + '</button>'\n : '';\n buttonPanel = showButtonPanel ?\n '<div class=\"ui-datepicker-buttonpane ui-widget-content\">' + (isRTL ? controls : '') +\n (this._isInRange(inst, gotoDate) ? '<button type=\"button\" class=\"ui-datepicker-current ' +\n 'ui-state-default ui-priority-secondary ui-corner-all\" data-handler=\"today\" data-event=\"click\"' +\n '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';\n firstDay = parseInt(this._get(inst, 'firstDay'), 10);\n firstDay = isNaN(firstDay) ? 0 : firstDay;\n\n for (row = 0; row < numMonths[0]; row++) {\n this.maxRows = 4;\n\n for (col = 0; col < numMonths[1]; col++) {\n selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));\n\n calender = '';\n\n if (isMultiMonth) {\n calender += '<div class=\"ui-datepicker-group';\n\n if (numMonths[1] > 1) {\n switch (col) {\n case 0: calender += ' ui-datepicker-group-first';\n cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left');\n break;\n\n case numMonths[1] - 1: calender += ' ui-datepicker-group-last';\n cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right');\n break;\n\n default: calender += ' ui-datepicker-group-middle'; cornerClass = '';\n }\n }\n calender += '\">';\n }\n calender += '<div class=\"ui-datepicker-header ' +\n 'ui-widget-header ui-helper-clearfix' + cornerClass + '\">' +\n (/all|left/.test(cornerClass) && parseInt(row, 10) === 0 ? isRTL ? next : prev : '') +\n (/all|right/.test(cornerClass) && parseInt(row, 10) === 0 ? isRTL ? prev : next : '') +\n this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,\n row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers\n '</div><table class=\"ui-datepicker-calendar\"><thead>' +\n '<tr>';\n thead = showWeek ?\n '<th class=\"ui-datepicker-week-col\">' + this._get(inst, 'weekHeader') + '</th>' : '';\n\n for (dow = 0; dow < 7; dow++) { // days of the week\n day = (dow + firstDay) % 7;\n thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ?\n ' class=\"ui-datepicker-week-end\"' : '') + '>' +\n '<span title=\"' + dayNames[day] + '\">' + dayNamesMin[day] + '</span></th>';\n }\n calender += thead + '</tr></thead><tbody>';\n daysInMonth = this._getDaysInMonth(drawYear, drawMonth);\n\n if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {\n inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);\n }\n leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;\n curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate\n numRows = isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows;\n this.maxRows = numRows;\n printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));\n\n for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows\n calender += '<tr>';\n tbody = !showWeek ? '' : '<td class=\"ui-datepicker-week-col\">' +\n this._get(inst, 'calculateWeek')(printDate) + '</td>';\n\n for (dow = 0; dow < 7; dow++) { // create date picker days\n daySettings = beforeShowDay ?\n beforeShowDay.apply(inst.input ? inst.input[0] : null, [printDate]) : [true, ''];\n otherMonth = printDate.getMonth() !== drawMonth;\n unselectable = otherMonth && !selectOtherMonths || !daySettings[0] ||\n minDate && printDate < minDate || maxDate && printDate > maxDate;\n tbody += '<td class=\"' +\n ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends\n (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months\n (printDate.getTime() === selectedDate.getTime() &&\n drawMonth === inst.selectedMonth && inst._keyEvent || // user pressed key\n defaultDate.getTime() === printDate.getTime() &&\n defaultDate.getTime() === selectedDate.getTime() ?\n // or defaultDate is current printedDate and defaultDate is selectedDate\n ' ' + this._dayOverClass : '') + // highlight selected day\n (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled' : '') +\n (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates\n (printDate.getTime() === currentDate.getTime() ? ' ' + this._currentClass : '') +\n (printDate.getDate() === today.getDate() && printDate.getMonth() === today.getMonth() &&\n printDate.getYear() === today.getYear() ? ' ui-datepicker-today' : '')) + '\"' +\n ((!otherMonth || showOtherMonths) && daySettings[2] ?\n ' title=\"' + daySettings[2] + '\"' : '') + // cell title\n (unselectable ? '' : ' data-handler=\"selectDay\" data-event=\"click\" data-month=\"' +\n '' + printDate.getMonth() + '\" data-year=\"' + printDate.getFullYear() + '\"') + '>' +\n (otherMonth && !showOtherMonths ? ' ' : // display for other months\n unselectable ? '<span class=\"ui-state-default\">' + printDate.getDate() + '</span>'\n : '<a class=\"ui-state-default' +\n (printDate.getTime() === today.getTime() ? ' ' : '') +\n (printDate.getTime() === currentDate.getTime() ? ' ui-state-active' : '') +\n (otherMonth ? ' ui-priority-secondary' : '') +\n '\" href=\"#\">' + printDate.getDate() + '</a>') + '</td>';\n printDate.setDate(printDate.getDate() + 1);\n printDate = this._daylightSavingAdjust(printDate);\n }\n calender += tbody + '</tr>';\n }\n drawMonth++;\n\n if (drawMonth > 11) {\n drawMonth = 0;\n drawYear++;\n }\n calender += '</tbody></table>' + (isMultiMonth ? '</div>' +\n (numMonths[0] > 0 && col === numMonths[1] - 1 ? '<div class=\"ui-datepicker-row-break\"></div>'\n : '') : '');\n group += calender;\n }\n html += group;\n }\n html += buttonPanel + ($.ui.ie6 && !inst.inline ?\n '<iframe src=\"javascript:false;\" class=\"ui-datepicker-cover\" frameborder=\"0\"></iframe>' : '');\n inst._keyEvent = false;\n\n return html;\n };\n },\n\n /**\n * Set current date if the date is not set\n * @protected\n * @param {Object} element\n */\n _setCurrentDate: function (element) {\n if (!element.val()) {\n element[this._picker()]('setTimezoneDate').val('');\n }\n },\n\n /**\n * Init Datetimepicker\n * @protected\n * @param {Object} element\n */\n _initPicker: function (element) {\n var picker = element[this._picker()](this.options),\n pickerButtonText = picker.next('.ui-datepicker-trigger')\n .find('img')\n .attr('title');\n\n picker.next('.ui-datepicker-trigger')\n .addClass('v-middle')\n .text('') // Remove jQuery UI datepicker generated image\n .append('<span>' + pickerButtonText + '</span>');\n\n $(element).attr('autocomplete', this.options.autoComplete ? 'on' : 'off');\n\n this._setCurrentDate(element);\n },\n\n /**\n * destroy instance of datetimepicker\n */\n _destroy: function () {\n this.element[this._picker()]('destroy');\n this._super();\n },\n\n /**\n * Method is kept for backward compatibility and unit-tests acceptance\n * see \\mage\\calendar\\calendar-test.js\n * @return {Object} date\n */\n getTimezoneDate: function () {\n return datepickerPrototype._getTimezoneDate.call(this, this.options);\n }\n });\n\n calendarBasePrototype = $.mage.calendar.prototype;\n\n /**\n * Extension for Calendar - date and time format convert functionality\n * @var {Object}\n */\n $.widget('mage.calendar', $.extend({}, calendarBasePrototype,\n /** @lends {$.mage.calendar.prototype} */ {\n /**\n * key - backend format, value - jquery format\n * @type {Object}\n * @private\n */\n dateTimeFormat: {\n date: {\n 'EEEE': 'DD',\n 'EEE': 'D',\n 'EE': 'D',\n 'E': 'D',\n 'D': 'o',\n 'MMMM': 'MM',\n 'MMM': 'M',\n 'MM': 'mm',\n 'M': 'mm',\n 'yyyy': 'yy',\n 'y': 'yy',\n 'Y': 'yy',\n 'yy': 'yy' // Always long year format on frontend\n },\n time: {\n 'a': 'TT'\n }\n },\n\n /**\n * Add Date and Time converting to _create method\n * @protected\n */\n _create: function () {\n if (this.options.dateFormat) {\n this.options.dateFormat = this._convertFormat(this.options.dateFormat, 'date');\n }\n\n if (this.options.timeFormat) {\n this.options.timeFormat = this._convertFormat(this.options.timeFormat, 'time');\n }\n calendarBasePrototype._create.apply(this, arguments);\n },\n\n /**\n * Converting date or time format\n * @protected\n * @param {String} format\n * @param {String} type\n * @return {String}\n */\n _convertFormat: function (format, type) {\n var symbols = format.match(/([a-z]+)/ig),\n separators = format.match(/([^a-z]+)/ig),\n self = this,\n convertedFormat = '';\n\n if (symbols) {\n $.each(symbols, function (key, val) {\n convertedFormat +=\n (self.dateTimeFormat[type][val] || val) +\n (separators[key] || '');\n });\n }\n\n return convertedFormat;\n }\n })\n );\n\n /**\n * Widget dateRange\n * @extends $.mage.calendar\n */\n $.widget('mage.dateRange', $.mage.calendar, {\n\n /**\n * creates two instances of datetimepicker for date range selection\n * @protected\n */\n _initPicker: function () {\n var from,\n to;\n\n if (this.options.from && this.options.to) {\n from = this.element.find('#' + this.options.from.id);\n to = this.element.find('#' + this.options.to.id);\n this.options.onSelect = $.proxy(function (selectedDate) {\n to[this._picker()]('option', 'minDate', selectedDate);\n }, this);\n $.mage.calendar.prototype._initPicker.call(this, from);\n from.on('change', $.proxy(function () {\n to[this._picker()]('option', 'minDate', from[this._picker()]('getDate'));\n }, this));\n this.options.onSelect = $.proxy(function (selectedDate) {\n from[this._picker()]('option', 'maxDate', selectedDate);\n }, this);\n $.mage.calendar.prototype._initPicker.call(this, to);\n to.on('change', $.proxy(function () {\n from[this._picker()]('option', 'maxDate', to[this._picker()]('getDate'));\n }, this));\n }\n },\n\n /**\n * destroy two instances of datetimepicker\n */\n _destroy: function () {\n if (this.options.from) {\n this.element.find('#' + this.options.from.id)[this._picker()]('destroy');\n }\n\n if (this.options.to) {\n this.element.find('#' + this.options.to.id)[this._picker()]('destroy');\n }\n this._super();\n }\n });\n\n // Overrides the \"today\" button functionality to select today's date when clicked.\n $.datepicker._gotoTodayOriginal = $.datepicker._gotoToday;\n\n /**\n * overwrite jQuery UI _showDatepicker function for proper HTML generation conditions.\n *\n */\n $.datepicker._showDatepickerOriginal = $.datepicker._showDatepicker;\n\n /**\n * Triggers original method showDataPicker for rendering calendar\n * @param {HTMLObject} input\n * @private\n */\n $.datepicker._showDatepicker = function (input) {\n if (!input.disabled) {\n $.datepicker._showDatepickerOriginal.call(this, input);\n }\n };\n\n /**\n * _gotoToday\n * @param {Object} el\n */\n $.datepicker._gotoToday = function (el) {\n //Set date/time according to timezone offset\n $(el).datepicker('setTimezoneDate')\n // To ensure that user can re-select date field without clicking outside it first.\n .blur().trigger('change');\n };\n\n return {\n dateRange: $.mage.dateRange,\n calendar: $.mage.calendar\n };\n}));\n","mage/common.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'domReady!'\n], function ($) {\n 'use strict';\n\n /* Form with auto submit feature */\n $('form[data-auto-submit=\"true\"]').submit();\n\n //Add form keys.\n $(document).on(\n 'submit',\n 'form',\n function (e) {\n var formKeyElement,\n form = $(e.target),\n formKey = $('input[name=\"form_key\"]').val();\n\n if (formKey && !form.find('input[name=\"form_key\"]').length && form[0].method !== 'get') {\n formKeyElement = document.createElement('input');\n formKeyElement.setAttribute('type', 'hidden');\n formKeyElement.setAttribute('name', 'form_key');\n formKeyElement.setAttribute('value', formKey);\n formKeyElement.setAttribute('auto-added-form-key', '1');\n form.get(0).appendChild(formKeyElement);\n }\n }\n );\n});\n","mage/fieldset-controls.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n /**\n * This widget will allow a control with the fieldsetResetControl widget attached to reset a set of input fields.\n * The input fields to reset are defined by the inputSelector selector. The widget will store a clone of the fields\n * on create, and on trigger of fieldsetReset event it resets the defined fields. The event is triggered by the\n * reset control widget.\n *\n * For inputs of type file, the whole dom element is replaced as changing the value is a security violation\n * For inputs of type checkbox or radio, the checked attribute is added or removed as appropriate\n * For all others the jquery .val method is used to update to value to the original.\n */\n $.widget('mage.fieldsetControls', {\n original: undefined,\n options: {\n inputSelector: '[data-reset=\"true\"]'\n },\n\n /**\n * @private\n */\n _create: function () {\n this.original = this.element.find(this.options.inputSelector).clone(true);\n this._bind();\n },\n\n /**\n * @private\n */\n _bind: function () {\n this._on({\n 'fieldsetReset': '_onReset'\n });\n },\n\n /**\n * @param {jQuery.Event} e\n * @private\n */\n _onReset: function (e) {\n var items;\n\n e.stopPropagation();\n // find all the ones we have to remove\n items = this.element.find(this.options.inputSelector);\n // loop over replacing each one.\n items.each($.proxy(function (index, item) {\n if ($(item).attr('type') == 'file') { //eslint-disable-line eqeqeq\n // Replace the current one we found with a clone of the original saved earlier\n $(item).replaceWith($(this.original[index]).clone(true));\n } else if ($(item).attr('type') == 'checkbox' || $(item).attr('type') == 'radio') { //eslint-disable-line\n // Return to original state.\n if ($(this.original[index]).attr('checked') === undefined) {\n $(item).removeAttr('checked');\n } else {\n $(item).attr('checked', $(this.original[index]).attr('checked'));\n }\n } else {\n // Replace the value with the original\n $(item).val($(this.original[index]).val());\n }\n }, this));\n }\n });\n\n $.widget('mage.fieldsetResetControl', {\n /**\n * @private\n */\n _create: function () {\n this._bind();\n },\n\n /**\n * @private\n */\n _bind: function () {\n this._on({\n click: '_onClick'\n });\n },\n\n /**\n * @param {jQuery.Event} e\n * @private\n */\n _onClick: function (e) {\n e.stopPropagation();\n $(this.element).trigger('fieldsetReset');\n }\n });\n\n return {\n fieldsetControls: $.mage.fieldsetControls,\n fieldsetResetControl: $.mage.fieldsetResetControl\n };\n});\n","mage/translate.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable strict */\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/mage'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($) {\n $.extend(true, $, {\n mage: {\n translate: (function () {\n /**\n * Key-value translations storage\n * @type {Object}\n * @private\n */\n var _data = {};\n\n /**\n * Add new translation (two string parameters) or several translations (object)\n */\n this.add = function () {\n if (arguments.length > 1) {\n _data[arguments[0]] = arguments[1];\n } else if (typeof arguments[0] === 'object') {\n $.extend(_data, arguments[0]);\n }\n };\n\n /**\n * Make a translation with parsing (to handle case when _data represents tuple)\n * @param {String} text\n * @return {String}\n */\n this.translate = function (text) {\n return _data[text] ? _data[text] : text;\n };\n\n return this;\n }())\n }\n });\n $.mage.__ = $.proxy($.mage.translate.translate, $.mage.translate);\n\n return $.mage.__;\n}));\n","mage/ie-class-fixer.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable strict */\n(function () {\n var userAgent = navigator.userAgent, // user agent identifier\n html = document.documentElement, // html tag\n gap = ''; // gap between classes\n\n if (html.className) { // check if neighbour class exist in html tag\n gap = ' ';\n } // end if\n\n if (userAgent.match(/Trident.*rv[ :]*11\\./)) { // Special case for IE11\n html.className += gap + 'ie11';\n } // end if\n\n})();\n","mage/template.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (root, factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'underscore'\n ], factory);\n } else {\n root.mageTemplate = factory(root._);\n }\n}(this, function (_) {\n 'use strict';\n\n /**\n * Checks if provided string is a valid DOM selector.\n *\n * @param {String} selector - Selector to be checked.\n * @returns {Boolean}\n */\n function isSelector(selector) {\n try {\n document.querySelector(selector);\n\n return true;\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Unescapes characters used in underscore templates.\n *\n * @param {String} str - String to be processed.\n * @returns {String}\n */\n function unescape(str) {\n return str.replace(/<%|%3C%/g, '<%').replace(/%>|%%3E/g, '%>');\n }\n\n /**\n * If 'tmpl' is a valid selector, returns target node's innerHTML if found.\n * Else, returns empty string and emits console warning.\n * If 'tmpl' is not a selector, returns 'tmpl' as is.\n *\n * @param {String} tmpl\n * @returns {String}\n */\n function getTmplString(tmpl) {\n if (isSelector(tmpl)) {\n tmpl = document.querySelector(tmpl);\n\n if (tmpl) {\n tmpl = tmpl.innerHTML.trim();\n } else {\n console.warn('No template was found by selector: ' + tmpl);\n\n tmpl = '';\n }\n }\n\n return unescape(tmpl);\n }\n\n /**\n * Compiles or renders template provided either\n * by selector or by the template string.\n *\n * @param {String} tmpl - Template string or selector.\n * @param {(Object|Array|Function)} [data] - Data object with which to render template.\n * @returns {String|Function}\n */\n return function (tmpl, data) {\n var render;\n\n tmpl = getTmplString(tmpl);\n render = _.template(tmpl);\n\n return !_.isUndefined(data) ?\n render(data) :\n render;\n };\n}));\n","mage/polyfill.js":"try {\n if (!window.localStorage || !window.sessionStorage) {\n throw new Error();\n }\n\n localStorage.setItem('storage_test', 1);\n localStorage.removeItem('storage_test');\n} catch (e) {\n (function () {\n 'use strict';\n\n /**\n * Returns a storage object to shim local or sessionStorage\n * @param {String} type - either 'local' or 'session'\n */\n var Storage = function (type) {\n var data;\n\n /**\n * Creates a cookie\n * @param {String} name\n * @param {String} value\n * @param {Integer} days\n */\n function createCookie(name, value, days) {\n var date, expires;\n\n if (days) {\n date = new Date();\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n expires = '; expires=' + date.toGMTString();\n } else {\n expires = '';\n }\n document.cookie = name + '=' + value + expires + '; path=/';\n }\n\n /**\n * Reads value of a cookie\n * @param {String} name\n */\n function readCookie(name) {\n var nameEQ = name + '=',\n ca = document.cookie.split(';'),\n i = 0,\n c;\n\n for (i = 0; i < ca.length; i++) {\n c = ca[i];\n\n while (c.charAt(0) === ' ') {\n c = c.substring(1, c.length);\n }\n\n if (c.indexOf(nameEQ) === 0) {\n return c.substring(nameEQ.length, c.length);\n }\n }\n\n return null;\n }\n\n /**\n * Returns cookie name based upon the storage type.\n * If this is session storage, the function returns a unique cookie per tab\n */\n function getCookieName() {\n\n if (type !== 'session') {\n return 'localstorage';\n }\n\n if (!window.name) {\n window.name = new Date().getTime();\n }\n\n return 'sessionStorage' + window.name;\n }\n\n /**\n * Sets storage cookie to a data object\n * @param {Object} dataObject\n */\n function setData(dataObject) {\n data = encodeURIComponent(JSON.stringify(dataObject));\n createCookie(getCookieName(), data, 365);\n }\n\n /**\n * Clears value of cookie data\n */\n function clearData() {\n createCookie(getCookieName(), '', 365);\n }\n\n /**\n * @returns value of cookie data\n */\n function getData() {\n var dataResponse = readCookie(getCookieName());\n\n return dataResponse ? JSON.parse(decodeURIComponent(dataResponse)) : {};\n }\n\n data = getData();\n\n return {\n length: 0,\n\n /**\n * Clears data from storage\n */\n clear: function () {\n data = {};\n this.length = 0;\n clearData();\n },\n\n /**\n * Gets an item from storage\n * @param {String} key\n */\n getItem: function (key) {\n return data[key] === undefined ? null : data[key];\n },\n\n /**\n * Gets an item by index from storage\n * @param {Integer} i\n */\n key: function (i) {\n var ctr = 0,\n k;\n\n for (k in data) {\n\n if (data.hasOwnProperty(k)) {\n\n // eslint-disable-next-line max-depth\n if (ctr.toString() === i.toString()) {\n return k;\n }\n ctr++;\n }\n }\n\n return null;\n },\n\n /**\n * Removes an item from storage\n * @param {String} key\n */\n removeItem: function (key) {\n delete data[key];\n this.length--;\n setData(data);\n },\n\n /**\n * Sets an item from storage\n * @param {String} key\n * @param {String} value\n */\n setItem: function (key, value) {\n data[key] = value.toString();\n this.length++;\n setData(data);\n }\n };\n };\n\n window.localStorage.prototype = window.localStorage = new Storage('local');\n window.sessionStorage.prototype = window.sessionStorage = new Storage('session');\n })();\n}\n","mage/edit-trigger.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\n(function (root, factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/template',\n 'jquery/ui'\n ], factory);\n } else {\n factory(root.jQuery, root.mageTemplate);\n }\n}(this, function ($, mageTemplate) {\n 'use strict';\n\n var editTriggerPrototype;\n\n $.widget('mage.editTrigger', {\n options: {\n img: '',\n alt: '[TR]',\n template: '#translate-inline-icon',\n zIndex: 2000,\n editSelector: '[data-translate]',\n delay: 2000,\n offsetTop: -3,\n singleElement: true\n },\n\n /**\n * editTriger creation\n * @protected\n */\n _create: function () {\n this.tmpl = mageTemplate(this.options.template);\n this._initTrigger();\n this._bind();\n },\n\n /**\n * @return {Object}\n * @private\n */\n _getCss: function () {\n return {\n position: 'absolute',\n cursor: 'pointer',\n display: 'none',\n 'z-index': this.options.zIndex\n };\n },\n\n /**\n * @param {*} appendTo\n * @return {*|jQuery}\n * @private\n */\n _createTrigger: function (appendTo) {\n var tmpl = this.tmpl({\n data: this.options\n });\n\n return $(tmpl)\n .css(this._getCss())\n .data('role', 'edit-trigger-element')\n .appendTo(appendTo);\n },\n\n /**\n * @private\n */\n _initTrigger: function () {\n this.trigger = this._createTrigger($('body'));\n },\n\n /**\n * Bind on mousemove event\n * @protected\n */\n _bind: function () {\n this.trigger.on('click.' + this.widgetName, $.proxy(this._onClick, this));\n this.element.on('mousemove.' + this.widgetName, $.proxy(this._onMouseMove, this));\n },\n\n /**\n * Show editTriger\n */\n show: function () {\n if (this.trigger.is(':hidden')) {\n this.trigger.show();\n }\n },\n\n /**\n * Hide editTriger\n */\n hide: function () {\n this.currentTarget = null;\n\n if (this.trigger && this.trigger.is(':visible')) {\n this.trigger.hide();\n }\n },\n\n /**\n * Set editTriger position\n * @protected\n */\n _setPosition: function (el) {\n var offset = el.offset();\n\n this.trigger.css({\n top: offset.top + el.outerHeight() + this.options.offsetTop,\n left: offset.left\n });\n },\n\n /**\n * Show/hide trigger on mouse move.\n *\n * @param {jQuery.Event} e\n * @protected\n */\n _onMouseMove: function (e) {\n var target = $(e.target),\n inner = target.find(this.options.editSelector);\n\n if ($(e.target).is('button') && inner.length) {\n target = inner;\n } else if (!target.is(this.trigger) && !target.is(this.options.editSelector)) {\n target = target.parents(this.options.editSelector).first();\n }\n\n if (target.length) {\n if (!target.is(this.trigger)) {\n this._setPosition(target);\n this.currentTarget = target;\n }\n this.show();\n } else {\n this.hide();\n }\n },\n\n /**\n * Trigger event \"edit\" on element for translate.\n *\n * @param {jQuery.Event} e\n * @protected\n */\n _onClick: function (e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n $(this.currentTarget).trigger('edit.' + this.widgetName);\n this.hide(true);\n },\n\n /**\n * Destroy editTriger\n */\n destroy: function () {\n this.trigger.remove();\n this.element.off('.' + this.widgetName);\n\n return $.Widget.prototype.destroy.call(this);\n }\n });\n\n /**\n * Extention for widget editTrigger - hide trigger with delay\n */\n editTriggerPrototype = $.mage.editTrigger.prototype;\n\n $.widget('mage.editTrigger', $.extend({}, editTriggerPrototype, {\n /**\n * Added clear timeout on trigger show\n */\n show: function () {\n editTriggerPrototype.show.apply(this, arguments);\n\n if (this.options.delay) {\n this._clearTimer();\n }\n },\n\n /**\n * Added setTimeout on trigger hide\n */\n hide: function (immediate) {\n if (!immediate && this.options.delay) {\n if (!this.timer) {\n this.timer = setTimeout($.proxy(function () {\n editTriggerPrototype.hide.apply(this, arguments);\n this._clearTimer();\n }, this), this.options.delay);\n }\n } else {\n editTriggerPrototype.hide.apply(this, arguments);\n }\n },\n\n /**\n * Clear timer\n * @protected\n */\n _clearTimer: function () {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n }));\n\n return $.mage.editTrigger;\n}));\n","mage/tabs.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'jquery/ui',\n 'mage/mage',\n 'mage/collapsible'\n], function ($) {\n 'use strict';\n\n $.widget('mage.tabs', {\n options: {\n active: 0,\n disabled: [],\n openOnFocus: true,\n collapsible: false,\n collapsibleElement: '[data-role=collapsible]',\n header: '[data-role=title]',\n content: '[data-role=content]',\n trigger: '[data-role=trigger]',\n closedState: null,\n openedState: null,\n disabledState: null,\n ajaxUrlElement: '[data-ajax=true]',\n ajaxContent: false,\n loadingClass: null,\n saveState: false,\n animate: false,\n icons: {\n activeHeader: null,\n header: null\n }\n },\n\n /**\n * @private\n */\n _create: function () {\n if (typeof this.options.disabled === 'string') {\n this.options.disabled = this.options.disabled.split(' ').map(function (item) {\n return parseInt(item, 10);\n });\n }\n this._processPanels();\n this._handleDeepLinking();\n this._processTabIndex();\n this._closeOthers();\n this._bind();\n },\n\n /**\n * @private\n */\n _destroy: function () {\n $.each(this.collapsibles, function () {\n $(this).collapsible('destroy');\n });\n },\n\n /**\n * If deep linking is used, all sections must be closed but the one that contains the anchor.\n * @private\n */\n _handleDeepLinking: function () {\n var self = this,\n anchor = window.location.hash,\n isValid = $.mage.isValidSelector(anchor),\n anchorId = anchor.replace('#', '');\n\n if (anchor && isValid) {\n $.each(self.contents, function (i) {\n if ($(this).attr('id') === anchorId) {\n self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate');\n\n return false;\n }\n });\n }\n },\n\n /**\n * When the widget gets instantiated, the first tab that is not disabled receive focusable property\n * All tabs receive tabIndex 0\n * @private\n */\n _processTabIndex: function () {\n var self = this;\n\n self.triggers.attr('tabIndex', 0);\n $.each(this.collapsibles, function (i) {\n self.triggers.attr('tabIndex', 0);\n self.triggers.eq(i).attr('tabIndex', 0);\n });\n },\n\n /**\n * Prepare the elements for instantiating the collapsible widget\n * @private\n */\n _processPanels: function () {\n this.contents = this.element.find(this.options.content);\n\n this.collapsibles = this.element.find(this.options.collapsibleElement);\n\n this.collapsibles\n .attr('role', 'presentation')\n .parent()\n .attr('role', 'tablist');\n\n this.headers = this.element.find(this.options.header);\n\n if (this.headers.length === 0) {\n this.headers = this.collapsibles;\n }\n this.triggers = this.element.find(this.options.trigger);\n\n if (this.triggers.length === 0) {\n this.triggers = this.headers;\n }\n this._callCollapsible();\n },\n\n /**\n * Setting the disabled and active tabs and calling instantiation of collapsible\n * @private\n */\n _callCollapsible: function () {\n var self = this,\n disabled = false,\n active = false;\n\n $.each(this.collapsibles, function (i) {\n disabled = active = false;\n\n if ($.inArray(i, self.options.disabled) !== -1) {\n disabled = true;\n }\n\n if (i === self.options.active) {\n active = true;\n }\n self._instantiateCollapsible(this, i, active, disabled);\n });\n },\n\n /**\n * Instantiate collapsible.\n *\n * @param {HTMLElement} element\n * @param {Number} index\n * @param {*} active\n * @param {*} disabled\n * @private\n */\n _instantiateCollapsible: function (element, index, active, disabled) {\n $(element).collapsible(\n $.extend({}, this.options, {\n active: active,\n disabled: disabled,\n header: this.headers.eq(index),\n content: this.contents.eq(index),\n trigger: this.triggers.eq(index)\n })\n );\n },\n\n /**\n * Adding callback to close others tabs when one gets opened\n * @private\n */\n _closeOthers: function () {\n var self = this;\n\n $.each(this.collapsibles, function () {\n $(this).on('beforeOpen', function () {\n self.collapsibles.not(this).collapsible('forceDeactivate');\n });\n });\n },\n\n /**\n * @param {*} index\n */\n activate: function (index) {\n this._toggleActivate('activate', index);\n },\n\n /**\n * @param {*} index\n */\n deactivate: function (index) {\n this._toggleActivate('deactivate', index);\n },\n\n /**\n * @param {*} action\n * @param {*} index\n * @private\n */\n _toggleActivate: function (action, index) {\n this.collapsibles.eq(index).collapsible(action);\n },\n\n /**\n * @param {*} index\n */\n disable: function (index) {\n this._toggleEnable('disable', index);\n },\n\n /**\n * @param {*} index\n */\n enable: function (index) {\n this._toggleEnable('enable', index);\n },\n\n /**\n * @param {*} action\n * @param {*} index\n * @private\n */\n _toggleEnable: function (action, index) {\n var self = this;\n\n if ($.isArray(index)) {\n $.each(index, function () {\n self.collapsibles.eq(this).collapsible(action);\n });\n } else if (index === undefined) {\n this.collapsibles.collapsible(action);\n } else {\n this.collapsibles.eq(index).collapsible(action);\n }\n },\n\n /**\n * @param {jQuery.Event} event\n * @private\n */\n _keydown: function (event) {\n var self = this,\n keyCode, toFocus, toFocusIndex, enabledTriggers, length, currentIndex, nextToFocus;\n\n if (event.altKey || event.ctrlKey) {\n return;\n }\n keyCode = $.ui.keyCode;\n toFocus = false;\n enabledTriggers = [];\n\n $.each(this.triggers, function () {\n if (!self.collapsibles.eq(self.triggers.index($(this))).collapsible('option', 'disabled')) {\n enabledTriggers.push(this);\n }\n });\n length = $(enabledTriggers).length;\n currentIndex = $(enabledTriggers).index(event.target);\n\n /**\n * @param {String} direction\n * @return {*}\n */\n nextToFocus = function (direction) {\n if (length > 0) {\n if (direction === 'right') {\n toFocusIndex = (currentIndex + 1) % length;\n } else {\n toFocusIndex = (currentIndex + length - 1) % length;\n }\n\n return enabledTriggers[toFocusIndex];\n }\n\n return event.target;\n };\n\n switch (event.keyCode) {\n case keyCode.RIGHT:\n case keyCode.DOWN:\n toFocus = nextToFocus('right');\n break;\n\n case keyCode.LEFT:\n case keyCode.UP:\n toFocus = nextToFocus('left');\n break;\n\n case keyCode.HOME:\n toFocus = enabledTriggers[0];\n break;\n\n case keyCode.END:\n toFocus = enabledTriggers[length - 1];\n break;\n }\n\n if (toFocus) {\n toFocusIndex = this.triggers.index(toFocus);\n $(event.target).attr('tabIndex', -1);\n $(toFocus).attr('tabIndex', 0);\n toFocus.focus();\n\n if (this.options.openOnFocus) {\n this.activate(toFocusIndex);\n }\n event.preventDefault();\n }\n },\n\n /**\n * @private\n */\n _bind: function () {\n var events = {\n keydown: '_keydown'\n };\n\n this._off(this.triggers);\n this._on(this.triggers, events);\n }\n });\n\n return $.mage.tabs;\n});\n","mage/dialog.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\ndefine([\n 'jquery',\n 'jquery/ui'\n], function ($) {\n 'use strict';\n\n /**\n * Dialog Widget - this widget is a wrapper for the jQuery UI Dialog\n */\n $.widget('mage.dialog', $.ui.dialog, {});\n\n return $.mage.dialog;\n});\n","mage/gallery/gallery.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'fotorama/fotorama',\n 'underscore',\n 'matchMedia',\n 'mage/template',\n 'text!mage/gallery/gallery.html',\n 'uiClass',\n 'mage/translate'\n], function ($, fotorama, _, mediaCheck, template, galleryTpl, Class, $t) {\n 'use strict';\n\n /**\n * Retrieves index if the main item.\n * @param {Array.<Object>} data - Set of gallery items.\n */\n var getMainImageIndex = function (data) {\n var mainIndex;\n\n if (_.every(data, function (item) {\n return _.isObject(item);\n })\n ) {\n mainIndex = _.findIndex(data, function (item) {\n return item.isMain;\n });\n }\n\n return mainIndex > 0 ? mainIndex : 0;\n },\n\n /**\n * Helper for parse translate property\n *\n * @param {Element} el - el that to parse\n * @returns {Array} - array of properties.\n */\n getTranslate = function (el) {\n var slideTransform = $(el).attr('style').split(';');\n\n slideTransform = $.map(slideTransform, function (style) {\n style = style.trim();\n\n if (style.startsWith('transform: translate3d')) {\n return style.match(/transform: translate3d\\((.+)px,(.+)px,(.+)px\\)/);\n }\n\n return false;\n });\n\n return slideTransform.filter(Boolean);\n },\n\n /**\n * @param {*} str\n * @return {*}\n * @private\n */\n _toNumber = function (str) {\n var type = typeof str;\n\n if (type === 'string') {\n return parseInt(str); //eslint-disable-line radix\n }\n\n return str;\n };\n\n return Class.extend({\n\n defaults: {\n settings: {},\n config: {},\n startConfig: {}\n },\n\n /**\n * Checks if device has touch interface.\n * @return {Boolean} The result of searching touch events on device.\n */\n isTouchEnabled: (function () {\n return 'ontouchstart' in document.documentElement;\n })(),\n\n /**\n * Initializes gallery.\n * @param {Object} config - Gallery configuration.\n * @param {String} element - String selector of gallery DOM element.\n */\n initialize: function (config, element) {\n var self = this;\n\n this._super();\n\n _.bindAll(this,\n '_focusSwitcher'\n );\n\n /*turn off arrows for touch devices*/\n if (this.isTouchEnabled) {\n config.options.arrows = false;\n\n if (config.fullscreen) {\n config.fullscreen.arrows = false;\n }\n }\n\n config.options.width = _toNumber(config.options.width);\n config.options.height = _toNumber(config.options.height);\n config.options.thumbwidth = _toNumber(config.options.thumbwidth);\n config.options.thumbheight = _toNumber(config.options.thumbheight);\n\n config.options.swipe = true;\n this.config = config;\n\n this.settings = {\n $element: $(element),\n $pageWrapper: $('body>.page-wrapper'),\n currentConfig: config,\n defaultConfig: _.clone(config),\n fullscreenConfig: _.clone(config.fullscreen),\n breakpoints: config.breakpoints,\n activeBreakpoint: {},\n fotoramaApi: null,\n isFullscreen: false,\n api: null,\n data: _.clone(config.data)\n };\n config.options.ratio = config.options.width / config.options.height;\n config.options.height = null;\n\n $.extend(true, this.startConfig, config);\n\n this.initGallery();\n this.initApi();\n this.setupBreakpoints();\n this.initFullscreenSettings();\n\n this.settings.$element.on('mouseup', '.fotorama__stage__frame', function () {\n if (\n !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length &&\n !$(this).hasClass('fotorama-video-container')\n ) {\n self.openFullScreen();\n }\n });\n\n if (this.isTouchEnabled && this.settings.isFullscreen) {\n this.settings.$element.on('tap', '.fotorama__stage__frame', function () {\n var translate = getTranslate($(this).parents('.fotorama__stage__shaft'));\n\n if (translate[1] === '0' && !$(this).hasClass('fotorama-video-container')) {\n self.openFullScreen();\n self.settings.$pageWrapper.hide();\n }\n });\n }\n },\n\n /**\n * Open gallery fullscreen\n */\n openFullScreen: function () {\n this.settings.api.fotorama.requestFullScreen();\n this.settings.$fullscreenIcon.css({\n opacity: 1,\n visibility: 'visible',\n display: 'block'\n });\n },\n\n /**\n * Gallery fullscreen settings.\n */\n initFullscreenSettings: function () {\n var settings = this.settings,\n self = this;\n\n settings.$gallery = this.settings.$element.find('[data-gallery-role=\"gallery\"]');\n settings.$fullscreenIcon = this.settings.$element.find('[data-gallery-role=\"fotorama__fullscreen-icon\"]');\n settings.focusableStart = this.settings.$element.find('[data-gallery-role=\"fotorama__focusable-start\"]');\n settings.focusableEnd = this.settings.$element.find('[data-gallery-role=\"fotorama__focusable-end\"]');\n settings.closeIcon = this.settings.$element.find('[data-gallery-role=\"fotorama__fullscreen-icon\"]');\n settings.fullscreenConfig.swipe = true;\n\n settings.$gallery.on('fotorama:fullscreenenter', function () {\n settings.closeIcon.show();\n settings.focusableStart.attr('tabindex', '0');\n settings.focusableEnd.attr('tabindex', '0');\n settings.focusableStart.bind('focusin', self._focusSwitcher);\n settings.focusableEnd.bind('focusin', self._focusSwitcher);\n settings.api.updateOptions(settings.defaultConfig.options, true);\n settings.api.updateOptions(settings.fullscreenConfig, true);\n\n if (!_.isEqual(settings.activeBreakpoint, {}) && settings.breakpoints) {\n settings.api.updateOptions(settings.activeBreakpoint.options, true);\n }\n settings.isFullscreen = true;\n });\n\n settings.$gallery.on('fotorama:fullscreenexit', function () {\n settings.closeIcon.hide();\n settings.focusableStart.attr('tabindex', '-1');\n settings.focusableEnd.attr('tabindex', '-1');\n settings.api.updateOptions(settings.defaultConfig.options, true);\n settings.focusableStart.unbind('focusin', this._focusSwitcher);\n settings.focusableEnd.unbind('focusin', this._focusSwitcher);\n settings.closeIcon.hide();\n\n if (!_.isEqual(settings.activeBreakpoint, {}) && settings.breakpoints) {\n settings.api.updateOptions(settings.activeBreakpoint.options, true);\n }\n settings.isFullscreen = false;\n settings.$element.data('gallery').updateOptions({\n swipe: true\n });\n });\n },\n\n /**\n * Switcher focus.\n */\n _focusSwitcher: function (e) {\n var target = $(e.target),\n settings = this.settings;\n\n if (target.is(settings.focusableStart)) {\n this._setFocus('start');\n } else if (target.is(settings.focusableEnd)) {\n this._setFocus('end');\n }\n },\n\n /**\n * Set focus to element.\n * @param {String} position - can be \"start\" and \"end\"\n * positions.\n * If position is \"end\" - sets focus to first\n * focusable element in modal window scope.\n * If position is \"start\" - sets focus to last\n * focusable element in modal window scope\n */\n _setFocus: function (position) {\n var settings = this.settings,\n focusableElements,\n infelicity;\n\n if (position === 'end') {\n settings.$gallery.find(settings.closeIcon).focus();\n } else if (position === 'start') {\n infelicity = 3; //Constant for find last focusable element\n focusableElements = settings.$gallery.find(':focusable');\n focusableElements.eq(focusableElements.length - infelicity).focus();\n }\n },\n\n /**\n * Initializes gallery with configuration options.\n */\n initGallery: function () {\n var breakpoints = {},\n settings = this.settings,\n config = this.config,\n tpl = template(galleryTpl, {\n next: $t('Next'),\n previous: $t('Previous')\n }),\n mainImageIndex;\n\n if (settings.breakpoints) {\n _.each(_.values(settings.breakpoints), function (breakpoint) {\n var conditions;\n\n _.each(_.pairs(breakpoint.conditions), function (pair) {\n conditions = conditions ? conditions + ' and (' + pair[0] + ': ' + pair[1] + ')' :\n '(' + pair[0] + ': ' + pair[1] + ')';\n });\n breakpoints[conditions] = breakpoint.options;\n });\n settings.breakpoints = breakpoints;\n }\n\n _.extend(config, config.options);\n config.options = undefined;\n\n config.click = false;\n config.breakpoints = null;\n settings.currentConfig = config;\n settings.$element.html(tpl);\n settings.$element.removeClass('_block-content-loading');\n settings.$elementF = $(settings.$element.children()[0]);\n settings.$elementF.fotorama(config);\n settings.fotoramaApi = settings.$elementF.data('fotorama');\n $.extend(true, config, this.startConfig);\n\n mainImageIndex = getMainImageIndex(config.data);\n\n if (mainImageIndex) {\n this.settings.fotoramaApi.show({\n index: mainImageIndex,\n time: 0\n });\n }\n },\n\n /**\n * Creates breakpoints for gallery.\n */\n setupBreakpoints: function () {\n var pairs,\n settings = this.settings,\n config = this.config,\n startConfig = this.startConfig,\n isTouchEnabled = this.isTouchEnabled;\n\n if (_.isObject(settings.breakpoints)) {\n pairs = _.pairs(settings.breakpoints);\n _.each(pairs, function (pair) {\n mediaCheck({\n media: pair[0],\n\n /**\n * Is triggered when breakpoint enties.\n */\n entry: function () {\n $.extend(true, config, _.clone(startConfig));\n\n settings.api.updateOptions(settings.defaultConfig.options, true);\n\n if (settings.isFullscreen) {\n settings.api.updateOptions(settings.fullscreenConfig, true);\n }\n\n if (isTouchEnabled) {\n settings.breakpoints[pair[0]].options.arrows = false;\n\n if (settings.breakpoints[pair[0]].options.fullscreen) {\n settings.breakpoints[pair[0]].options.fullscreen.arrows = false;\n }\n }\n\n settings.api.updateOptions(settings.breakpoints[pair[0]].options, true);\n $.extend(true, config, settings.breakpoints[pair[0]]);\n settings.activeBreakpoint = settings.breakpoints[pair[0]];\n },\n\n /**\n * Is triggered when breakpoint exits.\n */\n exit: function () {\n $.extend(true, config, _.clone(startConfig));\n settings.api.updateOptions(settings.defaultConfig.options, true);\n\n if (settings.isFullscreen) {\n settings.api.updateOptions(settings.fullscreenConfig, true);\n }\n settings.activeBreakpoint = {};\n }\n });\n });\n }\n },\n\n /**\n * Creates gallery's API.\n */\n initApi: function () {\n var settings = this.settings,\n config = this.config,\n api = {\n\n /**\n * Contains fotorama's API methods.\n */\n fotorama: settings.fotoramaApi,\n\n /**\n * Displays the last image on preview.\n */\n last: function () {\n settings.fotoramaApi.show('>>');\n },\n\n /**\n * Displays the first image on preview.\n */\n first: function () {\n settings.fotoramaApi.show('<<');\n },\n\n /**\n * Displays previous element on preview.\n */\n prev: function () {\n settings.fotoramaApi.show('<');\n },\n\n /**\n * Displays next element on preview.\n */\n next: function () {\n settings.fotoramaApi.show('>');\n },\n\n /**\n * Displays image with appropriate count number on preview.\n * @param {Number} index - Number of image that should be displayed.\n */\n seek: function (index) {\n if (_.isNumber(index) && index !== 0) {\n\n if (index > 0) {\n index -= 1;\n }\n settings.fotoramaApi.show(index);\n }\n },\n\n /**\n * Updates gallery with new set of options.\n * @param {Object} configuration - Standart gallery configuration object.\n * @param {Boolean} isInternal - Is this function called via breakpoints.\n */\n updateOptions: function (configuration, isInternal) {\n\n var $selectable = $('a[href], area[href], input, select, ' +\n 'textarea, button, iframe, object, embed, *[tabindex], *[contenteditable]')\n .not('[tabindex=-1], [disabled], :hidden'),\n $focus = $(':focus'),\n index;\n\n if (_.isObject(configuration)) {\n\n //Saves index of focus\n $selectable.each(function (number) {\n if ($(this).is($focus)) {\n index = number;\n }\n });\n\n if (this.isTouchEnabled) {\n configuration.arrows = false;\n }\n configuration.click = false;\n configuration.breakpoints = null;\n\n if (!isInternal) {\n !_.isEqual(settings.activeBreakpoint, {} && settings.brekpoints) ?\n $.extend(true, settings.activeBreakpoint.options, configuration) :\n\n settings.isFullscreen ?\n $.extend(true, settings.fullscreenConfig, configuration) :\n $.extend(true, settings.defaultConfig.options, configuration);\n\n }\n $.extend(true, settings.currentConfig.options, configuration);\n settings.fotoramaApi.setOptions(settings.currentConfig.options);\n\n if (_.isNumber(index)) {\n $selectable.eq(index).focus();\n }\n }\n },\n\n /**\n * Updates gallery with specific set of items.\n * @param {Array.<Object>} data - Set of gallery items to update.\n */\n updateData: function (data) {\n var mainImageIndex;\n\n if (_.isArray(data)) {\n settings.fotoramaApi.load(data);\n mainImageIndex = getMainImageIndex(data);\n\n if (mainImageIndex) {\n settings.fotoramaApi.show({\n index: mainImageIndex,\n time: 0\n });\n }\n\n $.extend(false, settings, {\n data: data,\n defaultConfig: data\n });\n $.extend(false, config, {\n data: data\n });\n }\n },\n\n /**\n * Returns current images list\n *\n * @returns {Array}\n */\n returnCurrentImages: function () {\n var images = [];\n\n _.each(this.fotorama.data, function (item) {\n images.push(_.omit(item, '$navThumbFrame', '$navDotFrame', '$stageFrame', 'labelledby'));\n });\n\n return images;\n },\n\n /**\n * Updates gallery data partially by index\n * @param {Number} index - Index of image in data array to be updated.\n * @param {Object} item - Standart gallery image object.\n *\n */\n updateDataByIndex: function (index, item) {\n settings.fotoramaApi.spliceByIndex(index, item);\n }\n };\n\n settings.$element.data('gallery', api);\n settings.api = settings.$element.data('gallery');\n settings.$element.trigger('gallery:loaded');\n }\n });\n});\n","mage/apply/scripts.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'jquery'\n], function (_, $) {\n 'use strict';\n\n var scriptSelector = 'script[type=\"text/x-magento-init\"]',\n dataAttr = 'data-mage-init',\n virtuals = [];\n\n /**\n * Adds components to the virtual list.\n *\n * @param {Object} components\n */\n function addVirtual(components) {\n virtuals.push({\n el: false,\n data: components\n });\n }\n\n /**\n * Merges provided data with a current data\n * of a elements' \"data-mage-init\" attribute.\n *\n * @param {Object} components - Object with components and theirs configuration.\n * @param {HTMLElement} elem - Element whose data should be modified.\n */\n function setData(components, elem) {\n var data = elem.getAttribute(dataAttr);\n\n data = data ? JSON.parse(data) : {};\n _.each(components, function (obj, key) {\n if (_.has(obj, 'mixins')) {\n data[key] = data[key] || {};\n data[key].mixins = data[key].mixins || [];\n data[key].mixins = data[key].mixins.concat(obj.mixins);\n delete obj.mixins;\n }\n });\n\n data = $.extend(true, data, components);\n data = JSON.stringify(data);\n elem.setAttribute(dataAttr, data);\n }\n\n /**\n * Search for the elements by privded selector and extends theirs data.\n *\n * @param {Object} components - Object with components and theirs configuration.\n * @param {String} selector - Selector for the elements.\n */\n function processElems(components, selector) {\n var elems,\n iterator;\n\n if (selector === '*') {\n addVirtual(components);\n\n return;\n }\n\n elems = document.querySelectorAll(selector);\n iterator = setData.bind(null, components);\n\n _.toArray(elems).forEach(iterator);\n }\n\n /**\n * Parses content of a provided script node.\n * Note: node will be removed from DOM.\n *\n * @param {HTMLScriptElement} node - Node to be processed.\n * @returns {Object}\n */\n function getNodeData(node) {\n var data = node.textContent;\n\n node.parentNode.removeChild(node);\n\n return JSON.parse(data);\n }\n\n /**\n * Parses 'script' tags with a custom type attribute and moves it's data\n * to a 'data-mage-init' attribute of an element found by provided selector.\n * Note: All found script nodes will be removed from DOM.\n *\n * @returns {Array} An array of components not assigned to the specific element.\n *\n * @example Sample declaration.\n * <script type=\"text/x-magento-init\">\n * {\n * \"body\": {\n * \"path/to/component\": {\"foo\": \"bar\"}\n * }\n * }\n * </script>\n *\n * @example Providing data without selector.\n * {\n * \"*\": {\n * \"path/to/component\": {\"bar\": \"baz\"}\n * }\n * }\n */\n return function () {\n var nodes = document.querySelectorAll(scriptSelector);\n\n _.toArray(nodes)\n .map(getNodeData)\n .forEach(function (item) {\n _.each(item, processElems);\n });\n\n return virtuals.splice(0, virtuals.length);\n };\n});\n","mage/apply/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'jquery',\n './scripts'\n], function (_, $, processScripts) {\n 'use strict';\n\n var dataAttr = 'data-mage-init',\n nodeSelector = '[' + dataAttr + ']';\n\n /**\n * Initializes components assigned to a specified element via data-* attribute.\n *\n * @param {HTMLElement} el - Element to initialize components with.\n * @param {Object|String} config - Initial components' config.\n * @param {String} component - Components' path.\n */\n function init(el, config, component) {\n require([component], function (fn) {\n\n if (typeof fn === 'object') {\n fn = fn[component].bind(fn);\n }\n\n if (_.isFunction(fn)) {\n fn(config, el);\n } else if ($(el)[component]) {\n $(el)[component](config);\n }\n }, function (error) {\n if ('console' in window && typeof window.console.error === 'function') {\n console.error(error);\n }\n\n return true;\n });\n }\n\n /**\n * Parses elements 'data-mage-init' attribute as a valid JSON data.\n * Note: data-mage-init attribute will be removed.\n *\n * @param {HTMLElement} el - Element whose attribute should be parsed.\n * @returns {Object}\n */\n function getData(el) {\n var data = el.getAttribute(dataAttr);\n\n el.removeAttribute(dataAttr);\n\n return {\n el: el,\n data: JSON.parse(data)\n };\n }\n\n return {\n /**\n * Initializes components assigned to HTML elements via [data-mage-init].\n *\n * @example Sample 'data-mage-init' declaration.\n * data-mage-init='{\"path/to/component\": {\"foo\": \"bar\"}}'\n */\n apply: function (context) {\n var virtuals = processScripts(!context ? document : context),\n nodes = document.querySelectorAll(nodeSelector);\n\n _.toArray(nodes)\n .map(getData)\n .concat(virtuals)\n .forEach(function (itemContainer) {\n var element = itemContainer.el;\n\n _.each(itemContainer.data, function (obj, key) {\n if (obj.mixins) {\n require(obj.mixins, function () { //eslint-disable-line max-nested-callbacks\n var i, len;\n\n for (i = 0, len = arguments.length; i < len; i++) {\n $.extend(\n true,\n itemContainer.data[key],\n arguments[i](itemContainer.data[key], element)\n );\n }\n\n delete obj.mixins;\n init.call(null, element, obj, key);\n });\n } else {\n init.call(null, element, obj, key);\n }\n\n }\n );\n\n });\n },\n applyFor: init\n };\n});\n","mage/app/config.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\n/* eslint-disable strict */\ndefine([], function () {\n return {\n /**\n * Get base url.\n */\n getBaseUrl: function () {\n return this.values.baseUrl;\n },\n\n /**\n * Get form key.\n */\n getFormKey: function () {\n return this.values.formKey;\n }\n };\n});\n","mage/requirejs/baseUrlResolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Sample configuration:\n *\n require.config({\n \"config\": {\n \"baseUrlInterceptor\": {\n \"Magento_Ui/js/lib/knockout/bindings/collapsible.js\": \"../../../../frontend/Magento/luma/en_US/\"\n }\n }\n });\n */\n\n/* global jsSuffixRegExp */\n/* eslint-disable max-depth */\ndefine('baseUrlInterceptor', [\n 'module'\n], function (module) {\n 'use strict';\n\n /**\n * RequireJS Context object\n */\n var ctx = require.s.contexts._,\n\n /**\n * Original function\n *\n * @type {Function}\n */\n origNameToUrl = ctx.nameToUrl,\n\n /**\n * Original function\n *\n * @type {Function}\n */\n newContextConstr = require.s.newContext;\n\n /**\n * Remove dots from URL\n *\n * @param {Array} ary\n */\n function trimDots(ary) {\n var i, part, length = ary.length;\n\n for (i = 0; i < length; i++) {\n part = ary[i];\n\n if (part === '.') {\n ary.splice(i, 1);\n i -= 1;\n } else if (part === '..') {\n if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {\n //End of the line. Keep at least one non-dot\n //path segment at the front so it can be mapped\n //correctly to disk. Otherwise, there is likely\n //no path mapping for a path starting with '..'.\n //This can still fail, but catches the most reasonable\n //uses of ..\n break;\n } else if (i > 0) {\n ary.splice(i - 1, 2);\n i -= 2;\n }\n }\n }\n }\n\n /**\n * Normalize URL string (remove '/../')\n *\n * @param {String} name\n * @param {String} baseName\n * @param {Object} applyMap\n * @param {Object} localContext\n * @returns {*}\n */\n function normalize(name, baseName, applyMap, localContext) {\n var lastIndex,\n baseParts = baseName && baseName.split('/'),\n normalizedBaseParts = baseParts;\n\n //Adjust any relative paths.\n if (name && name.charAt(0) === '.') {\n //If have a base name, try to normalize against it,\n //otherwise, assume it is a top-level require that will\n //be relative to baseUrl in the end.\n if (baseName) {\n //Convert baseName to array, and lop off the last part,\n //so that . matches that 'directory' and not name of the baseName's\n //module. For instance, baseName of 'one/two/three', maps to\n //'one/two/three.js', but we want the directory, 'one/two' for\n //this normalization.\n normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);\n name = name.split('/');\n lastIndex = name.length - 1;\n\n // If wanting node ID compatibility, strip .js from end\n // of IDs. Have to do this here, and not in nameToUrl\n // because node allows either .js or non .js to map\n // to same file.\n if (localContext.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n }\n\n name = normalizedBaseParts.concat(name);\n trimDots(name);\n name = name.join('/');\n } else if (name.indexOf('./') === 0) {\n // No baseName, so this is ID is resolved relative\n // to baseUrl, pull off the leading dot.\n name = name.substring(2);\n }\n }\n\n return name;\n }\n\n /**\n * Get full url.\n *\n * @param {Object} context\n * @param {String} url\n * @return {String}\n */\n function getUrl(context, url) {\n var baseUrl = context.config.baseUrl,\n newConfig = context.config,\n modulePath = url.replace(baseUrl, ''),\n newBaseUrl,\n rewrite = module.config()[modulePath];\n\n if (!rewrite) {\n return url;\n }\n\n newBaseUrl = normalize(rewrite, baseUrl, undefined, newConfig);\n\n return newBaseUrl + modulePath;\n }\n\n /**\n * Replace original function.\n *\n * @returns {*}\n */\n ctx.nameToUrl = function () {\n return getUrl(ctx, origNameToUrl.apply(ctx, arguments));\n };\n\n /**\n * Replace original function.\n *\n * @return {*}\n */\n require.s.newContext = function () {\n var newCtx = newContextConstr.apply(require.s, arguments),\n newOrigNameToUrl = newCtx.nameToUrl;\n\n /**\n * New implementation of native function.\n *\n * @returns {String}\n */\n newCtx.nameToUrl = function () {\n return getUrl(newCtx, newOrigNameToUrl.apply(newCtx, arguments));\n };\n\n return newCtx;\n };\n});\n\nrequire(['baseUrlInterceptor'], function () {\n 'use strict';\n\n});\n","mage/requirejs/text.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/* inspired by http://github.com/requirejs/text */\n/*global XMLHttpRequest, XDomainRequest */\n\ndefine(['module'], function (module) {\n 'use strict';\n\n var xmlRegExp = /^\\s*<\\?xml(\\s)+version=[\\'\\\"](\\d)*.(\\d)*[\\'\\\"](\\s)*\\?>/im,\n bodyRegExp = /<body[^>]*>\\s*([\\s\\S]+)\\s*<\\/body>/im,\n stripReg = /!strip$/i,\n defaultConfig = module.config && module.config() || {};\n\n /**\n * Strips <?xml ...?> declarations so that external SVG and XML documents can be\n * added to a document without worry.\n * Also, if the string is an HTML document, only the part inside the body tag is returned.\n *\n * @param {String} external\n * @returns {String}\n */\n function stripContent(external) {\n var matches;\n\n if (!external) {\n return '';\n }\n\n matches = external.match(bodyRegExp);\n external = matches ?\n matches[1] :\n external.replace(xmlRegExp, '');\n\n return external;\n }\n\n /**\n * Checks that url match current location\n *\n * @param {String} url\n * @returns {Boolean}\n */\n function sameDomain(url) {\n var uProtocol, uHostName, uPort,\n xdRegExp = /^([\\w:]+)?\\/\\/([^\\/\\\\]+)/i,\n location = window.location,\n match = xdRegExp.exec(url);\n\n if (!match) {\n return true;\n }\n uProtocol = match[1];\n uHostName = match[2];\n\n uHostName = uHostName.split(':');\n uPort = uHostName[1] || '';\n uHostName = uHostName[0];\n\n return (!uProtocol || uProtocol === location.protocol) &&\n (!uHostName || uHostName.toLowerCase() === location.hostname.toLowerCase()) &&\n (!uPort && !uHostName || uPort === location.port);\n }\n\n /**\n * @returns {XMLHttpRequest|XDomainRequest|null}\n */\n function createRequest(url) {\n var xhr = new XMLHttpRequest();\n\n if (!sameDomain(url) && typeof XDomainRequest !== 'undefined') {\n xhr = new XDomainRequest();\n }\n\n return xhr;\n }\n\n /**\n * XHR requester. Returns value to callback.\n *\n * @param {String} url\n * @param {Function} callback\n * @param {Function} fail\n * @param {Object} headers\n */\n function getContent(url, callback, fail, headers) {\n var xhr = createRequest(url),\n header;\n\n xhr.open('GET', url);\n\n /*eslint-disable max-depth */\n if ('setRequestHeader' in xhr && headers) {\n for (header in headers) {\n if (headers.hasOwnProperty(header)) {\n xhr.setRequestHeader(header.toLowerCase(), headers[header]);\n }\n }\n }\n\n /**\n * @inheritdoc\n */\n xhr.onreadystatechange = function () {\n var status, err;\n\n //Do not explicitly handle errors, those should be\n //visible via console output in the browser.\n if (xhr.readyState === 4) {\n status = xhr.status || 0;\n\n if (status > 399 && status < 600) {\n //An http 4xx or 5xx error. Signal an error.\n err = new Error(url + ' HTTP status: ' + status);\n err.xhr = xhr;\n\n if (fail) {\n fail(err);\n }\n } else {\n callback(xhr.responseText);\n\n if (defaultConfig.onXhrComplete) {\n defaultConfig.onXhrComplete(xhr, url);\n }\n }\n }\n };\n\n /*eslint-enable max-depth */\n\n if (defaultConfig.onXhr) {\n defaultConfig.onXhr(xhr, url);\n }\n\n xhr.send();\n }\n\n /**\n * Main method used by RequireJs.\n *\n * @param {String} name - has format: some.module.filext!strip\n * @param {Function} req\n * @param {Function|undefined} onLoad\n */\n function loadContent(name, req, onLoad) {\n\n var toStrip = stripReg.test(name),\n url = req.toUrl(name.replace(stripReg, '')),\n headers = defaultConfig.headers;\n\n getContent(url, function (content) {\n content = toStrip ? stripContent(content) : content;\n onLoad(content);\n }, onLoad.error, headers);\n }\n\n return {\n load: loadContent,\n get: getContent\n };\n});\n","mage/requirejs/resolver.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'underscore',\n 'domReady!'\n], function (_) {\n 'use strict';\n\n var context = require.s.contexts._,\n execCb = context.execCb,\n registry = context.registry,\n callbacks = [],\n retries = 10,\n updateDelay = 1,\n ready,\n update;\n\n /**\n * Checks if provided callback already exists in the callbacks list.\n *\n * @param {Object} callback - Callback object to be checked.\n * @returns {Boolean}\n */\n function isSubscribed(callback) {\n return !!_.findWhere(callbacks, callback);\n }\n\n /**\n * Checks if provided module is rejected during load.\n *\n * @param {Object} module - Module to be checked.\n * @return {Boolean}\n */\n function isRejected(module) {\n return registry[module.id] && (registry[module.id].inited || registry[module.id].error);\n }\n\n /**\n * Checks if provided module has unresolved dependencies.\n *\n * @param {Object} module - Module to be checked.\n * @returns {Boolean}\n */\n function isPending(module) {\n if (!module.depCount) {\n return false;\n }\n\n return module.depCount > _.filter(module.depMaps, isRejected).length;\n }\n\n /**\n * Checks if requirejs's registry object contains pending modules.\n *\n * @returns {Boolean}\n */\n function hasPending() {\n return _.some(registry, isPending);\n }\n\n /**\n * Checks if 'resolver' module is in ready\n * state and that there are no pending modules.\n *\n * @returns {Boolean}\n */\n function isReady() {\n return ready && !hasPending();\n }\n\n /**\n * Invokes provided callback handler.\n *\n * @param {Object} callback\n */\n function invoke(callback) {\n callback.handler.call(callback.ctx);\n }\n\n /**\n * Sets 'resolver' module to a ready state\n * and invokes pending callbacks.\n */\n function resolve() {\n ready = true;\n\n callbacks.splice(0).forEach(invoke);\n }\n\n /**\n * Drops 'ready' flag and runs the update process.\n */\n function tick() {\n ready = false;\n\n update(retries);\n }\n\n /**\n * Adds callback which will be invoked\n * when all of the pending modules are initiated.\n *\n * @param {Function} handler - 'Ready' event handler function.\n * @param {Object} [ctx] - Optional context with which handler\n * will be invoked.\n */\n function subscribe(handler, ctx) {\n var callback = {\n handler: handler,\n ctx: ctx\n };\n\n if (!isSubscribed(callback)) {\n callbacks.push(callback);\n\n if (isReady()) {\n _.defer(tick);\n }\n }\n }\n\n /**\n * Checks for all modules to be initiated\n * and invokes pending callbacks if it's so.\n *\n * @param {Number} [retry] - Number of retries\n * that will be used to repeat the 'update' function\n * invokation in case if there are no pending requests.\n */\n update = _.debounce(function (retry) {\n if (!hasPending()) {\n retry ? update(--retry) : resolve();\n }\n }, updateDelay);\n\n /**\n * Overrides requirejs's original 'execCb' method\n * in order to track pending modules.\n *\n * @returns {*} Result of original method call.\n */\n context.execCb = function () {\n var exported = execCb.apply(context, arguments);\n\n tick();\n\n return exported;\n };\n\n return subscribe;\n});\n","mage/view/composite.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated since version 2.2.0\n */\n/* eslint-disable strict */\ndefine(['jquery'], function ($) {\n return function () {\n var renderedChildren = {},\n children = {};\n\n return {\n /**\n * @param {*} child\n * @param {String} key\n */\n addChild: function (child, key) {\n children[key] = child;\n },\n\n /**\n * @param {*} root\n */\n render: function (root) {\n $.each(children, function (key, child) {\n var childRoot = $('<div>');\n\n renderedChildren[key] = child.render(childRoot);\n root.append(childRoot);\n });\n }\n };\n };\n});\n","mage/validation/url.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([], function () {\n 'use strict';\n\n return {\n\n /**\n * Redirects to the url if it is considered safe\n *\n * @param {String} path - url to be redirected to\n */\n redirect: function (path) {\n path = this.sanitize(path);\n\n if (this.validate(path)) {\n window.location.href = path;\n }\n },\n\n /**\n * Validates url\n *\n * @param {Object} path - url to be validated\n * @returns {Boolean}\n */\n validate: function (path) {\n var hostname = window.location.hostname;\n\n if (path.indexOf(hostname) === -1 ||\n path.indexOf('javascript:') !== -1 ||\n path.indexOf('vbscript:') !== -1) {\n return false;\n }\n\n return true;\n },\n\n /**\n * Sanitize url, replacing disallowed chars\n *\n * @param {String} path - url to be normalized\n * @returns {String}\n */\n sanitize: function (path) {\n return path.replace('[^-A-Za-z0-9+&@#/%?=~_|!:,.;\\(\\)]', '');\n }\n };\n});\n","mage/validation/validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function (factory) {\n 'use strict';\n\n if (typeof define === 'function' && define.amd) {\n define([\n 'jquery',\n 'mage/validation',\n 'mage/translate'\n ], factory);\n } else {\n factory(jQuery);\n }\n}(function ($) {\n 'use strict';\n\n $.each({\n 'validate-grouped-qty': [\n function (value, element, params) {\n var result = false,\n total = 0;\n\n $(params).find('input[data-validate*=\"validate-grouped-qty\"]').each(function (i, e) {\n var val = $(e).val(),\n valInt;\n\n if (val && val.length > 0) {\n result = true;\n valInt = parseFloat(val) || 0;\n\n if (valInt >= 0) {\n total += valInt;\n } else {\n result = false;\n\n return result;\n }\n }\n });\n\n return result && total > 0;\n },\n $.mage.__('Please specify the quantity of product(s).')\n ],\n 'validate-one-checkbox-required-by-name': [\n function (value, element, params) {\n var checkedCount = 0,\n container;\n\n if (element.type === 'checkbox') {\n $('[name=\"' + element.name + '\"]').each(function () {\n if ($(this).is(':checked')) {\n checkedCount += 1;\n\n return false;\n }\n });\n }\n container = '#' + params;\n\n if (checkedCount > 0) {\n $(container).removeClass('validation-failed');\n $(container).addClass('validation-passed');\n\n return true;\n }\n $(container).addClass('validation-failed');\n $(container).removeClass('validation-passed');\n\n return false;\n },\n $.mage.__('Please select one of the options.')\n ],\n 'validate-date-between': [\n function (value, element, params) {\n var minDate = new Date(params[0]),\n maxDate = new Date(params[1]),\n inputDate = new Date(element.value),\n message;\n\n minDate.setHours(0);\n maxDate.setHours(0);\n\n if (inputDate >= minDate && inputDate <= maxDate) {\n return true;\n }\n message = $.mage.__('Please enter a date between %min and %max.');\n this.dateBetweenErrorMessage = message.replace('%min', minDate).replace('%max', maxDate);\n\n return false;\n },\n function () {\n return this.dateBetweenErrorMessage;\n }\n ],\n 'validate-dob': [\n function (val, element, params) {\n var dob = $(element).parents('.customer-dob'),\n dayVal, monthVal, yearVal, dobLength, day, month, year, curYear,\n validYearMessage, validateDayInMonth, validDateMessage, today, dateEntered;\n\n $(dob).find('.' + this.settings.errorClass).removeClass(this.settings.errorClass);\n dayVal = $(dob).find(params[0]).find('input:text').val();\n monthVal = $(dob).find(params[1]).find('input:text').val();\n yearVal = $(dob).find(params[2]).find('input:text').val();\n dobLength = dayVal.length + monthVal.length + yearVal.length;\n\n if (params[3] && dobLength === 0) {\n this.dobErrorMessage = $.mage.__('This is a required field.');\n\n return false;\n }\n\n if (!params[3] && dobLength === 0) {\n return true;\n }\n day = parseInt(dayVal, 10) || 0;\n month = parseInt(monthVal, 10) || 0;\n year = parseInt(yearVal, 10) || 0;\n curYear = (new Date()).getFullYear();\n\n if (!day || !month || !year) {\n this.dobErrorMessage = $.mage.__('Please enter a valid full date.');\n\n return false;\n }\n\n if (month < 1 || month > 12) {\n this.dobErrorMessage = $.mage.__('Please enter a valid month (1-12).');\n\n return false;\n }\n\n if (year < 1900 || year > curYear) {\n validYearMessage = $.mage.__('Please enter a valid year (1900-%1).');\n this.dobErrorMessage = validYearMessage.replace('%1', curYear.toString());\n\n return false;\n }\n validateDayInMonth = new Date(year, month, 0).getDate();\n\n if (day < 1 || day > validateDayInMonth) {\n validDateMessage = $.mage.__('Please enter a valid day (1-%1).');\n this.dobErrorMessage = validDateMessage.replace('%1', validateDayInMonth.toString());\n\n return false;\n }\n today = new Date();\n dateEntered = new Date();\n dateEntered.setFullYear(year, month - 1, day);\n\n if (dateEntered > today) {\n this.dobErrorMessage = $.mage.__('Please enter a date from the past.');\n\n return false;\n }\n\n day = day % 10 === day ? '0' + day : day;\n month = month % 10 === month ? '0' + month : month;\n $(element).val(month + '/' + day + '/' + year);\n\n return true;\n },\n function () {\n return this.dobErrorMessage;\n }\n ]\n }, function (i, rule) {\n rule.unshift(i);\n $.validator.addMethod.apply($.validator, rule);\n });\n}));\n","mage/utils/wrapper.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * Utility methods used to wrap and extend functions.\n *\n * @example Usage of a 'wrap' method with arguments delegation.\n * var multiply = function (a, b) {\n * return a * b;\n * };\n *\n * multiply = module.wrap(multiply, function (orig) {\n * return 'Result is: ' + orig();\n * });\n *\n * multiply(2, 2);\n * => 'Result is: 4'\n *\n * @example Usage of 'wrapSuper' method.\n * var multiply = function (a, b) {\n * return a * b;\n * };\n *\n * var obj = {\n * multiply: module.wrapSuper(multiply, function () {\n * return 'Result is: ' + this._super();\n * });\n * };\n *\n * obj.multiply(2, 2);\n * => 'Result is: 4'\n */\ndefine([\n 'underscore'\n], function (_) {\n 'use strict';\n\n /**\n * Checks if string has a '_super' substring.\n */\n var superReg = /\\b_super\\b/;\n\n return {\n\n /**\n * Wraps target function with a specified wrapper, which will receive\n * reference to the original function as a first argument.\n *\n * @param {Function} target - Function to be wrapped.\n * @param {Function} wrapper - Wrapper function.\n * @returns {Function} Wrapper function.\n */\n wrap: function (target, wrapper) {\n if (!_.isFunction(target) || !_.isFunction(wrapper)) {\n return wrapper;\n }\n\n return function () {\n var args = _.toArray(arguments),\n ctx = this,\n _super;\n\n /**\n * Function that will be passed to the wrapper.\n * If no arguments will be passed to it, then the original\n * function will be called with an arguments of a wrapper function.\n */\n _super = function () {\n var superArgs = arguments.length ? arguments : args.slice(1);\n\n return target.apply(ctx, superArgs);\n };\n\n args.unshift(_super);\n\n return wrapper.apply(ctx, args);\n };\n },\n\n /**\n * Wraps the incoming function to implement support of the '_super' method.\n *\n * @param {Function} target - Function to be wrapped.\n * @param {Function} wrapper - Wrapper function.\n * @returns {Function} Wrapped function.\n */\n wrapSuper: function (target, wrapper) {\n if (!this.hasSuper(wrapper) || !_.isFunction(target)) {\n return wrapper;\n }\n\n return function () {\n var _super = this._super,\n args = arguments,\n result;\n\n /**\n * Temporary define '_super' method which\n * contains call to the original function.\n */\n this._super = function () {\n var superArgs = arguments.length ? arguments : args;\n\n return target.apply(this, superArgs);\n };\n\n result = wrapper.apply(this, args);\n\n this._super = _super;\n\n return result;\n };\n },\n\n /**\n * Checks wether the incoming method contains calls of the '_super' method.\n *\n * @param {Function} fn - Function to be checked.\n * @returns {Boolean}\n */\n hasSuper: function (fn) {\n return _.isFunction(fn) && superReg.test(fn);\n },\n\n /**\n * Extends target object with provided extenders.\n * If property in target and extender objects is a function,\n * then it will be wrapped using 'wrap' method.\n *\n * @param {Object} target - Object to be extended.\n * @param {...Object} extenders - Multiple extenders objects.\n * @returns {Object} Modified target object.\n */\n extend: function (target) {\n var extenders = _.toArray(arguments).slice(1),\n iterator = this._extend.bind(this, target);\n\n extenders.forEach(iterator);\n\n return target;\n },\n\n /**\n * Same as the 'extend' method, but operates only on one extender object.\n *\n * @private\n * @param {Object} target\n * @param {Object} extender\n */\n _extend: function (target, extender) {\n _.each(extender, function (value, key) {\n target[key] = this.wrap(target[key], extender[key]);\n }, this);\n }\n };\n});\n","mage/utils/arrays.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n './strings'\n], function (_, utils) {\n 'use strict';\n\n /**\n * Defines index of an item in a specified container.\n *\n * @param {*} item - Item whose index should be defined.\n * @param {Array} container - Container upon which to perform search.\n * @returns {Number}\n */\n function getIndex(item, container) {\n var index = container.indexOf(item);\n\n if (~index) {\n return index;\n }\n\n return _.findIndex(container, function (value) {\n return value && value.name === item;\n });\n }\n\n return {\n /**\n * Facade method to remove/add value from/to array\n * without creating a new instance.\n *\n * @param {Array} arr - Array to be modified.\n * @param {*} value - Value to add/remove.\n * @param {Boolean} add - Flag that specfies operation.\n * @returns {Utils} Chainable.\n */\n toggle: function (arr, value, add) {\n return add ?\n this.add(arr, value) :\n this.remove(arr, value);\n },\n\n /**\n * Removes the incoming value from array in case\n * without creating a new instance of it.\n *\n * @param {Array} arr - Array to be modified.\n * @param {*} value - Value to be removed.\n * @returns {Utils} Chainable.\n */\n remove: function (arr, value) {\n var index = arr.indexOf(value);\n\n if (~index) {\n arr.splice(index, 1);\n }\n\n return this;\n },\n\n /**\n * Adds the incoming value to array if\n * it's not alredy present in there.\n *\n * @param {Array} arr - Array to be modifed.\n * @param {...*} arguments - Values to be added.\n * @returns {Utils} Chainable.\n */\n add: function (arr) {\n var values = _.toArray(arguments).slice(1);\n\n values.forEach(function (value) {\n if (!~arr.indexOf(value)) {\n arr.push(value);\n }\n });\n\n return this;\n },\n\n /**\n * Inserts specified item into container at a specified position.\n *\n * @param {*} item - Item to be inserted into container.\n * @param {Array} container - Container of items.\n * @param {*} [position=-1] - Position at which item should be inserted.\n * Position can represent:\n * - specific index in container\n * - item which might already be present in container\n * - structure with one of these properties: after, before\n * @returns {Boolean|*}\n * - true if element has changed its' position\n * - false if nothing has changed\n * - inserted value if it wasn't present in container\n */\n insert: function (item, container, position) {\n var currentIndex = getIndex(item, container),\n newIndex,\n target;\n\n if (typeof position === 'undefined') {\n position = -1;\n } else if (typeof position === 'string') {\n position = isNaN(+position) ? position : +position;\n }\n\n newIndex = position;\n\n if (~currentIndex) {\n target = container.splice(currentIndex, 1)[0];\n\n if (typeof item === 'string') {\n item = target;\n }\n }\n\n if (typeof position !== 'number') {\n target = position.after || position.before || position;\n\n newIndex = getIndex(target, container);\n\n if (~newIndex && (position.after || newIndex >= currentIndex)) {\n newIndex++;\n }\n }\n\n if (newIndex < 0) {\n newIndex += container.length + 1;\n }\n\n container[newIndex] ?\n container.splice(newIndex, 0, item) :\n container[newIndex] = item;\n\n return !~currentIndex ? item : currentIndex !== newIndex;\n },\n\n /**\n * @param {Array} elems\n * @param {Number} offset\n * @return {Number|*}\n */\n formatOffset: function (elems, offset) {\n if (utils.isEmpty(offset)) {\n offset = -1;\n }\n\n offset = +offset;\n\n if (offset < 0) {\n offset += elems.length + 1;\n }\n\n return offset;\n }\n };\n});\n","mage/utils/compare.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'underscore',\n 'mage/utils/objects'\n], function (_, utils) {\n 'use strict';\n\n var result = [];\n\n /**\n * Checks if all of the provided arrays contains equal values.\n *\n * @param {(Boolean|Array)} [keepOrder=false]\n * @param {Array} target\n * @returns {Boolean}\n */\n function equalArrays(keepOrder, target) {\n var args = _.toArray(arguments),\n arrays;\n\n if (!Array.isArray(keepOrder)) {\n arrays = args.slice(2);\n } else {\n target = keepOrder;\n keepOrder = false;\n arrays = args.slice(1);\n }\n\n if (!arrays.length) {\n return true;\n }\n\n return arrays.every(function (array) {\n if (array === target) {\n return true;\n } else if (array.length !== target.length) {\n return false;\n } else if (!keepOrder) {\n return !_.difference(target, array).length;\n }\n\n return array.every(function (value, index) {\n return target.indexOf(value) === index;\n });\n });\n }\n\n /**\n * Checks if two values are different.\n *\n * @param {*} a - First value.\n * @param {*} b - Second value.\n * @returns {Boolean}\n */\n function isDifferent(a, b) {\n var oldIsPrimitive = utils.isPrimitive(a);\n\n if (Array.isArray(a) && Array.isArray(b)) {\n return !equalArrays(true, a, b);\n }\n\n return oldIsPrimitive ? a !== b : true;\n }\n\n /**\n * @param {String} prefix\n * @param {String} part\n */\n function getPath(prefix, part) {\n return prefix ? prefix + '.' + part : part;\n }\n\n /**\n * Checks if object has own specified property.\n *\n * @param {*} obj - Value to be checked.\n * @param {String} key - Key of the property.\n * @returns {Boolean}\n */\n function hasOwn(obj, key) {\n return Object.prototype.hasOwnProperty.call(obj, key);\n }\n\n /**\n * @param {Array} changes\n */\n function getContainers(changes) {\n var containers = {},\n indexed = _.indexBy(changes, 'path');\n\n _.each(indexed, function (change, name) {\n var path;\n\n name.split('.').forEach(function (part) {\n path = getPath(path, part);\n\n if (path in indexed) {\n return;\n }\n\n (containers[path] = containers[path] || []).push(change);\n });\n });\n\n return containers;\n }\n\n /**\n * @param {String} path\n * @param {String} name\n * @param {String} type\n * @param {String} newValue\n * @param {String} oldValue\n */\n function addChange(path, name, type, newValue, oldValue) {\n var data;\n\n data = {\n path: path,\n name: name,\n type: type\n };\n\n if (type !== 'remove') {\n data.value = newValue;\n data.oldValue = oldValue;\n } else {\n data.oldValue = newValue;\n }\n\n result.push(data);\n }\n\n /**\n * @param {String} ns\n * @param {String} name\n * @param {String} type\n * @param {String} iterator\n * @param {String} placeholder\n */\n function setAll(ns, name, type, iterator, placeholder) {\n var key;\n\n if (arguments.length > 4) {\n type === 'add' ?\n addChange(ns, name, 'update', iterator, placeholder) :\n addChange(ns, name, 'update', placeholder, iterator);\n } else {\n addChange(ns, name, type, iterator);\n }\n\n if (!utils.isObject(iterator)) {\n return;\n }\n\n for (key in iterator) {\n if (hasOwn(iterator, key)) {\n setAll(getPath(ns, key), key, type, iterator[key]);\n }\n }\n }\n\n /*eslint-disable max-depth*/\n /**\n * @param {Object} old\n * @param {Object} current\n * @param {String} ns\n * @param {String} name\n */\n function compare(old, current, ns, name) {\n var key,\n oldIsObj = utils.isObject(old),\n newIsObj = utils.isObject(current);\n\n if (oldIsObj && newIsObj) {\n for (key in old) {\n if (hasOwn(old, key) && !hasOwn(current, key)) {\n setAll(getPath(ns, key), key, 'remove', old[key]);\n }\n }\n\n for (key in current) {\n if (hasOwn(current, key)) {\n hasOwn(old, key) ?\n compare(old[key], current[key], getPath(ns, key), key) :\n setAll(getPath(ns, key), key, 'add', current[key]);\n }\n }\n } else if (oldIsObj) {\n setAll(ns, name, 'remove', old, current);\n } else if (newIsObj) {\n setAll(ns, name, 'add', current, old);\n } else if (isDifferent(old, current)) {\n addChange(ns, name, 'update', current, old);\n }\n }\n\n /*eslint-enable max-depth*/\n\n return {\n\n /**\n *\n * @returns {Object}\n */\n compare: function () {\n var changes;\n\n compare.apply(null, arguments);\n\n changes = result.splice(0);\n\n return {\n containers: getContainers(changes),\n changes: changes,\n equal: !changes.length\n };\n },\n\n equalArrays: equalArrays\n };\n});\n","mage/utils/strings.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'underscore'\n], function (_) {\n 'use strict';\n\n var jsonRe = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/;\n\n return {\n\n /**\n * Attempts to convert string to one of the primitive values,\n * or to parse it as a valid json object.\n *\n * @param {String} str - String to be processed.\n * @returns {*}\n */\n castString: function (str) {\n try {\n str = str === 'true' ? true :\n str === 'false' ? false :\n str === 'null' ? null :\n +str + '' === str ? +str :\n jsonRe.test(str) ? JSON.parse(str) :\n str;\n } catch (e) {\n }\n\n return str;\n },\n\n /**\n * Splits string by separator if it's possible,\n * otherwise returns the incoming value.\n *\n * @param {(String|Array|*)} str - String to split.\n * @param {String} [separator=' '] - Seperator based on which to split the string.\n * @returns {Array|*} Splitted string or the incoming value.\n */\n stringToArray: function (str, separator) {\n separator = separator || ' ';\n\n return typeof str === 'string' ?\n str.split(separator) :\n str;\n },\n\n /**\n * Converts the incoming string which consists\n * of a specified delimiters into a format commonly used in form elements.\n *\n * @param {String} name - The incoming string.\n * @param {String} [separator='.']\n * @returns {String} Serialized string.\n *\n * @example\n * utils.serializeName('one.two.three');\n * => 'one[two][three]';\n */\n serializeName: function (name, separator) {\n var result;\n\n separator = separator || '.';\n name = name.split(separator);\n\n result = name.shift();\n\n name.forEach(function (part) {\n result += '[' + part + ']';\n });\n\n return result;\n },\n\n /**\n * Checks wether the incoming value is not empty,\n * e.g. not 'null' or 'undefined'\n *\n * @param {*} value - Value to check.\n * @returns {Boolean}\n */\n isEmpty: function (value) {\n return value === '' || _.isUndefined(value) || _.isNull(value);\n },\n\n /**\n * Adds 'prefix' to the 'part' value if it was provided.\n *\n * @param {String} prefix\n * @param {String} part\n * @returns {String}\n */\n fullPath: function (prefix, part) {\n return prefix ? prefix + '.' + part : part;\n },\n\n /**\n * Splits incoming string and returns its' part specified by offset.\n *\n * @param {String} parts\n * @param {Number} [offset]\n * @param {String} [delimiter=.]\n * @returns {String}\n */\n getPart: function (parts, offset, delimiter) {\n delimiter = delimiter || '.';\n parts = parts.split(delimiter);\n offset = this.formatOffset(parts, offset);\n\n parts.splice(offset, 1);\n\n return parts.join(delimiter) || '';\n },\n\n /**\n * Converts nameThroughCamelCase to name-through-minus\n *\n * @param {String} string\n * @returns {String}\n */\n camelCaseToMinus: function camelCaseToMinus(string) {\n return ('' + string)\n .split('')\n .map(function (symbol, index) {\n return index ?\n symbol.toUpperCase() === symbol ?\n '-' + symbol.toLowerCase() :\n symbol :\n symbol.toLowerCase();\n })\n .join('');\n },\n\n /**\n * Converts name-through-minus to nameThroughCamelCase\n *\n * @param {String} string\n * @returns {String}\n */\n minusToCamelCase: function minusToCamelCase(string) {\n return ('' + string)\n .split('-')\n .map(function (part, index) {\n return index ? part.charAt(0).toUpperCase() + part.slice(1) : part;\n })\n .join('');\n }\n };\n});\n","mage/utils/main.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine(function (require) {\n 'use strict';\n\n var utils = {},\n _ = require('underscore');\n\n return _.extend(\n utils,\n require('./arrays'),\n require('./compare'),\n require('./misc'),\n require('./objects'),\n require('./strings'),\n require('./template')\n );\n});\n","mage/utils/template.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* eslint-disable no-shadow */\n\ndefine([\n 'jquery',\n 'underscore',\n 'mage/utils/objects',\n 'mage/utils/strings'\n], function ($, _, utils, stringUtils) {\n 'use strict';\n\n var tmplSettings = _.templateSettings,\n interpolate = /\\$\\{([\\s\\S]+?)\\}/g,\n opener = '${',\n template,\n hasStringTmpls;\n\n /**\n * Identifies whether ES6 templates are supported.\n */\n hasStringTmpls = (function () {\n var testString = 'var foo = \"bar\"; return `${ foo }` === foo';\n\n try {\n return Function(testString)();\n } catch (e) {\n return false;\n }\n })();\n\n if (hasStringTmpls) {\n\n /*eslint-disable no-unused-vars, no-eval*/\n /**\n * Evaluates template string using ES6 templates.\n *\n * @param {String} tmpl - Template string.\n * @param {Object} $ - Data object used in a template.\n * @returns {String} Compiled template.\n */\n template = function (tmpl, $) {\n return eval('`' + tmpl + '`');\n };\n\n /*eslint-enable no-unused-vars, no-eval*/\n } else {\n\n /**\n * Fallback function used when ES6 templates are not supported.\n * Uses underscore templates renderer.\n *\n * @param {String} tmpl - Template string.\n * @param {Object} data - Data object used in a template.\n * @returns {String} Compiled template.\n */\n template = function (tmpl, data) {\n var cached = tmplSettings.interpolate;\n\n tmplSettings.interpolate = interpolate;\n\n tmpl = _.template(tmpl, {\n variable: '$'\n })(data);\n\n tmplSettings.interpolate = cached;\n\n return tmpl;\n };\n }\n\n /**\n * Checks if provided value contains template syntax.\n *\n * @param {*} value - Value to be checked.\n * @returns {Boolean}\n */\n function isTemplate(value) {\n return typeof value === 'string' &&\n value.indexOf(opener) !== -1 &&\n // the below pattern almost always indicates an accident which should not cause template evaluation\n // refuse to evaluate\n value.indexOf('${{') === -1;\n }\n\n /**\n * Iteratively processes provided string\n * until no templates syntax will be found.\n *\n * @param {String} tmpl - Template string.\n * @param {Object} data - Data object used in a template.\n * @param {Boolean} [castString=false] - Flag that indicates whether template\n * should be casted after evaluation to a value of another type or\n * that it should be leaved as a string.\n * @returns {*} Compiled template.\n */\n function render(tmpl, data, castString) {\n var last = tmpl;\n\n while (~tmpl.indexOf(opener)) {\n tmpl = template(tmpl, data);\n\n if (tmpl === last) {\n break;\n }\n\n last = tmpl;\n }\n\n return castString ?\n stringUtils.castString(tmpl) :\n tmpl;\n }\n\n return {\n\n /**\n * Applies provided data to the template.\n *\n * @param {Object|String} tmpl\n * @param {Object} [data] - Data object to match with template.\n * @param {Boolean} [castString=false] - Flag that indicates whether template\n * should be casted after evaluation to a value of another type or\n * that it should be leaved as a string.\n * @returns {*}\n *\n * @example Template defined as a string.\n * var source = { foo: 'Random Stuff', bar: 'Some' };\n *\n * utils.template('${ $.bar } ${ $.foo }', source);\n * => 'Some Random Stuff';\n *\n * @example Template defined as an object.\n * var tmpl = {\n * key: {'${ $.$data.bar }': '${ $.$data.foo }'},\n * foo: 'bar',\n * x1: 2, x2: 5,\n * delta: '${ $.x2 - $.x1 }',\n * baz: 'Upper ${ $.foo.toUpperCase() }'\n * };\n *\n * utils.template(tmpl, source);\n * => {\n * key: {'Some': 'Random Stuff'},\n * foo: 'bar',\n * x1: 2, x2: 5,\n * delta: 3,\n * baz: 'Upper BAR'\n * };\n */\n template: function (tmpl, data, castString, dontClone) {\n if (typeof tmpl === 'string') {\n return render(tmpl, data, castString);\n }\n\n if (!dontClone) {\n tmpl = utils.copy(tmpl);\n }\n\n tmpl.$data = data || {};\n\n /**\n * Template iterator function.\n */\n _.each(tmpl, function iterate(value, key, list) {\n if (key === '$data') {\n return;\n }\n\n if (isTemplate(key)) {\n delete list[key];\n\n key = render(key, tmpl);\n list[key] = value;\n }\n\n if (isTemplate(value)) {\n list[key] = render(value, tmpl, castString);\n } else if ($.isPlainObject(value) || Array.isArray(value)) {\n _.each(value, iterate);\n }\n });\n\n delete tmpl.$data;\n\n return tmpl;\n }\n };\n});\n","mage/utils/misc.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'underscore',\n 'jquery',\n 'FormData'\n], function (_, $) {\n 'use strict';\n\n var defaultAttributes,\n ajaxSettings,\n map;\n\n defaultAttributes = {\n method: 'post',\n enctype: 'multipart/form-data'\n };\n\n ajaxSettings = {\n default: {\n method: 'POST',\n cache: false,\n processData: false,\n contentType: false\n },\n simple: {\n method: 'POST',\n dataType: 'json'\n }\n };\n\n map = {\n 'D': 'DDD',\n 'dd': 'DD',\n 'd': 'D',\n 'EEEE': 'dddd',\n 'EEE': 'ddd',\n 'e': 'd',\n 'yyyy': 'YYYY',\n 'yy': 'YY',\n 'y': 'YYYY',\n 'a': 'A'\n };\n\n return {\n\n /**\n * Generates a unique identifier.\n *\n * @param {Number} [size=7] - Length of a resulting identifier.\n * @returns {String}\n */\n uniqueid: function (size) {\n var code = Math.random() * 25 + 65 | 0,\n idstr = String.fromCharCode(code);\n\n size = size || 7;\n\n while (idstr.length < size) {\n code = Math.floor(Math.random() * 42 + 48);\n\n if (code < 58 || code > 64) {\n idstr += String.fromCharCode(code);\n }\n }\n\n return idstr;\n },\n\n /**\n * Limits function call.\n *\n * @param {Object} owner\n * @param {String} target\n * @param {Number} limit\n */\n limit: function (owner, target, limit) {\n var fn = owner[target];\n\n owner[target] = _.debounce(fn.bind(owner), limit);\n },\n\n /**\n * Converts mage date format to a moment.js format.\n *\n * @param {String} mageFormat\n * @returns {String}\n */\n normalizeDate: function (mageFormat) {\n var result = mageFormat;\n\n _.each(map, function (moment, mage) {\n result = result.replace(mage, moment);\n });\n\n return result;\n },\n\n /**\n * Puts provided value in range of min and max parameters.\n *\n * @param {Number} value - Value to be located.\n * @param {Number} min - Min value.\n * @param {Number} max - Max value.\n * @returns {Number}\n */\n inRange: function (value, min, max) {\n return Math.min(Math.max(min, value), max);\n },\n\n /**\n * Serializes and sends data via POST request.\n *\n * @param {Object} options - Options object that consists of\n * a 'url' and 'data' properties.\n * @param {Object} attrs - Attributes that will be added to virtual form.\n */\n submit: function (options, attrs) {\n var form = document.createElement('form'),\n data = this.serialize(options.data),\n attributes = _.extend({}, defaultAttributes, attrs || {});\n\n if (!attributes.action) {\n attributes.action = options.url;\n }\n\n data['form_key'] = window.FORM_KEY;\n\n _.each(attributes, function (value, name) {\n form.setAttribute(name, value);\n });\n\n data = _.map(\n data,\n function (value, name) {\n return '<input type=\"hidden\" ' +\n 'name=\"' + _.escape(name) + '\" ' +\n 'value=\"' + _.escape(value) + '\"' +\n ' />';\n }\n ).join('');\n\n form.insertAdjacentHTML('afterbegin', data);\n document.body.appendChild(form);\n\n form.submit();\n },\n\n /**\n * Serializes and sends data via AJAX POST request.\n *\n * @param {Object} options - Options object that consists of\n * a 'url' and 'data' properties.\n * @param {Object} config\n */\n ajaxSubmit: function (options, config) {\n var t = new Date().getTime(),\n settings;\n\n options.data['form_key'] = window.FORM_KEY;\n options.data = this.prepareFormData(options.data, config.ajaxSaveType);\n settings = _.extend({}, ajaxSettings[config.ajaxSaveType], options || {});\n\n if (!config.ignoreProcessEvents) {\n $('body').trigger('processStart');\n }\n\n return $.ajax(settings)\n .done(function (data) {\n if (config.response) {\n data.t = t;\n config.response.data(data);\n config.response.status(undefined);\n config.response.status(!data.error);\n }\n })\n .fail(function () {\n config.response.status(undefined);\n config.response.status(false);\n config.response.data({\n error: true,\n messages: 'Something went wrong.',\n t: t\n });\n })\n .always(function () {\n if (!config.ignoreProcessEvents) {\n $('body').trigger('processStop');\n }\n });\n },\n\n /**\n * Creates FormData object and append this data.\n *\n * @param {Object} data\n * @param {String} type\n * @returns {FormData}\n */\n prepareFormData: function (data, type) {\n var formData;\n\n if (type === 'default') {\n formData = new FormData();\n _.each(this.serialize(data), function (val, name) {\n formData.append(name, val);\n });\n } else if (type === 'simple') {\n formData = this.serialize(data);\n }\n\n return formData;\n },\n\n /**\n * Filters data object. Finds properties with suffix\n * and sets their values to properties with the same name without suffix.\n *\n * @param {Object} data - The data object that should be filtered\n * @param {String} suffix - The string by which data object should be filtered\n * @param {String} separator - The string that is separator between property and suffix\n *\n * @returns {Object} Filtered data object\n */\n filterFormData: function (data, suffix, separator) {\n data = data || {};\n suffix = suffix || 'prepared-for-send';\n separator = separator || '-';\n\n _.each(data, function (value, key) {\n if (_.isObject(value) && !value.length) {\n this.filterFormData(value, suffix, separator);\n } else if (_.isString(key) && ~key.indexOf(suffix)) {\n data[key.split(separator)[0]] = value;\n delete data[key];\n }\n }, this);\n\n return data;\n },\n\n /**\n * Replaces symbol codes with their unescaped counterparts.\n *\n * @param {String} data\n *\n * @returns {String}\n */\n unescape: function (data) {\n var unescaped = _.unescape(data),\n mapCharacters = {\n ''': '\\''\n };\n\n _.each(mapCharacters, function (value, key) {\n unescaped = unescaped.replace(key, value);\n });\n\n return unescaped;\n },\n\n /**\n * Converts PHP IntlFormatter format to moment format.\n *\n * @param {String} format - PHP format\n * @returns {String} - moment compatible formatting\n */\n convertToMomentFormat: function (format) {\n var newFormat;\n\n newFormat = format.replace(/yyyy|yy|y/, 'YYYY'); // replace the year\n newFormat = newFormat.replace(/dd|d/g, 'DD'); // replace the date\n\n return newFormat;\n },\n\n /**\n * Get Url Parameters.\n *\n * @param {String} url - Url string\n * @returns {Object}\n */\n getUrlParameters: function (url) {\n var params = {},\n queries = url.split('?'),\n temp,\n i,\n l;\n\n if (!queries[1]) {\n return params;\n }\n\n queries = queries[1].split('&');\n\n for (i = 0, l = queries.length; i < l; i++) {\n temp = queries[i].split('=');\n\n if (temp[1]) {\n params[temp[0]] = decodeURIComponent(temp[1].replace(/\\+/g, '%20'));\n } else {\n params[temp[0]] = '';\n }\n }\n\n return params;\n }\n };\n});\n","mage/utils/objects.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n 'ko',\n 'jquery',\n 'underscore'\n], function (ko, $, _) {\n 'use strict';\n\n var primitives = [\n 'undefined',\n 'boolean',\n 'number',\n 'string'\n ];\n\n /**\n * Sets nested property of a specified object.\n * @private\n *\n * @param {Object} parent - Object to look inside for the properties.\n * @param {Array} path - Splitted path the property.\n * @param {*} value - Value of the last property in 'path' array.\n * returns {*} New value for the property.\n */\n function setNested(parent, path, value) {\n var last = path.pop(),\n len = path.length,\n pi = 0,\n part = path[pi];\n\n for (; pi < len; part = path[++pi]) {\n if (!_.isObject(parent[part])) {\n parent[part] = {};\n }\n\n parent = parent[part];\n }\n\n if (typeof parent[last] === 'function') {\n parent[last](value);\n } else {\n parent[last] = value;\n }\n\n return value;\n }\n\n /**\n * Retrieves value of a nested property.\n * @private\n *\n * @param {Object} parent - Object to look inside for the properties.\n * @param {Array} path - Splitted path the property.\n * @returns {*} Value of the property.\n */\n function getNested(parent, path) {\n var exists = true,\n len = path.length,\n pi = 0;\n\n for (; pi < len && exists; pi++) {\n parent = parent[path[pi]];\n\n if (typeof parent === 'undefined') {\n exists = false;\n }\n }\n\n if (exists) {\n if (ko.isObservable(parent)) {\n parent = parent();\n }\n\n return parent;\n }\n }\n\n /**\n * Removes property from a specified object.\n * @private\n *\n * @param {Object} parent - Object from which to remove property.\n * @param {Array} path - Splitted path to the property.\n */\n function removeNested(parent, path) {\n var field = path.pop();\n\n parent = getNested(parent, path);\n\n if (_.isObject(parent)) {\n delete parent[field];\n }\n }\n\n return {\n\n /**\n * Retrieves or defines objects' property by a composite path.\n *\n * @param {Object} data - Container for the properties specified in path.\n * @param {String} path - Objects' properties divided by dots.\n * @param {*} [value] - New value for the last property.\n * @returns {*} Returns value of the last property in chain.\n *\n * @example\n * utils.nested({}, 'one.two', 3);\n * => { one: {two: 3} }\n */\n nested: function (data, path, value) {\n var action = arguments.length > 2 ? setNested : getNested;\n\n path = path ? path.split('.') : [];\n\n return action(data, path, value);\n },\n\n /**\n * Removes nested property from an object.\n *\n * @param {Object} data - Data source.\n * @param {String} path - Path to the property e.g. 'one.two.three'\n */\n nestedRemove: function (data, path) {\n path = path.split('.');\n\n removeNested(data, path);\n },\n\n /**\n * Flattens objects' nested properties.\n *\n * @param {Object} data - Object to flatten.\n * @param {String} [separator='.'] - Objects' keys separator.\n * @returns {Object} Flattened object.\n *\n * @example Example with a default separator.\n * utils.flatten({one: { two: { three: 'value'} }});\n * => { 'one.two.three': 'value' };\n *\n * @example Example with a custom separator.\n * utils.flatten({one: { two: { three: 'value'} }}, '=>');\n * => {'one=>two=>three': 'value'};\n */\n flatten: function (data, separator, parent, result) {\n separator = separator || '.';\n result = result || {};\n\n _.each(data, function (node, name) {\n if (parent) {\n name = parent + separator + name;\n }\n\n typeof node === 'object' ?\n this.flatten(node, separator, name, result) :\n result[name] = node;\n\n }, this);\n\n return result;\n },\n\n /**\n * Opposite operation of the 'flatten' method.\n *\n * @param {Object} data - Previously flattened object.\n * @param {String} [separator='.'] - Keys separator.\n * @returns {Object} Object with nested properties.\n *\n * @example Example using custom separator.\n * utils.unflatten({'one=>two': 'value'}, '=>');\n * => {\n * one: { two: 'value' }\n * };\n */\n unflatten: function (data, separator) {\n var result = {};\n\n separator = separator || '.';\n\n _.each(data, function (value, nodes) {\n nodes = nodes.split(separator);\n\n setNested(result, nodes, value);\n });\n\n return result;\n },\n\n /**\n * Same operation as 'flatten' method,\n * but returns objects' keys wrapped in '[]'.\n *\n * @param {Object} data - Object that should be serialized.\n * @returns {Object} Serialized data.\n *\n * @example\n * utils.serialize({one: { two: { three: 'value'} }});\n * => { 'one[two][three]': 'value' }\n */\n serialize: function (data) {\n var result = {};\n\n data = this.flatten(data);\n\n _.each(data, function (value, keys) {\n keys = this.serializeName(keys);\n value = _.isUndefined(value) ? '' : value;\n\n result[keys] = value;\n }, this);\n\n return result;\n },\n\n /**\n * Performs deep extend of specified objects.\n *\n * @returns {Object|Array} Extended object.\n */\n extend: function () {\n var args = _.toArray(arguments);\n\n args.unshift(true);\n\n return $.extend.apply($, args);\n },\n\n /**\n * Performs a deep clone of a specified object.\n *\n * @param {(Object|Array)} data - Data that should be copied.\n * @returns {Object|Array} Cloned object.\n */\n copy: function (data) {\n var result = data,\n isArray = Array.isArray(data),\n placeholder;\n\n if (this.isObject(data) || isArray) {\n placeholder = isArray ? [] : {};\n result = this.extend(placeholder, data);\n }\n\n return result;\n },\n\n /**\n * Performs a deep clone of a specified object.\n * Doesn't save links to original object.\n *\n * @param {*} original - Object to clone\n * @returns {*}\n */\n hardCopy: function (original) {\n if (original === null || typeof original !== 'object') {\n return original;\n }\n\n return JSON.parse(JSON.stringify(original));\n },\n\n /**\n * Removes specified nested properties from the target object.\n *\n * @param {Object} target - Object whose properties should be removed.\n * @param {(...String|Array|Object)} list - List that specifies properties to be removed.\n * @returns {Object} Modified object.\n *\n * @example Basic usage\n * var obj = {a: {b: 2}, c: 'a'};\n *\n * omit(obj, 'a.b');\n * => {'a.b': 2};\n * obj => {a: {}, c: 'a'};\n *\n * @example Various syntaxes that would return same result\n * omit(obj, ['a.b', 'c']);\n * omit(obj, 'a.b', 'c');\n * omit(obj, {'a.b': true, 'c': true});\n */\n omit: function (target, list) {\n var removed = {},\n ignored = list;\n\n if (this.isObject(list)) {\n ignored = [];\n\n _.each(list, function (value, key) {\n if (value) {\n ignored.push(key);\n }\n });\n } else if (_.isString(list)) {\n ignored = _.toArray(arguments).slice(1);\n }\n\n _.each(ignored, function (path) {\n var value = this.nested(target, path);\n\n if (!_.isUndefined(value)) {\n removed[path] = value;\n\n this.nestedRemove(target, path);\n }\n }, this);\n\n return removed;\n },\n\n /**\n * Checks if provided value is a plain object.\n *\n * @param {*} value - Value to be checked.\n * @returns {Boolean}\n */\n isObject: function (value) {\n var objProto = Object.prototype;\n\n return typeof value == 'object' ?\n objProto.toString.call(value) === '[object Object]' :\n false;\n },\n\n /**\n *\n * @param {*} value\n * @returns {Boolean}\n */\n isPrimitive: function (value) {\n return value === null || ~primitives.indexOf(typeof value);\n },\n\n /**\n * Iterates over obj props/array elems recursively, applying action to each one\n *\n * @param {Object|Array} data - Data to be iterated.\n * @param {Function} action - Callback to be called with each item as an argument.\n * @param {Number} [maxDepth=7] - Max recursion depth.\n */\n forEachRecursive: function (data, action, maxDepth) {\n maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n return;\n }\n\n if (!_.isObject(data)) {\n action(data);\n\n return;\n }\n\n _.each(data, function (value) {\n this.forEachRecursive(value, action, maxDepth);\n }, this);\n\n action(data);\n },\n\n /**\n * Maps obj props/array elems recursively\n *\n * @param {Object|Array} data - Data to be iterated.\n * @param {Function} action - Callback to transform each item.\n * @param {Number} [maxDepth=7] - Max recursion depth.\n *\n * @returns {Object|Array}\n */\n mapRecursive: function (data, action, maxDepth) {\n var newData;\n\n maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;\n\n if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {\n return data;\n }\n\n if (!_.isObject(data)) {\n return action(data);\n }\n\n if (_.isArray(data)) {\n newData = _.map(data, function (item) {\n return this.mapRecursive(item, action, maxDepth);\n }, this);\n\n return action(newData);\n }\n\n newData = _.mapObject(data, function (val, key) {\n if (data.hasOwnProperty(key)) {\n return this.mapRecursive(val, action, maxDepth);\n }\n\n return val;\n }, this);\n\n return action(newData);\n },\n\n /**\n * Removes empty(in common sence) obj props/array elems\n *\n * @param {*} data - Data to be cleaned.\n * @returns {*}\n */\n removeEmptyValues: function (data) {\n if (!_.isObject(data)) {\n return data;\n }\n\n if (_.isArray(data)) {\n return data.filter(function (item) {\n return !this.isEmptyObj(item);\n }, this);\n }\n\n return _.omit(data, this.isEmptyObj.bind(this));\n },\n\n /**\n * Checks that argument of any type is empty in common sence:\n * empty string, string with spaces only, object without own props, empty array, null or undefined\n *\n * @param {*} val - Value to be checked.\n * @returns {Boolean}\n */\n isEmptyObj: function (val) {\n\n return _.isObject(val) && _.isEmpty(val) ||\n this.isEmpty(val) ||\n val && val.trim && this.isEmpty(val.trim());\n }\n };\n});\n","Magento_CheckoutAgreements/js/model/place-order-mixin.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/utils/wrapper',\n 'Magento_CheckoutAgreements/js/model/agreements-assigner'\n], function ($, wrapper, agreementsAssigner) {\n 'use strict';\n\n return function (placeOrderAction) {\n\n /** Override default place order action and add agreement_ids to request */\n return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) {\n agreementsAssigner(paymentData);\n\n return originalAction(paymentData, messageContainer);\n });\n };\n});\n","Magento_CheckoutAgreements/js/model/agreement-validator.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'mage/validation'\n], function ($) {\n 'use strict';\n\n var checkoutConfig = window.checkoutConfig,\n agreementsConfig = checkoutConfig ? checkoutConfig.checkoutAgreements : {},\n agreementsInputPath = '.payment-method._active div.checkout-agreements input';\n\n return {\n /**\n * Validate checkout agreements\n *\n * @returns {Boolean}\n */\n validate: function (hideError) {\n var isValid = true;\n\n if (!agreementsConfig.isEnabled || $(agreementsInputPath).length === 0) {\n return true;\n }\n\n $(agreementsInputPath).each(function (index, element) {\n if (!$.validator.validateSingleElement(element, {\n errorElement: 'div',\n hideError: hideError || false\n })) {\n isValid = false;\n }\n });\n\n return isValid;\n }\n };\n});\n","Magento_CheckoutAgreements/js/model/agreements-modal.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'jquery',\n 'Magento_Ui/js/modal/modal',\n 'mage/translate'\n], function ($, modal, $t) {\n 'use strict';\n\n return {\n modalWindow: null,\n\n /**\n * Create popUp window for provided element.\n *\n * @param {HTMLElement} element\n */\n createModal: function (element) {\n var options;\n\n this.modalWindow = element;\n options = {\n 'type': 'popup',\n 'modalClass': 'agreements-modal',\n 'responsive': true,\n 'innerScroll': true,\n 'trigger': '.show-modal',\n 'buttons': [\n {\n text: $t('Close'),\n class: 'action secondary action-hide-popup',\n\n /** @inheritdoc */\n click: function () {\n this.closeModal();\n }\n }\n ]\n };\n modal(options, $(this.modalWindow));\n },\n\n /** Show login popup window */\n showModal: function () {\n $(this.modalWindow).modal('openModal');\n }\n };\n});\n","Magento_CheckoutAgreements/js/model/agreements-assigner.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/*global alert*/\ndefine([\n 'jquery'\n], function ($) {\n 'use strict';\n\n var agreementsConfig = window.checkoutConfig.checkoutAgreements;\n\n /** Override default place order action and add agreement_ids to request */\n return function (paymentData) {\n var agreementForm,\n agreementData,\n agreementIds;\n\n if (!agreementsConfig.isEnabled) {\n return;\n }\n\n agreementForm = $('.payment-method._active div[data-role=checkout-agreements] input');\n agreementData = agreementForm.serializeArray();\n agreementIds = [];\n\n agreementData.forEach(function (item) {\n agreementIds.push(item.value);\n });\n\n if (paymentData['extension_attributes'] === undefined) {\n paymentData['extension_attributes'] = {};\n }\n\n paymentData['extension_attributes']['agreement_ids'] = agreementIds;\n };\n});\n","Magento_CheckoutAgreements/js/model/set-payment-information-mixin.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/*global alert*/\ndefine([\n 'jquery',\n 'mage/utils/wrapper',\n 'Magento_CheckoutAgreements/js/model/agreements-assigner'\n], function ($, wrapper, agreementsAssigner) {\n 'use strict';\n\n return function (placeOrderAction) {\n\n /** Override place-order-mixin for set-payment-information action as they differs only by method signature */\n return wrapper.wrap(placeOrderAction, function (originalAction, messageContainer, paymentData) {\n agreementsAssigner(paymentData);\n\n return originalAction(messageContainer, paymentData);\n });\n };\n});\n","Magento_CheckoutAgreements/js/view/agreement-validation.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'uiComponent',\n 'Magento_Checkout/js/model/payment/additional-validators',\n 'Magento_CheckoutAgreements/js/model/agreement-validator'\n], function (Component, additionalValidators, agreementValidator) {\n 'use strict';\n\n additionalValidators.registerValidator(agreementValidator);\n\n return Component.extend({});\n});\n","Magento_CheckoutAgreements/js/view/checkout-agreements.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n 'ko',\n 'jquery',\n 'uiComponent',\n 'Magento_CheckoutAgreements/js/model/agreements-modal'\n], function (ko, $, Component, agreementsModal) {\n 'use strict';\n\n var checkoutConfig = window.checkoutConfig,\n agreementManualMode = 1,\n agreementsConfig = checkoutConfig ? checkoutConfig.checkoutAgreements : {};\n\n return Component.extend({\n defaults: {\n template: 'Magento_CheckoutAgreements/checkout/checkout-agreements'\n },\n isVisible: agreementsConfig.isEnabled,\n agreements: agreementsConfig.agreements,\n modalTitle: ko.observable(null),\n modalContent: ko.observable(null),\n modalWindow: null,\n\n /**\n * Checks if agreement required\n *\n * @param {Object} element\n */\n isAgreementRequired: function (element) {\n return element.mode == agreementManualMode; //eslint-disable-line eqeqeq\n },\n\n /**\n * Show agreement content in modal\n *\n * @param {Object} element\n */\n showContent: function (element) {\n this.modalTitle(element.checkboxText);\n this.modalContent(element.content);\n agreementsModal.showModal();\n },\n\n /**\n * build a unique id for the term checkbox\n *\n * @param {Object} context - the ko context\n * @param {Number} agreementId\n */\n getCheckboxId: function (context, agreementId) {\n var paymentMethodName = '',\n paymentMethodRenderer = context.$parents[1];\n\n // corresponding payment method fetched from parent context\n if (paymentMethodRenderer) {\n // item looks like this: {title: \"Check / Money order\", method: \"checkmo\"}\n paymentMethodName = paymentMethodRenderer.item ?\n paymentMethodRenderer.item.method : '';\n }\n\n return 'agreement_' + paymentMethodName + '_' + agreementId;\n },\n\n /**\n * Init modal window for rendered element\n *\n * @param {Object} element\n */\n initModal: function (element) {\n agreementsModal.createModal(element);\n }\n });\n});\n","Magento_OfflinePayments/js/view/payment/offline-payments.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'uiComponent',\n 'Magento_Checkout/js/model/payment/renderer-list'\n], function (Component, rendererList) {\n 'use strict';\n\n rendererList.push(\n {\n type: 'checkmo',\n component: 'Magento_OfflinePayments/js/view/payment/method-renderer/checkmo-method'\n },\n {\n type: 'banktransfer',\n component: 'Magento_OfflinePayments/js/view/payment/method-renderer/banktransfer-method'\n },\n {\n type: 'cashondelivery',\n component: 'Magento_OfflinePayments/js/view/payment/method-renderer/cashondelivery-method'\n },\n {\n type: 'purchaseorder',\n component: 'Magento_OfflinePayments/js/view/payment/method-renderer/purchaseorder-method'\n }\n );\n\n /** Add view logic here if needed */\n return Component.extend({});\n});\n","Magento_OfflinePayments/js/view/payment/method-renderer/checkmo-method.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'Magento_Checkout/js/view/payment/default'\n], function (Component) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Magento_OfflinePayments/payment/checkmo'\n },\n\n /**\n * Returns send check to info.\n *\n * @return {*}\n */\n getMailingAddress: function () {\n return window.checkoutConfig.payment.checkmo.mailingAddress;\n },\n\n /**\n * Returns payable to info.\n *\n * @return {*}\n */\n getPayableTo: function () {\n return window.checkoutConfig.payment.checkmo.payableTo;\n }\n });\n});\n","Magento_OfflinePayments/js/view/payment/method-renderer/banktransfer-method.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'ko',\n 'Magento_Checkout/js/view/payment/default'\n], function (ko, Component) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Magento_OfflinePayments/payment/banktransfer'\n },\n\n /**\n * Get value of instruction field.\n * @returns {String}\n */\n getInstructions: function () {\n return window.checkoutConfig.payment.instructions[this.item.method];\n }\n });\n});\n","Magento_OfflinePayments/js/view/payment/method-renderer/cashondelivery-method.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'Magento_Checkout/js/view/payment/default'\n], function (Component) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Magento_OfflinePayments/payment/cashondelivery'\n },\n\n /**\n * Returns payment method instructions.\n *\n * @return {*}\n */\n getInstructions: function () {\n return window.checkoutConfig.payment.instructions[this.item.method];\n }\n });\n});\n","Magento_OfflinePayments/js/view/payment/method-renderer/purchaseorder-method.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* @api */\ndefine([\n 'Magento_Checkout/js/view/payment/default',\n 'jquery',\n 'mage/validation'\n], function (Component, $) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Magento_OfflinePayments/payment/purchaseorder-form',\n purchaseOrderNumber: ''\n },\n\n /** @inheritdoc */\n initObservable: function () {\n this._super()\n .observe('purchaseOrderNumber');\n\n return this;\n },\n\n /**\n * @return {Object}\n */\n getData: function () {\n return {\n method: this.item.method,\n 'po_number': this.purchaseOrderNumber(),\n 'additional_data': null\n };\n },\n\n /**\n * @return {jQuery}\n */\n validate: function () {\n var form = 'form[data-role=purchaseorder-form]';\n\n return $(form).validation() && $(form).validation('isValid');\n }\n });\n});\n","Amazon_Login/js/amazon-logout.js":"/**\n * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\ndefine([\n 'jquery',\n 'amazonCore',\n 'jquery/ui',\n 'mage/cookies'\n], function ($, core) {\n 'use strict';\n\n $.widget('amazon.AmazonLogout', {\n options: {\n onInit: false\n },\n\n /**\n * Create Amazon Logout Widget\n * @private\n */\n _create: function () {\n if (this.options.onInit) {\n core.AmazonLogout(); //logout amazon user on init\n $.mage.cookies.clear('amazon_Login_accessToken');\n }\n },\n\n /**\n * Logs out a user if called directly\n * @private\n */\n _logoutAmazonUser: function () {\n core.AmazonLogout();\n $.mage.cookies.clear('amazon_Login_accessToken');\n }\n });\n\n return $.amazon.AmazonLogout;\n});\n","Amazon_Login/js/amazon-csrf.js":"/**\n * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\ndefine([\n 'sjcl',\n 'jquery',\n 'mage/cookies'\n], function (sjcl, $) {\n 'use strict';\n\n return {\n options: {\n wordsLength: 8,\n cookieName: 'amazon-csrf-state'\n },\n\n /**\n * Create random string for Amazon CSRF cookie\n */\n generateNewValue: function () {\n var randomString = sjcl.codec.base64.fromBits(sjcl.random.randomWords(this.options.wordsLength));\n\n $.mage.cookies.set(this.options.cookieName, randomString);\n\n return randomString;\n },\n\n /**\n * Check if Amazon CSRF cookie is valid and clear cookie\n * @param {String} stateString\n * @returns {Boolean}\n */\n isValid: function (stateString) {\n var isValid = $.mage.cookies.get(this.options.cookieName) === stateString;\n\n this.clear(); // always clear nonce when validating\n\n return isValid;\n },\n\n /**\n * Clear Amazon CSRF cookie\n */\n clear: function () {\n $.mage.cookies.clear(this.options.cookieName);\n }\n };\n});\n","Amazon_Login/js/amazon-redirect.js":"/**\n * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\ndefine([\n 'jquery',\n 'amazonCore',\n 'amazonPaymentConfig',\n 'amazonCsrf',\n 'Magento_Customer/js/customer-data',\n 'mage/loader',\n 'jquery/ui',\n 'mage/cookies'\n], function ($, amazonCore, amazonPaymentConfig, amazonCsrf, customerData) {\n 'use strict';\n\n var self;\n\n $.widget('amazon.AmazonRedirect', {\n\n /**\n * @private\n */\n _create: function () {\n\n self = this;\n // start the loading animation. WIll en on redirect, no explicit stop here\n $('body').trigger('processStart');\n\n //verify nonce first\n this.redirectOnInvalidState();\n\n // we don't have the customer's consent or invalid request\n this.redirectOnRequestWithError();\n this.setAuthStateCookies();\n amazonCore.amazonDefined.subscribe(function () {\n //only set this on the redirect page\n amazon.Login.setUseCookie(true); //eslint-disable-line no-undef\n amazonCore.verifyAmazonLoggedIn().then(function (loggedIn) {\n if (loggedIn) {\n self.redirect();\n } else {\n window.location = amazonPaymentConfig.getValue('customerLoginPageUrl');\n }\n }, function(error) {\n $('body').trigger('processStop');\n customerData.set('messages', {\n messages: [{\n type: 'error',\n text: error\n }]\n });\n });\n }, this);\n },\n\n /**\n * getURLParamater from URL for access OAuth Token\n * @param {String} name\n * @param {String} source\n * @returns {String|Null}\n */\n getURLParameter: function (name, source) {\n return decodeURIComponent((new RegExp('[?|&|#]' + name + '=' +\n '([^&]+?)(&|#|;|$)').exec(source) || [,''])[1].replace(\n /\\+/g,\n '%20'\n )) || null;\n },\n\n /**\n * Set State Cache Auth Cookies if they aren't already set\n * @returns {Boolean}\n */\n setAuthStateCookies: function () {\n var token = this.getURLParameter('access_token', location.hash);\n\n if (typeof token === 'string' && token.match(/^Atza/)) {\n $.mage.cookies.set('amazon_Login_accessToken', token);\n }\n\n return true;\n },\n\n /**\n * Redirect user to correct controller which logs them into M2 via Amazon hash\n */\n redirect: function () {\n window.location = amazonPaymentConfig.getValue('redirectUrl') + '?access_token=' +\n this.getURLParameter('access_token', location.hash);\n },\n\n /**\n * Redirect user on invalid state\n */\n redirectOnInvalidState: function () {\n var state = this.getURLParameter('state', location.hash);\n\n if (!state || !amazonCsrf.isValid(state)) {\n window.location = amazonPaymentConfig.getValue('customerLoginPageUrl');\n }\n },\n\n /**\n * Redirect user on request error\n */\n redirectOnRequestWithError: function () {\n if (this.getURLParameter('error', window.location)) {\n window.location = amazonPaymentConfig.getValue('customerLoginPageUrl');\n }\n }\n });\n\n return $.amazon.AmazonRedirect;\n});\n","Amazon_Login/js/view/login-button-wrapper.js":"/**\n * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\ndefine(['uiRegistry', 'Amazon_Login/js/view/login-button', 'uiComponent'], function(registry, login, component) {\n 'use strict';\n var amazonPayment = registry.get('amazonPayment');\n\n if (amazonPayment !== undefined && amazonPayment.allowAmLoginLoading === true) {\n return login;\n }\n return component;\n});\n","Amazon_Login/js/view/login-button.js":"/**\n * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\ndefine(\n [\n 'jquery',\n 'uiComponent',\n 'ko',\n 'Magento_Customer/js/model/customer',\n 'Amazon_Payment/js/model/storage',\n 'amazonPaymentConfig'\n ],\n function (\n $,\n Component,\n ko,\n customer,\n amazonStorage,\n amazonPaymentConfig\n ) {\n 'use strict';\n\n return Component.extend({\n defaults: {\n template: 'Amazon_Login/login-button'\n },\n isCustomerLoggedIn: customer.isLoggedIn,\n isAmazonAccountLoggedIn: amazonStorage.isAmazonAccountLoggedIn,\n isLwaVisible: ko.observable(amazonPaymentConfig.getValue('isLwaEnabled')),\n\n /**\n * Initialize login button\n */\n initialize: function () {\n this._super();\n }\n });\n }\n);\n","Magento_ConfigurableProduct/js/configurable.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n/**\n * @api\n */\ndefine([\n 'jquery',\n 'underscore',\n 'mage/template',\n 'mage/translate',\n 'priceUtils',\n 'priceBox',\n 'jquery/ui',\n 'jquery/jquery.parsequery'\n], function ($, _, mageTemplate, $t, priceUtils) {\n 'use strict';\n\n $.widget('mage.configurable', {\n options: {\n superSelector: '.super-attribute-select',\n selectSimpleProduct: '[name=\"selected_configurable_option\"]',\n priceHolderSelector: '.price-box',\n spConfig: {},\n state: {},\n priceFormat: {},\n optionTemplate: '<%- data.label %>' +\n '<% if (typeof data.finalPrice.value !== \"undefined\") { %>' +\n ' <%- data.finalPrice.formatted %>' +\n '<% } %>',\n mediaGallerySelector: '[data-gallery-role=gallery-placeholder]',\n mediaGalleryInitial: null,\n slyOldPriceSelector: '.sly-old-price',\n normalPriceLabelSelector: '.normal-price .price-label',\n\n /**\n * Defines the mechanism of how images of a gallery should be\n * updated when user switches between configurations of a product.\n *\n * As for now value of this option can be either 'replace' or 'prepend'.\n *\n * @type {String}\n */\n gallerySwitchStrategy: 'replace',\n tierPriceTemplateSelector: '#tier-prices-template',\n tierPriceBlockSelector: '[data-role=\"tier-price-block\"]',\n tierPriceTemplate: ''\n },\n\n /**\n * Creates widget\n * @private\n */\n _create: function () {\n // Initial setting of various option values\n this._initializeOptions();\n\n // Override defaults with URL query parameters and/or inputs values\n this._overrideDefaults();\n\n // Change events to check select reloads\n this._setupChangeEvents();\n\n // Fill state\n this._fillState();\n\n // Setup child and prev/next settings\n this._setChildSettings();\n\n // Setup/configure values to inputs\n this._configureForValues();\n\n $(this.element).trigger('configurable.initialized');\n },\n\n /**\n * Initialize tax configuration, initial settings, and options values.\n * @private\n */\n _initializeOptions: function () {\n var options = this.options,\n gallery = $(options.mediaGallerySelector),\n priceBoxOptions = $(this.options.priceHolderSelector).priceBox('option').priceConfig || null;\n\n if (priceBoxOptions && priceBoxOptions.optionTemplate) {\n options.optionTemplate = priceBoxOptions.optionTemplate;\n }\n\n if (priceBoxOptions && priceBoxOptions.priceFormat) {\n options.priceFormat = priceBoxOptions.priceFormat;\n }\n options.optionTemplate = mageTemplate(options.optionTemplate);\n options.tierPriceTemplate = $(this.options.tierPriceTemplateSelector).html();\n\n options.settings = options.spConfig.containerId ?\n $(options.spConfig.containerId).find(options.superSelector) :\n $(options.superSelector);\n\n options.values = options.spConfig.defaultValues || {};\n options.parentImage = $('[data-role=base-image-container] img').attr('src');\n\n this.inputSimpleProduct = this.element.find(options.selectSimpleProduct);\n\n gallery.data('gallery') ?\n this._onGalleryLoaded(gallery) :\n gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery));\n\n },\n\n /**\n * Override default options values settings with either URL query parameters or\n * initialized inputs values.\n * @private\n */\n _overrideDefaults: function () {\n var hashIndex = window.location.href.indexOf('#');\n\n if (hashIndex !== -1) {\n this._parseQueryParams(window.location.href.substr(hashIndex + 1));\n }\n\n if (this.options.spConfig.inputsInitialized) {\n this._setValuesByAttribute();\n }\n },\n\n /**\n * Parse query parameters from a query string and set options values based on the\n * key value pairs of the parameters.\n * @param {*} queryString - URL query string containing query parameters.\n * @private\n */\n _parseQueryParams: function (queryString) {\n var queryParams = $.parseQuery({\n query: queryString\n });\n\n $.each(queryParams, $.proxy(function (key, value) {\n this.options.values[key] = value;\n }, this));\n },\n\n /**\n * Override default options values with values based on each element's attribute\n * identifier.\n * @private\n */\n _setValuesByAttribute: function () {\n this.options.values = {};\n $.each(this.options.settings, $.proxy(function (index, element) {\n var attributeId;\n\n if (element.value) {\n attributeId = element.id.replace(/[a-z]*/, '');\n this.options.values[attributeId] = element.value;\n }\n }, this));\n },\n\n /**\n * Set up .on('change') events for each option element to configure the option.\n * @private\n */\n _setupChangeEvents: function () {\n $.each(this.options.settings, $.proxy(function (index, element) {\n $(element).on('change', this, this._configure);\n }, this));\n },\n\n /**\n * Iterate through the option settings and set each option's element configuration,\n * attribute identifier. Set the state based on the attribute identifier.\n * @private\n */\n _fillState: function () {\n $.each(this.options.settings, $.proxy(function (index, element) {\n var attributeId = element.id.replace(/[a-z]*/, '');\n\n if (attributeId && this.options.spConfig.attributes[attributeId]) {\n element.config = this.options.spConfig.attributes[attributeId];\n element.attributeId = attributeId;\n this.options.state[attributeId] = false;\n }\n }, this));\n },\n\n /**\n * Set each option's child settings, and next/prev option setting. Fill (initialize)\n * an option's list of selections as needed or disable an option's setting.\n * @private\n */\n _setChildSettings: function () {\n var childSettings = [],\n settings = this.options.settings,\n index = settings.length,\n option;\n\n while (index--) {\n option = settings[index];\n\n if (index) {\n option.disabled = true;\n } else {\n this._fillSelect(option);\n }\n\n _.extend(option, {\n childSettings: childSettings.slice(),\n prevSetting: settings[index - 1],\n nextSetting: settings[index + 1]\n });\n\n childSettings.push(option);\n }\n },\n\n /**\n * Setup for all configurable option settings. Set the value of the option and configure\n * the option, which sets its state, and initializes the option's choices, etc.\n * @private\n */\n _configureForValues: function () {\n if (this.options.values) {\n this.options.settings.each($.proxy(function (index, element) {\n var attributeId = element.attributeId;\n\n element.value = this.options.values[attributeId] || '';\n this._configureElement(element);\n }, this));\n }\n },\n\n /**\n * Event handler for configuring an option.\n * @private\n * @param {Object} event - Event triggered to configure an option.\n */\n _configure: function (event) {\n event.data._configureElement(this);\n },\n\n /**\n * Configure an option, initializing it's state and enabling related options, which\n * populates the related option's selection and resets child option selections.\n * @private\n * @param {*} element - The element associated with a configurable option.\n */\n _configureElement: function (element) {\n this.simpleProduct = this._getSimpleProductId(element);\n\n if (element.value) {\n this.options.state[element.config.id] = element.value;\n\n if (element.nextSetting) {\n element.nextSetting.disabled = false;\n this._fillSelect(element.nextSetting);\n this._resetChildren(element.nextSetting);\n } else {\n if (!!document.documentMode) { //eslint-disable-line\n this.inputSimpleProduct.val(element.options[element.selectedIndex].config.allowedProducts[0]);\n } else {\n this.inputSimpleProduct.val(element.selectedOptions[0].config.allowedProducts[0]);\n }\n }\n } else {\n this._resetChildren(element);\n }\n\n this._reloadPrice();\n this._displayRegularPriceBlock(this.simpleProduct);\n this._displayTierPriceBlock(this.simpleProduct);\n this._displayNormalPriceLabel();\n this._changeProductImage();\n },\n\n /**\n * Change displayed product image according to chosen options of configurable product\n *\n * @private\n */\n _changeProductImage: function () {\n var images,\n initialImages = this.options.mediaGalleryInitial,\n galleryObject = $(this.options.mediaGallerySelector).data('gallery');\n\n if (!galleryObject) {\n return;\n }\n\n images = this.options.spConfig.images[this.simpleProduct];\n\n if (images) {\n images = this._sortImages(images);\n\n if (this.options.gallerySwitchStrategy === 'prepend') {\n images = images.concat(initialImages);\n }\n\n images = $.extend(true, [], images);\n images = this._setImageIndex(images);\n\n galleryObject.updateData(images);\n\n $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({\n selectedOption: this.simpleProduct,\n dataMergeStrategy: this.options.gallerySwitchStrategy\n });\n } else {\n galleryObject.updateData(initialImages);\n $(this.options.mediaGallerySelector).AddFotoramaVideoEvents();\n }\n\n },\n\n /**\n * Sorting images array\n *\n * @private\n */\n _sortImages: function (images) {\n return _.sortBy(images, function (image) {\n return image.position;\n });\n },\n\n /**\n * Set correct indexes for image set.\n *\n * @param {Array} images\n * @private\n */\n _setImageIndex: function (images) {\n var length = images.length,\n i;\n\n for (i = 0; length > i; i++) {\n images[i].i = i + 1;\n }\n\n return images;\n },\n\n /**\n * For a given option element, reset all of its selectable options. Clear any selected\n * index, disable the option choice, and reset the option's state if necessary.\n * @private\n * @param {*} element - The element associated with a configurable option.\n */\n _resetChildren: function (element) {\n if (element.childSettings) {\n _.each(element.childSettings, function (set) {\n set.selectedIndex = 0;\n set.disabled = true;\n });\n\n if (element.config) {\n this.options.state[element.config.id] = false;\n }\n }\n },\n\n /**\n * Populates an option's selectable choices.\n * @private\n * @param {*} element - Element associated with a configurable option.\n */\n _fillSelect: function (element) {\n var attributeId = element.id.replace(/[a-z]*/, ''),\n options = this._getAttributeOptions(attributeId),\n prevConfig,\n index = 1,\n allowedProducts,\n i,\n j,\n basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount),\n optionFinalPrice,\n optionPriceDiff,\n optionPrices = this.options.spConfig.optionPrices,\n allowedProductMinPrice;\n\n this._clearSelect(element);\n element.options[0] = new Option('', '');\n element.options[0].innerHTML = this.options.spConfig.chooseText;\n prevConfig = false;\n\n if (element.prevSetting) {\n prevConfig = element.prevSetting.options[element.prevSetting.selectedIndex];\n }\n\n if (options) {\n for (i = 0; i < options.length; i++) {\n allowedProducts = [];\n optionPriceDiff = 0;\n\n /* eslint-disable max-depth */\n if (prevConfig) {\n for (j = 0; j < options[i].products.length; j++) {\n // prevConfig.config can be undefined\n if (prevConfig.config &&\n prevConfig.config.allowedProducts &&\n prevConfig.config.allowedProducts.indexOf(options[i].products[j]) > -1) {\n allowedProducts.push(options[i].products[j]);\n }\n }\n } else {\n allowedProducts = options[i].products.slice(0);\n\n if (typeof allowedProducts[0] !== 'undefined' &&\n typeof optionPrices[allowedProducts[0]] !== 'undefined') {\n allowedProductMinPrice = this._getAllowedProductWithMinPrice(allowedProducts);\n optionFinalPrice = parseFloat(optionPrices[allowedProductMinPrice].finalPrice.amount);\n optionPriceDiff = optionFinalPrice - basePrice;\n\n if (optionPriceDiff !== 0) {\n options[i].label = options[i].label + ' ' + priceUtils.formatPrice(\n optionPriceDiff,\n this.options.priceFormat,\n true);\n }\n }\n }\n\n if (allowedProducts.length > 0) {\n options[i].allowedProducts = allowedProducts;\n element.options[index] = new Option(this._getOptionLabel(options[i]), options[i].id);\n\n if (typeof options[i].price !== 'undefined') {\n element.options[index].setAttribute('price', options[i].price);\n }\n\n element.options[index].config = options[i];\n index++;\n }\n\n /* eslint-enable max-depth */\n }\n }\n },\n\n /**\n * Generate the label associated with a configurable option. This includes the option's\n * label or value and the option's price.\n * @private\n * @param {*} option - A single choice among a group of choices for a configurable option.\n * @return {String} The option label with option value and price (e.g. Black +1.99)\n */\n _getOptionLabel: function (option) {\n return option.label;\n },\n\n /**\n * Removes an option's selections.\n * @private\n * @param {*} element - The element associated with a configurable option.\n */\n _clearSelect: function (element) {\n var i;\n\n for (i = element.options.length - 1; i >= 0; i--) {\n element.remove(i);\n }\n },\n\n /**\n * Retrieve the attribute options associated with a specific attribute Id.\n * @private\n * @param {Number} attributeId - The id of the attribute whose configurable options are sought.\n * @return {Object} Object containing the attribute options.\n */\n _getAttributeOptions: function (attributeId) {\n if (this.options.spConfig.attributes[attributeId]) {\n return this.options.spConfig.attributes[attributeId].options;\n }\n },\n\n /**\n * Reload the price of the configurable product incorporating the prices of all of the\n * configurable product's option selections.\n */\n _reloadPrice: function () {\n $(this.options.priceHolderSelector).trigger('updatePrice', this._getPrices());\n },\n\n /**\n * Get product various prices\n * @returns {{}}\n * @private\n */\n _getPrices: function () {\n var prices = {},\n elements = _.toArray(this.options.settings),\n allowedProduct;\n\n _.each(elements, function (element) {\n var selected = element.options[element.selectedIndex],\n config = selected && selected.config,\n priceValue = {};\n\n if (config && config.allowedProducts.length === 1) {\n priceValue = this._calculatePrice(config);\n } else if (element.value) {\n allowedProduct = this._getAllowedProductWithMinPrice(config.allowedProducts);\n priceValue = this._calculatePrice({\n 'allowedProducts': [\n allowedProduct\n ]\n });\n }\n\n if (!_.isEmpty(priceValue)) {\n prices.prices = priceValue;\n }\n }, this);\n\n return prices;\n },\n\n /**\n * Get product with minimum price from selected options.\n *\n * @param {Array} allowedProducts\n * @returns {String}\n * @private\n */\n _getAllowedProductWithMinPrice: function (allowedProducts) {\n var optionPrices = this.options.spConfig.optionPrices,\n product = {},\n optionMinPrice, optionFinalPrice;\n\n _.each(allowedProducts, function (allowedProduct) {\n optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount);\n\n if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) {\n optionMinPrice = optionFinalPrice;\n product = allowedProduct;\n }\n }, this);\n\n return product;\n },\n\n /**\n * Returns prices for configured products\n *\n * @param {*} config - Products configuration\n * @returns {*}\n * @private\n */\n _calculatePrice: function (config) {\n var displayPrices = $(this.options.priceHolderSelector).priceBox('option').prices,\n newPrices = this.options.spConfig.optionPrices[_.first(config.allowedProducts)];\n\n _.each(displayPrices, function (price, code) {\n if (newPrices[code]) {\n displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount;\n }\n });\n\n return displayPrices;\n },\n\n /**\n * Returns Simple product Id\n * depending on current selected option.\n *\n * @private\n * @param {HTMLElement} element\n * @returns {String|undefined}\n */\n _getSimpleProductId: function (element) {\n // TODO: Rewrite algorithm. It should return ID of\n // simple product based on selected options.\n var allOptions = element.config.options,\n value = element.value,\n config;\n\n config = _.filter(allOptions, function (option) {\n return option.id === value;\n });\n config = _.first(config);\n\n return _.isEmpty(config) ?\n undefined :\n _.first(config.allowedProducts);\n\n },\n\n /**\n * Show or hide regular price block\n *\n * @param {*} optionId\n * @private\n */\n _displayRegularPriceBlock: function (optionId) {\n var shouldBeShown = true;\n\n _.each(this.options.settings, function (element) {\n if (element.value === '') {\n shouldBeShown = false;\n }\n });\n\n if (shouldBeShown &&\n this.options.spConfig.optionPrices[optionId].oldPrice.amount !==\n this.options.spConfig.optionPrices[optionId].finalPrice.amount\n ) {\n $(this.options.slyOldPriceSelector).show();\n } else {\n $(this.options.slyOldPriceSelector).hide();\n }\n\n $(document).trigger('updateMsrpPriceBlock',\n [\n optionId,\n this.options.spConfig.optionPrices\n ]\n );\n },\n\n /**\n * Show or hide normal price label\n *\n * @private\n */\n _displayNormalPriceLabel: function () {\n var shouldBeShown = false;\n\n _.each(this.options.settings, function (element) {\n if (element.value === '') {\n shouldBeShown = true;\n }\n });\n\n if (shouldBeShown) {\n $(this.options.normalPriceLabelSelector).show();\n } else {\n $(this.options.normalPriceLabelSelector).hide();\n }\n },\n\n /**\n * Callback which fired after gallery gets initialized.\n *\n * @param {HTMLElement} element - DOM element associated with gallery.\n */\n _onGalleryLoaded: function (element) {\n var galleryObject = element.data('gallery');\n\n this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();\n },\n\n /**\n * Show or hide tier price block\n *\n * @param {*} optionId\n * @private\n */\n _displayTierPriceBlock: function (optionId) {\n var options, tierPriceHtml;\n\n if (typeof optionId != 'undefined' &&\n this.options.spConfig.optionPrices[optionId].tierPrices != [] // eslint-disable-line eqeqeq\n ) {\n options = this.options.spConfig.optionPrices[optionId];\n\n if (this.options.tierPriceTemplate) {\n tierPriceHtml = mageTemplate(this.options.tierPriceTemplate, {\n 'tierPrices': options.tierPrices,\n '$t': $t,\n 'currencyFormat': this.options.spConfig.currencyFormat,\n 'priceUtils': priceUtils\n });\n $(this.options.tierPriceBlockSelector).html(tierPriceHtml).show();\n }\n } else {\n $(this.options.tierPriceBlockSelector).hide();\n }\n }\n });\n\n return $.mage.configurable;\n});\n","Magento_ConfigurableProduct/js/catalog-add-to-cart.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\nrequire([\n 'jquery'\n], function ($) {\n 'use strict';\n\n /**\n * Add selected configurable attributes to redirect url\n *\n * @see Magento_Catalog/js/catalog-add-to-cart\n */\n $('body').on('catalogCategoryAddToCartRedirect', function (event, data) {\n $(data.form).find('select[name*=\"super\"]').each(function (index, item) {\n data.redirectParameters.push(item.config.id + '=' + $(item).val());\n });\n });\n});\n","Magento_ConfigurableProduct/js/options-updater.js":"define([\n 'jquery',\n 'underscore',\n 'Magento_Customer/js/customer-data'\n], function ($, _, customerData) {\n 'use strict';\n\n var selectors = {\n formSelector: '#product_addtocart_form',\n productIdSelector: '#product_addtocart_form [name=\"product\"]',\n itemIdSelector: '#product_addtocart_form [name=\"item\"]'\n },\n cartData = customerData.get('cart'),\n productId = $(selectors.productIdSelector).val(),\n itemId = $(selectors.itemIdSelector).val(),\n\n /**\n * set productOptions according to cart data from customer-data\n *\n * @param {Object} data - cart data from customer-data\n * @returns {Boolean} - whether the new options differ from previous\n */\n setProductOptions = function (data) {\n var changedProductOptions;\n\n if (!(data && data.items && data.items.length && productId)) {\n return false;\n }\n changedProductOptions = _.find(data.items, function (item) {\n if (item['item_id'] === itemId) {\n return item['product_id'] === productId;\n }\n });\n changedProductOptions = changedProductOptions && changedProductOptions.options &&\n changedProductOptions.options.reduce(function (obj, val) {\n obj[val['option_id']] = val['option_value'];\n\n return obj;\n }, {});\n\n if (JSON.stringify(this.productOptions || {}) === JSON.stringify(changedProductOptions || {})) {\n return false;\n }\n\n this.productOptions = changedProductOptions;\n\n return true;\n },\n\n /**\n * Listens to update of cart data or options initialization and update selected option according to customer data\n *\n */\n listen = function () {\n cartData.subscribe(function (updateCartData) {\n if (this.setProductOptions(updateCartData)) {\n this.updateOptions();\n }\n }.bind(this));\n $(selectors.formSelector).on(this.eventName, function () {\n this.setProductOptions(cartData());\n this.updateOptions();\n }.bind(this));\n },\n\n /**\n * Updater constructor function\n *\n */\n Updater = function (eventName, updateOptionsCallback) {\n if (this instanceof Updater) {\n this.eventName = eventName;\n this.updateOptions = updateOptionsCallback;\n this.productOptions = {};\n }\n };\n\n Updater.prototype.setProductOptions = setProductOptions;\n Updater.prototype.listen = listen;\n\n return Updater;\n});\n","Magento_ConfigurableProduct/js/configurable-customer-data.js":"require([\n 'jquery',\n 'Magento_ConfigurableProduct/js/options-updater'\n], function ($, Updater) {\n 'use strict';\n\n var selectors = {\n formSelector: '#product_addtocart_form'\n },\n configurableWidgetName = 'mageConfigurable',\n widgetInitEvent = 'configurable.initialized',\n\n /**\n * Sets all configurable attribute's selected values\n */\n updateConfigurableOptions = function () {\n var configurableWidget = $(selectors.formSelector).data(configurableWidgetName);\n\n if (!configurableWidget) {\n return;\n }\n configurableWidget.options.values = this.productOptions || {};\n configurableWidget._configureForValues();\n },\n updater = new Updater(widgetInitEvent, updateConfigurableOptions);\n\n updater.listen();\n});\n"} }});