'\n ].join(newLine);\n }\n};\n\nvar storesListing = function (stores) {\n // list all stores on PDP page\n if ($('.store-list-pdp-container').length) {\n $('.store-list-pdp-container').remove();\n }\n $('.availability-results').append(pdpStoresListingTemplate(stores));\n};\n\nvar productInventory = {\n setPreferredStore: function (storeId) {\n User.storeId = storeId;\n $.ajax({\n url: Urls.setPreferredStore,\n type: 'POST',\n data: {storeId: storeId}\n });\n },\n productSelectStore: function () {\n var self = this;\n inventory.getStoresInventory(this.pid).then(function (stores) {\n inventory.selectStoreDialog({\n stores: stores,\n selectedStoreId: User.storeId,\n selectedStoreText: Resources.PREFERRED_STORE,\n continueCallback: storesListing,\n selectStoreCallback: self.setPreferredStore\n });\n }).done();\n },\n init: function () {\n var $availabilityContainer = $('.availability-results'),\n self = this;\n this.pid = $('input[name=\"pid\"]').val();\n\n $('#product-content .set-preferred-store').on('click', function (e) {\n e.preventDefault();\n if (!User.zip) {\n inventory.zipPrompt(function () {\n self.productSelectStore();\n });\n } else {\n self.productSelectStore();\n }\n });\n\n if ($availabilityContainer.length) {\n if (User.storeId) {\n inventory.getStoresInventory(this.pid).then(storesListing);\n }\n\n // See more or less stores in the listing\n $availabilityContainer.on('click', '.stores-toggle', function (e) {\n e.preventDefault();\n $('.store-list-pdp .store-list-item').toggleClass('visible');\n if ($(this).hasClass('collapsed')) {\n $(this).text(Resources.SEE_LESS);\n } else {\n $(this).text(Resources.SEE_MORE);\n }\n $(this).toggleClass('collapsed');\n });\n }\n }\n};\n\nmodule.exports = productInventory;\n","'use strict';\n\n/**\n * Checks the TLS and displays a warning if appropriate\n * @function getUserAgent Checks the TLS and displays a warning if appropriate\n **/\nfunction getUserAgent() {\n // Use an external service to check the TLS of the browser\n // NOTE: this implementation uses https://www.howsmyssl.com\n // you may also wish to consider the API available at https://www.ssllabs.com/projects/ssllabs-apis/index.html\n var url = 'https://www.howsmyssl.com/a/check';\n var cookieName = 'dw_TLSWarning';\n var cookieValue = getCookie(cookieName);\n\n // Test to see if this browser has already been flagged by looking at its cookies\n if (!cookieValue) {\n getTLS(url, function (message) {\n if (message.length > 0) {\n showWarning(message[0]);\n\n // the browser is bad - set the cookie to true (for 15 minutes)\n setCookie(cookieName, 'true', 15);\n } else {\n // else the browser is good, set the cookie to false (for 30 days) so we don't check again\n setCookie(cookieName, 'false', 60 * 24 * 30);\n }\n });\n } else if (cookieValue === 'true') {\n // if we already know that this is an invalid browser, show the warning\n showWarning(Resources.TLS_WARNING);\n }\n}\n\n/**\n * Calls out to the TLS service and calls the callback with a message (if necessary)\n * @function getTLS\n *\n * @param {string} url - URL of external TLS-checking API\n * @param {function} callback - function to call with response\n **/\nfunction getTLS(url, callback) {\n var message = [];\n\n // First, see if the browser is among the suspect browsers to see if a TLS check is necessary\n var userAgent = navigator.userAgent;\n\n /** This list derived from https://www.ssllabs.com/ssltest/clients.html **/\n var badBrowsers = ['MSIE 6.0','MSIE 7.0','MSIE 8.0','MSIE 9.0','MSIE 10.0',\n 'Android 2.3.7', 'Android 4.0.4', 'Android 4.1.1', 'Android 4.2.2', 'Android 4.3',\n 'Safari 5.1.9 / OS X 10.6.8', 'Safari 6.0.4 / OS X 10.8.4 '];\n function checkTLSLevel(data) {\n // If we can determine the TLS level, check to see if it's less than 1.2\n if (parseFloat(data.tls_version.split(' ')[1]) < 1.1) {\n message.push(Resources.TLS_WARNING);\n callback(message);\n\n //If you want to track statistics on bad TLS hits, include this call\n $.ajax({url: Urls.TLSBadTLS});\n }\n }\n\n function reportBadBrowser () {\n // If the TLS level cannot be determined just report that this browser is suspect\n message.push(Resources.TLS_WARNING);\n callback(message);\n\n //If you want to track statistics on deprecated browsers, include this call\n $.ajax({url: Urls.TLSBadBrowser});\n }\n\n for (var i = 0; i < badBrowsers.length; i++) {\n if (userAgent.match(badBrowsers[i])) {\n // It's a suspect browser, let's see what it's TLS level is\n $.ajax({\n url: url\n }).done(checkTLSLevel).fail(reportBadBrowser);\n break;\n }\n }\n\n /** For testing purposes, uncomment this block\n message.push(Resources.TLS_WARNING);\n **/\n callback(message);\n}\n\n/**\n * @function showWarning turns on the browser-compatibility-alert and sets the message\n *\n * @param {string} message - the message that will be shown upon detection of a bad browser\n **/\nfunction showWarning(message) {\n $('').addClass('browser-compatibility-alert').append($('').addClass('browser-error').html(message)).appendTo('#browser-check');\n}\n\n/**\n * @function getCookie\n *\n * @param {string} key - The cookie name\n * @returns {string} value - the value of the cookie if found, null otherwise\n **/\nfunction getCookie(key) {\n var cookies = document.cookie.split(';');\n for (var i = 0; i < cookies.length; i++) {\n var tokens = cookies[i].split('=');\n var cookieKey = tokens[0].trim();\n if (cookieKey === key) {\n return tokens[1];\n }\n }\n return '';\n}\n\n/**\n * @function setCookie\n *\n * @param {string} key - The cookie name\n * @param {string} value - The cookie value\n * @param {integer} minutes - The number of minutes to expire the cookie\n **/\nfunction setCookie (key, value, minutes) {\n var date = new Date();\n date.setTime(date + (minutes * 60 * 1000));\n\n document.cookie = key + '=' + value + '; expires=' + date.toGMTString() + '; path=/';\n}\n\n/**\n * Export the getUserAgent function\n */\n\nexports.getUserAgent = getUserAgent;\n","'use strict';\n\nvar _ = require('lodash');\n\nvar util = {\n /**\n * @function\n * @description appends the parameter with the given name and value to the given url and returns the changed url\n * @param {String} url the url to which the parameter will be added\n * @param {String} name the name of the parameter\n * @param {String} value the value of the parameter\n */\n appendParamToURL: function (url, name, value) {\n // quit if the param already exists\n if (url.indexOf(name + '=') !== -1) {\n return url;\n }\n var separator = url.indexOf('?') !== -1 ? '&' : '?';\n return url + separator + name + '=' + encodeURIComponent(value);\n },\n\n /**\n * @function\n * @description remove the parameter and its value from the given url and returns the changed url\n * @param {String} url the url from which the parameter will be removed\n * @param {String} name the name of parameter that will be removed from url\n */\n removeParamFromURL: function (url, name) {\n if (url.indexOf('?') === -1 || url.indexOf(name + '=') === -1) {\n return url;\n }\n var hash;\n var params;\n var domain = url.split('?')[0];\n var paramUrl = url.split('?')[1];\n var newParams = [];\n // if there is a hash at the end, store the hash\n if (paramUrl.indexOf('#') > -1) {\n hash = paramUrl.split('#')[1] || '';\n paramUrl = paramUrl.split('#')[0];\n }\n params = paramUrl.split('&');\n for (var i = 0; i < params.length; i++) {\n // put back param to newParams array if it is not the one to be removed\n if (params[i].split('=')[0] !== name) {\n newParams.push(params[i]);\n }\n }\n return domain + '?' + newParams.join('&') + (hash ? '#' + hash : '');\n },\n\n /**\n * @function\n * @description appends the parameters to the given url and returns the changed url\n * @param {String} url the url to which the parameters will be added\n * @param {Object} params\n */\n appendParamsToUrl: function (url, params) {\n var _url = url;\n _.each(params, function (value, name) {\n _url = this.appendParamToURL(_url, name, value);\n }.bind(this));\n return _url;\n },\n /**\n * @function\n * @description extract the query string from URL\n * @param {String} url the url to extra query string from\n **/\n getQueryString: function (url) {\n var qs;\n if (!_.isString(url)) { return; }\n var a = document.createElement('a');\n a.href = url;\n if (a.search) {\n qs = a.search.substr(1); // remove the leading ?\n }\n return qs;\n },\n /**\n * @function\n * @description\n * @param {String}\n * @param {String}\n */\n elementInViewport: function (el, offsetToTop) {\n var top = el.offsetTop,\n left = el.offsetLeft,\n width = el.offsetWidth,\n height = el.offsetHeight;\n\n while (el.offsetParent) {\n el = el.offsetParent;\n top += el.offsetTop;\n left += el.offsetLeft;\n }\n\n if (typeof(offsetToTop) !== 'undefined') {\n top -= offsetToTop;\n }\n\n if (window.pageXOffset !== null) {\n return (\n top < (window.pageYOffset + window.innerHeight) &&\n left < (window.pageXOffset + window.innerWidth) &&\n (top + height) > window.pageYOffset &&\n (left + width) > window.pageXOffset\n );\n }\n\n if (document.compatMode === 'CSS1Compat') {\n return (\n top < (window.document.documentElement.scrollTop + window.document.documentElement.clientHeight) &&\n left < (window.document.documentElement.scrollLeft + window.document.documentElement.clientWidth) &&\n (top + height) > window.document.documentElement.scrollTop &&\n (left + width) > window.document.documentElement.scrollLeft\n );\n }\n },\n\n /**\n * @function\n * @description Appends the parameter 'format=ajax' to a given path\n * @param {String} path the relative path\n */\n ajaxUrl: function (path) {\n return this.appendParamToURL(path, 'format', 'ajax');\n },\n\n /**\n * @function\n * @description\n * @param {String} url\n */\n toAbsoluteUrl: function (url) {\n if (url.indexOf('http') !== 0 && url.charAt(0) !== '/') {\n url = '/' + url;\n }\n return url;\n },\n /**\n * @function\n * @description Loads css dynamically from given urls\n * @param {Array} urls Array of urls from which css will be dynamically loaded.\n */\n loadDynamicCss: function (urls) {\n var i, len = urls.length;\n for (i = 0; i < len; i++) {\n this.loadedCssFiles.push(this.loadCssFile(urls[i]));\n }\n },\n\n /**\n * @function\n * @description Loads css file dynamically from given url\n * @param {String} url The url from which css file will be dynamically loaded.\n */\n loadCssFile: function (url) {\n return $('').appendTo($('head')).attr({\n type: 'text/css',\n rel: 'stylesheet'\n }).attr('href', url); // for i.e. <9, href must be added after link has been appended to head\n },\n // array to keep track of the dynamically loaded CSS files\n loadedCssFiles: [],\n\n /**\n * @function\n * @description Removes all css files which were dynamically loaded\n */\n clearDynamicCss: function () {\n var i = this.loadedCssFiles.length;\n while (0 > i--) {\n $(this.loadedCssFiles[i]).remove();\n }\n this.loadedCssFiles = [];\n },\n /**\n * @function\n * @description Extracts all parameters from a given query string into an object\n * @param {String} qs The query string from which the parameters will be extracted\n */\n getQueryStringParams: function (qs) {\n if (!qs || qs.length === 0) { return {}; }\n var params = {},\n unescapedQS = decodeURIComponent(qs);\n // Use the String::replace method to iterate over each\n // name-value pair in the string.\n unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'),\n function ($0, $1, $2, $3) {\n params[$1] = $3;\n }\n );\n return params;\n },\n\n fillAddressFields: function (address, $form) {\n for (var field in address) {\n if (field === 'ID' || field === 'UUID' || field === 'key') {\n continue;\n }\n // if the key in address object ends with 'Code', remove that suffix\n // keys that ends with 'Code' are postalCode, stateCode and countryCode\n $form.find('[name$=\"' + field.replace('Code', '') + '\"]').val(address[field]);\n // update the state fields\n if (field === 'countryCode') {\n $form.find('[name$=\"country\"]').trigger('change');\n // retrigger state selection after country has changed\n // this results in duplication of the state code, but is a necessary evil\n // for now because sometimes countryCode comes after stateCode\n $form.find('[name$=\"state\"]').val(address.stateCode);\n }\n }\n },\n /**\n * @function\n * @description Updates the number of the remaining character\n * based on the character limit in a text area\n */\n limitCharacters: function () {\n $('form').find('textarea[data-character-limit]').each(function () {\n var characterLimit = $(this).data('character-limit');\n var charCountHtml = String.format(Resources.CHAR_LIMIT_MSG,\n '' + characterLimit + '',\n '' + characterLimit + '');\n var charCountContainer = $(this).next('div.char-count');\n if (charCountContainer.length === 0) {\n charCountContainer = $('
').insertAfter($(this));\n }\n charCountContainer.html(charCountHtml);\n // trigger the keydown event so that any existing character data is calculated\n $(this).change();\n });\n },\n /**\n * @function\n * @description Binds the onclick-event to a delete button on a given container,\n * which opens a confirmation box with a given message\n * @param {String} container The name of element to which the function will be bind\n * @param {String} message The message the will be shown upon a click\n */\n setDeleteConfirmation: function (container, message) {\n $(container).on('click', '.delete', function () {\n return window.confirm(message);\n });\n },\n /**\n * @function\n * @description Scrolls a browser window to a given x point\n * @param {String} The x coordinate\n */\n scrollBrowser: function (xLocation) {\n $('html, body').animate({scrollTop: xLocation}, 500);\n },\n\n isMobile: function () {\n var mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];\n var idx = 0;\n var isMobile = false;\n var userAgent = (navigator.userAgent).toLowerCase();\n\n while (mobileAgentHash[idx] && !isMobile) {\n isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);\n idx++;\n }\n return isMobile;\n }\n};\n\nmodule.exports = util;\n","'use strict';\n\nvar util = require('org/util');\nvar page = require('base/page');\nvar ajax = require('base/ajax');\nvar cache = {};\nvar marker = false;\n\nfunction initCache() {\n var currentForm = '',\n saveButton = '',\n source = '',\n lookupButton = '',\n url = Urls.qasVerifyAddress;\n\n if ($('#RegistrationForm') && ($('#RegistrationForm').length > 0)){\n currentForm = $('#RegistrationForm');\n source = 'dwfrm_profile';\n saveButton = 'dwfrm_profile_confirm';\n url = Urls.qasVerifyEmail;\n }\n else if ($('#dwfrm_singleshipping_shippingAddress') && ($('#dwfrm_singleshipping_shippingAddress').length > 0)){\n currentForm = $('#dwfrm_singleshipping_shippingAddress');\n source = 'dwfrm_singleshipping_shippingAddress_addressFields';\n saveButton = 'dwfrm_singleshipping_shippingAddress_save';\n lookupButton = 'qas-lookup';\n }\n else if ($('#dwfrm_billing') && ($('#dwfrm_billing').length > 0)){\n currentForm = $('#dwfrm_billing');\n source = 'dwfrm_billing_billingAddress_addressFields';\n saveButton = 'dwfrm_billing_save';\n lookupButton = 'qas-lookup';\n }\n else if ($('#edit-address-form') && ($('#edit-address-form').length > 0)){\n currentForm = $('#edit-address-form');\n source = 'dwfrm_profile_address';\n saveButton = currentForm.find('.apply-button').attr('name');\n }\n\n cache = {\n dialog: require('org/dialog'),\n $form: currentForm, //change to - check the form\n $source: source,\n $save: saveButton,\n $lookupButton: lookupButton,\n $url: url\n }\n}\n\nfunction initEvents() {\n if (cache.$form.length > 0){\n cache.$form.on('click', 'button[name=\"'+cache.$save+'\"]', qas.eventHandlers.validate);\n }\n}\n\nfunction applyEditAddress($form) {\n if (!$form.valid()) {\n return false;\n }\n if ($('#addressid').length === 0) {\n var addressId = $form.find('input[name$=\"_addressid\"]').val();\n var url = util.appendParamsToUrl(Urls.checkIfAddressExists, {'addressID': addressId, 'format': 'ajax'});\n ajax.getJson({\n url: url,\n callback: function (data) {\n if (data && data.exists) {\n var $addressIdField = $form.find('input[name$=\"_addressid\"]');\n var $error = $addressIdField.next('span.error');\n if ($error.length > 0) {\n $error.html(data.message).show();\n } else {\n $addressIdField.after('' + data.message + '').show();\n }\n } else {\n applyAddress($form);\n }\n }\n });\n } else {\n applyAddress($form);\n }\n}\n\nfunction applyAddress($form) {\n $.ajax({\n url: util.appendParamToURL($form.attr('action'), 'format', 'ajax'),\n data: $form.serialize() + '&' + $form.find('.apply-button').attr('name') + '=x',\n type: 'POST'\n }).done(function () {\n page.refresh();\n });\n}\n\nfunction cacheFormSubmit() {\n if (cache.$source == 'dwfrm_profile_address') {\n applyEditAddress(cache.$form);\n }\n else {\n if (cache.$save == 'dwfrm_billing_save' && !marker){\n validation(cache.$form.serialize()+'&source='+cache.$source, Urls.qasVerifyEmail);\n return false;\n }\n cache.$form.append('');\n cache.$form.submit();\n }\n}\n\nfunction formAddrUpdate(addr, form) {\n var $form = cache.$form;\n if ((cache.$form == undefined || cache.$form.length == 0) && form != undefined && form.length > 0) {\n $form = form;\n }\n var state = addr.state || '';\n if ($form.find('input[name$=\"_address1\"]').val() != undefined && $form.find('input[name$=\"_address1\"]').val() != addr.add1) {\n $form.find('input[name$=\"_address1\"]').val(addr.add1);\n }\n if ($form.find('input[name$=\"_address2\"]').val() != undefined && $form.find('input[name$=\"_address2\"]').val() != addr.add2) {\n $form.find('input[name$=\"_address2\"]').val(addr.add2);\n }\n if ($form.find('input[name$=\"_state\"]').val() != undefined && $form.find('input[name$=\"_state\"]').val() != state.toUpperCase()) {\n $form.find('input[name$=\"_state\"]').val(state.toUpperCase());\n }\n if ($form.find('input[name$=\"_city\"]').val() != undefined && $form.find('input[name$=\"_city\"]').val() != addr.city) {\n $form.find('input[name$=\"_city\"]').val(addr.city);\n }\n if ($form.find('input[name$=\"_postal\"]').val() != undefined && $form.find('input[name$=\"_postal\"]').val() != addr.zip) {\n $form.find('input[name$=\"_postal\"]').val(addr.zip);\n }\n if ($form.find('input[name$=\"_lookup\"]').val() != undefined && $form.find('input[name$=\"_lookup\"]').val() != addr.zip) {\n $form.find('input[name$=\"_lookup\"]').val(addr.zip);\n }\n}\n\nfunction validation(data, url) {\n $.ajax({\n type: 'POST',\n url: url,\n data: data,\n dataType: 'html'\n }).done(function(content, textStatus, request) {\n var response = request.getResponseHeader('Content-Type');\n if (/json/.test(response)) {\n response = jQuery.parseJSON(content);\n if (response.action === 'error') console.log(response);\n if (response.action === 'addrVerified') {\n formAddrUpdate(response.address);\n cacheFormSubmit();\n }\n if (response.action === 'verified') {\n if (cache.$save == 'dwfrm_billing_save' && url == Urls.qasVerifyEmail){\n marker = true;\n }\n cacheFormSubmit();\n }\n }\n else {\n cache.dialog.create({\n target: $('#ModalViewDialog'),\n options: {\n width: '400',\n dialogClass: 'qas-dialog-address',\n title: 'Address Validation'\n }\n });\n cache.dialog.openWithContent({\n content: content\n });\n $('.addForm').on('click', 'button', qas.eventHandlers.submitAddr);\n $('.qas_email_phone_form').on('click', 'button', qas.eventHandlers.submitEmail);\n }\n }).fail(function () {\n console.log('[ERROR] ajax request: ' + url);\n });\n}\n\nvar qas = {\n\n init: function () {\n initCache();\n initEvents();\n if (!this.exists()) {\n this.$container = $('').attr('id', 'ModalViewDialog').appendTo(document.body);\n }\n },\n\n initAutocomplete: function(form) {\n var $form = $(form),\n $country = $form.find('select[id$=\"_country\"]:enabled');\n if ($country.length !== 0) {\n if ($country.val() === 'GB') {\n var $postalField = form.find('input[name$=\"_lookup\"]');\n $postalField.addClass('ui-autocomplete');\n $postalField.autocomplete({source: []}).data('ui-autocomplete')._renderItem = function (ul, item) {\n\n if (item.value === item.label){\n return $('
'+item.label+'
').appendTo(ul);\n } else {\n return $('
')\n .append('' + item.label + '')\n .appendTo(ul);\n }\n };\n $postalField.autocomplete('option', 'minLength', 0);\n }\n }\n },\n\n initLookupEvents: function(form) {\n var $form = $(form);\n if ($form.find('.qas-lookup-button').length > 0) {\n $form.on('click', 'button[name=\"qas-lookup\"]', qas.eventHandlers.lookup);\n }\n },\n\n eventHandlers: {\n\n submitAddr: function() {\n var form = $(this).parents('form');\n form.append('');\n var data = form.serialize();\n $.ajax({\n type: 'POST',\n url: Urls.qasUpdateAddress,\n data: data,\n dataType: 'html'\n }).done(function(response) {\n response = jQuery.parseJSON(response);\n if (response.action == 'reValidate') {\n cache.dialog.close();\n qas.eventHandlers.validate('reValidate');\n }\n else {\n if (response.action != 'useOrig') formAddrUpdate(response.address);\n cacheFormSubmit();\n }\n\n }).fail(function() {\n console.log('[ERROR] ajax request: ' + Urls.qasUpdateAddress);\n });\n cache.dialog.close();\n return false;\n },\n\n submitEmail: function() {\n var form = $(this).parents('form');\n var email = form.find('input[name=\"Email\"]').val();\n var useOrig = form.find('input[name=\"Override\"]').val();\n if (useOrig != 'true') {\n cache.$form.find('input[name$=\"_email\"]').val(email);\n cache.$form.find('input[name$=\"_emailconfirm\"]').val(email);\n if (cache.$save == 'dwfrm_billing_save'){\n cache.$form.find('input[name$=\"_emailAddress\"]').val(email);\n }\n }\n else {\n email = cache.$form.find('input[name$=\"_email\"]').val();\n cache.$form.find('input[name$=\"_emailconfirm\"]').val(email);\n }\n if (cache.$save == 'dwfrm_billing_save') marker = true;\n cacheFormSubmit();\n cache.dialog.close();\n return false;\n },\n\n validate: function(action) {\n if (cache.$form.validate().form()) {\n if ((cache.$source == 'dwfrm_singleshipping_shippingAddress_addressFields' || cache.$source == 'dwfrm_billing_billingAddress_addressFields')\n && cache.$form.find('select[id$=\"_country\"]:enabled').val() === 'GB' && cache.$form.find('.qas-lookup-button').length > 0) {\n return;\n }\n var data = cache.$form.serialize()+'&source='+cache.$source;\n if (typeof(action) === 'string') data = data + '&dwfrm_addForm_action='+action;\n validation(data, cache.$url);\n return false;\n }\n },\n\n lookup: function(e) {\n e.preventDefault();\n var form = $(this).parents('form');\n form.append('');\n var $postalField = form.find('input[name$=\"_lookup\"]');\n var source = ''\n if (form[0].id == 'dwfrm_singleshipping_shippingAddress') {\n source = 'dwfrm_singleshipping_shippingAddress_addressFields';\n } else if (form[0].id == 'dwfrm_billing') {\n source = 'dwfrm_billing_billingAddress_addressFields';\n }\n var data = form.serialize()+'&source='+source;\n $.ajax({\n type: 'POST',\n url: Urls.qasGetPickList,\n data: data,\n dataType: 'html'\n }).done(function(response) {\n response = jQuery.parseJSON(response);\n $postalField.autocomplete(\n {source: response});\n $postalField.autocomplete('search', '');\n $postalField.autocomplete('option', 'autoFocus', true);\n $postalField.autocomplete({\n select: function(event,ui) {\n console.log(ui.item.value);\n var url = util.appendParamsToUrl(Urls.qasSetPickListAddress, {'moniker': ui.item.value});\n $postalField.val($('.ui-state-hidden').text());\n $postalField.autocomplete({source: []});\n $postalField.autocomplete('search', '');\n var options = {\n url: url,\n type: 'GET'\n };\n\n $.ajax(options).done(function (response) {\n if (response.action === 'error') console.log(response);\n if (response.action === 'populate') {\n formAddrUpdate(response.address, form);\n }\n });\n }\n });\n\n }).fail(function() {\n console.log('[ERROR] ajax request: ' + Urls.qasUpdateAddress);\n });\n }\n },\n\n show: function () {\n if (!this.exists()) {\n this.init();\n }\n },\n\n exists: function () {\n return this.$container && (this.$container.length > 0);\n }\n};\n\nmodule.exports = qas;\n","'use strict';\nvar util = require('org/util');\nvar page = require('base/page');\nvar ajax = require('base/ajax');\nvar cache = {};\nvar marker = false;\n\nfunction initCache() {\n var currentForm = '',\n saveButton = '',\n source = '',\n lookupButton = '',\n url = Urls.qasVerifyAddress;\n\n if ($('#RegistrationForm') && ($('#RegistrationForm').length > 0)) {\n currentForm = $('#RegistrationForm');\n source = 'dwfrm_profile';\n saveButton = 'dwfrm_profile_confirm';\n url = Urls.qasVerifyEmail;\n } else if ($('#dwfrm_checkout').length > 0) {\n currentForm = $('#dwfrm_checkout');\n currentForm.addClass('QAS-enabled');\n source = ['dwfrm_checkout_shippingAddress_addressFields', 'dwfrm_checkout_billingAddress_addressFields'];\n saveButton = 'dwfrm_checkout_save';\n lookupButton = 'qas-lookup';\n } else if ($('#edit-address-form') && ($('#edit-address-form').length > 0)) {\n currentForm = $('#edit-address-form');\n source = 'dwfrm_profile_address';\n saveButton = currentForm.find('.apply-button').attr('name');\n }\n if (cache.$form && currentForm.attr('id') == cache.$form.attr('id')) {\n cache.inited = true;\n return;\n }\n\n cache = {\n dialog: require('org/dialog'),\n $form: currentForm, //change to - check the form\n $source: source,\n $save: saveButton,\n $lookupButton: lookupButton,\n $url: url,\n inited: false\n }\n}\n\nfunction initEvents() {\n if (cache.$form.length > 0 && !cache.inited) {\n cache.$form.on('click', 'button[name=\"'+cache.$save+'\"]', qas.eventHandlers.validate);\n }\n}\n\nfunction applyEditAddress($form) {\n if (!$form.valid()) {\n return false;\n }\n if ($('#addressid').length === 0) {\n var addressId = $form.find('input[name$=\"_addressid\"]').val();\n var url = util.appendParamsToUrl(Urls.checkIfAddressExists, {'addressID': addressId, 'format': 'ajax'});\n ajax.getJson({\n url: url,\n callback: function (data) {\n if (data && data.exists) {\n var $addressIdField = $form.find('input[name$=\"_addressid\"]');\n var $error = $addressIdField.next('span.error');\n if ($error.length > 0) {\n $error.html(data.message).show();\n } else {\n $addressIdField.after('' + data.message + '').show();\n }\n } else {\n applyAddress($form);\n }\n }\n });\n } else {\n applyAddress($form);\n }\n}\n\nfunction applyAddress($form) {\n $.ajax({\n url: util.appendParamToURL($form.attr('action'), 'format', 'ajax'),\n data: $form.serialize() + '&' + $form.find('.apply-button').attr('name') + '=x',\n type: 'POST'\n }).done(function () {\n page.refresh();\n });\n}\n\nfunction cacheFormSubmit() {\n if (cache.$source == 'dwfrm_profile_address') {\n applyEditAddress(cache.$form);\n } else {\n if (cache.$save == 'dwfrm_billing_save' && !marker){\n validation(cache.$form.serialize()+'&source='+cache.$source, Urls.qasVerifyEmail);\n return false;\n }\n\n cache.$form.append('');\n\n document.getElementById(cache.$form.attr('id')).submit();\n }\n}\n\nfunction formAddrUpdate(addr, form, source) {\n var $form = cache.$form;\n var state = addr.state || '';\n var name, nameLookup, qasAddressSection;\n var updateBilling = false;\n\n if ((cache.$form == undefined || cache.$form.length == 0) && form != undefined && form.length > 0) {\n $form = form;\n }\n\n if (source === 'dwfrm_checkout_shippingAddress_addressFields') {\n name = '_shippingAddress_addressFields';\n nameLookup = '_shippingAddress';\n qasAddressSection = 'shipping';\n if ($form.find('input[name$=\"_useAsBillingAddress\"]')[0].checked) {\n updateBilling = true;\n }\n } else if (source === 'dwfrm_checkout_billingAddress_addressFields') {\n name = '_billingAddress_addressFields';\n nameLookup = '_billingAddress';\n qasAddressSection = 'billing';\n } else {\n name = '';\n nameLookup ='';\n }\n\n $form.find('input[name$=\"' + name + '_address1\"]').val(addr.add1).trigger('change');\n $form.find('input[name$=\"' + name + '_address2\"]').val(addr.add2);\n $form.find('[id$=\"' + name + '_states_state\"]').val(state.toUpperCase());\n $form.find('input[name$=\"' + name + '_city\"]').val(addr.city).trigger('change');\n $form.find('input[name$=\"' + name + '_postal\"]').val(addr.zip).trigger('change');\n $form.find('input[name$=\"' + nameLookup + '_lookup\"]').val(addr.zip);\n if (updateBilling) {\n var country = $('select[id$=\"_shippingAddress_addressFields_country\"]').val();\n $('select[id$=\"_billingAddress_addressFields_country\"]').val(country).trigger('change');\n $form.find('input[name$=\"_billingAddress_addressFields_address1\"]').val(addr.add1).trigger('change');\n $form.find('input[name$=\"_billingAddress_addressFields_address2\"]').val(addr.add2);\n $form.find('[id$=\"_billingAddress_addressFields_states_state\"]').val(state.toUpperCase());\n $form.find('input[name$=\"_billingAddress_addressFields_city\"]').val(addr.city).trigger('change');\n $form.find('input[name$=\"_billingAddress_addressFields_postal\"]').val(addr.zip).trigger('change');\n }\n $('#' + qasAddressSection + '-address-container #qasAddress').remove();\n var qasAddress = '
'\n ].join('\\n');\n};\n\n// hide swatches that are not selected or not part of a Product Variation Group\nvar hideSwatches = function () {\n $('.bonus-product-item:not([data-producttype=\"master\"]) .swatches li').not('.selected').not('.variation-group-value').hide();\n // prevent unselecting the selected variant\n $('.bonus-product-item .swatches .selected').on('click', function () {\n return false;\n });\n};\n\n/**\n * @private\n * @function\n * @description Updates the summary page with the selected bonus product\n */\nfunction updateSummary() {\n var $bonusProductList = $('#bonus-product-list');\n if (!selectedList.length) {\n $bonusProductList.find('li.selected-bonus-item').remove();\n } else {\n var ulList = $bonusProductList.find('ul.selected-bonus-items').first();\n var i, len;\n for (i = 0, len = selectedList.length; i < len; i++) {\n var item = selectedList[i];\n var li = selectedItemTemplate(item);\n $(li).appendTo(ulList);\n }\n }\n\n // get remaining item count\n var remain = maxItems - selectedList.length;\n $bonusProductList.find('.bonus-items-available').text(remain);\n if (remain <= 0) {\n $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');\n } else {\n $bonusProductList.find('.select-bonus-item').removeAttr('disabled');\n }\n}\n\nfunction initializeGrid () {\n var $bonusProduct = $('#bonus-product-dialog'),\n $bonusProductList = $('#bonus-product-list'),\n bliData = $bonusProductList.data('line-item-detail');\n maxItems = bliData.maxItems;\n bliUUID = bliData.uuid;\n\n if (bliData.itemCount >= maxItems) {\n $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');\n }\n\n var cartItems = $bonusProductList.find('.selected-bonus-item');\n cartItems.each(function () {\n var ci = $(this);\n var product = {\n uuid: ci.data('uuid'),\n pid: ci.data('pid'),\n qty: ci.find('.item-qty').text(),\n name: ci.find('.item-name').html(),\n attributes: {}\n };\n var attributes = ci.find('ul.item-attributes li');\n attributes.each(function () {\n var li = $(this);\n product.attributes[li.data('attributeId')] = {\n displayName:li.children('.display-name').html(),\n displayValue:li.children('.display-value').html()\n };\n });\n selectedList.push(product);\n });\n\n $bonusProductList.on('click', '.bonus-product-item a[href].swatchanchor', function (e) {\n e.preventDefault();\n var url = this.href,\n $this = $(this);\n url = util.appendParamsToUrl(url, {\n 'source': 'bonus',\n 'format': 'ajax'\n });\n $.ajax({\n url: url,\n success: function (response) {\n $this.closest('.bonus-product-item').empty().html(response);\n hideSwatches();\n }\n });\n })\n .on('change', '.input-text', function () {\n $bonusProductList.find('.select-bonus-item').removeAttr('disabled');\n $(this).closest('.bonus-product-form').find('.quantity-error').text('');\n })\n .on('click', '.select-bonus-item', function (e) {\n e.preventDefault();\n if (selectedList.length >= maxItems) {\n $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');\n $bonusProductList.find('.bonus-items-available').text('0');\n return;\n }\n\n var form = $(this).closest('.bonus-product-form'),\n detail = $(this).closest('.product-detail'),\n uuid = form.find('input[name=\"productUUID\"]').val(),\n qtyVal = form.find('input[name=\"Quantity\"]').val(),\n qty = (isNaN(qtyVal)) ? 1 : (+qtyVal);\n\n if (qty > maxItems) {\n $bonusProductList.find('.select-bonus-item').attr('disabled', 'disabled');\n form.find('.quantity-error').text(Resources.BONUS_PRODUCT_TOOMANY);\n return;\n }\n\n var product = {\n uuid: uuid,\n pid: form.find('input[name=\"pid\"]').val(),\n qty: qty,\n name: detail.find('.product-name').text(),\n attributes: detail.find('.product-variations').data('attributes'),\n options: []\n };\n\n var optionSelects = form.find('.product-option');\n\n optionSelects.each(function () {\n product.options.push({\n name: this.name,\n value: $(this).val(),\n display: $(this).children(':selected').first().html()\n });\n });\n selectedList.push(product);\n updateSummary();\n\n var url = util.appendParamsToUrl(Urls.addBonusProduct, {bonusDiscountLineItemUUID: bliUUID});\n var bonusProducts = getBonusProducts();\n if (bonusProducts.bonusproducts[0].product.qty > maxItems) {\n bonusProducts.bonusproducts[0].product.qty = maxItems;\n }\n // make the server call\n $.ajax({\n type: 'POST',\n dataType: 'json',\n cache: false,\n contentType: 'application/json',\n url: url,\n data: JSON.stringify(bonusProducts)\n })\n .done(function () {\n // success\n page.refresh();\n })\n .fail(function (xhr, textStatus) {\n // failed\n if (textStatus === 'parsererror') {\n window.alert(Resources.BAD_RESPONSE);\n } else {\n window.alert(Resources.SERVER_CONNECTION_ERROR);\n }\n })\n .always(function () {\n $bonusProduct.dialog('close');\n });\n })\n .on('click', '.remove-link', function (e) {\n e.preventDefault();\n var container = $(this).closest('.selected-bonus-item');\n if (!container.data('uuid')) { return; }\n\n var uuid = container.data('uuid');\n var i, len = selectedList.length;\n for (i = 0; i < len; i++) {\n if (selectedList[i].uuid === uuid) {\n selectedList.splice(i, 1);\n break;\n }\n }\n updateSummary();\n })\n .on('click', '#more-bonus-products', function (e) {\n e.preventDefault();\n var uuid = $('#bonus-product-list').data().lineItemDetail.uuid;\n\n //get the next page of choice of bonus products\n var lineItemDetail = JSON.parse($('#bonus-product-list').attr('data-line-item-detail'));\n lineItemDetail.pageStart = lineItemDetail.pageStart + lineItemDetail.pageSize;\n $('#bonus-product-list').attr('data-line-item-detail', JSON.stringify(lineItemDetail));\n\n var url = util.appendParamsToUrl(Urls.getBonusProducts, {\n bonusDiscountLineItemUUID: uuid,\n format: 'ajax',\n lazyLoad: 'true',\n pageStart: lineItemDetail.pageStart,\n pageSize: $('#bonus-product-list').data().lineItemDetail.pageSize,\n bonusProductsTotal: $('#bonus-product-list').data().lineItemDetail.bpTotal\n });\n\n $.ajax({\n type: 'GET',\n cache: false,\n contentType: 'application/json',\n url: url\n })\n .done(function (data) {\n //add the new page to DOM and remove 'More' link if it is the last page of results\n $('#more-bonus-products').before(data);\n if ((lineItemDetail.pageStart + lineItemDetail.pageSize) >= $('#bonus-product-list').data().lineItemDetail.bpTotal) {\n $('#more-bonus-products').remove();\n }\n })\n .fail(function (xhr, textStatus) {\n if (textStatus === 'parsererror') {\n window.alert(Resources.BAD_RESPONSE);\n } else {\n window.alert(Resources.SERVER_CONNECTION_ERROR);\n }\n });\n });\n}\n\nvar bonusProductsView = {\n /**\n * @function\n * @description Open the list of bonus products selection dialog\n */\n show: function (url) {\n var $bonusProduct = $('#bonus-product-dialog');\n // create container if missing\n if (!$bonusProduct.length) {\n $bonusProduct = $('
').attr('id', 'bonus-product-dialog').appendTo('body');\n }\n\n // create the dialog\n dialog.open({\n target: $bonusProduct,\n url: url,\n options: {\n width: 795,\n title: Resources.BONUS_PRODUCTS\n },\n callback: function () {\n initializeGrid();\n hideSwatches();\n }\n });\n },\n /**\n * @function\n * @description Open bonus product promo prompt dialog\n */\n loadBonusOption: function () {\n var self = this,\n bonusDiscountContainer = document.querySelector('.bonus-discount-container');\n if (!bonusDiscountContainer) { return; }\n\n // get the html from minicart, then trash it\n var bonusDiscountContainerHtml = bonusDiscountContainer.outerHTML;\n bonusDiscountContainer.parentNode.removeChild(bonusDiscountContainer);\n\n dialog.open({\n html: bonusDiscountContainerHtml,\n options: {\n width: 400,\n title: Resources.BONUS_PRODUCT,\n buttons: [{\n text: Resources.SELECT_BONUS_PRODUCTS,\n click: function () {\n var uuid = $('.bonus-product-promo').data('lineitemid'),\n url = util.appendParamsToUrl(Urls.getBonusProducts, {\n bonusDiscountLineItemUUID: uuid,\n source: 'bonus',\n format: 'ajax',\n lazyLoad: 'false',\n pageStart: 0,\n pageSize: 10,\n bonusProductsTotal: -1\n });\n $(this).dialog('close');\n self.show(url);\n },\n class: 'button-fancy-medium add-to-cart'\n }, {\n text: Resources.NO_THANKS,\n click: function () {\n $(this).dialog('close');\n },\n class: 'button-fancy-medium add-to-cart'\n }],\n dialogClass: 'bonus-product-dialog'\n },\n callback: function () {\n // show hide promo details\n $('.show-promo-details').on('click', function () {\n $('.promo-details').toggleClass('visible');\n });\n }\n });\n }\n};\n\nmodule.exports = bonusProductsView;\n","'use strict';\n\nvar _ = require('lodash'),\n dialog = require('../dialog'),\n util = require('../util'),\n formPrepare = require('../pages/checkout/formPrepare'),\n address = require('../pages/checkout/address'),\n shipping = require('../pages/checkout/shipping');\n\n/**\n * @description test whether zipcode is valid\n * @return {Boolean} true if the zipcode is valid for either country, false if it's invalid for all\n **/\nvar validateZipCode = function(zipCode) {\n var regexes = {\n us: /^\\d{5}(-\\d{4})?$/,\n ca: /^[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$/i,\n fr: /^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/i,\n it: /^([0-9]){5}$/,\n jp: /^([0-9]){3}[-]([0-9]){4}$/,\n cn: /^([0-9]){6}$/,\n gb: /^[A-Z]{1,2}[0-9][0-9A-Z]?\\s?[0-9][A-Z]{2}$/i,\n default: /^([0-9]|[a-z]|[A-Z]|[ -]){4,16}$/\n },\n valid = false;\n if (!zipCode) { return; }\n _.each(regexes, function (re) {\n var regexp = new RegExp(re);\n valid = regexp.test(zipCode);\n });\n return valid;\n};\n\n/**\n * Show / Hide / Disable shipping fields basing on delivery type\n */\nvar updateFieldsByDeliveryType = function(deliveryType) {\n var addToEmailFieldID = 'dwfrm_singleshipping_shippingAddress_addToEmailList';\n var $homeCountryRow = $('.row-country-home');\n var $storeCountryRow = $('.row-country-store');\n var $storeEmailFields = $('.store-email-fields');\n var $storeAddressForm = $('#store-shipping-address-form');\n var $homeAddressForm = $('#shipping-address-area');\n if (deliveryType === 'store') {\n // Hide and disable home delivery country select\n $homeCountryRow.find('select').prop('disabled', true);\n $homeCountryRow.hide();\n // Show and enable store email fields\n if ($storeEmailFields.length) {\n $storeEmailFields.find('input').prop('disabled', false);\n $storeEmailFields.find('label[for$=_addToEmailList]').attr('for', addToEmailFieldID + '-store');\n $storeEmailFields.find('input[name$=_addToEmailList]').attr('id', addToEmailFieldID + '-store');\n $storeEmailFields.show();\n }\n if ($storeAddressForm.length) {\n // Disable store phone, firstName, email fields\n $storeAddressForm.find('[name$=_addressFields_phone]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addressFields_firstName]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addressFields_email]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addToEmailList]').prop('disabled', true);\n // Enable store country field\n $storeCountryRow.find('select').prop('disabled', false);\n // Turn off \"Use for Billing\" checkbox\n $storeAddressForm.find('[name$=_useAsBillingAddress]').prop('checked', false);\n // Hide store address form\n $storeAddressForm.hide();\n }\n } else {\n // Show and enable home delivery country select\n $homeCountryRow.find('select').prop('disabled', false);\n $homeCountryRow.show();\n // Disable store country select\n $storeCountryRow.find('select').prop('disabled', true);\n // Hide and disable store email fields\n if ($storeEmailFields.length) {\n $storeEmailFields.find('input').prop('disabled', true);\n $storeEmailFields.hide();\n }\n $homeAddressForm.find('label[for$=_addToEmailList]').attr('for', addToEmailFieldID + '-home');\n $homeAddressForm.find('input[name$=_addToEmailList]').attr('id', addToEmailFieldID + '-home');\n }\n};\n\nvar clickCollect = {\n setSelectedStore: function(storeId) {\n var self = this;\n $.ajax({\n url: Urls.setSelectedStore,\n data: {selectedStore: storeId},\n success: function () {\n self.loadShippingAddress('store', false);\n }\n });\n },\n /**\n * Click on Select Store button\n */\n initSelectStoreButton: function() {\n var self = this;\n $('.select-store-button').on('click', function(e) {\n e.preventDefault();\n var storeId = $(this).data('storeId');\n $('.store-list .store-tile.selected').removeClass('selected')\n .find('.select-store-button').text(Resources.SELECT_STORE);\n $(this).text(Resources.SELECTED_STORE)\n .closest('.store-tile').addClass('selected');\n self.setSelectedStore(storeId);\n dialog.close();\n });\n },\n /**\n * Click on Change button\n */\n initChangeButton: function() {\n var self = this;\n $('.change-preferred-store').bind('click', function (e) {\n e.preventDefault();\n // disable radio buttons if 'Change' store address is clicked\n $('.item-delivery-options .delivery-option').attr('disabled', 'disabled');\n self.setSearchParams(null, null, null, null);\n self.zipPrompt();\n });\n },\n /**\n * Set Default Country in country search dropdown\n */\n setDefaultCountry: function() {\n var country = $('#storeCountrySelect').data('defaultcountry');\n var ddlValue = $('#storeCountrySelect option[value='+ country +']');\n\n if (ddlValue.length > 0) {\n $('#storeCountrySelect').val(ddlValue.val());\n }\n },\n /**\n * Load stores\n */\n loadStoreSearchResults: function() {\n var self = this,\n url = util.appendParamsToUrl(Urls.selectStoreDialog, {\n zipCode: User.zip,\n units: User.units,\n distance: User.distance,\n storeCountryCode: User.storeCountryCode\n });\n $.ajax({\n url: url,\n success: function(response) {\n $('#store-search-results').html(response);\n self.initFindAStore();\n self.initSelectStoreButton();\n }\n });\n },\n /**\n * Updates checkout summary\n */\n updateCheckoutSummary: function() {\n $.ajax({\n url: Urls.updateCheckoutSummary,\n success: function (response) {\n $('#secondary').html(response);\n }\n });\n },\n /**\n * Loads shipping address area\n */\n loadShippingAddress: function(deliveryType, cleanAddress) {\n var self = this;\n $.ajax({\n url: Urls.loadShippingAddress,\n data: {deliveryType: deliveryType, cleanAddress: cleanAddress},\n success: function (response) {\n $('#shipping-address-area').html(response);\n $('.item-delivery-options .delivery-option').removeAttr('disabled');\n // remove fields which are optional for store address\n updateFieldsByDeliveryType(deliveryType);\n if (deliveryType === 'store') {\n formPrepare.init({\n continueSelector: '[name$=\"shippingAddress_save\"]',\n formSelector:'[id$=\"singleshipping_shippingAddress\"]',\n skipDisabled: true\n });\n\n shipping.updateShippingMethodList();\n // disable qas\n $('[name$=\"shippingAddress_save\"]').unbind('click');\n // hide shipping address form\n // $('#store-shipping-address-form').hide();\n self.updateCheckoutSummary();\n self.initSelectStore();\n // self.initChangeButton();\n // self.initLocalStoreButton();\n } else {\n // re-init events\n address.init();\n shipping.init();\n }\n // disable delivery options radio buttons if 'Continue' button is clicked\n $('[name$=\"shippingAddress_save\"]').on('click', function () {\n $('.item-delivery-options .delivery-option').attr('disabled', 'disabled');\n });\n }\n });\n },\n /**\n * Open dialog on click 'Select Store'\n */\n initSelectStore: function() {\n var self = this;\n $('#shipping-address-area .set-preferred-store').bind('click', function (e) {\n e.preventDefault();\n // disable radio buttons if 'Select Store' is clicked\n $('.item-delivery-options .delivery-option').attr('disabled', 'disabled');\n self.setSearchParams(null, null, null, null);\n self.zipPrompt();\n self.setDefaultCountry();\n });\n },\n /**\n * Dialog with search form\n */\n zipPrompt: function() {\n var self = this;\n var url = util.ajaxUrl(Urls.zipPromptDialog);\n dialog.open({\n url: url,\n options: {\n title: Resources.STORE_SELECT,\n width: 500,\n open: function() {\n self.initFindAStore();\n },\n close: function() {\n $('.item-delivery-options .delivery-option').removeAttr('disabled');\n }\n }\n });\n },\n /**\n * Click on Find button\n */\n initFindAStore: function() {\n var self = this;\n\n $('button[name=findAStore]').on('click', function() {\n var zipCode = $('#user-zip').val();\n var distance = $('#storeSearchRadius').val();\n var units = $('#storeSearchRadiusUnits').val();\n var storeCountryCode = $('#storeCountrySelect option:selected').text().trim();\n if (validateZipCode(zipCode)) {\n self.setSearchParams(zipCode, distance, units, storeCountryCode);\n self.loadStoreSearchResults();\n }\n });\n\n $('button[name=findAStoreCurrentLocation]').on('click', function() {\n var distance = $('#storeSearchRadius').val();\n var units = $('#storeSearchRadiusUnits').val();\n self.setCurrentLocationParams(distance, units);\n self.loadStoreSearchResults();\n });\n },\n /**\n * Save search params in session\n */\n setSearchParams: function(zip, distance, units, storeCountry) {\n User.zip = zip;\n User.distance = distance;\n User.units = units;\n User.storeCountryCode = storeCountry;\n $.ajax({\n type: 'POST',\n url: Urls.setStoreSearchParams,\n data: {\n zipCode: zip,\n distance: distance,\n units: units,\n storeCountryCode: storeCountry\n }\n });\n },\n /**\n * Save search params in session\n */\n setCurrentLocationParams: function(distance, units) {\n User.distance = distance;\n User.units = units;\n $.ajax({\n type: 'POST',\n url: Urls.setCurrentLocationParams,\n data: {\n distance: distance,\n units: units\n }\n });\n },\n /**\n * Click on delivery options (radio buttons)\n */\n initDeliveryOptions: function() {\n var self = this;\n $('.item-delivery-options .delivery-option').bind('click', function() {\n // load shipping address area\n self.loadShippingAddress($(this).val(), true);\n $(this).unbind('click');\n if ($(this).val() === 'home') {\n $('#shipping-address-area .set-preferred-store').unbind('click');\n $('.change-preferred-store').unbind('click');\n } else if ($(this).val() === 'store') {\n $.ajax({\n url: Urls.getLocalStore,\n success: function(response) {\n var storeId = response.storeId;\n if (storeId) {\n self.setSelectedStore(storeId);\n }\n }\n });\n }\n });\n },\n /**\n * Returns local store ID\n */\n getLocalStore: function() {\n $.ajax({\n url: Urls.getLocalStore,\n success: function(response) {\n var storeId = response.storeId;\n return storeId;\n }\n });\n },\n init: function() {\n var self = this;\n var deliveryType = $('.item-delivery-options').find('input:checked').val();\n if (typeof deliveryType === 'undefined') {\n $('#delivery-options-home').attr('checked', 'checked');\n }\n updateFieldsByDeliveryType(deliveryType);\n // self.initChangeButton();\n \n // disable delivery options radio buttons if 'Continue' button is clicked\n $('[name$=\"shippingAddress_save\"]').on('click', function () {\n $('.item-delivery-options .delivery-option').attr('disabled', 'disabled');\n });\n\n // init click event on delivery option\n self.initDeliveryOptions();\n\n // validates email address input\n formPrepare.init({\n formSelector: 'form[id$=\"dwfrm_singleshipping_shippingAddress\"]',\n continueSelector: '[name$=\"dwfrm_singleshipping_shippingAddress_save\"]'\n });\n }\n};\n\nmodule.exports = clickCollect;\n","'use strict';\n\nvar _ = require('lodash');\nvar dialog = require('../dialog');\nvar util = require('../util');\nvar formPrepare = require('../pages/checkout/formPrepareV2');\nvar address = require('../pages/checkout/addressV2');\nvar checkout = require('../pages/checkout/checkout');\n\n/**\n * @description test whether zipcode is valid\n * @return {Boolean} true if the zipcode is valid for either country, false if it's invalid for all\n**/\nvar validateZipCode = function(zipCode) {\n var regexes = {\n us: /^\\d{5}(-\\d{4})?$/,\n ca: /^[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$/i,\n fr: /^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/i,\n it: /^([0-9]){5}$/,\n jp: /^([0-9]){3}[-]([0-9]){4}$/,\n cn: /^([0-9]){6}$/,\n gb: /^[A-Z]{1,2}[0-9][0-9A-Z]?\\s?[0-9][A-Z]{2}$/i,\n default: /^([0-9]|[a-z]|[A-Z]|[ -]){4,16}$/\n },\n valid = false;\n if (!zipCode) { return; }\n _.each(regexes, function (re) {\n var regexp = new RegExp(re);\n valid = regexp.test(zipCode);\n });\n return valid;\n};\n\n/**\n * Show / Hide / Disable shipping fields basing on delivery type\n */\nvar updateFieldsByDeliveryType = function(deliveryType) {\n var addToEmailFieldID = 'dwfrm_checkout_shippingAddress_addToEmailList';\n var $homeCountryRow = $('.row-country-home');\n var $storeCountryRow = $('.row-country-store');\n var $storeEmailFields = $('.store-email-fields');\n var $storeAddressForm = $('#store-shipping-address-form');\n var $homeAddressForm = $('#shipping-address-area');\n if (deliveryType === 'store') {\n // Hide and disable home delivery country select\n // $homeCountryRow.find('select').prop('disabled', true);\n // $homeCountryRow.hide();\n // Show and enable store email fields\n $('.billing-section').show();\n $('#update-billing-address').hide();\n if ($storeEmailFields.length) {\n $storeEmailFields.find('input').prop('disabled', false);\n $storeEmailFields.find('label[for$=_addToEmailList]').attr('for', addToEmailFieldID + '-store');\n $storeEmailFields.find('input[name$=_addToEmailList]').attr('id', addToEmailFieldID + '-store');\n $storeEmailFields.show();\n }\n if ($storeAddressForm.length) {\n // Disable store phone, firstName, email fields\n $storeAddressForm.find('[name$=_addressFields_phone]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addressFields_firstName]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addressFields_email]').prop('disabled', true);\n $storeAddressForm.find('[name$=_addToEmailList]').prop('disabled', true);\n // Enable store country field\n $storeCountryRow.find('select').prop('disabled', false);\n // Turn off \"Use for Billing\" checkbox\n $storeAddressForm.find('[name$=_useAsBillingAddress]').prop('checked', false);\n // Hide store address form\n $storeAddressForm.hide();\n }\n if ($('#checkout-selected-store').length > 0) {\n var storePostalCode = $('.hidden-store-postalCode input').val();\n $('.valid-store-checkout').val(storePostalCode);\n }\n } else {\n // Show and enable home delivery country select\n $homeCountryRow.find('select').prop('disabled', false);\n $homeCountryRow.show();\n $storeCountryRow.hide();\n // Disable store country select\n $storeCountryRow.find('select').prop('disabled', true);\n // Hide and disable store email fields\n if ($storeEmailFields.length) {\n $storeEmailFields.find('input').prop('disabled', true);\n $storeEmailFields.hide();\n }\n $homeAddressForm.find('label[for$=_addToEmailList]').attr('for', addToEmailFieldID + '-home');\n $homeAddressForm.find('input[name$=_addToEmailList]').attr('id', addToEmailFieldID + '-home');\n\n // $('select[class$=\"shipping-address-list\"] option:eq(1)').prop('selected', 'selected').trigger('change');\n address.init();\n }\n};\n\n/*+++++++++++++++++++++++++++++ BOPIS code starts +++++++++++++++++++++++++++++*/\nvar bopis = {\n /**\n * Click on edit button in Pick up details section\n */\n initEditStoreButton: function() {\n // if there is a previously selected store from a page reload...\n if ($('#selected-store-container-bopis').length > 0) {\n var storeForm = document.getElementsByClassName('store-form')[0];\n if (storeForm) {\n $(storeForm).css('display', 'none');\n $('#store-details__edit-button').on('click', function (e) {\n e.preventDefault();\n $('#checkout-selected-store').empty();\n $(storeForm).css('display', 'block');\n });\n }\n }\n },\n /**\n * Pick up person show/hide events\n */\n initPickUpPersonControl: function () {\n // if there is a previously saved pick up person...\n if ($('.pickperson__name').length > 0) {\n $('#other-pickup-person').attr('checked', true);\n $('.pickperson').show();\n $('#dwfrm_pickuppersonform_name, #dwfrm_pickuppersonform_phoneNumber').attr(\"required\", \"true\");\n }\n\n $('#other-pickup-person').on('change', function (e) {\n if (e.target.checked === true) {\n $('.pickperson').show();\n $('#dwfrm_pickuppersonform_name, #dwfrm_pickuppersonform_phoneNumber').attr(\"required\", \"true\");\n }\n else {\n bopis.pickupPersonChangeEvent()\n }\n $('#billing-name').on('change', function (e) {\n if ($('#billing-name').is(':checked')) {\n $('.pickperson').hide();\n }\n bopis.pickupPersonChangeEvent()\n });\n })\n },\n \n pickupPersonChangeEvent: function () {\n $('#dwfrm_pickuppersonform_name, #dwfrm_pickuppersonform_phoneNumber')\n .val('')\n .removeAttr('required')\n .attr('aria-required', 'false')\n .attr('aria-invalid', 'false')\n .removeClass('error')\n .next('.error')\n .hide();\n bopis.handlePersonFormButtonDisable('');\n $('#pickperson__formerrorresult').text('');\n\n // if there is a previously saved pick up person, reset it as empty\n if ($('#pickperson__name').length > 0) {\n var pickupPersonForm = $('#pickperson__form');\n var pickupPersonResult = $('#pickperson__result');\n var csrfToken = pickupPersonForm.attr('data-csrftoken');\n // set empty person name and phone\n $.ajax({\n url: Urls.savePickupPersonInfo,\n type: 'POST',\n data: {\n csrf_token: csrfToken,// eslint-disable-line\n personName: '',\n personPhone: ''\n },\n success: function success(response) {\n if (response.status === 'fail') {\n $('#pickperson__formerrorresult').text('Error in saving information');\n } else {\n $('.pickperson').hide();\n $(pickupPersonForm).css('display', 'block');\n $(pickupPersonResult).empty();\n }\n }\n });\n } else {\n $('.pickperson').hide();\n }\n },\n\n /**\n * Handle button disabled attribute\n */\n handlePersonFormButtonDisable: function(personName) {\n setTimeout(function() {\n if (personName === '') {\n $('#dwfrm_pickuppersonform_saveinformation').attr('disabled', 'disabled');\n } else {\n $('#dwfrm_pickuppersonform_saveinformation').removeAttr('disabled');\n }\n if ($('#pickperson__form').find('.input-text.error').length) {\n $('#dwfrm_pickuppersonform_saveinformation').attr('disabled', 'disabled');\n }\n }, 500)\n },\n /**\n * Handle person name input field\n */\n handlePersonNameInput: function () {\n $('#dwfrm_pickuppersonform_name').on('keyup', function(e) {\n bopis.handlePersonFormButtonDisable(e.target.value);\n });\n },\n /**\n * Handle person name input field\n */\n handlePersonNameInputFocus: function () {\n $('#dwfrm_pickuppersonform_name, #dwfrm_pickuppersonform_phoneNumber').on('focusout', function() {\n bopis.handlePersonFormButtonDisable();\n });\n },\n /**\n * Click on edit button in Pick up person details\n */\n initEditPickUpPersonButton: function() {\n // if there is a previously saved pick up person...\n if ($('.pickperson__name').length > 0) {\n var pickupPersonForm = $('#pickperson__form');\n var pickupPersonResult = $('#pickperson__result');\n if (pickupPersonForm.length > 0) {\n $(pickupPersonForm).css('display', 'none');\n $('#pickperson__edit-button').on('click', function (e) {\n e.preventDefault();\n var personName = $.trim($('#pickperson__name').text());\n var personPhone = $.trim($('#pickperson__phone').text());\n $('#dwfrm_pickuppersonform_name').val(personName);\n $('#dwfrm_pickuppersonform_phoneNumber').val(personPhone);\n bopis.handlePersonFormButtonDisable(personName);\n $(pickupPersonResult).empty();\n $(pickupPersonForm).css('display', 'block');\n });\n }\n }\n },\n /**\n * Click on 'Save Information' button in Pick up person section\n */\n initPickupPersonFormButton: function() {\n if ($('#pickperson__form').length > 0) {\n bopis.handlePersonFormButtonDisable($('#dwfrm_pickuppersonform_name').val());\n\n $('#dwfrm_pickuppersonform_saveinformation').on('click', function (e) {\n var pickupPersonForm = $('#pickperson__form');\n var pickupPersonResult = $('#pickperson__result');\n var personName = $('#dwfrm_pickuppersonform_name').val();\n var personPhone = $('#dwfrm_pickuppersonform_phoneNumber').val();\n e.preventDefault();\n $('#pickperson__formerrorresult').text('');\n var URL = Urls.savePickupPersonInfo;\n var csrfToken = pickupPersonForm.attr('data-csrftoken');\n $.ajax({\n url: URL,\n type: 'POST',\n data: {\n csrf_token: csrfToken,// eslint-disable-line\n personName: personName,\n personPhone: personPhone\n },\n success: function success(response) {\n if (response.status === 'fail') {\n $('#pickperson__formerrorresult').text('Error in saving information');\n } else {\n $(pickupPersonForm).css('display', 'none');\n $(pickupPersonResult).html(response);\n bopis.initEditPickUpPersonButton();\n }\n }\n });\n });\n }\n },\n /**\n * Init bopis events\n */\n initEvents: function() {\n this.initEditStoreButton();\n this.initPickUpPersonControl();\n this.handlePersonNameInput();\n this.handlePersonNameInputFocus();\n this.initEditPickUpPersonButton();\n this.initPickupPersonFormButton();\n this.pickupPersonChangeEvent();\n }\n}\n/*+++++++++++++++++++++++++++++ BOPIS code ends +++++++++++++++++++++++++++++*/\n\n\nvar clickCollect = {\n setSelectedStore: function(storeId) {\n var self = this;\n $.ajax({\n url: Urls.setSelectedStore,\n data: {selectedStore: storeId},\n success: function () {\n self.loadShippingAddress('store', false);\n }\n });\n },\n /**\n * Click on Select Store button\n */\n initSelectStoreButton: function() {\n var self = this;\n $('.select-store-button').on('click', function(e) {\n e.preventDefault();\n var storeId = $(this).data('storeId');\n $('.store-list .store-tile.selected').removeClass('selected')\n .find('.select-store-button').text(Resources.SELECT_STORE);\n $(this).text(Resources.SELECTED_STORE)\n .closest('.store-tile').addClass('selected');\n self.setSelectedStore(storeId);\n dialog.close();\n });\n },\n /**\n * Click on Change button\n */\n initChangeButton: function() {\n var self = this;\n $('.change-preferred-store').bind('click', function (e) {\n e.preventDefault();\n // disable radio buttons if 'Change' store address is clicked\n $('.op-item-delivery-options .delivery-option').attr('disabled', 'disabled');\n self.setSearchParams(null, null, null, null);\n self.zipPrompt();\n });\n },\n /**\n * Set Default Country in country search dropdown\n */\n setDefaultCountry: function() {\n var country = $('#storeCountrySelect').data('defaultcountry');\n var ddlValue = $('#storeCountrySelect option[value='+ country +']');\n\n if (ddlValue.length > 0) {\n $('#storeCountrySelect').val(ddlValue.val());\n }\n },\n /**\n * Load stores\n */\n loadStoreSearchResults: function() {\n var self = this,\n url = util.appendParamsToUrl(Urls.selectStoreDialog, {\n zipCode: User.zip,\n units: User.units,\n distance: User.distance,\n storeCountryCode: User.storeCountryCode\n });\n $.ajax({\n url: url,\n success: function(response) {\n $('#store-search-results').html(response);\n self.initFindAStore();\n self.initSelectStoreButton();\n }\n });\n },\n /**\n * Updates checkout summary\n */\n updateCheckoutSummary: function() {\n $.ajax({\n url: Urls.updateCheckoutSummary,\n success: function (response) {\n $('#secondary').html(response);\n }\n });\n },\n /**\n * Loads shipping address area\n */\n loadShippingAddress: function(deliveryType, cleanAddress) {\n var self = this;\n $.ajax({\n url: Urls.loadShippingAddress,\n data: {deliveryType: deliveryType, cleanAddress: cleanAddress},\n success: function (response) {\n $('#shipping-address-area').html(response);\n $('.op-item-delivery-options .delivery-option').removeAttr('disabled');\n // remove fields which are optional for store address\n updateFieldsByDeliveryType(deliveryType);\n if (deliveryType === 'store') {\n formPrepare.init({\n continueSelector: '[name$=\"shippingAddress_save\"]',\n formSelector:'[id$=\"checkout_shippingAddress\"]',\n skipDisabled: true\n });\n checkout.updateShippingMethodList();\n // disable qas\n $('[name$=\"shippingAddress_save\"]').unbind('click');\n // hide shipping address form\n // $('#store-shipping-address-form').hide();\n self.updateCheckoutSummary();\n self.initSelectStore();\n // self.initChangeButton();\n\n /*++++++++++++ BOPIS code starts ++++++++++++++*/\n bopis.initEvents();\n /*++++++++++++ BOPIS code ends ++++++++++++++*/\n\n } else {\n // re-init events\n address.init();\n checkout.init();\n }\n // disable delivery options radio buttons if 'Continue' button is clicked\n $('[name$=\"shippingAddress_save\"]').on('click', function () {\n $('.op-item-delivery-options .delivery-option').attr('disabled', 'disabled');\n });\n }\n });\n },\n /**\n * Open dialog on click 'Select Store'\n */\n initSelectStore: function() {\n var self = this;\n $('#shipping-address-area .set-preferred-store').bind('click', function (e) {\n e.preventDefault();\n // disable radio buttons if 'Select Store' is clicked\n $('.op-item-delivery-options .delivery-option').attr('disabled', 'disabled');\n self.setSearchParams(null, null, null, null);\n self.zipPrompt();\n self.setDefaultCountry();\n });\n },\n /**\n * Dialog with search form\n */\n zipPrompt: function() {\n var self = this;\n var url = util.ajaxUrl(Urls.zipPromptDialog);\n dialog.open({\n url: url,\n options: {\n title: Resources.STORE_SELECT,\n width: 500,\n open: function() {\n self.initFindAStore();\n },\n close: function() {\n $('.op-item-delivery-options .delivery-option').removeAttr('disabled');\n }\n }\n });\n },\n /**\n * Click on Find button\n */\n initFindAStore: function() {\n var self = this;\n\n $('button[name=findAStore]').on('click', function() {\n var zipCode = $('#user-zip').val();\n var distance = $('#storeSearchRadius').val();\n var units = $('#storeSearchRadiusUnits').val();\n var storeCountryCode = $('#storeCountrySelect option:selected').text().trim();\n if (validateZipCode(zipCode)) {\n self.setSearchParams(zipCode, distance, units, storeCountryCode);\n self.loadStoreSearchResults();\n }\n });\n\n $('button[name=findAStoreCurrentLocation]').on('click', function() {\n var distance = $('#storeSearchRadius').val();\n var units = $('#storeSearchRadiusUnits').val();\n self.setCurrentLocationParams(distance, units);\n self.loadStoreSearchResults();\n });\n },\n /**\n * Save search params in session\n */\n setSearchParams: function(zip, distance, units, storeCountry) {\n User.zip = zip;\n User.distance = distance;\n User.units = units;\n User.storeCountryCode = storeCountry;\n $.ajax({\n type: 'POST',\n url: Urls.setStoreSearchParams,\n data: {\n zipCode: zip,\n distance: distance,\n units: units,\n storeCountryCode: storeCountry\n }\n });\n },\n /**\n * Save search params in session\n */\n setCurrentLocationParams: function(distance, units) {\n User.distance = distance;\n User.units = units;\n $.ajax({\n type: 'POST',\n url: Urls.setCurrentLocationParams,\n data: {\n distance: distance,\n units: units\n }\n });\n },\n /**\n * Click on delivery options (radio buttons)\n */\n initDeliveryOptions: function() {\n var self = this;\n $('.op-item-delivery-options .delivery-option').bind('click', function() {\n // load shipping address area\n self.loadShippingAddress($(this).val(), true);\n $(this).unbind('click');\n if ($(this).val() === 'home') {\n $('#shipping-address-area .set-preferred-store').unbind('click');\n $('.change-preferred-store').unbind('click');\n } else if ($(this).val() === 'store') {\n $('.checkout-shipping.address #shipping-address-container input').val('');\n $.ajax({\n url: Urls.getLocalStore,\n success: function(response) {\n var storeId = response.storeId;\n if (storeId) {\n self.setSelectedStore(storeId);\n }\n }\n });\n }\n });\n },\n /**\n * Returns local store ID\n */\n getLocalStore: function() {\n $.ajax({\n url: Urls.getLocalStore,\n success: function(response) {\n var storeId = response.storeId;\n return storeId;\n }\n });\n },\n init: function() {\n var self = this;\n var deliveryType = $('.op-item-delivery-options').find('input:checked').val();\n if (typeof deliveryType === 'undefined') {\n $('#delivery-options-home').attr('checked', 'checked');\n }\n updateFieldsByDeliveryType(deliveryType);\n\n // disable delivery options radio buttons if 'Continue' button is clicked\n $('[name$=\"shippingAddress_save\"]').on('click', function () {\n $('.op-item-delivery-options .delivery-option').attr('disabled', 'disabled');\n });\n\n // init click event on delivery option\n self.initDeliveryOptions();\n\n /*++++++++++++ BOPIS code starts ++++++++++++++*/\n bopis.initEvents();\n /*++++++++++++ BOPIS code ends ++++++++++++++*/\n\n // validates email address input\n formPrepare.init({\n formSelector: 'form[id$=\"dwfrm_checkout_shippingAddress\"]',\n continueSelector: '[name$=\"dwfrm_checkout_shippingAddress_save\"]'\n });\n }\n};\n\nmodule.exports = clickCollect;\n","'use strict';\n\nvar ajax = require('base/ajax');\n\n/**\n * @function cookieprivacy Used to display/control the scrim containing the cookie privacy code\n **/\nmodule.exports = function () {\n /**\n * If we have not accepted cookies AND we're not on the Privacy Policy page, then show the notification\n * NOTE: You will probably want to adjust the Privacy Page test to match your site's specific privacy / cookie page\n */\n if (SitePreferences.COOKIE_HINT === true && document.cookie.indexOf('dw_cookies_accepted') < 0) {\n // check for privacy policy page\n if ($('.privacy-policy').length === 0) {\n var $container = $('.cookie-header');\n ajax.load({\n url: Urls.cookieHint,\n method: 'GET',\n target: $container\n });\n $('.cookie-header-wrapper').addClass('active');\n enableCookies();\n }\n\n $('body').on('click','.cookie-header #close-cookie-header', function(){\n enableCookies();\n $('.pt_storefront .cookie-header').remove();\n });\n } else {\n // Otherwise, we don't need to show the asset, just enable the cookies\n enableCookies();\n }\n\n function enableCookies() {\n if (document.cookie.indexOf('dw=1') < 0) {\n document.cookie = 'dw=1; path=/';\n }\n if (document.cookie.indexOf('dw_cookies_accepted') < 0) {\n document.cookie = 'dw_cookies_accepted=1; path=/';\n }\n }\n};\n","'use strict';\nvar dialog = require('./dialog'),\n util = require('./util');\n\nexports.init = function init () {\n $(document).on('click', function(e) {\n if ($(e.target).is('.country-selector .current-country') === false) {\n $('.country-selector .selector').removeClass('active');\n $('.country-selector .current-country').removeClass('selector-active');\n }\n });\n $('.country-selector .current-country').on('click', function (e) {\n\n $('.user-info').removeClass('active');\n $('.country-selector .selector').toggleClass('active');\n if ($('.country-selector .selector').hasClass('active')) {\n $('#mini-cart').css('z-index', '2');\n } else {\n $('#mini-cart').css('z-index', '3');\n }\n $(this).toggleClass('selector-active');\n e.stopPropagation();\n });\n\n $('.country-selector .selector .locale').on('click', function (e) {\n e.preventDefault();\n var url = this.href;\n if (window.Constants.CURRENT_SITE_ID == this.getAttribute('data-site')) {\n // set currency first before reload\n var currency = this.getAttribute('data-currency');\n $.ajax({\n dataType: 'json',\n url: Urls.setSessionCurrency,\n data: {\n format: 'ajax',\n currencyMnemonic: currency\n }\n })\n .done(function (response) {\n if (!response.success) {\n throw new Error('Unable to set currency');\n }\n window.location.href = url;\n });\n } else {\n $.ajax({\n url: Urls.cartHasItems,\n dataType: 'json'\n }).done(function (response) {\n if (response.success) {\n //show warning popup\n var confirmationUrl = util.ajaxUrl(Urls.countrySelectorWarning);\n dialog.open({\n url: confirmationUrl,\n options: {\n buttons: [{\n text: Resources.NO_STAY_HERE,\n click: function () {\n dialog.close();\n }\n }, {\n text: Resources.CONTINUE,\n click: function () {\n window.location.href = url;\n }\n }],\n dialogClass: 'change-country-warning',\n title: Resources.CHANGE_COUNTRY_TITLE\n }\n });\n } else {\n window.location.href = url;\n }\n });\n }\n });\n};\n","'use strict';\n\n/**\n * @private\n * @function\n * @description Initializes events for the Loyalty Program\n */\nfunction initializeEvents() {\n var $eventContainer = $('#cppaForm');\n if (!$eventContainer.length) {\n return;\n }\n \n $eventContainer.on('submit', function(e) {\n var $phoneField = $eventContainer.find('.cppaphone');\n var $emailField = $eventContainer.find('.cppaemail');\n \n if (!$phoneField.val() && !$emailField.val()) {\n e.preventDefault();\n $('.cppaerror').removeClass('hide');\n }\n });\n \n $eventContainer.on('focus', '.cppaphone, .cppaemail', function() {\n $('.cppaerror').addClass('hide');\n });\n \n return;\n}\n\nmodule.exports = {\n init: function () {\n initializeEvents();\n }\n};\n","'use strict';\n\nvar ajax = require('base/ajax'),\n util = require('org/util'),\n _ = require('lodash'),\n imagesLoaded = require('imagesloaded');\n\nvar dialog = {\n /**\n * @function\n * @description Appends a dialog to a given container (target)\n * @param {Object} params params.target can be an id selector or an jquery object\n */\n create: function (params) {\n var $target, id;\n\n if (_.isString(params.target)) {\n if (params.target.charAt(0) === '#') {\n $target = $(params.target);\n } else {\n $target = $('#' + params.target);\n }\n } else if (params.target instanceof jQuery) {\n $target = params.target;\n } else {\n $target = $('#dialog-container');\n $target.selector = '#dialog-container';\n }\n\n // if no element found, create one\n if ($target.length === 0) {\n // following code is not working since v3 of jQuery, so containers should be created before-hand\n window.console.warn('missing dialog $target', $target);\n if ($target.selector && $target.selector.charAt(0) === '#') {\n id = $target.selector.substr(1);\n $target = $('
').attr('id', id).addClass('dialog-content').appendTo('body');\n }\n }\n\n // create the dialog\n this.$container = $target;\n this.$container.dialog(_.merge({}, this.settings, params.options || {}));\n },\n /**\n * @function\n * @description Opens a dialog using the given url (params.url) or html (params.html)\n * @param {Object} params\n * @param {Object} params.url should contain the url\n * @param {String} params.html contains the html of the dialog content\n */\n open: function (params) {\n // close any open dialog\n this.close();\n this.create(params);\n this.replace(params);\n $('#wrapper').attr('aria-hidden', 'true');\n },\n /**\n * @description populate the dialog with html content, then open it\n **/\n openWithContent: function (params) {\n var content, position, callback;\n\n if (!this.$container) { return; }\n content = params.content || params.html;\n if (!content) { return; }\n this.$container.empty().html(content);\n if (!this.$container.dialog('isOpen')) {\n this.$container.dialog('open');\n }\n\n if (params.options) {\n position = params.options.position;\n }\n if (!position) {\n position = this.settings.position;\n }\n imagesLoaded(this.$container).on('done', function () {\n this.$container.dialog('option', 'position', position);\n }.bind(this));\n\n callback = (typeof params.callback === 'function') ? params.callback : function () {};\n callback();\n },\n /**\n * @description Replace the content of current dialog\n * @param {object} params\n * @param {string} params.url - If the url property is provided, an ajax call is performed to get the content to replace\n * @param {string} params.html - If no url property is provided, use html provided to replace\n */\n replace: function (params) {\n if (!this.$container) {\n return;\n }\n if (params.url) {\n params.url = util.appendParamToURL(params.url, 'format', 'ajax');\n ajax.load({\n url: params.url,\n data: params.data,\n callback: function (response) {\n params.content = response;\n this.openWithContent(params);\n }.bind(this)\n });\n } else if (params.html) {\n this.openWithContent(params);\n }\n },\n /**\n * @function\n * @description Closes the dialog\n */\n close: function () {\n if (!this.$container) {\n return;\n }\n this.$container.dialog('close');\n },\n exists: function () {\n return this.$container && (this.$container.length > 0);\n },\n isActive: function () {\n return this.exists() && (this.$container.children.length > 0);\n },\n settings: {\n autoOpen: false,\n height: 'auto',\n modal: true,\n overlay: {\n opacity: 0.5,\n background: 'black'\n },\n resizable: false,\n title: '',\n width: '800',\n close: function () {\n $(this).dialog('close');\n $('#wrapper').attr('aria-hidden', 'false');\n },\n position: {\n my: 'center',\n at: 'center',\n of: window,\n collision: 'flipfit'\n }\n }\n};\n\nmodule.exports = dialog;\n","'use strict';\n\nvar einstein = {\n // storage elements\n registeredEinsteinListeners: {},\n loaded: window.einstein.loaded || [],\n load: function (id) {\n einstein.loaded.push(id);\n var einstainElement = $('#' + id).parents('[id^=\"cq_recomm_slot\"]');\n if (einstainElement.hasClass('einstain-inited')) {\n return window.console.log('einstain-inited', id);\n }\n for (var selector in this.registeredEinsteinListeners) {\n if (this.onEinstainContentAdded(selector, this.registeredEinsteinListeners[selector])) {\n return window.console.log('einstain-event-triggered', id, selector);\n }\n }\n },\n // binds function to moment when Einstein content added, if present on page it's executed instantly\n // return true if function is executed\n onEinstainContentAdded: function (selector, callback) {\n // check if element present in dom\n var einsteinContainers = $('[id^=\"cq_recomm_slot\"]:not(.einstain-inited)');\n var targetElements = einsteinContainers.find(selector);\n if (targetElements && targetElements.length) {\n // mark container as loaded if required child is already present\n einsteinContainers.each(function () {\n $(this).addClass('einstain-inited');\n });\n // call callback with small delay, to make sure elements processed before it\n setTimeout(callback, 200);\n return true;\n } else {\n this.registeredEinsteinListeners[selector] = callback;\n return false;\n }\n }\n};\n\nmodule.exports = einstein;\nwindow.einstein = einstein;\n","'use strict';\n\n/**\n * @function\n * @description Load details to a given gift certificate\n * @param {String} id The ID of the gift certificate\n * @param {Function} callback A function to called\n */\nexports.checkBalance = function (data,callback) {\n $.ajax({\n type: 'POST',\n url: Urls.giftCardCheckBalance,\n data: data,\n success: callback\n });\n};\n","'use strict';\n\nvar checkout = require('org/pages/checkout/checkout');\n\nvar dialog = require('org/dialog'),\n page = require('base/page'),\n validator = require('site/validator');\n\nvar login = {\n /**\n * @private\n * @function\n * @description init events for the loginPage\n */\n init: function () {\n //o-auth binding for which icon is clicked\n $('.oAuthIcon').bind('click', function () {\n $('#OAuthProvider').val(this.id);\n });\n\n if ($('.returning-customers div').length > 0) {\n $('.returning-customers div').removeAttr('aria-required')\n }\n\n //toggle the value of the rememberme checkbox\n $('#dwfrm_login_rememberme').bind('change', function () {\n if ($(this).prop('checked')) {\n $(this).val('true');\n } else {\n $(this).val('false');\n }\n });\n\n $('#password-reset').on('click', function (e) {\n e.preventDefault();\n dialog.open({\n url: $(e.target).attr('href'),\n options: {\n open: function () {\n validator.init();\n $(document).on('click','[name$=\"_requestpassword_send\"]', function (e) {\n if (!$('[name$=\"_requestpassword\"]').valid()) {\n return;\n }\n e.preventDefault();\n var data = $('[name$=\"_requestpassword\"]').serialize();\n // add form action to data\n data += '&' + $('[name$=\"_requestpassword_send\"]').attr('name') + '=';\n // make sure the server knows this is an ajax request\n if (data.indexOf('ajax') === -1) {\n data += '&format=ajax';\n }\n $.ajax({\n type: 'POST',\n url: $('[name$=\"_requestpassword\"]').attr('action'),\n data: data,\n success: function (response) {\n if (typeof response === 'object' &&\n !response.success &&\n response.error === 'CSRF Token Mismatch') {\n page.redirect(Urls.csrffailed);\n } else if (typeof response === 'string') {\n dialog.$container.html(response);\n }\n },\n failure: function () {\n dialog.$container.html('
' + Resources.SERVER_ERROR + '
');\n }\n });\n });\n }\n }\n });\n });\n },\n loginInit: function () {\n // Remove border from last div element\n $('.cart-row-v2:last-child').each(function() {\n $(this).css('border-bottom', 'none');\n });\n\n $('.title-area-wrapper:first').css('box-shadow', 'none');\n $('.title-area-wrapper.title-area-wrapper-icing:first').css('box-shadow', '2px 2px 4px 0 rgba(34, 34, 34, 0.2)');\n\n // Display item summary depending on device screen size ( mobile/desktop/tablet)\n checkout.itemSummaryDisplay();\n\n // Auto check remember me checkbox\n //$('#dwfrm_login_rememberme').prop('checked', true);\n }\n}\n\nmodule.exports = login;\n","'use strict';\n\nvar util = require('./util');\nvar $loyaltyEnforce = true;\nvar $loyaltySMS = $(\"#consent-sms\");\nvar $loyaltyPhone = $(\"input.phone\");\n\n/**\n * @private\n * @function\n * @description Initializes events for the Loyalty Program\n */\nfunction initializeEvents() {\n var $eventContainer = $('.loyalty');\n var $registrationForm = $('#RegistrationForm');\n var $loyaltysubscription = $('#LoyaltySubscriptionForm');\n var $loyaltysubscriptionsmslabel = $loyaltysubscription.find('label[for=\"consent-sms\"]');\n var $emaillabel = $('#consent-email');\n var $loyaltylabel = $('#consent-loyalty');\n var $smslabel = $('#consent-sms');\n var $loyaltysubscriptionphone = $loyaltysubscription.find('#dwfrm_loyalty_customer_phone');\n var $loyaltysubscriptionphonelabel = $('label[for=\"dwfrm_loyalty_customer_phone\"]');\n var $accountOptions = $('.account-options');\n $eventContainer.on('click', '.trigger-event-component .cta', function (e) {\n e.preventDefault();\n var event = $(this).parents('.trigger-event-component').get(0).id;\n var downloadUrl = $(this).attr('href');\n $.ajax({\n type: 'POST',\n url: Urls.inteqRecordEvent,\n data: {\n event: event\n },\n dataType: 'html',\n async: false\n }).done(function(response) {\n response = jQuery.parseJSON(response);\n if (response.success == true) {\n window.open(downloadUrl);\n // TODO Update Customer points at Loyalty page\n } else {\n // TODO show error\n }\n }).fail(function() {\n window.console.log('[ERROR] ajax request: ' + Urls.inteqRecordEvent);\n });\n });\n\n var $eventMappingInteq = $(\"#ways-to-earn .social-event\");\n $eventMappingInteq.on('click', function (e) {\n e.preventDefault();\n var socialEvent = $(this);\n var event = socialEvent.data('id');\n var canComplete = socialEvent.parents('.can-complete');\n var activityCard = socialEvent.parents('.activity-card');\n var completed = $('#completed-img').html();\n var downloadUrl = socialEvent.attr('href');\n $.ajax({\n type: 'POST',\n url: Urls.inteqRecordEvent,\n data: {\n event: event\n },\n dataType: 'html',\n async: false\n }).done(function(res) {\n var response = JSON.parse(res);\n if (response.success == true) {\n window.open(downloadUrl);\n activityCard.addClass('disabled');\n canComplete.html(completed);\n } else {\n console.log('Something went wrong!');\n }\n }).fail(function() {\n window.console.log('[ERROR] ajax request: ' + Urls.inteqRecordEvent);\n });\n });\n\n $(\".anytimeReward\").on('click', function(e){\n e.preventDefault();\n var anytimeReward = $(this);\n var anytimeRewardReplace = $('#anytimeRewardReplace').html();\n var redeemStatus = anytimeReward.data('redeem');\n if(redeemStatus == 0){\n var innerDiv = anytimeReward.children('.inner-div');\n var useRewards = innerDiv.children('a.use-rewards');\n var url = anytimeReward.data('url');\n var event = useRewards.attr('id');\n $.ajax({\n type: 'POST',\n url: url,\n data: {event: event},\n dataType: 'json',\n success: function (res) {\n if(res.success){\n anytimeReward.html(anytimeRewardReplace);\n anytimeReward.removeClass('pointer');\n anytimeReward.data('redeem', '1');\n }\n }\n });\n }\n });\n\n $('.preference-submit').on('click', function(e) {\n e.preventDefault();\n var msgSelector = '.unsubscribe-case';\n var subscribe = false;\n var $container = $(this).parents('.preference-container');\n var preferenceID = $(this).attr('id');\n var $preference = $(this).parents('.preference-container').find('.preference-value');\n\n if ($preference.length) {\n subscribe = $preference[0].checked;\n }\n\n if (subscribe) {\n msgSelector = '.subscribe-case';\n }\n \n $.ajax({\n type: 'POST',\n url: Urls.updatePreference,\n data: {\n preference: preferenceID,\n subscribe: subscribe\n },\n dataType: 'html'\n }).done(function(response) {\n response = jQuery.parseJSON(response);\n if (response.success == true) {\n $container.find(msgSelector).removeClass('visually-hidden');\n setTimeout(function(){\n $container.find(msgSelector).addClass('visually-hidden');\n window.location = util.appendParamToURL(Urls.showAccount);\n }, 4000);\n } else {\n // TODO show error\n }\n }).fail(function() {\n window.console.log('[ERROR] ajax request: ' + Urls.inteqRecordEvent);\n });\n });\n $('.nevermind').on('click', function(e) {\n e.preventDefault();\n var $container = $(this).parents('.preference-container');\n var $link = $(this).attr('href');\n $container.find('.subscribe-case').removeClass('visually-hidden');\n setTimeout(function(){\n $container.find('.subscribe-case').addClass('visually-hidden');\n window.location.href = $link;\n }, 3000);\n });\n \n $('#loyaltystatusCode.disabled').on('click', function(e) {\n e.preventDefault();\n });\n \n $('label.checkbox-label-agree').on('click', function() {\n if ($('#loyaltystatusCode').hasClass('disabled')) {\n $('#agreeCheckbox').prop('checked',true).trigger('change');\n $('#loyaltystatusCode').removeClass('disabled');\n return false;\n } else {\n $('#agreeCheckbox').prop('checked',false).trigger('change');\n $('#loyaltystatusCode').addClass('disabled');\n return false;\n }\n });\n \n $(document).ready(function() {\n if ($('#graph').length > 0){\n var el = document.getElementById('graph'); // get canvas\n var options = {\n points: el.getAttribute('data-points') || 25,\n toReward: el.getAttribute('data-points-to-reward') || 25,\n size: el.getAttribute('data-size') || 224,\n lineWidth: el.getAttribute('data-line') || 15,\n rotate: el.getAttribute('data-rotate') || 0\n }\n var pointsSum = parseInt(options.points, 10) + parseInt(options.toReward, 10);\n var progress = ((parseInt(options.points, 10) / parseInt(pointsSum, 10)) * 100).toFixed();\n el.setAttribute('data-percent', options.percent);\n var canvas = document.createElement('canvas');\n var ctx = canvas.getContext('2d');\n canvas.width = canvas.height = options.size;\n el.appendChild(canvas);\n ctx.translate(options.size / 2, options.size / 2); // change center\n ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI); // rotate -90 deg\n var radius = (options.size - options.lineWidth) / 2;\n var drawCircle = function(color, lineWidth, percent) {\n percent = Math.min(Math.max(0, percent || 1), 1);\n ctx.beginPath();\n ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, false);\n ctx.strokeStyle = color;\n ctx.lineCap = 'butt'; // butt, round or square\n ctx.lineWidth = lineWidth\n ctx.stroke();\n };\n drawCircle('#f4c2db', options.lineWidth, 100 / 100);\n if (progress > 0) {\n drawCircle('#e2188f', options.lineWidth, progress / 100);\n }\n } else { return true}\n });\n $registrationForm.on('click', '.create-account', calculateProfileProgressValue);\n if ($emaillabel.prop('checked') == true){\n window.localStorage.setItem('emailOptin', true);\n }\n $emaillabel.on('click', function() {\n if ($emaillabel.prop('checked') == true){\n window.localStorage.setItem('emailOptin', true);\n } else {\n window.localStorage.setItem('emailOptin', false);\n }\n });\n $smslabel.on('click', function() {\n if ($smslabel.prop('checked') == true){\n window.localStorage.setItem('smsOptin', true);\n } else {\n window.localStorage.setItem('smsOptin', false);\n }\n });\n $loyaltylabel.on('click', function() {\n if ($loyaltylabel.prop('checked') == true){\n window.localStorage.setItem('loyaltyOptin', true);\n } else {\n window.localStorage.setItem('loyaltyOptin', false);\n }\n });\n $loyaltysubscriptionsmslabel.on('click', function (){\n if ($loyaltysubscriptionphone.hasClass('verify-mobile-sms-checkbox')){ \n $loyaltysubscriptionphone.toggleClass('verify-mobile-sms-checkbox');\n $loyaltysubscriptionphonelabel.toggleClass('required');\n } else {\n $loyaltysubscriptionphone.toggleClass('verify-mobile-sms-checkbox');\n $loyaltysubscriptionphonelabel.toggleClass('required');\n }\n });\n \n if ($('.header-banner-loyalty').length) {\n $('.secondary-navigation .loyalty').removeClass('visually-hidden');\n }\n\n $accountOptions.on('click', '.banner', function () {\n window.location = Urls.showAccount;\n });\n\n var loylatyBoxValue = false;\n $('select[name$=\"_country\"]').on('change', function () {\n var countryCode = this.value;\n var $consentBox = $('input[name$=\"consent-loyalty\"]');\n var $suscribeLoyaltyButton = $('button.subscribe-loyalty');\n var $errorMessage = $('.country_not_available.error');\n if (!isLoyaltyEnabledInCountry(countryCode)) {\n loylatyBoxValue = $consentBox.prop('checked');\n $consentBox.parents('.form-row').hide();\n $consentBox.prop('checked', false);\n $suscribeLoyaltyButton.prop('disabled', true);\n $errorMessage.show();\n } else {\n $consentBox.prop('checked', loylatyBoxValue);\n $consentBox.parents('.form-row').show();\n $suscribeLoyaltyButton.prop('disabled', false);\n $errorMessage.hide();\n }\n });\n\n $('form[role=\"form\"').find('[name$=\"_country\"]').on('change', function () {\n setPhoneNumberCountryCode($(this).val());\n });\n\n setPhoneNumberCountryCode();\n\n $('#LoyaltySubscriptionForm').on('submit', function () {\n var isLoyaltyEnable = $('#consent-loyalty').is(':checked');\n var $phoneField = $('input.phone');\n var isPhoneValid = !$phoneField.hasClass('error');\n var $submitLoyaltyBtn = $(this).find('.subscribe-loyalty[type=\"submit\"]');\n if (isLoyaltyEnable && isPhoneValid) {\n $submitLoyaltyBtn.addClass('disabled');\n }\n });\n\n if ($loyaltySMS.length) {\n isLoyaltySMSAvailabilityNew();\n\n $loyaltyPhone.on('keyup', function () {\n isLoyaltySMSAvailabilityNew();\n });\n }\n}\n\nfunction isLoyaltySMSAvailabilityNew() {\n var phoneVal = $loyaltyPhone.val();\n if (phoneVal.trim() !== '') {\n $loyaltySMS.prop('disabled', false);\n } else {\n $loyaltySMS.prop('checked', false);\n $loyaltySMS.prop('disabled', true);\n }\n}\n\n\nfunction setPhoneNumberCountryCode(countryCode) {\n if (countryCode == undefined) {\n countryCode = $('form[role=\"form\"').find('[name$=\"_country\"]').val();\n }\n var phoneCodesMapping = SitePreferences.PHONE_NUMBER_COUNTRY_CODES;\n if (countryCode && phoneCodesMapping) {\n var phoneCode = phoneCodesMapping[countryCode];\n $('form[role=\"form\"').find('[name$=\"_phoneNumberCountryCode\"]').val(phoneCode);\n }\n}\n\nfunction isLoyaltyEnabledInCountry(countryCode) {\n if (!countryCode || !SitePreferences.LOYALTY_ENABLED_COUNTRIES) {\n return false;\n }\n\n return SitePreferences.LOYALTY_ENABLED_COUNTRIES.indexOf(countryCode) >= 0;\n}\n\nfunction validateDob($form, myDate) {\n if ($form.find('[name=\"s_date_of_birth\"]').length) {\n var currDate = new Date();\n var minAge = SitePreferences.MIN_AGE;\n currDate.setFullYear(currDate.getFullYear() - minAge);\n if (currDate < myDate) {\n $form.find('.loyalty-dob-error').show();\n window.dataLayer.push({\n 'event' : 'user.loyaltyregistration',\n 'data' : {\n 'status' : 'failed',\n 'reason' : 'age less than minimum'\n }});\n return false;\n }\n }\n window.localStorage.setItem('loyaltyDone', true);\n return true;\n}\n\nfunction initYearsDropDown() {\n var $yearSelect = $('#dwfrm_loyalty_customer_year');\n var start = new Date().getFullYear();\n var end = SitePreferences.MAX_AGE_FOR_SELECT;\n var options = '';\n for (var year = start; year >= end; year--){\n options += '';\n }\n $yearSelect.append(options);\n $('#dwfrm_loyalty_customer_day').addClass('customer-day');\n $('#dwfrm_loyalty_customer_month').addClass('customer-month');\n $('#dwfrm_loyalty_customer_year').addClass('customer-year');\n $('#dwfrm_loyalty_customer_day option:eq(0)').attr('disabled','disabled');\n $('#dwfrm_loyalty_customer_month option:eq(0)').attr('disabled','disabled');\n $('#dwfrm_loyalty_customer_year option:eq(0)').attr('disabled','disabled');\n}\n\nfunction dateOfBirthInitActions() {\n var $form = $('#LoyaltySubscriptionForm');\n var $button = $('.subscribe-loyalty');\n var dateValue = $form.find('.date-of-birth').val();\n if (dateValue && dateValue != 'null') {\n var date = new Date(dateValue);\n $('#dwfrm_loyalty_customer_year').val(date.getUTCFullYear());\n $('#dwfrm_loyalty_customer_month').val(date.getUTCMonth() + 1);\n $('#dwfrm_loyalty_customer_day').val(date.getUTCDate());\n } else {\n $('#dwfrm_loyalty_customer_day').val('DD');\n $('#dwfrm_loyalty_customer_month').val('MM');\n $('#dwfrm_loyalty_customer_year').val('YYYY');\n $form.find('[name=\"s_date_of_birth\"]').val((new Date()).toLocaleDateString('en-GB'));\n }\n $button.on('click', function() {\n var dayVal = $('#dwfrm_loyalty_customer_day').val();\n var monthVal = $('#dwfrm_loyalty_customer_month').val();\n var yearVal = $('#dwfrm_loyalty_customer_year').val();\n var myDate = new Date();\n var loyaltylabelinternal = $('#consent-loyalty');\n myDate.setFullYear(yearVal, monthVal - 1, dayVal);\n $form.find('.date-of-birth').val(myDate.toLocaleDateString('en-GB'));\n if (dayVal && monthVal && yearVal) {\n validateDob($form, myDate);\n } else {\n window.dataLayer.push({\n 'event' : 'user.loyaltyregistration',\n 'data' : {\n 'status' : 'failed',\n 'reason' : 'required field missing'\n }});\n }\n if (loyaltylabelinternal.prop('checked') == false && window.localStorage.getItem('loyaltySignUpLoaded')){\n window.dataLayer.push({\n 'event' : 'user.loyaltyregistration',\n 'data' : {\n 'status' : 'failed',\n 'reason' : 'required field missing'\n }\n });\n window.localStorage.removeItem('loyaltySignUpLoaded');\n }\n });\n}\n\nfunction calculateProfileProgressValue() {\n var $error = $(\"#error-checkbox\");\n if ($('#isLoyaltyUser').length > 0) {\n window.localStorage.setItem('loyaltyOptin', true);\n }\n var $loyaltyConsent = $('#consent-loyalty');\n if(!$loyaltyConsent.length) {\n return true;\n } else{\n if (($loyaltyConsent.length > 0 && $loyaltyConsent.prop('checked') == true) || $loyaltyEnforce == false){\n return true;\n } else {\n $error.text(Resources.LOALTY_SIGNUP_OPTIN_ERROR);\n $loyaltyEnforce = false;\n return false;\n }\n }\n\n var $widgetContainer = $('.profile-detail-loyalty-widget');\n\n if (!$widgetContainer.length) {\n return true;\n }\n var progressValue = $('[name=\"complete-progress-value\"]').val();\n if (progressValue < 100) {\n $widgetContainer.find('.title').removeClass('visually-hidden');\n $widgetContainer.find('.detail').removeClass('visually-hidden');\n } else {\n $widgetContainer.find('.completed').removeClass('visually-hidden');\n }\n $('.progress').each(function(){\n var $bar = $(this).find('.bar');\n var $val = $(this).find('span');\n var perc = parseInt(progressValue, 10);\n $(this).css('opacity','1');\n $({p:0}).animate({p:perc}, {\n duration: 1000,\n easing: 'swing',\n step: function(p) {\n $bar.css({\n transform: 'rotate('+ (45+(p*1.8)) +'deg)'\n });\n $val.text(p|0);\n }\n });\n });\n return true;\n}\n\nmodule.exports = {\n init: function () {\n initializeEvents();\n if ($('#dwfrm_loyalty_customer_year').length) {\n initYearsDropDown();\n dateOfBirthInitActions();\n }\n if ($('.profile-detail-loyalty-widget').length) {\n calculateProfileProgressValue();\n }\n }\n};\n","'use strict';\n\nvar util = require('org/util'),\n ajax = require('base/ajax'),\n bonusProductsView = require('org/bonus-products-view');\n\nvar timer = {\n id: null,\n clear: function () {\n if (this.id) {\n window.clearTimeout(this.id);\n delete this.id;\n }\n },\n start: function (duration, callback) {\n this.id = setTimeout(callback, duration);\n }\n};\n\nvar minicart = {\n init: function () {\n this.$el = $('#mini-cart');\n this.$eltab = $('.mini-cart-tab');\n this.$content = this.$el.find('.mini-cart-content');\n var errorMessageInput = $('#cart-limit-error-message');\n if (errorMessageInput.length > 0) {\n var parentDiv = $('.product-add-to-cart');\n var errorMessage = errorMessageInput.val();\n var errorDiv = '
' + errorMessage + '
';\n parentDiv.append(errorDiv);\n }\n // events\n if ($(document).width() > 767 && !util.isMobile()) {\n this.$el.find('.mini-cart-total').on('mouseenter', function () {\n if (this.$content.not(':visible')) {\n this.slide();\n $('.mini-cart-link').attr('aria-expanded', 'true');\n }\n }.bind(this));\n this.$content.on('mouseenter', function () {\n timer.clear();\n }).on('mouseleave', function () {\n timer.clear();\n timer.start(30, this.close.bind(this));\n $('.mini-cart-link').attr('aria-expanded', 'false');\n }.bind(this));\n\n $('.mini-cart-close-btn').on('click', function () {\n $('.mini-cart-content').slideUp('fast');\n $('.mini-cart-link').attr('aria-expanded', 'false');\n });\n } else if ($(document).width() > 767 && util.isMobile()){\n this.$el.find('.mini-cart-link').on('click', function (e) {\n if (this.$content.css('display') != 'block') {\n e.preventDefault();\n }\n if (this.$content.not(':visible')) {\n this.slide();\n timer.clear();\n $('.mini-cart-link').attr('aria-expanded', 'true');\n\n }\n }.bind(this));\n $('.mini-cart-close-btn').on('click', function () {\n $('.mini-cart-content').slideUp('fast');\n $('.mini-cart-link').attr('aria-expanded', 'false');\n });\n }\n $(document).off('basketUpdate.minicart').on('basketUpdate.minicart', reloadCartAjax);\n },\n /**\n * @function\n * @description Shows the given content in the mini cart\n * @param {String} A HTML string with the content which will be shown\n */\n show: function (html) {\n if ($(document).width() > 767) {\n this.$el.html(html);\n this.$eltab.html(html);\n //util.scrollBrowser(0);\n this.init();\n this.slide();\n bonusProductsView.loadBonusOption();\n $('.mini-cart-link').focus();\n this.setFocus();\n } else {\n this.$el.html(html);\n this.$eltab.html(html);\n this.init();\n bonusProductsView.loadBonusOption();\n }\n },\n /**\n * @function\n * @description Updates minicart quantity\n * @param {String} A HTML string with the content which will be shown\n */\n updateQty: function (html) {\n this.$el.html(html);\n this.init();\n },\n /**\n * @function\n * @description Slides down and show the contents of the mini cart\n */\n slide: function () {\n timer.clear();\n // show the item\n this.$content.slideDown('slow');\n // after a time out automatically close it\n timer.start(3000, this.close.bind(this));\n $('.mini-cart-link').attr('aria-expanded', 'true');\n },\n /**\n * @function\n * @description Closes the mini cart with given delay\n * @param {Number} delay The delay in milliseconds\n */\n close: function (delay) {\n timer.clear();\n this.$content.slideUp(delay);\n $('.mini-cart-link').attr('aria-expanded', 'false');\n },\n /**\n * @function\n * @description Sets up the focus events for the mini cart\n */\n setFocus: function(){\n /**\n * @listener\n * @description Set the aria-expanded value to true, show the mini cart and focus on the mini cart header when the mini cart icon is focused or moused over\n */\n $('.mini-cart-link').on('focus mouseenter', function() {\n $(this).attr('aria-expanded','true');\n\n if (!$(this).parent().next('.mini-cart-content').is(':visible')) {\n $(this).parent().next('.mini-cart-content').show();\n }\n\n $('.mini-cart-header').focus();\n });\n\n /**\n * @listener\n * @description Set the aria-expanded value to false when the mini cart is hidden\n */\n $('.mini-cart-link-checkout').on('blur', function() {\n $('.mini-cart-content').hide();\n $('.mini-cart-link').attr('aria-expanded','false');\n });\n\n /**\n * @listener\n * @description Allow the user to exit out of the mini cart via keyboard via the escape key\n */\n $('.mini-cart-link').on('keyup', function(e) {\n if (e.keyCode == 27) {\n $(this).attr('aria-expanded','false');\n $(this).parent().next('.mini-cart-content').hide();\n }\n });\n },\n cartRefresh: function(){\n reloadCartAjax();\n }\n};\n\n// refresh minicart for specific pages\nfunction reloadCartAjax() {\n if (\n !window.pageContext ||\n !window.pageContext.ns ||\n (\n window.pageContext.ns !== 'orderconfirmation' &&\n window.pageContext.ns !== 'cart' &&\n window.pageContext.ns !== 'account'\n )) {\n return;\n }\n ajax.load({\n url: util.ajaxUrl(Urls.minicartUpdate),\n data: '',\n callback: function (response) {\n minicart.show(response);\n }\n });\n}\n\nmodule.exports = minicart;\n","'use strict';\n\nvar giftcert = require('base/giftcert'),\n tooltip = require('org/tooltip'),\n util = require('org/util'),\n dialog = require('org/dialog'),\n page = require('base/page'),\n login = require('org/login'),\n validator = require('site/validator'),\n billing = require('./checkout/billing'),\n loyalty = require('org/loyalty'),\n cppa = require('org/cppa'),\n qas = require('int_QASc/qas'),\n recaptcha = require('org/recaptcha'),\n rewards = require('org/rewards');\nvar progress = require('base/progress');\nvar minicart = require('../minicart');\n\n/* ++++++++++++++++ SUBSCRIPTION CODE STARTS ++++++++++++++++ */\n\n//var customOrdergroove = require('org/subscriptions/ordergroove');\n\n/* ++++++++++++++++ SUBSCRIPTION CODE ENDS ++++++++++++++++ */\n\n// For Account Dashboard\nconst isShowWhoYouShopModal = $(\"#isShowWhoYouShopModal\").val();\nconst isShowAddressModal = $(\"#isShowAddressModal\").val();\nconst isLoyaltyDashboard = $(\"#isLoyaltyDashboard\").val();\n\n/**\n * @function\n * @description Initializes the events on the address form (apply, cancel, delete)\n * @param {Element} form The form which will be initialized\n */\nfunction initializeAddressForm() {\n var $form = $('#edit-address-form');\n\n $form.find('input[name=\"format\"]').remove();\n tooltip.init();\n //$(\"\").attr({type:\"hidden\", name:\"format\", value:\"ajax\"}).appendTo(form);\n\n util.updateStateField($form);\n $('select[name$=\"_country\"]', $form).on('change', function () {\n util.updateStateField($form);\n });\n\n $form.on('click', '.apply-button', function (e) {\n e.preventDefault();\n if (!$form.valid()) {\n return false;\n }\n var url = util.appendParamToURL($form.attr('action'), 'format', 'ajax');\n var applyName = $form.find('.apply-button').attr('name');\n var options = {\n url: url,\n data: $form.serialize() + '&' + applyName + '=x',\n type: 'POST'\n };\n $.ajax(options).done(function (data) {\n if (typeof(data) !== 'string') {\n if (data.success) {\n dialog.close();\n page.refresh();\n } else if (data.error) {\n page.redirect(Urls.csrffailed);\n } else {\n console.log(data.message);\n return false;\n }\n } else {\n $('#dialog-container').html(data);\n account.init();\n tooltip.init();\n }\n });\n })\n .on('click', '.cancel-button, .close-button', function (e) {\n e.preventDefault();\n dialog.close();\n })\n .on('click', '.delete-button', function (e) {\n e.preventDefault();\n if (window.confirm(String.format(Resources.CONFIRM_DELETE, Resources.TITLE_ADDRESS))) {\n var url = util.appendParamsToUrl(Urls.deleteAddress, {\n AddressID: $form.find('#addressid').val(),\n format: 'ajax'\n });\n $.ajax({\n url: url,\n method: 'POST',\n dataType: 'json'\n }).done(function (data) {\n if (data.status.toLowerCase() === 'ok') {\n dialog.close();\n page.refresh();\n } else if (data.message.length > 0) {\n window.console.log(data.message);\n return false;\n } else {\n dialog.close();\n page.refresh();\n }\n });\n }\n });\n\n validator.init();\n qas.init();\n}\n/**\n * @private\n * @function\n * @description Toggles the list of Orders\n */\nfunction toggleFullOrder () {\n $('.order-items')\n .find('li.hidden:first')\n .prev('li')\n .append('View All')\n .children('.toggle')\n .click(function () {\n $(this).parent().siblings('li.hidden').show();\n $(this).remove();\n });\n}\n/**\n * @private\n * @function\n * @description Binds the events on the address form (edit, create, delete)\n */\nfunction initAddressEvents() {\n var addresses = $('#addresses');\n if (addresses.length === 0) { return; }\n\n addresses.on('click', '.address-edit, .address-create', function (e) {\n e.preventDefault();\n dialog.open({\n url: this.href,\n options: {\n open: initializeAddressForm\n }\n });\n }).on('click', '.delete', function (e) {\n e.preventDefault();\n if (window.confirm(String.format(Resources.CONFIRM_DELETE, Resources.TITLE_ADDRESS))) {\n $.ajax({\n url: util.appendParamToURL($(this).attr('href'), 'format', 'ajax'),\n dataType: 'json'\n }).done(function (data) {\n if (data.status.toLowerCase() === 'ok') {\n page.redirect(Urls.addressesList);\n } else if (data.message.length > 0) {\n console.log(data.message);\n } else {\n page.refresh();\n }\n });\n }\n });\n}\n\nfunction initAccountDashboard(){\n\n $(\"#thanksContent\").dialog({ \n modal: true,\n overlay: {\n opacity: 0.5,\n background: 'white'\n },\n autoOpen: false,\n classes: { \"ui-dialog\": \"whoshop-dialog thanks-dialog\" },\n position: { my: $(window).width() > 600 ? \"right-13% bottom-30%\" : 'center bottom', at: $(window).width() > 600 ? \"right bottom\" : 'center bottom' },\n height: 'auto'\n });\n\n if(isLoyaltyDashboard == 'true' && isShowWhoYouShopModal == 'true' && sessionStorage.getItem('whoYouShopModal') !== 'true'){\n initWhoYouShopForModal();\n } else if(isLoyaltyDashboard == 'true' && isShowAddressModal == 'true' && sessionStorage.getItem('whoYouShopModal') !== 'true'){\n initAddressModal();\n }\n}\n\n\nfunction initWhoYouShopForModal() {\n dialog.open({\n url: Urls.whoShopModal,\n options: {\n width: 342,\n open: initializeWhoYouShopForm,\n close: function() {\n sessionStorage.setItem(\"whoYouShopModal\", true);\n },\n classes: { \"ui-dialog\": \"whoshop-dialog\"},\n position: { my: $(window).width() > 600 ? \"right-13% bottom-10%\" : 'center bottom', at: $(window).width() > 600 ? \"right bottom\" : 'center bottom' },\n height: 'auto'\n }\n });\n}\n\nfunction initializeWhoYouShopForm() {\n $(\".whoshopclick .input-checkbox\").on('click', function(){\n var whoShopActivity = $(\"#whoShopActivity\");\n var canComplete = whoShopActivity.parents('.can-complete');\n var activityCard = whoShopActivity.parents('.activity-card');\n var completed = $('#completed-img').html();\n var form = $(this).closest('form');\n var url = util.appendParamToURL(form.attr('action'), 'format', 'ajax');\n $.ajax({\n url: url,\n method: 'POST',\n data: form.serialize(),\n success: function(res){\n if(res.success){\n $( \"#thanksContent\" ).dialog( \"open\" );\n dialog.close();\n activityCard.addClass('disabled');\n canComplete.html(completed);\n $(\".ui-widget-overlay\").on('click', function(){\n $( \"#thanksContent\" ).dialog( \"close\" );\n });\n } else {\n $(\"#whoYouShopContent\").html('
Something Went Wrong!
');\n }\n }\n });\n });\n}\n\nfunction initAddressModal() {\n dialog.open({\n url: Urls.addressModal,\n options: {\n width: 342,\n open: initializeAddressModal,\n close: function() {\n sessionStorage.setItem(\"whoYouShopModal\", true);\n },\n classes: { \"ui-dialog\": \"address-dialog\"},\n position: { my: $(window).width() > 600 ? \"right-12% bottom-9%\" : 'center bottom', at: $(window).width() > 600 ? \"right bottom\" : 'center bottom' },\n height: 'auto'\n }\n });\n}\n\nfunction initializeAddressModal(){\n var addressActivity = $(\"#addressActivity\");\n var canComplete = addressActivity.parents('.can-complete');\n var activityCard = addressActivity.parents('.activity-card');\n var completed = $('#completed-img').html();\n\tvar $form = $('#add-address-modal');\n $form.on('click', '#address-btn', function (e) {\n e.preventDefault();\n if (!$form.valid()) {\n return false;\n }\n var url = util.appendParamToURL($form.attr('action'), 'format', 'ajax');\n var applyName = $form.find('#address-btn').attr('name');\n var options = {\n url: url,\n data: $form.serialize() + '&' + applyName + '=x',\n type: 'POST'\n };\n $.ajax(options).done(function (data) {\n if (typeof(data) !== 'string') {\n if (data.success) {\n $( \"#thanksContent\" ).dialog( \"open\" );\n dialog.close();\n activityCard.addClass('disabled');\n canComplete.html(completed);\n $(\".ui-widget-overlay\").on('click', function(){\n $( \"#thanksContent\" ).dialog( \"close\" );\n });\n } else if (data.error) {\n page.redirect(Urls.csrffailed);\n } else {\n console.log(data.message);\n return false;\n }\n } else {\n console.log(data);\n }\n });\n });\n}\n\n/**\n * @private\n * @function\n * @description Binds the events to account navigation expandable tabs\n */\nfunction initAccountNavigationEvents() {\n $('body').on('click', '.rewards .account-options__title', function (e) {\n e.preventDefault();\n var itemWrapper = $(this).closest('.account-options__item');\n var itemBody = $(this).next();\n itemBody.slideToggle();\n itemWrapper.toggleClass('active');\n });\n $('.reward-history-showmore').on('click', function () {\n var rewardsHistoryHiddenItems = $('.show-rewards-container .used-reward-item.hidden');\n var loopLimit = rewardsHistoryHiddenItems.length < 10 ? rewardsHistoryHiddenItems.length : 10;\n for (var i = 0; i < loopLimit; i++) {\n var item = $(rewardsHistoryHiddenItems[i]);\n item.removeClass('hidden');\n }\n if (loopLimit < 10) {\n $(this).hide();\n }\n });\n\n $('.activity-history .all-activities').on('click', function () {\n var rewardsHistoryHiddenItems = $('.activity-history .activities.hidden');\n var lessActivitiesButton = $('.activity-history .less-activities.hidden');\n rewardsHistoryHiddenItems.addClass('hiddenItems');\n rewardsHistoryHiddenItems.removeClass('hidden');\n lessActivitiesButton.removeClass('hidden');\n $(this).addClass('hidden');\n });\n\n $('.banner-btn-wrap .view-activities-btn').on('click', function(){\n $('html, body').animate({\n scrollTop: $(\"#activity-history\").offset().top-150\n }, 1000);\n });\n $('.banner-btn-wrap .ways-to-earn-btn').on('click', function(){\n $('html, body').animate({\n scrollTop: $(\"#ways-to-earn\").offset().top\n }, 1000);\n });\n $('.see-details-btn').on('click', function(){\n $('html, body').animate({\n scrollTop: $(\"#availableRewards\").offset().top + 110\n }, 1000);\n });\n\n $('.activity-history .less-activities').on('click', function () {\n var rewardsHistoryHiddenItems = $('.activity-history .activities.hiddenItems');\n var allActivitiesButton = $('.activity-history .all-activities.hidden');\n rewardsHistoryHiddenItems.addClass('hidden');\n rewardsHistoryHiddenItems.removeClass('hiddenItems');\n allActivitiesButton.removeClass('hidden');\n $(this).addClass('hidden');\n $('html, body').animate({\n scrollTop: $(\"#activity-history\").offset().top-150\n }, 1000);\n });\n $('.move-to-top-svg').on('click', function(){\n $('html, body').animate({\n scrollTop: 0\n }, 1000);\n })\n $('.go-to-arrow-svg-m').on('click', function(){\n $('html, body').animate({\n scrollTop: 0\n }, 1000);\n })\n $('.left-nav-close-icon').on('click', function(){\n $('.left-nav-close-icon').parent().removeClass(\"show-left-nav\");\n $('body').removeClass(\"over-hidden\");\n $('.arrow-right').show();\n });\n $('.mobile-left-nav').on('click', function(){\n $('.left-nav').addClass(\"show-left-nav\");\n $('body').addClass(\"over-hidden\");\n $('.arrow-right').hide();\n });\n\n $(\"#who-you-shop-for-modal .x-mark-icon\").on('click', function(){\n $(\"#who-you-shop-for-modal\").hide();\n });\n\n\n $(document).ready(function(){\n var text = \"SHOP.GET REWARDS.REPEAT.\";\n var text1 = \"P.GET REWARDS.REPEAT.SHO\";\n var result = text.repeat(15);\n var result1 = text1.repeat(15);\n $('.shop_repeat_text').text(result);\n $('.shop_repeat_text1').text(result1);\n\n // Reward price updated with saperate the value and prefix\n\n $('.rewards-count').each(function( index ){\n var suffix = $(this).text();\n var only_num = suffix.replace(/[^0-9]/g,'');\n var only_text = suffix.slice(0,suffix.indexOf(only_num)).trim();\n var currencyFinalPrefix = `${only_text}`;\n $(this).html(currencyFinalPrefix + only_num);\n })\n let urlPath = window.location.href;\n $('.loyalty-side-nav ul li a').each(function() {\n if (this.href === urlPath) {\n $(this).closest('li').addClass('active');\n }\n });\n });\n}\n\nfunction checkingProfileFill() {\n var $el = $('.form-row:not(\".checkbox\") input');\n $el.each(function () {\n var el = $(this).closest('.form-row');\n if ($(this).val().trim().length != 0 && !el.hasClass('complete')) {\n el.addClass('complete');\n } else if ($(this).val().trim().length == 0 && el.hasClass('complete')) {\n el.removeClass('complete');\n }\n });\n chekingDataBirthdayFill();\n chekingCountryFill();\n chekingLocalStoreFill();\n chekingShoppingForFill();\n chekingGenderFill();\n}\nfunction chekingFormTextInputFill() {\n var el = $(this).closest('.form-row');\n\n if ($(this).val() != 0 && !el.hasClass('complete') && $(this).hasClass('valid')) {\n el.addClass('complete');\n } else if ($(this).val() != 0 && el.hasClass('complete') && $(this).hasClass('error')) {\n el.removeClass('complete');\n } else if ($(this).val() == 0 && el.hasClass('complete')) {\n el.removeClass('complete');\n }\n}\nfunction chekingCountryFill() {\n var el = $('.form-row.selectbox select.country');\n if (el.val() != '') {\n el.closest('.form-row').addClass('complete');\n } else {\n el.closest('.form-row').removeClass('complete');\n }\n}\nfunction chekingGenderFill() {\n var el = $('.form-row.selectbox select.gender');\n if (el.val() != '4') {\n el.closest('.form-row').addClass('complete');\n } else {\n el.closest('.form-row').removeClass('complete');\n }\n}\nfunction chekingShoppingForFill() {\n var parrent = $('.date-wrapper.shoppingForSet');\n if (parrent.find('input[type=checkbox]').is(':checked')) {\n if (!parrent.hasClass('complete')) {\n parrent.addClass('complete');\n }\n } else {\n parrent.removeClass('complete');\n }\n}\nfunction chekingDataBirthdayFill() {\n var elBirthdaySet = $('.date-wrapper.birthday-set');\n var chosenDay = $('select.customer-day').val();\n var chosenMonth = $('select.customer-month').val();\n var chosenYear = $('select.customer-year').val();\n var chosen = (chosenDay === null || chosenMonth === null || chosenYear === null) ? false : true;\n if (!elBirthdaySet.hasClass('complete') && chosen) {\n elBirthdaySet.addClass('complete');\n } else if (elBirthdaySet.hasClass('complete') && !chosen) {\n elBirthdaySet.removeClass('complete');\n }\n}\nfunction chekingLocalStoreFill() {\n var el = $('.preferred-store-section');\n var storeInfo = el.find('.preferred-store__body');\n if (storeInfo.length && !el.hasClass('complete')) {\n el.addClass('complete');\n } else if (!storeInfo.length && el.hasClass('complete')) {\n el.removeClass('complete');\n }\n}\n/**\n * @private\n * @function\n * @description checking form fields fill and visualise completion icon\n */\nfunction initCheckFieldsdsFillCompletion() {\n checkingProfileFill();\n $('body').on('focusout', '.form-row:not(\".checkbox\") input', chekingFormTextInputFill);\n $('body').on('change', '.birthday-set .form-row select', chekingDataBirthdayFill);\n $('body').on('change', '.preferred-store-section', chekingLocalStoreFill);\n $('body').on('change', '.shoppingForSet .form-row input[type=checkbox]', chekingShoppingForFill);\n $('body').on('change', '.form-row select.country', chekingCountryFill);\n $('body').on('change', '.form-row select.gender', chekingGenderFill);\n}\n\n\n/**\n * @private\n * @function\n * @description Binds the events of the payment methods list (delete card)\n */\nfunction initPaymentEvents() {\n $('.add-card').on('click', function (e) {\n e.preventDefault();\n dialog.open({\n url: $(e.target).attr('href'),\n options: {\n open: initializePaymentForm\n }\n });\n });\n\n var paymentList = $('.payment-list');\n if (paymentList.length === 0) { return; }\n\n util.setDeleteConfirmation(paymentList, String.format(Resources.CONFIRM_DELETE, Resources.TITLE_CREDITCARD));\n\n $('form[name=\"payment-edit\"]').on('submit', function (e) {\n e.preventDefault();\n dialog.open({\n url: this.action,\n options: {\n open: initializePaymentForm\n }\n });\n });\n\n $('form[name=\"payment-remove\"]').on('submit', function (e) {\n e.preventDefault();\n // override form submission in order to prevent refresh issues\n var button = $(this).find('.delete');\n $('').attr({\n type: 'hidden',\n name: button.attr('name'),\n value: button.attr('value') || 'delete card'\n }).appendTo($(this));\n var data = $(this).serialize();\n $.ajax({\n type: 'POST',\n url: $(this).attr('action'),\n data: data\n })\n .done(function () {\n page.redirect(Urls.paymentsList);\n });\n });\n}\n\nfunction initDetectCardType() {\n var $cardNumbValue = $('input[name$=\"_newcreditcard_number\"]').val();\n var $cardTypeEl = $('input[name$=\"_newcreditcard_type\"]');\n\n if ($cardNumbValue.match(/^3/)) {\n $cardTypeEl.val('Amex');\n } else if ($cardNumbValue.match(/^4/)) {\n $cardTypeEl.val('Visa');\n } else if ($cardNumbValue.match(/^5/)) {\n $cardTypeEl.val('MasterCard');\n } else {\n $cardTypeEl.val('');\n }\n}\n\nfunction processCreditCardFormSubmit($form) {\n var url = util.appendParamToURL($form.attr('action'), 'format', 'ajax');\n var applyName = $form.find('#applyBtn').attr('name');\n var options = {\n url: url,\n data: $form.serialize() + '&' + applyName + '=x',\n type: 'POST'\n };\n\n $form.find('.payment-form-error').empty();\n $form.find('.payment-form-error').removeClass('error-form');\n $.ajax(options).done(function (data) {\n if (typeof(data) == 'object') {\n if (data.success) {\n dialog.close();\n page.refresh();\n } else if (data.error) {\n page.redirect(Urls.csrffailed);\n } else {\n $form.find('.payment-form-error').empty().html(data.message);\n $form.find('.payment-form-error').addClass('error-form');\n }\n return;\n }\n $form.find('.payment-form-error').empty().html('Unpexpected response durring form submit. Please try again.');\n $form.find('.payment-form-error').addClass('error-form');\n window.console.warn('Unexpected response', data);\n });\n}\n\nfunction initializePaymentForm() {\n var $form = $('#CreditCardForm');\n $('input[name$=\"_number\"]').val('');\n\n $('select[name$=\"_addressList\"]', $form).on('change', function () {\n var selected = $(this).children(':selected').first();\n var selectedAddress = $(selected).data('address');\n if (!selectedAddress) { return; }\n util.fillAddressFields(selectedAddress, $form);\n });\n\n $form.on('click', '#applyBtn', function (e) {\n e.preventDefault();\n if (!$form.valid()) {\n return false;\n }\n initDetectCardType();\n\n // handle recaptcha verification, then sent updated form for process\n recaptcha.check('addCreditCard', function(token) {\n recaptcha.updateFormTokenInput($form, token);\n processCreditCardFormSubmit($form);\n });\n })\n .on('click', '.cancel-button, .close-button', function (e) {\n e.preventDefault();\n dialog.close();\n });\n\n $('select[name$=\"_addressList\"] option:eq(1)').prop('selected', 'selected').trigger('change');\n}\n\nfunction initializeContactUsForm() {\n var $form = $('#ContactUsForm');\n var $contactUsDiv = $('.contactus-message')\n var $messageSentDiv = $('.contactus-sent');\n $form.on('click', '#sendBtn', function (e) {\n e.preventDefault();\n if (!$form.valid()) {\n return false;\n }\n // handle recaptcha verification, then sent updated form for process\n recaptcha.check('contactUs', function(token) {\n recaptcha.updateFormTokenInput($form, token);\n var url = util.appendParamToURL($form.attr('action'), 'format', 'ajax');\n var applyName = $form.find('#sendBtn').attr('name');\n var options = {\n url: url,\n data: $form.serialize() + '&' + applyName + '=x',\n type: 'POST'\n };\n $.ajax(options).done(function (data) {\n if (typeof(data) == 'object') {\n if (data.error) {\n page.redirect(Urls.csrffailed);\n } else {\n if (!data.success) {\n $messageSentDiv.addClass('error-form');\n $messageSentDiv.css('color', 'red');\n } else {\n $contactUsDiv.hide();\n $messageSentDiv.removeClass('error-form');\n $messageSentDiv.css('color', 'black');\n }\n $messageSentDiv.show();\n $messageSentDiv.empty().append(data.message);\n }\n return;\n }\n\n window.console.warn('Unexpected response', data);\n });\n });\n });\n}\n\nfunction initRegistrationEvents() {\n $('#RegistrationForm').on('submit', function () {\n if (!$('#s_date_of_birth-error').is(':visible')) {\n $(this).find('.create-account').addClass('disabled');\n }\n });\n}\n\nfunction initEventListenerData(){\n window.addEventListener('load',function(event){\n\t\tvar flag = window.localStorage.getItem('successRegister');\n\t\tvar loyaltyFlag = window.localStorage.getItem('loyaltyDone');\n\t\tvar sms = window.localStorage.getItem('smsOptin') ? window.localStorage.getItem('smsOptin') : false;\n\t\tvar email = window.localStorage.getItem('emailOptin') ? window.localStorage.getItem('emailOptin') : false;\n\t\tvar loyalty = window.localStorage.getItem('loyaltyOptin') ? window.localStorage.getItem('loyaltyOptin') : false;\n\t\tif(loyaltyFlag){\n\t\t\twindow.dataLayer.push({\n 'event' : 'user.loyaltyRegistration',\n 'data' : {\n 'status' : 'success',\n\t\t\t\t'userId' : `${pdict.CurrentCustomer.ID}`,\n\t\t\t\t'optins':{\n\t\t\t\t\t'sms': sms,\n\t\t\t\t\t'email': `${pdict.CurrentCustomer.profile.custom.mc_emailOptins}`,\n\t\t\t\t\t'loyalty': loyalty \n\t\t\t\t},\n\t\t\t\t'userType': `${pdict.CurrentSession.customer.externallyAuthenticated && ('Facebook' == pdict.CurrentSession.customer.profile.credentials.authenticationProviderID) ? 'sso-facebook' : 'claires'}`\n\t\t\t}});\n\t\t\twindow.localStorage.removeItem('loyaltyDone');\n\t\t} \n\t\tif(flag){\n\t\t\twindow.dataLayer.push({\n 'event' : 'user.registration',\n 'data' : {\n 'status' : 'success',\n\t\t\t\t'userId' : `${pdict.CurrentCustomer.ID}`,\n\t\t\t\t'optins':{\n\t\t\t\t\t'sms': sms,\n\t\t\t\t\t'email': email,\n\t\t\t\t\t'loyalty': loyalty\n\t\t\t\t},\n\t\t\t\t'userType': `${pdict.CurrentSession.customer.externallyAuthenticated && ('Facebook' == pdict.CurrentSession.customer.profile.credentials.authenticationProviderID) ? 'sso-facebook' : 'claires'}`\n\t\t\t}});\n\t\t\t window.localStorage.removeItem('successRegister');\n\t\t}\n\t\twindow.localStorage.removeItem('smsOptin'); \n\t\twindow.localStorage.removeItem('emailOptin');\n\t\twindow.localStorage.removeItem('loyaltyOptin');\n\t});\n}\n\n/**\n * @private\n * @function\n * @description Binds the events of the order, address and payment pages\n */\nfunction initializeEvents() {\n initAccountNavigationEvents();\n initCheckFieldsdsFillCompletion();\n initVerifyEmailEvents();\n initLoyaltyRewardsEvents();\n toggleFullOrder();\n initAddressEvents();\n initAccountDashboard();\n initPaymentEvents();\n initializeContactUsForm();\n initRegistrationEvents();\n //initEventListenerData();\n login.init();\n login.loginInit();\n loyalty.init();\n cppa.init();\n localStore.initDropdown();\n}\n\nvar localStore = {\n initStorePreferenceButton: function() {\n var self = this;\n $('.change-preferred-store').bind('click', function(e) {\n var selectedCountry = $(this).closest('form').find('.customer-address .country').val();\n e.preventDefault();\n self.openZipPrompt(selectedCountry);\n });\n },\n /**\n * Click on Select Store button\n */\n initSelectStoreButton: function() {\n var self = this;\n $('.select-store-button').on('click', function (e) {\n e.preventDefault();\n var selectedStoreId = $(this).data('storeId');\n $('.store-list .store-tile.selected').removeClass('selected')\n .find('.select-store-button').text(Resources.SELECT_STORE);\n $(this).text(Resources.SELECTED_STORE)\n .closest('.store-tile').addClass('selected');\n self.setLocalStore(selectedStoreId);\n dialog.close();\n });\n },\n /**\n * Click on Select Store button to save as local store\n */\n setLocalStore: function(selectedStoreId) {\n var isProfile = $('#isProfileMyInfoEdit').val();\n isProfile = isProfile !== undefined ? isProfile : '';\n var storeId = selectedStoreId;\n var self = this;\n var page = isProfile === 'true' ? 'myinfo' : 'account';\n $.ajax({\n url: Urls.setLocalStore,\n type: 'POST',\n data: {\n storeId: storeId,\n page: page\n },\n success: function(response) {\n if (response) {\n var $preferedStoreContent = $('#RegistrationForm').find('.preferred-store-section');\n if ($preferedStoreContent.length) {\n $preferedStoreContent.html(response);\n self.initStorePreferenceButton();\n }\n }\n },\n error: function(error) {\n console.log(error);\n }\n });\n },\n /**\n * Load stores\n */\n loadStoreSearchResults: function(distance, units, storeCountryCode, zipCode) {\n var isProfile = $('#isProfileMyInfoEdit').val();\n var selectedCountry = $('#storeCountrySelect').val();\n isProfile = isProfile !== undefined ? isProfile : '';\n selectedCountry = selectedCountry !== undefined ? selectedCountry : '';\n var self = this,\n url = util.appendParamsToUrl(Urls.accountSelectStoreDialog, {\n zipCode: zipCode,\n units: units,\n distance: distance,\n storeCountryCode: storeCountryCode,\n isProfile: isProfile,\n selectedCountry: selectedCountry\n });\n $.ajax({\n url: url,\n success: function(response) {\n $('#store-search-results').html(response);\n self.initFindStoresButton();\n self.initSelectStoreButton();\n }\n });\n },\n /**\n * Load stores by customer geo position\n */\n loadResultsByPosition: function(position) {\n var myLatLng = {\n lat: position.coords.latitude,\n lng: position.coords.longitude\n };\n\n //Call Google API to get customer country code and zip code\n var URL = window.SitePreferences.GOOGLE_GEOCODING_Endpoint+'?latlng='+myLatLng.lat+','+myLatLng.lng;\n $.ajax({\n url: URL,\n type: 'GET',\n success: function success(response) {\n var results = (response.results.length > 0) ? response.results[0].address_components : [],\n distance = $('#storeSearchRadius').val(),\n units = $('#storeSearchRadiusUnits').val(),\n storeCountryCode = '',\n zipCode = '';\n\n for (var i = 0; i < results.length; i++) {\n var addressType = results[i].types[0];\n\n if (addressType == 'country') {\n storeCountryCode = results[i].short_name;\n } else if (addressType == 'postal_code') {\n zipCode = results[i].short_name;\n }\n }\n\n localStore.loadStoreSearchResults(distance, units, storeCountryCode, zipCode);\n }\n });\n },\n /**\n * Click on Find button\n */\n initFindStoresButton: function() {\n var self = this;\n var $countrySelect = $('#storeCountrySelect');\n var selectedCountryCode = $countrySelect.data('defaultcountry');\n $countrySelect.val(selectedCountryCode).change();\n $('button[name=findAStore]').on('click', function() {\n var zipCode = $('#user-zip').val() ? $('#user-zip').val() : '';\n var distance = $('#storeSearchRadius').val() ? $('#storeSearchRadius').val() : '';\n var units = $('#storeSearchRadiusUnits').val() ? $('#storeSearchRadiusUnits').val() : '';\n var storeCountryCode = $('#storeCountrySelect option:selected').text() ? $('#storeCountrySelect option:selected').text().trim() : '';\n if (self.validateZipCode(zipCode)) {\n self.loadStoreSearchResults(distance, units, storeCountryCode, zipCode);\n }\n });\n\n $('button[name=findAStoreCurrentLocation]').on('click', function() {\n if (navigator.geolocation) {\n navigator.geolocation.getCurrentPosition(self.loadResultsByPosition);\n }\n });\n },\n /**\n * Dialog with search form\n */\n openZipPrompt: function(selectedCountry) {\n var self = this;\n var isProfileMyInfoEdit = $(\"#isProfileMyInfoEdit\").val();\n isProfileMyInfoEdit = isProfileMyInfoEdit !== undefined ? isProfileMyInfoEdit : '';\n var url = util.ajaxUrl(Urls.accountZipPromptDialog);\n var data = {\n selectedCountry: selectedCountry,\n isProfileMyInfoEdit: isProfileMyInfoEdit\n }\n dialog.open({\n url: url,\n data: data,\n options: {\n title: Resources.STORE_SELECT,\n width: 500,\n open: function() {\n self.initFindStoresButton();\n },\n classes: isProfileMyInfoEdit == 'true' ? { \"ui-dialog\": \"findStore-dialog\"} : {},\n position: isProfileMyInfoEdit == 'true' ? { my: $(window).width() > 600 ? \"right-13% bottom-30%\" : 'center bottom', at: $(window).width() > 600 ? \"right bottom\" : 'center bottom' } : \"\",\n width: isProfileMyInfoEdit == 'true' ? $(window).width() > 600 ? 342 : 'auto' : 500,\n height: 'auto',\n close: function() {\n //location.reload();\n //alert('closed');\n //self.initChangeButton();\n }\n }\n });\n },\n /**\n * @description test whether zipcode is valid\n * @return {Boolean} true if the zipcode is valid for either country, false if it's invalid for all\n **/\n validateZipCode: function(zipCode) {\n var country = $('#store-search-results').find('#storeCountrySelect');\n var zipInput =$('#store-search-results').find('#user-zip');\n if (!zipCode) {\n if (!zipInput.hasClass('error')) {\n zipInput.addClass('error');\n $('#preferred-store-panel').addClass('error');\n }\n } else {\n if (zipInput.hasClass('error')) {\n zipInput.removeClass('error');\n $('#preferred-store-panel').removeClass('error');\n }\n }\n if (country.length < 1 || country.val() === null || !zipCode) {\n return;\n }\n var regexes = {\n us: /^\\d{5}(-\\d{4})?$/,\n ca: /^[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$/i,\n fr: /^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/i,\n it: /^([0-9]){5}$/,\n jp: /^([0-9]){3}[-]([0-9]){4}$/,\n cn: /^([0-9]){6}$/,\n gb: /^[A-Z]{1,2}[0-9][0-9A-Z]?\\s?[0-9][A-Z]{2}$/i,\n default: /^([0-9]|[a-z]|[A-Z]|[ -]){4,16}$/\n },\n valid = false;\n var rgx = regexes[country.val().toLowerCase()] || regexes.default;\n valid = rgx.test($.trim(zipCode));\n if (valid == false) {\n if (!zipInput.hasClass('error')) {\n zipInput.addClass('error');\n $('#preferred-store-panel').addClass('error');\n }\n } else {\n if (zipInput.hasClass('error')) {\n zipInput.removeClass('error');\n $('#preferred-store-panel').removeClass('error');\n }\n }\n return valid;\n },\n initDropdown: function() {\n $('#RegistrationForm').on('click', '.preferred-dropdown', function(e) {\n e.preventDefault();\n $('#preferredDetails').toggleClass('visually-hidden');\n });\n }\n};\n\nfunction initVerifyEmailEvents() {\n $('body').on('click', '.verifyemail .confirm-email', function (e) {\n e.preventDefault();\n $(this).addClass('hidden').attr({'disabled': true});\n $(this).closest('.verifyemail').find('.email-not-success').addClass('hidden');\n $(this).closest('.verifyemail').find('.email-success').removeClass('hidden');\n $(this).closest('.verifyemail').find('#customer-email-notconfirm').addClass('hidden');\n $.ajax({\n type: 'GET',\n url: Urls.sendVerificationEmail,\n success: function (response) {\n //some basic error message\n if (!response.succcess) {\n //display response.message\n }\n },\n error: function () {\n //\n }\n });\n });\n}\n\nfunction initLoyaltyRewardsEvents() {\n $('body').on('click', 'a.use-rewards', function (e) {\n e.preventDefault();\n var link = $(this);\n var url = link.attr('href');\n var rewardNumber = link.parent('.reward-item').prop('id');\n url = util.appendParamsToUrl(url, {\n action: link.text()\n });\n $.ajax({\n type: 'GET',\n url: url,\n success: function (response) {\n var $error = $('#reward-redemption-error-' + rewardNumber);\n $error.hide();\n if (response.success) {\n if (response.status == 0) {\n link.text(Resources.LOALTY_REWARD_APPLIED);\n } else if (response.status == -1) {\n link.text(Resources.LOALTY_REWARD_REDEEM);\n }\n } else {\n $error.find('span.v2-medium').text(response.message);\n $error.show();\n }\n },\n error: function () {\n //\n }\n });\n });\n\n\n $(\"#availableRewards .available-rewards-redeem-tile\").on('click', function(e){\n e.preventDefault();\n var $error = $('#availableRewards #v3-error');\n var availableRewards = $(this);\n var redeemStatus = availableRewards.data('redeem');\n if(redeemStatus == 0){\n var tileID = availableRewards.attr('id');\n progress.show('#' + tileID);\n $error.children('#v3-error-message').text('');\n $error.hide();\n var useRewards = availableRewards.children('a.use-rewards-new');\n var url = useRewards.attr('href');\n url = util.appendParamsToUrl(url, {\n action: useRewards.data('action')\n });\n $.ajax({\n type: 'GET',\n url: url,\n success: function (response) {\n if (response.success) {\n if (response.status == 0) {\n var replaceClickToRedeem = ''+Resources.LOALTY_REWARD_REDEEM_NEW+'';\n availableRewards.removeClass('pointer');\n availableRewards.data('redeem', '1');\n useRewards.replaceWith(replaceClickToRedeem);\n minicart.cartRefresh();\n } else if (response.status == -1) {\n useRewards.text(Resources.LOALTY_REWARD_CLICKREDEEM);\n }\n } else {\n $error.children('#v3-error-message').text(response.message);\n $error.show();\n $('html, body').animate({\n scrollTop: $(\"#v3-error\").offset().top - 150\n }, 500);\n }\n progress.hide('#' + tileID);\n },\n error: function () {\n //\n }\n });\n }\n });\n}\n\n\nvar account = {\n init: function () {\n initializeEvents();\n giftcert.init();\n billing.initGiftCardCheckBalance();\n localStore.initStorePreferenceButton();\n /* ++++++++++++++++ SUBSCRIPTION CODE STARTS ++++++++++++++++ */\n //customOrdergroove.makeChangesInMsi();\n /* ++++++++++++++++ SUBSCRIPTION CODE ENDS ++++++++++++++++ */\n rewards.init();\n },\n initCartLogin: function () {\n login.init();\n }\n};\n\nmodule.exports = account;\n","'use strict';\nvar bonusProductsView = require('../bonus-products-view'),\n quickview = require('org/quickview'),\n cartStoreInventory = require('base/storeinventory/cart'),\n paypal = require('int_paypal/paypal'),\n tagmanager = require('int_googletags/tagmanager'),\n util = require('org/util'),\n progress = require('base/progress');\n\n/* ++++++++++++++++ SUBSCRIPTION CODE STARTS ++++++++++++++++ */\n\n// For legacy OG implementation\n//var ordergroove = require('int_ordergroove/pages/cart');\n\n// OG customizations\n//var customOrdergroove = require('org/subscriptions/ordergroove');\n\n/* ++++++++++++++++ SUBSCRIPTION CODE ENDS ++++++++++++++++ */\n\n\nfunction checkCoupon(code) {\n if (code.length == 0) {\n $('#no-active-promotion span.v2-medium').html(Resources.COUPON_CODE_MISSING);\n $('#no-active-promotion').show();\n $('#no-active-promotion div.coupon-apply').hide();\n\n return;\n }\n\n var url = util.appendParamsToUrl(Urls.checkCoupon, {\n couponCode: code,\n format: 'ajax'\n });\n\n $.getJSON(url, function (data) {\n if (data.status == 'NO_CART') {\n window.location.reload(true);\n }\n\n var fail = false;\n var msg = '';\n\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n\n if (fail) {\n $('#no-active-promotion span.v2-medium').html(msg);\n $('#no-active-promotion').show();\n\n if (data.guestLoginUrl && !$('.linkToLogin').length) {\n var linkToLoginHtml = 'Login';\n $('#no-active-promotion').append(linkToLoginHtml);\n }\n\n if (data.status == 'SHOW_NOTIFICATION') {\n $('#dropdown-coupon-apply').data('couponcode', code);\n $('#no-active-promotion div.v2-medium').hide();\n $('#no-active-promotion div.coupon-apply').show();\n } else {\n $('#no-active-promotion div.coupon-apply').hide();\n }\n\n initPromoButtonsEvent();\n\n return;\n }\n\n if (data.success) {\n cartAjax({\n 'csrf_token': $('#csrf_token').val()\n });\n }\n });\n}\n\n/**\n * @private\n * @function\n * @description init promo buttons event\n */\nfunction initPromoButtonsEvent () {\n $('#dropdown-coupon-cancel').on('click', function () {\n $('#no-active-promotion').hide();\n });\n\n $('#dropdown-coupon-apply').on('click', function () {\n var code = $(this).data('couponcode');\n var url = util.appendParamsToUrl(Urls.addCoupon, {\n couponCode: code,\n format: 'ajax',\n needRemoveOldCoupons: 'true'\n });\n\n $.getJSON(url, function() {\n cartAjax({\n 'csrf_token': $('#csrf_token').val()\n });\n });\n });\n}\n\n/**\n * @private\n * @function\n * @description checks for coupon errors and displays them if present\n */\nfunction cartResultCheck(result) {\n if (result.CouponError != false && result.CouponError != null && result.CouponError != undefined) {\n if (result.CouponError == 'COUPON_CODE_MISSING') {\n $('#coupon-code-missing').css('display', 'block');\n } else if (result.CouponError == 'NO_ACTIVE_PROMOTION') {\n $('#no-active-promotion').css('display', 'block');\n } else if (result.CouponError == 'NO_APPLICABLE_PROMOTION') {\n $('#no-applicable-promotion').css('display', 'block');\n } else {\n $('#coupon-code-missing').css('display', 'none');\n $('#no-active-promotion').css('display', 'none');\n $('#no-applicable-promotion').css('display', 'none');\n }\n } else {\n $('#coupon-code-missing').css('display', 'none');\n $('#no-active-promotion').css('display', 'none');\n $('#no-applicable-promotion').css('display', 'none');\n }\n}\n\n/**\n * @private\n * @function\n * @description submit cart ajax call\n */\nfunction cartAjax(data) {\n $.ajax({\n method: 'POST',\n url: Urls.submitForm,\n data: data,\n beforeSend: function(){\n progress.show('#main-container').addClass('sticky');\n }\n }).done(function(cartUpdateResponse) {\n // trigger basket update\n progress.hide();\n if(window.ApplePaySession) {\n $('.applepay-btn').css('display','block');\n } else {\n $('.applepay-btn').css('display','none');\n }\n $('body').trigger('basketUpdate', cartUpdateResponse);\n });\n}\n\n/**\n * @private\n * @function\n * @description dropdown handler\n */\nfunction cartDropdownHandler(body, image) {\n if (body.css('display') == 'none') {\n body.css('display', 'block');\n body.attr('aria-hidden', 'false');\n image.css('transform', 'rotate(0deg)');\n $('.cart-v2-main-right-sticky').addClass('cart-right-sticky-promo');\n if (body.find('.cart-v2-coupon').length > 0) {\n body.addClass('cart-v2-coupon-scroll');\n } else {\n body.removeClass('cart-v2-coupon-scroll');\n }\n } else {\n body.css('display', 'none');\n body.attr('aria-hidden', 'true');\n image.css('transform', 'rotate(180deg)');\n $('.cart-v2-main-right-sticky').removeClass('cart-right-sticky-promo');\n }\n}\n\n/**\n * @private\n * @function\n * @description add coupon code\n */\n\n/* function addCouponCode(couponSource) {\n var data = new Object;\n\n var couponAdd = 'dwfrm_cart_addCoupon';\n var couponCode = 'dwfrm_cart_couponCode';\n var couponVal = couponSource.val();\n var csrfToken = 'csrf_token';\n var csrfTokenValue = $('#csrf_token').val();\n\n data[couponAdd] = couponAdd;\n data[couponCode] = couponVal;\n data[csrfToken] = csrfTokenValue;\n\n cartAjax(data);\n} */\n\n/**\n * @private\n * @function\n * @description binds events to the cart page (edit item's details, bonus item's actions, coupon code entry)\n */\nfunction initializeEvents() {\n $('#cart-table').on('click', '.item-edit-details a', function (e) {\n e.preventDefault();\n quickview.show({\n url: e.target.href,\n source: 'cart'\n });\n });\n\n // override enter key for coupon code entry\n $('form input[name$=\"_couponCode\"]').on('keydown', function (e) {\n if (e.which == 13 && $(this).val().length == 0) {\n return false;\n }\n });\n\n // to prevent multiple submissions of the form when removing a product from the cart\n var removeItemEvent = false;\n $('button[name$=\"deleteProduct\"]').on('click', function (e) {\n if (removeItemEvent) {\n e.preventDefault();\n } else {\n removeItemEvent = true;\n }\n });\n\n $('input[name$=\"_cart_couponCode\"]').on('keypress keyup keydown', function (e) {\n if (e.which == 13 && $(this).val() && $(this).val().length > 0) {\n $('button[name$=\"_cart_addCoupon\"]').click();\n e.preventDefault();\n return false;\n }\n });\n\n $('#carousel-recommendations:not(.slick-initialized)').on('init', function () {\n $('.product-tile-info', $(this)).syncHeight();\n });\n\n $('#carousel-recommendations:not(.slick-initialized)').slick({\n speed: 300,\n dots: (util.isIcingSite()) ? true : false,\n arrows: true,\n slidesToShow: 4,\n slidesToScroll: 1,\n responsive: [\n {\n breakpoint: util.getViewports('desktop'),\n settings: {\n slidesToShow: 2,\n arrows: false,\n centerMode: true\n }\n },\n {\n breakpoint: util.getViewports('large'),\n settings: {\n slidesToShow: 1,\n arrows: false,\n centerMode: true,\n centerPadding: '24.5%'\n }\n }\n ]\n });\n\n // launches bonus product selection\n $('.select-bonus-product').on('click', function (e) {\n e.preventDefault();\n bonusProductsView.show(this.href);\n });\n\n // item summary display\n $('#cart-v2-item-head-image').click(function() {\n cartDropdownHandler($('#cart-v2-item-body'), $('#cart-v2-item-head-image'));\n });\n\n // item summary display multiship\n $('#cart-v2-item-head-image-sth').click(function() {\n cartDropdownHandler($('#cart-v2-item-body-sth'), $('#cart-v2-item-head-image-sth'));\n });\n\n $('#cart-v2-item-head-image-bopis').click(function() {\n cartDropdownHandler($('#cart-v2-item-body-bopis'), $('#cart-v2-item-head-image-bopis'));\n });\n\n // bonus products display\n $('#cart-v2-bonus-products-head-image').click(function() {\n cartDropdownHandler($('#cart-v2-bonus-products-body'), $('#cart-v2-bonus-products-head-image'));\n });\n\n $('#cart-v2-coupon-head-image').on('click keypress', function(e) {\n e.preventDefault();\n cartDropdownHandler($('#cart-v2-coupon-body'), $('#cart-v2-coupon-head-image'));\n });\n\n //SFL dropdown\n $('#cart-v2-sfl-head-image').click(function() {\n cartDropdownHandler($('#cart-v2-sfl-body'), $('#cart-v2-sfl-head-image'));\n });\n\n //prevent wrong behavior of Promo code dropdown arrow\n $('#cart-v2-coupon-head-image').css('transform', 'rotate(180deg)');\n\n // product qty ajax\n $(document).off('change', '.cart-v2-select-editable').on('change', '.cart-v2-select-editable' , function() {\n var data = new Object;\n var updateCart = 'dwfrm_cart_updateCart';\n var productName = '' + $('input', this).attr('name');\n var productQty = $('input', this).val();\n var productID = $('input', this).attr('data-productID');\n\n data[updateCart] = updateCart;\n data[productName] = productQty;\n data.Quantity = productQty;\n data.productID = productID;\n cartAjax(data);\n });\n // product remove ajax\n $('.cart-v2-remove-product').on('click', function() {\n var data = new Object;\n var deleteProduct = $(this).attr('name');\n data[deleteProduct] = 'Remove Product';\n cartAjax(data);\n });\n\n // Remove all the product from the unavialble section if products is not enligible for both the shipment\n function handleRemoveProducts(element, removedProducts) {\n $(element).on('click', function () {\n var pids = [];\n var setRemoveMethodUrl = $(this).attr('data-url');\n var removeProductIds = $(removedProducts);\n for (var i = 0; i < removeProductIds.length; i++) {\n pids.push(removeProductIds[i].getAttribute('data-pid'));\n }\n var url = util.ajaxUrl(util.appendParamToURL(setRemoveMethodUrl, 'pids', pids.join(';')));\n progress.show('#main-container').addClass('sticky');\n $.ajax({\n url: url,\n type: 'POST',\n data: {\n pids: pids,\n page: 'cart',\n },\n success: function (response) {\n if (response.status == 'success') {\n cartAjax(response);\n }\n else {\n progress.hide();\n }\n },\n error: function (response) {\n if(response.status == 'fail')\n progress.hide();\n }\n });\n });\n }\n \n handleRemoveProducts('.cart-v2-remove-products-unavailable', '.cart-v2-remove-products');\n \n handleRemoveProducts('.cart-v2-sts-remove-products-unavailable', '.cart-v2-remove-sts-products');\n \n\n $('#sfl-shipping-method-change-bopis').on('click',function (){\n var pids = [];\n var setmoveToBopisMethodUrl = $(this).attr('data-url');\n var storeId = $(this).attr('data-store-id');\n var productIds = $('.sth-unavailable-products');\n for (var i = 0; i < productIds.length; i++) {\n pids.push(productIds[i].getAttribute('data-pid'));\n }\n var url = util.ajaxUrl(util.appendParamToURL(setmoveToBopisMethodUrl, 'pids', pids.join(';')));\n progress.show('#main-container').addClass('sticky');\n $.ajax({\n url: url,\n type: 'POST',\n data: {\n pids: pids,\n storeId:storeId,\n page: 'cart',\n },\n success: function (response) {\n if (response.status == 'success') {\n \ttagmanager.cartChanged(response.shipmentType);\n cartAjax(response);\n }\n else {\n progress.hide();\n }\n },\n error: function (response) {\n if(response.status == 'fail')\n progress.hide();\n }\n });\n });\n\n $('#sfl-shipping-method-change-shipToHome').on('click',function (){\n var pids = [];\n var setmoveToSthMethodUrl = $(this).attr('data-url');\n var productIds = $('.bopis-unavailable-products');\n for (var i = 0; i < productIds.length; i++) {\n pids.push(productIds[i].getAttribute('data-pid'));\n }\n var url = util.ajaxUrl(util.appendParamToURL(setmoveToSthMethodUrl, 'pids', pids.join(';')));\n progress.show('#main-container').addClass('sticky');\n $.ajax({\n url: url,\n type: 'POST',\n data: {\n pids: pids,\n page: 'cart',\n },\n success: function (response) {\n if (response.status == 'success') {\n \ttagmanager.cartChanged(response.shipmentType);\n cartAjax(response);\n }\n else {\n progress.hide();\n }\n },\n error: function (response) {\n if(response.status == 'fail')\n progress.hide();\n }\n });\n });\n\n $('.fake-checkout-button').on('click', function() {\n var deliveryMethod = $('input[name=\"delivery-option\"]:checked').val();\n $('.fake-checkout-button').addClass('disabled');\n $('.sfl-error-message').hide();\n $('.sfl-error-message.' + deliveryMethod).show();\n });\n\n // coupon code apply ajax\n $('#cart-v2-add-coupon').on('click', function() {\n checkCoupon($('input[name$=\"_couponCode\"]').val());\n // addCouponCode($('#dwfrm_cart_couponCode'));\n });\n\n // coupon code display apply ajax\n $('#cart-v2-add-coupon-display').on('click', function() {\n checkCoupon($('#coupon-code-display').val());\n // addCouponCode($('#coupon-code-display'));\n });\n\n // coupon code remove ajax\n $('.cart-v2-coupon-remove').on('click', function() {\n var data = new Object;\n\n var deleteCoupon = $(this).attr('name');\n data[deleteCoupon] = 'Remove';\n\n cartAjax(data);\n });\n // this event specific to some pages and carousels\n\n $(window).on('einsteinRecLoaded', function() {\n $('.carousel-recommendations-einstein:not(.carousel-inited)').each(function () {\n $(this).parents('einstain-inited').addClass('einstain-clear');\n $(this).addClass('carousel-inited');\n $(this).slick({\n infinite: false,\n speed: 300,\n slidesToShow: 4,\n dots: true,\n responsive: [\n {\n breakpoint: util.getViewports('desktop'),\n settings: {\n slidesToShow: 4,\n infinite: true,\n dots: true\n }\n },\n {\n breakpoint: util.getViewports('large'),\n settings: {\n slidesToShow: 1,\n infinite: true,\n centerMode: true,\n dots: true,\n centerPadding: '20%'\n }\n }\n ]\n });\n $('.carousel-recommendations-einstein .product-tile-info').syncHeight();\n });\n });\n\n $('body').off('submit', '.add-to-cart-recommend').on('submit', '.add-to-cart-recommend', function(e) {\n e.preventDefault();\n var data = $(this).serialize();\n $.ajax({\n type: 'POST',\n url: $(this).attr('action'),\n data: data\n })\n .done(function () {\n cartAjax({\n 'csrf_token': $('#csrf_token').val(),\n 'einsteinCall': true\n });\n util.scrollBrowser(0);\n });\n });\n\n /*++++++++++++ BOPIS code starts ++++++++++++++*/\n\n // delivery method display\n $('#cart-v2-delivery-head-image').on('click keypress', function(e) {\n e.preventDefault();\n cartDropdownHandler($('#cart-v2-delivery-body'), $('#cart-v2-delivery-head-image'));\n });\n\n // added dataLayer ckeckout step 2 event for registered users\n var checkoutAdditionalStepEventRequired = true;\n $('button[name=\"dwfrm_cart_checkoutCart\"]').on('click', function (e) {\n if (checkoutAdditionalStepEventRequired) {\n e.preventDefault();\n var newCheckoutElement = {};\n $.ajax({\n url: Urls.getCheckoutDatalayerData,\n success: function(response) {\n newCheckoutElement = response;\n if (newCheckoutElement.loginType == 'registered' || newCheckoutElement.loginType == 'Inscrit') {\n newCheckoutElement.ecommerce.checkout.actionField.step = '2';\n newCheckoutElement.ecommerce.checkout.actionField.option = 'logged-in';\n window.dataLayer.push(newCheckoutElement);\n }\n checkoutAdditionalStepEventRequired = false;\n $('button[name=\"dwfrm_cart_checkoutCart\"]').trigger('click');\n }\n });\n }\n });\n\n // handle delivery method display\n $('input[name=\"delivery-option\"]').on('change', function(event) {\n var deliveryMethod = event.target.value;\n var deliveryBody = $('#cart-v2-delivery-body');\n var storeId = deliveryBody.attr('data-store-id');\n var setDeliveryMethodUrl = deliveryBody.attr('data-url');\n $('input[name=\"delivery-option\"]').attr('disabled', 'disabled');\n deliveryBody.css('opacity', '0.5');\n $.ajax({\n url: setDeliveryMethodUrl,\n type: 'POST',\n data: {\n storeId: storeId,\n page: 'cart',\n deliveryMethod: deliveryMethod\n },\n success: function (response) {\n if (response.status === 'fail') {\n $('input[name=\"delivery-option\"]').removeAttr('disabled');\n deliveryBody.css('opacity', '1');\n $('.cart-v2-error-container-bopis').show();\n $('button[name=\"dwfrm_cart_checkoutCart\"]').attr('disabled', 'disabled');\n } else {\n $('.cart-v2-error-container-bopis').hide();\n setTimeout(location.reload(), 0);\n }\n },\n failure: function () {\n $('input[name=\"delivery-option\"]').removeAttr('disabled');\n deliveryBody.css('opacity', '1');\n $('.cart-v2-error-container-bopis').show();\n $('button[name=\"dwfrm_cart_checkoutCart\"]').attr('disabled', 'disabled');\n }\n });\n });\n\n //handle Multiship shipment option\n $('.delivery-option-change').on('change', function(event) {\n var deliveryMethod = event.target.value;\n var deliveryBody = $('.cart-delivery-body');\n var storeId = deliveryBody.attr('data-store-id');\n var pid = event.target.dataset.pid;\n var setDeliveryMethodUrl = deliveryBody.attr('data-url');\n deliveryBody.css('opacity', '0.5');\n //Spinner starts here\n progress.show('#main-container').addClass('sticky');\n $.ajax({\n url: setDeliveryMethodUrl,\n type: 'POST',\n data: {\n storeId: storeId,\n page: 'cart',\n deliveryMethod: deliveryMethod,\n pid: pid\n },\n success: function (response) {\n if (response.status === 'fail') {\n deliveryBody.css('opacity', '1');\n $('.cart-v2-error-container-bopis').show();\n $('button[name=\"dwfrm_cart_checkoutCart\"]').attr('disabled', 'disabled');\n progress.hide();\n } else {\n \ttagmanager.cartChanged(response.shipmentType);\n $('.cart-v2-error-container-bopis').hide();\n cartAjax(response);\n }\n },\n failure: function () {\n progress.hide();\n deliveryBody.css('opacity', '1');\n $('.cart-v2-error-container-bopis').show();\n $('button[name=\"dwfrm_cart_checkoutCart\"]').attr('disabled', 'disabled');\n }\n });\n });\n\n // handle store look up modal\n var storeLookUpContainer = $('#store-look-up-from-cart');\n var existPreviousModal = $('.store-look-up-modal').length > 0;\n if (existPreviousModal) {\n $('.store-look-up-modal')[0].remove();\n }\n\n if (storeLookUpContainer[0]) {\n var storeLookUpTitle = $('.store-look-up-title-html-from-cart').text();\n storeLookUpContainer.dialog({\n autoOpen: false,\n modal: true,\n width: 500,\n title: storeLookUpTitle,\n dialogClass: 'store-look-up-modal',\n draggable: false\n });\n\n // force it to be the first one if there's another instance of the modal, so it will be placed in the center\n storeLookUpContainer.dialog('moveToTop');\n\n // block window scrolling\n storeLookUpContainer.on('dialogopen', function() {\n $('html').css('overflow', 'hidden');\n });\n\n // unblock window scrolling\n storeLookUpContainer.on('dialogclose', function() {\n $('html').css('overflow', 'visible');\n });\n\n $('.delivery-option__link, .nearest-store-name').click(function(e) {\n e.preventDefault();\n storeLookUpContainer.dialog('open');\n if($('#store-discoverer__text-input').val().length > 0) {\n $('.store-discoverer__button').removeAttr('disabled');\n }\n });\n }\n\n if(window.ApplePaySession) {\n $('.applepay-btn').css('display','block');\n } else {\n $('.applepay-btn').css('display','none');\n }\n\n if($('input[name=\"showError\"]').val() == 'true'){\n $('.show-error').css('display', 'block');\n }\n /*++++++++++++ BOPIS code ends ++++++++++++++*/\n\n\n /* ++++++++++++++++ SUBSCRIPTION CODE STARTS ++++++++++++++++ */\n // customOrdergroove.addOffersCustomizations();\n //customOrdergroove.validateCartSubscriptions();\n //customOrdergroove.addSubscriptionClassToItems($('.cart-v2-item'));\n /* ++++++++++++++++ SUBSCRIPTION CODE ENDS ++++++++++++++++ */\n\n tagmanager._removeFromCart();\n\n $('.sfl-not-available a').on('click keypress', function(e) {\n e.preventDefault();\n $('html, body').animate({\n scrollTop: $('.sfl-list').offset().top - $('header').height()\n }, 500);\n });\n\n // check if redirect from confirmation page and save for later\n var queryString = window.location.search;\n var urlParams = new URLSearchParams(queryString);\n var isFromConfirmation = urlParams.get('confirmation')\n if (isFromConfirmation && $('.sfl-see-more-button').length) {\n $('.sfl-see-more-button').trigger('click');\n }\n}\n\n/*\n * Primary method to refresh cart view\n * optional parameter for cart update result\n */\nfunction reloadCartAjax(event, cartUpdateResponse) {\n progress.show('#main-container').addClass('sticky');\n $('#main-container').load(document.URL + ' #main-v2', function(html) {\n initializeEvents();\n // add Googlepay button\n const container = ($(window).width() < 768) ? $('#container-gpay-mobile') : $('#container-gpay');\n if (container.length && container.attr('data-set') == \"0\") {\n addGooglePayButton();\n }\n if (cartUpdateResponse) {\n cartResultCheck(cartUpdateResponse);\n }\n\n progress.hide();\n\n // update GTM tracking that is outside of main-v2 block\n var htmlObject = document.createElement('div');\n htmlObject.innerHTML = html;\n var gtmScript = htmlObject.querySelector('#googletagmanager');\n $('#googletagmanager').replaceWith(gtmScript);\n // this one included into loaded container\n // $('#googletagmanagerbody').replaceWith($(htmlObject).find('#googletagmanagerbody'));\n $(document).trigger('basketContentUpdated');\n });\n}\n\nexports.init = function () {\n initializeEvents();\n\n if (SitePreferences.STORE_PICKUP) {\n cartStoreInventory.init();\n }\n /* ++++++++++++++++ SUBSCRIPTION CODE STARTS ++++++++++++++++ */\n // ordergroove();\n // customOrdergroove.addSubscriptionClasstoItemsWhenChange($('.cart-v2-item'));\n //customOrdergroove.reloadCartFromLocalStorage();\n /* ++++++++++++++++ SUBSCRIPTION CODE ENDS ++++++++++++++++ */\n\n $(document).on('basketUpdate', reloadCartAjax);\n\n // keep klarna on site messaging updated\n $(document).on('basketUpdate', function (){\n window.KlarnaOnsiteService = window.KlarnaOnsiteService || [];\n window.KlarnaOnsiteService.push({eventName: 'refresh-placements'});\n });\n\n //account.initCartLogin();\n paypal.init();\n};\n\nexports.checkCoupon = checkCoupon;\nexports.initPromoButtonsEvent = initPromoButtonsEvent;\nexports.cartResultCheck = cartResultCheck;\nexports.initializeEvents = initializeEvents;\n","'use strict';\n\nvar util = require('../../util');\nvar shipping = require('./shipping');\n\n/**\n * @function\n * @description Selects the first address from the list of addresses\n */\nexports.init = function () {\n var $form = $('.address');\n // select address from list\n $('select[name$=\"_addressList\"]', $form).on('change', function () {\n var selected = $(this).children(':selected').first();\n var selectedAddress = $(selected).data('address');\n if (!selectedAddress) { return; }\n if (selectedAddress.hasOwnProperty('type')) {\n delete selectedAddress.type;\n }\n util.fillAddressFields(selectedAddress, $form);\n shipping.updateShippingMethodList();\n });\n};\n","'use strict';\n\nvar checkout = require('./checkout');\nvar formPrepare = require('./formPrepareV2');\nvar qas = require('int_QASc/qasV2');\nvar _ = require('lodash');\n\n/**\n * @function\n * @description Selects the first address from the list of addresses\n */\nexports.init = function() {\n var $form = $('.address');\n\n $('input[name$=\"_shippingAddress_addressFields_email\"]').closest('.form-row').remove();\n $('input[name$=\"_shippingAddress_addressFields_country\"]').closest('.form-row').remove();\n\n $('select[name$=\"_addressList\"] option[value=\"\"]').prop('disabled','disabled');\n\n qas.init();\n $form.off('click', 'button[name$=\"qas-lookup\"]').on('click', 'button[name$=\"qas-lookup\"]', qas.eventHandlers.lookup);\n\n // TODO trigger change for first option in dropdown\n // $('select[name$=\"_addressList\"]').prop('selectedIndex', 2).trigger('change');\n // $('select[name$=\"_addressList\"] option:eq(1)').attr('selected', 'selected').trigger('change');\n\n $('select[name$=\"_addressList\"]', $form).off().on('change', function(e) {\n e.preventDefault();\n var selectEl = $(this);\n var selected = selectEl.children(':selected').first();\n var selectedAddressID = $(selected).val();\n var addressSection;\n var addressSectionID;\n var updateAddressBtn;\n var editAddressBtn;\n var addAddressBtn;\n var addressBookCheckbox;\n\n if (selectEl.hasClass('shipping-address-list')) {\n addressSection = 'shipping';\n addressSectionID = $('#shipping-address-section');\n updateAddressBtn = $('#update-shipping-address');\n editAddressBtn = $('#edit-shipping-address');\n addAddressBtn = $('#add-shipping-address');\n addressBookCheckbox = $('[name$=\"_shippingAddress_addToAddressBook\"]').parents('.form-row');\n } else if (selectEl.hasClass('billing-address-list')) {\n addressSection = 'billing';\n addressSectionID = $('#billing-address-section');\n updateAddressBtn = $('#update-billing-address');\n editAddressBtn = $('#edit-billing-address');\n addAddressBtn = $('#add-billing-address');\n addressBookCheckbox = $('[name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row');\n }\n\n $.ajax({\n url: Urls.setPreferredAddress,\n data: {\n selectedAddressID: selectedAddressID,\n addressSection: addressSection\n },\n success: function(response) {\n addressSectionID.html(response);\n addressBookCheckbox.hide();\n\n if (addressSection == 'shipping') {\n if ($('body.clairesEU').length && $('input[name$=\"_useAsBillingAddress\"]')[0].checked) {\n $('#billing-address-section').html(response);\n $('[name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row').hide();\n }\n var country = addressSectionID.find('.address-state')[0].textContent;\n $('[name$=\"_checkout_shippingAddress_addressFields_country\"]').val(country);\n checkout.orderSummaryRefresh();\n var getAddressFromCart = true;\n checkout.updateShippingMethodList(getAddressFromCart);\n }\n\n if (editAddressBtn.is(':hidden')) {\n updateAddressBtn.hide();\n editAddressBtn.show();\n addAddressBtn.show();\n }\n }\n });\n\n // util.fillAddressFields(selectedAddressID, $form);\n checkout.updateShippingMethodList();\n });\n\n $('.op-checkout-add-address-btn').off().on('click', function(e) {\n e.preventDefault();\n var $this = $(this);\n var addressSection;\n var addressSectionID;\n var addAddressBtn;\n var updateAddressBtn;\n var editAddressBtn;\n var addressBookCheckbox;\n\n if ($this.attr('id') === 'add-shipping-address') {\n addressSection = 'shipping';\n addressSectionID = $('#shipping-address-section');\n addAddressBtn = $('#add-shipping-address');\n updateAddressBtn = $('#update-shipping-address');\n editAddressBtn = $('#edit-shipping-address');\n addressBookCheckbox = $('[name$=\"_shippingAddress_addToAddressBook\"]').parents('.form-row');\n } else if ($this.attr('id') === 'add-billing-address') {\n addressSection = 'billing';\n addressSectionID = $('#billing-address-section');\n addAddressBtn = $('#add-billing-address');\n updateAddressBtn = $('#update-billing-address');\n editAddressBtn = $('#edit-billing-address');\n addressBookCheckbox = $('[name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row');\n }\n\n $.ajax({\n url: Urls.editPreferredAddress,\n data: {addressSection: addressSection},\n success: function(response) {\n addressSectionID.html(response);\n\n var addressContainer;\n\n if ($form.find('select[id$=\"_' + addressSection + 'Address_addressFields_country\"]:enabled').length > 0) {\n var country = $('select[name$=\"_checkout_' + addressSection + 'Address_addressFields_country\"]');\n var state = $('#dwfrm_checkout_' + addressSection + 'Address_addressFields_states_state');\n var $addressSection = $('#' + addressSection + '-address-section');\n checkout.initCountryState($form, country, state, $addressSection);\n }\n\n if ($this.attr('id') === 'add-shipping-address') {\n addressContainer = $('#shipping-address-container');\n } else if ($this.attr('id') === 'add-billing-address') {\n addressContainer = $('#billing-address-container');\n }\n\n // checkout.init();\n $('.row-country-store').hide();\n $('input[name$=\"_shippingAddress_addressFields_email\"]').closest('.form-row').remove();\n $('input[name$=\"_shippingAddress_addressFields_country\"]').closest('.form-row').remove();\n if ($('form.checkout-shipping').find('#' + addressSection + '-address-lookup-container').length > 0) {\n var inputDataS = $('.op-checkout-section-content .address-lookup-section.delivery-' + addressSection).detach();\n inputDataS.insertAfter('#' + addressSection + '-address-container .form-row.phone.required');\n if (window.SitePreferences.QAS_COUNTRIES_ENABLED.indexOf($('select[name$=\"_' + addressSection + 'Address_addressFields_country\"]')[0].value) == -1) {\n inputDataS.hide();\n inputDataS.closest('.op-checkout-address-container').find('.visible').removeClass('hidden');\n }\n }\n if (!$('.op-checkout-address-container .add-address2').length) {\n $('.op-checkout-address-container .address2').before('' + Resources.ADD_ADDRESS2 + '');\n }\n updateAddressBtn.hide();\n addAddressBtn.hide();\n editAddressBtn.hide();\n addressContainer.show();\n addressBookCheckbox.show();\n\n qas.init();\n qas.initLookupEvents($form);\n qas.initAutocomplete($form);\n\n // reset forms and selections\n if (addressSection === 'shipping') {\n $form.find('input[name*=\"_shippingAddress_addressFields\"]').val('');\n $('select[class*=\"shipping-address-list\"] option:eq(0)').prop('selected', 'selected');\n } else if (addressSection === 'billing') {\n $form.find('input[name*=\"_billingAddress_addressFields\"]').val('');\n $('select[class*=\"billing-address-list\"] option:eq(0)').prop('selected', 'selected');\n }\n\n formPrepare.init({\n continueSelector: '[name$=\"checkout_save\"]',\n formSelector: '[id$=\"_checkout\"]',\n skipDisabled: true,\n shippingListSelector: '#shipping-method-list'\n });\n if (window.reIntializeEdq) {\n window.reIntializeEdq();\n }\n }\n });\n });\n\n $('.op-checkout-edit-address').off().on('click', function(e) {\n e.preventDefault();\n var $this = $(this);\n var addressSection;\n var addressSectionID;\n var updateAddressBtn;\n var editAddressBtn;\n var addAddressBtn;\n var saveCheckbox;\n\n if ($this.attr('id') === 'edit-shipping-address') {\n addressSection = 'shipping';\n addressSectionID = $('#shipping-address-section');\n updateAddressBtn = $('#update-shipping-address');\n editAddressBtn = $('#edit-shipping-address');\n addAddressBtn = $('#add-shipping-address');\n saveCheckbox = $('[name$=\"_shippingAddress_addToAddressBook\"]').parents('.form-row');\n } else if ($this.attr('id') === 'edit-billing-address') {\n addressSection = 'billing';\n addressSectionID = $('#billing-address-section');\n updateAddressBtn = $('#update-billing-address');\n editAddressBtn = $('#edit-billing-address');\n addAddressBtn = $('#add-billing-address');\n saveCheckbox = $('[name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row');\n }\n\n $.ajax({\n url: Urls.editPreferredAddress,\n data: {addressSection: addressSection},\n success: function(response) {\n addressSectionID.html(response);\n\n if ($form.find('select[id$=\"_' + addressSection + 'Address_addressFields_country\"]:enabled').length > 0) {\n var country = $('select[name$=\"_checkout_' + addressSection + 'Address_addressFields_country\"]');\n var state = $('#dwfrm_checkout_' + addressSection + 'Address_addressFields_states_state');\n var $addressSection = $('#' + addressSection + '-address-section');\n checkout.initCountryState($form, country, state, $addressSection);\n }\n\n $('.address-lookup-section').show();\n $('.row-country-store').hide();\n $('input[name$=\"_shippingAddress_addressFields_email\"]').closest('.form-row').remove();\n $('input[name$=\"_shippingAddress_addressFields_country\"]').closest('.form-row').remove();\n\n if ($('form.checkout-shipping').find('#' + addressSection + '-address-lookup-container').length > 0) {\n var inputDataS = $('.op-checkout-section-content .address-lookup-section.delivery-' + addressSection).detach();\n inputDataS.insertAfter('#' + addressSection + '-address-container .form-row.phone.required');\n if (window.SitePreferences.QAS_COUNTRIES_ENABLED.indexOf($('select[name$=\"_' + addressSection + 'Address_addressFields_country\"]')[0].value) == -1) {\n inputDataS.hide();\n inputDataS.closest('.op-checkout-address-container').find('.visible').removeClass('hidden');\n }\n }\n if (!$('.op-checkout-address-container .add-address2').length) {\n $('.op-checkout-address-container .address2').before('' + Resources.ADD_ADDRESS2 + '');\n }\n updateAddressBtn.show();\n editAddressBtn.hide();\n addAddressBtn.hide();\n saveCheckbox.show();\n\n var $requiredFields = $(addressSectionID).find('.required:input:enabled');\n var callback = function() {\n var requiredValues = $requiredFields.map(function () {\n return $(this).val();\n });\n if ($requiredFields.hasClass('error') || _(requiredValues).includes('')) {\n $(updateAddressBtn).addClass('disabled');\n } else {\n $(updateAddressBtn).removeClass('disabled');\n }\n };\n\n formPrepare.init({\n continueSelector: '[name$=\"checkout_save\"]',\n formSelector: '[id$=\"_checkout\"]',\n skipDisabled: true,\n callback: callback,\n shippingListSelector: '#shipping-method-list'\n });\n callback();\n if (window.reIntializeEdq) {\n window.reIntializeEdq();\n }\n }\n });\n });\n\n $('.op-checkout-update-address').off().on('click', function(e) {\n e.preventDefault();\n if (!$(this).hasClass('disabled')) {\n var selectedAddress;\n var addressSection;\n var addressSectionID;\n var updateAddressBtn;\n var editAddressBtn;\n var addAddressBtn;\n var addressBookCheckbox;\n\n if ($(this).attr('id') === 'update-shipping-address') {\n addressSection = 'shipping';\n addressSectionID = $('#shipping-address-section');\n updateAddressBtn = $('#update-shipping-address');\n editAddressBtn = $('#edit-shipping-address');\n addAddressBtn = $('#add-shipping-address');\n selectedAddress = $('.shipping-address-list').children(':selected').first();\n addressBookCheckbox = $('[name$=\"_shippingAddress_addToAddressBook\"]').parents('.form-row');\n } else if ($(this).attr('id') === 'update-billing-address') {\n addressSection = 'billing';\n addressSectionID = $('#billing-address-section');\n updateAddressBtn = $('#update-billing-address');\n editAddressBtn = $('#edit-billing-address');\n addAddressBtn = $('#add-billing-address');\n selectedAddress = $('.billing-address-list').children(':selected').first();\n addressBookCheckbox = $('[name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row');\n }\n\n var $state = $('[name$=\"_' + addressSection + 'Address_addressFields_states_state\"]');\n var address = {\n ID: $(selectedAddress).val(),\n firstName: $('input[name$=\"_' + addressSection + 'Address_addressFields_firstName\"]').val(),\n lastName: $('input[name$=\"_' + addressSection + 'Address_addressFields_lastName\"]').val(),\n phone: $('input[name$=\"_' + addressSection + 'Address_addressFields_phone\"]').val(),\n address1: $('input[name$=\"_' + addressSection + 'Address_addressFields_address1\"]').val(),\n address2: $('input[name$=\"_' + addressSection + 'Address_addressFields_address2\"]').val(),\n city: $('input[name$=\"_' + addressSection + 'Address_addressFields_city\"]').val(),\n stateCode: $state.children(':selected').first().val() || $state.val() || '',\n postalCode: $('input[name$=\"_' + addressSection + 'Address_addressFields_postal\"]').val(),\n countryCode: $('select[name$=\"_' + addressSection + 'Address_addressFields_country\"]').children(':selected').first().val()\n };\n\n var isUpdateBothAddressLists = false;\n if ($('input[name$=\"_useAsBillingAddress\"]').length > 0) {\n if ($('input[name$=\"_useAsBillingAddress\"]')[0].checked) {\n isUpdateBothAddressLists = true;\n }\n }\n $.ajax({\n url: Urls.updatePreferredAddress,\n data: {\n selectedAddress: JSON.stringify(address),\n addressSection: addressSection,\n updateBoth: isUpdateBothAddressLists\n },\n success: function(response) {\n if (isUpdateBothAddressLists) {\n $('.op-checkout-address-section').html(response);\n $('.op-checkout-update-address').hide();\n $('.op-checkout-edit-address').show();\n $('.op-checkout-add-address-btn').show();\n $('[name$=\"_shippingAddress_addToAddressBook\"], [name$=\"_billingAddress_addToAddressBook\"]').parents('.form-row').hide();\n } else {\n addressSectionID.html(response);\n updateAddressBtn.hide();\n editAddressBtn.show();\n addAddressBtn.show();\n addressBookCheckbox.hide();\n }\n if ($('.shipping-address-list').children(':selected').first().val() == $('.billing-address-list').children(':selected').first().val()) {\n $('select[name$=\"_addressList\"]').trigger('change');\n }\n }\n });\n }\n return false;\n });\n\n $('select[class$=\"shipping-address-list\"] option:eq(1)').prop('selected', 'selected').trigger('change');\n $('select[class$=\"billing-address-list\"] option:eq(1)').prop('selected', 'selected').trigger('change');\n};\n","'use strict';\n\nvar ajax = require('base/ajax'),\n formPrepare = require('org/pages/checkout/formPrepare'),\n giftcard = require('org/giftcard'),\n shipping = require('org/pages/checkout/shipping'),\n util = require('org/util'),\n qas = require('int_QASc/qas');\n\n/**\n * @function\n * @description Fills the Credit Card form with the passed data-parameter and clears the former cvn input\n * @param {Object} data The Credit Card data (holder, type, masked number, expiration month/year)\n */\nfunction setCCFields(data) {\n var $selectPaymentMethod = $('.payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n var $creditCard = $('[data-method=\"'+selectedPaymentMethod+'\"]');\n\n $creditCard.find('input[name$=\"creditCard_owner\"]').val(data.holder).trigger('change');\n $creditCard.find('select[name$=\"_type\"]').val(data.type).trigger('change');\n $creditCard.find('input[name*=\"_creditCard_number\"]').val(data.maskedNumber).trigger('change');\n $creditCard.find('[name$=\"_month\"]').val(data.expirationMonth).trigger('change');\n $creditCard.find('[name$=\"_year\"]').val(data.expirationYear).trigger('change');\n\n if (data.showCvvField) {\n // hide CVV\n $creditCard.find('input[name*=\"_cvn\"]').removeClass('required');\n $creditCard.find('div.cvn').hide();\n } else {\n // show CVV\n $creditCard.find('input[name*=\"_cvn\"]').addClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').val('').trigger('change');\n }\n\n}\n\n/**\n * @function\n * @description Updates the credit card form with the attributes of a given card\n * @param {String} cardID the credit card ID of a given card\n */\nfunction populateCreditCardForm(cardID) {\n // load card details\n var url = util.appendParamToURL(Urls.billingSelectCC, 'creditCardUUID', cardID);\n ajax.getJson({\n url: url,\n callback: function (data) {\n if (!data) {\n window.alert(Resources.CC_LOAD_ERROR);\n return false;\n }\n setCCFields(data);\n }\n });\n}\n\n/**\n * @function\n * @description show/hide fields for Brazil(CPF, installment)\n * @param {String} payMethodID, countryCode\n */\nfunction fieldForBrazil(paymentMethodID,countryCode) {\n if (countryCode =='BR') {\n if (paymentMethodID == 'BOLETO-SSL') {\n $('#Payment_Brazil').show();\n var $fieldsSection = $('#Payment_Brazil');\n $fieldsSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $fieldsSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else if (paymentMethodID == 'CREDIT_CARD') {\n $('#Payment_Brazil').show();\n $('#Payment_Brazil').children().show();\n var\t $creditCardSection = $('#Payment_Brazil');\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else {\n $('#Payment_Brazil').hide();\n }\n } else {\n $('#Payment_Brazil').hide();\n }\n}\n\n/**\n * @function\n * @description Changes the payment method form depending on the passed paymentMethodID\n * @param {String} paymentMethodID the ID of the payment method, to which the payment method form should be changed to\n */\nfunction updatePaymentMethod(paymentMethodID) {\n $.ajax({\n url: Urls.loadPaymentMethods,\n type: 'Post',\n data: {'selectedPaymentMethodId': paymentMethodID},\n success: function(response) {\n $('.payment-method-container').html(response);\n // select credit card from list\n $('.creditCardList').on('change', function () {\n var cardUUID = $(this).val();\n if (!cardUUID) {\n var creditCardIndex = $('.creditCardList option:selected').index();\n if (creditCardIndex == '0') {\n if (SitePreferences.WP_DISABLE_CVV === true) {\n $('.payment-method-container').find('div.cvn').show();\n $('.payment-method-container').find('div.cvn').addClass('required');\n $('.payment-method-container').find('div.cvn .input-text').addClass('required');\n }\n\n }\n return;\n }\n // remove server side error\n $('.required.error').removeClass('error');\n $('.error-message').remove();\n var selectedPaymentMethod = $('.payment-method-options').find(':checked').val();\n if (selectedPaymentMethod=='WorldPay') {\n if (cardUUID != '') {\n $('.card-block').show();\n //$('.save-card-block').hide();\n } else {\n $('.card-block').hide();\n $('.save-card-block').show();\n }\n }\n populateCreditCardForm(cardUUID);\n var $creditCardSection = $('[data-method=\"WorldPay\"]');\n $creditCardSection.find('[name$=\"_cards\"]').val('');\n var $form = $('.address');\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n if (paymentMethodID == 'WorldPay' && countryCode == 'BR') {\n $('#Payment_Brazil').show();\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else if (paymentMethodID == 'CREDIT_CARD' && countryCode == 'BR') {\n $creditCardSection = $('[data-method=\"CREDIT_CARD\"]');\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n }\n });\n\n if (paymentMethodID=='WorldPay') {\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input,select').val('');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n }\n\n var $form = $('.address');\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n fieldForBrazil(paymentMethodID,countryCode);\n\n // select prefered credit card from list\n $('#preferedCreditCardList').on('change', function (e) {\n e.preventDefault();\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('input[name$=\"creditCard_owner\"]').val('');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').val('');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n $('#Payment_Brazil').hide();\n });\n\n $('#clearpaymentform').on('click', function(e) {\n e.preventDefault();\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input,select').val('');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n $('#Payment_Brazil').hide();\n });\n // clean cvv field\n $('[data-method=\"CREDIT_CARD\"]').find('input[name*=\"_cvn\"]').val('').trigger('change');\n resetCaptcha();\n formPrepare.init({\n formSelector: 'form[id$=\"billing\"]',\n continueSelector: '[name$=\"billing_save\"]'\n });\n paypalSaveCheckboxInit();\n moveTooltipsAndCaptions();\n }\n });\n\n $('input[name$=\"_selectedPaymentMethodID\"]').removeAttr('checked');\n $('input[value=' + paymentMethodID + ']').prop('checked', 'checked');\n}\n\n/**\n * @function\n * @description PayPal checkbox manipulations\n */\nfunction paypalSaveCheckboxInit() {\n if ($('input[id$=_paypal_paypalSaveAgreement]').length > 0) {\n\n // Put content asset as a label for checkbox\n $('label[for$=_paypal_paypalSaveAgreement]').html($('#paypal-sale-message-label').html()).addClass('paypal-save-agreement');\n $('#paypal-sale-message-label').remove();\n $('.billing-form-row .button-fancy-large').addClass('button-fancy-large-paypal');\n\n // Remove required error message for paypal save checkbox\n $('input[id$=_paypal_paypalSaveAgreement]').on('change', function () {\n resetCaptcha();\n if ($('span[id$=_paypal_paypalSaveAgreement-error]').length > 0) {\n $('span[id$=_paypal_paypalSaveAgreement-error]').remove();\n }\n if (!$(this).is(':checked')) {\n $('[name$=\"billing_save\"]').attr('disabled', 'disabled');\n }\n });\n if ($('span[id$=_paypal_paypalSaveAgreement-error]').length > 0) {\n $('span[id$=_paypal_paypalSaveAgreement-error]').remove();\n }\n if (!$(this).is(':checked')) {\n $('[name$=\"billing_save\"]').attr('disabled', 'disabled');\n }\n }\n}\n\n\nfunction resetCaptcha() {\n $('input[name$=\"billing_reCaptcha\"]').val('');\n if ($('#g-recaptcha-response').length) {\n if ($('#g-recaptcha-response').val()) {\n $('#dwfrm_billing_reCaptcha').val('on');\n $('input[name$=\"billing_reCaptcha\"]').trigger('change');\n }\n }\n}\n\n/**\n * @function\n * @description populates WorldPay APM payment methods.\n */\nfunction populateApmMethods() {\n // load APM details\n var countryCode = $('select[id$=\"_country\"]').val();\n\n if (countryCode == undefined || countryCode =='') {\n return;\n }\n\n var url = Urls.apmLookUp + '?billingCountry=' + countryCode;\n\n $.ajax({\n url: url,\n method: 'POST',\n dataType: 'json'\n }).done(function(data){\n var result = new Array();\n for (var i = 0; i < data.paymentMethod.length; i++) {\n result.push(data.paymentMethod[i].apm);\n }\n var paymentMethods= $('.payment-method-options').find('input[type=\"radio\"]');\n $(paymentMethods).each(function(index,value){\n if ($.inArray($(value).val(), result) == -1 && $(value).val() != 'CREDIT_CARD' && $(value).val() != 'WorldPay') {\n $(value).parent().parent('div').hide();\n } else {\n $(value).parent().parent('div').show();\n }\n });\n });\n}\n\nfunction initGiftCardCheckBalance() {\n $('#GCBalanceCheckForm').on('submit', function(e) {\n e.preventDefault();\n\n var $giftCertPin = $('input[name$=\"_giftCertPin\"]');\n var $giftCertCode = $('input[name$=\"_giftCertCode\"]');\n var $form = $('#GCBalanceCheckForm');\n var $checkoutForm = $('.checkout-billing');\n var $error = $checkoutForm.find('.giftcert-error');\n var $balance = $('.balance');\n var $csrfToken = $form.find('input[name$=\"csrf_token\"]');\n var $recaptchaToken = $('.g-recaptcha-response');\n\n $balance.html('');\n $error.html('');\n\n if ($form.length) {\n if (!$form.validate().form()) {\n return;\n }\n } else {\n if ($giftCertCode.length === 0 || $giftCertCode.val().length === 0) {\n $error.html(Resources.GIFT_CERT_MISSING);\n return;\n }\n if ($giftCertPin.length === 0 || $giftCertPin.val().length === 0) {\n $error.html(Resources.GIFT_CERT_PIN_MISSING);\n return;\n }\n }\n var jsondata = {'giftCertID': $giftCertCode.val(), 'giftCertPin': $giftCertPin.val(), 'csrf_token': $csrfToken.val(), 'recaptchatoken': $recaptchaToken.val()};\n giftcard.checkBalance(jsondata, function (data) {\n if (!data || !data.giftCertificate) {\n if (data && data.error == 'Recaptcha is required' && !jsondata.recaptchatoken){\n $error.html(Resources.GIFT_CERT_INVALID);\n window.grecaptcha.reset();\n } if (data && data.error){\n $error.html(data.error);\n window.grecaptcha.reset();\n } else {\n $error.html(Resources.GIFT_CERT_INVALID);\n }\n return;\n }\n $balance.html(Resources.GIFT_CERT_BALANCE + ' ' + data.giftCertificate.balance);\n });\n });\n}\n\nfunction updateGiftCardInfo() {\n $('#gift-card-payment-instruments').load(Urls.updateGiftCardInfo, function () {\n initRemoveGiftCard();\n });\n}\n\nfunction updatePaymentMethodsSection() {\n $('#payment-methods-section').load(Urls.refreshPaymentInfo, function () {\n initPaymentMethods();\n $('[name$=\"billing_save\"]').text(Resources.PLACE_ORDER);\n });\n}\n\nfunction initPaymentMethods() {\n var $selectPaymentMethod = $('.payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n\n // default payment method to 'CREDIT_CARD'\n updatePaymentMethod((selectedPaymentMethod) ? selectedPaymentMethod : 'CREDIT_CARD');\n $selectPaymentMethod.on('click', 'input[type=\"radio\"]', function () {\n updatePaymentMethod($(this).val());\n // Change button text if PayPal\n if ($(this).val() === 'PayPal') {\n $('[name$=\"billing_save\"]').text(Resources.KEEP_ON_GOING);\n } else {\n $('[name$=\"billing_save\"]').text(Resources.PLACE_ORDER);\n }\n // remove server side error\n if ($(this).val() !== selectedPaymentMethod) {\n $('.error-form.payment').remove();\n }\n });\n}\n\nfunction initRemoveGiftCard() {\n $('.remove-gift-card').on('click', function (e) {\n e.preventDefault();\n var $checkoutForm = $('.checkout-billing');\n var $error = $checkoutForm.find('.giftcert-error');\n $error.html('');\n $('.balance').html('');\n $.getJSON(this.href, function (data) {\n var fail = false;\n var msg = '';\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n $error.html(msg);\n return;\n } else {\n // shipping.updateSummary();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n }\n });\n });\n}\n\nfunction stickySummaryBlock() {\n if (util.isDesktopViewport()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('#secondary.summary').trigger('sticky_kit:detach');\n $('#secondary.summary').stick_in_parent();\n }\n\n // updating sticky sidebar onResize (recalculating position and events) and destroy on mobile VP\n util.smartResize(function() {\n if (!util.isDesktopViewport()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('#secondary.summary').trigger('sticky_kit:detach');\n } else {\n $(document.body).trigger('sticky_kit:recalc');\n $('#secondary.summary').stick_in_parent();\n }\n })\n}\n\nfunction initBlockFolding() {\n $('.checkout-billing').on('click', '.coupon-code-header, .gift-cert-header', function () {\n $(this).toggleClass('collapsed');\n });\n}\n\nfunction moveTooltipsAndCaptions() {\n var $checkoutForm = $('.checkout-billing');\n $checkoutForm.find('.form-row .inputfield-caption').each(function () {\n var $caption = $(this);\n $caption.insertBefore($caption.closest('.form-row').children().first());\n });\n $checkoutForm.find('.form-row .form-field-tooltip').each(function () {\n var $tooltip = $(this);\n $tooltip.insertBefore($tooltip.closest('.form-row').children().first());\n });\n}\n\nfunction initPromoButtonsEvent () {\n $('#dropdown-coupon-cancel').on('click', function () {\n $(this).parent().parent().html('');\n });\n\n $('#dropdown-coupon-apply').on('click', function () {\n var code = $(this).data('couponcode');\n var url = util.appendParamsToUrl(Urls.addCoupon, {couponCode: code, format: 'ajax', needRemoveOldCoupons: 'true'});\n var msg = '';\n\n $.getJSON(url, function (data) {\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n }\n\n if (data.success) {\n msg = data.message.split('<').join('<').split('>').join('>')\n var $sucessContainer = $('.checkout-billing').find('.redemption.coupon').find('span.success');\n\n if ($sucessContainer.length > 0) {\n $sucessContainer.html(msg);\n } else {\n var successMsg = '' + msg + '';\n $('.checkout-billing').find('.redemption.coupon').html(successMsg);\n }\n shipping.updateSummary();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n stickySummaryBlock();\n $('.checkout-billing').find('.coupon-error').html('');\n }\n });\n });\n}\n\n/**\n * @function\n * @description loads billing address, Gift Certificates, Coupon and Payment methods\n */\nexports.init = function () {\n var $checkoutForm = $('.checkout-billing');\n var $addGiftCert = $('#add-giftcert');\n var $giftCertPin = $('input[name$=\"_giftCertPin\"]');\n var $giftCertCode = $('input[name$=\"_giftCertCode\"]');\n var $addCoupon = $('#add-coupon');\n var $couponCode = $('input[name$=\"_couponCode\"]');\n var $selectPaymentMethod = $('.payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n var $placeOrderButton = $('[name$=\"billing_save\"]');\n\n $placeOrderButton.removeAttr('disabled');\n formPrepare.init({\n formSelector: 'form[id$=\"billing\"]',\n continueSelector: '[name$=\"billing_save\"]'\n });\n\n var $countrySelector = $('select[name$=\"_country\"]');\n $countrySelector.on('change', function() {\n if (Constants.ENABLE_APMLOOKUP) {\n populateApmMethods();\n }\n var $form = $('.address');\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n fieldForBrazil((selectedPaymentMethod) ? selectedPaymentMethod : 'CREDIT_CARD', countryCode);\n util.updateStateField($checkoutForm);\n if ($('body.clairesEU').length) {\n var lookupState = util.displayLookupButton($checkoutForm);\n if (lookupState) qas.initAutocomplete($checkoutForm);\n }\n });\n util.updateStateField($checkoutForm);\n\n if ($('body.clairesEU').length) {\n var $lookupFormRow = $('#billing-address-area input[name$=\"_lookup\"]').closest('.form-row');\n $lookupFormRow.addClass('form-row-lookup').hide();\n if ($('#billing-address-area .lookup-button').length > 0) {\n $('#billing-address-area .lookup-button').detach().insertAfter($lookupFormRow);\n }\n\n qas.initLookupEvents($checkoutForm);\n util.displayLookupButton($checkoutForm);\n qas.initAutocomplete($checkoutForm);\n }\n\n if (Constants.ENABLE_APMLOOKUP) {\n populateApmMethods();\n }\n\n initPaymentMethods();\n\n $('[name$=\"billing_save\"]').on('click', function() {\n if ($selectPaymentMethod.find(':checked').val() == 'CREDIT_CARD') {\n var $creditCard = $('[data-method=\"CREDIT_CARD\"]');\n $creditCard.find('[name$=\"_encryptedData\"]').val('');\n var data = {\n cvc: $creditCard.find('input[name*=\"_cvn\"]').val(),\n cardHolderName: $creditCard.find('input[name$=\"creditCard_owner\"]').val(),\n cardNumber: $creditCard.find('input[name*=\"_creditCard_number\"]').val(),\n expiryMonth: $creditCard.find('[name$=\"_month\"]').val() < 10 ? '0' + $creditCard.find('[name$=\"_month\"]').val() : $creditCard.find('[name$=\"_month\"]').val(),\n expiryYear: $creditCard.find('[name$=\"_year\"]').val()\n }\n if (window.SitePreferences.WP_ENABLE_CLIENT_SIDE_ENCRYPTION) {\n window.Worldpay.setPublicKey(window.SitePreferences.WP_CSE_PUBLIC_KEY);\n var encryptedData = window.Worldpay.encrypt(data, function(e) {\n window.console.log('Worldpay Client Side Encryption validation error: ' + e);\n });\n if (encryptedData) {\n $creditCard.find('[name$=\"_encryptedData\"]').val(encryptedData);\n }\n }\n }\n $checkoutForm.setAttrib('action','sale');\n $checkoutForm.submit();\n $placeOrderButton.attr('disabled', 'disabled');\n });\n\n initGiftCardCheckBalance();\n initRemoveGiftCard();\n initBlockFolding();\n\n $addGiftCert.on('click', function (e) {\n e.preventDefault();\n var code = $giftCertCode.val(),\n pin = $giftCertPin.val(),\n $error = $checkoutForm.find('.giftcert-error');\n\n $error.html('');\n $('.balance').html('');\n if (code.length === 0) {\n $error.html(Resources.GIFT_CERT_MISSING);\n return;\n }\n if (pin.length === 0) {\n $error.html(Resources.GIFT_CERT_PIN_MISSING);\n return;\n }\n\n var url = util.appendParamsToUrl(Urls.applyGiftCard, {giftCertID: code, giftCertPin: pin, format: 'ajax'});\n $.getJSON(url, function (data) {\n var fail = false;\n var msg = '';\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.error.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n $error.html(msg);\n return;\n } else {\n $error.html('');\n $giftCertCode.val('');\n $giftCertPin.val('');\n // shipping.updateSummary();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n stickySummaryBlock();\n }\n });\n });\n\n $addCoupon.on('click', function (e) {\n e.preventDefault();\n var $error = $checkoutForm.find('.coupon-error'),\n $success = $checkoutForm.find('.redemption.coupon'),\n code = $couponCode.val();\n if (code.length === 0) {\n $error.html(Resources.COUPON_CODE_MISSING);\n return;\n }\n\n var url = util.appendParamsToUrl(Urls.checkCoupon, {couponCode: code, format: 'ajax'});\n $.getJSON(url, function (data) {\n var fail = false;\n var msg = '';\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n if (data.status == 'SHOW_NOTIFICATION') {\n msg += ' ';\n }\n\n $error.html(msg);\n initPromoButtonsEvent();\n return;\n }\n\n //basket check for displaying the payment section, if the adjusted total of the basket is 0 after applying the coupon\n //this will force a page refresh to display the coupon message based on a parameter message\n if (data.success && data.baskettotal === 0) {\n window.location.assign(Urls.billing);\n }\n\n if (data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n var $sucessContainer = $success.find('span.success');\n if ($sucessContainer.length > 0) {\n $sucessContainer.html(msg);\n } else {\n var successMsg = '' + msg + '';\n $success.html(successMsg);\n }\n // shipping.updateSummary();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n stickySummaryBlock();\n }\n });\n });\n\n // trigger events on enter\n $couponCode.on('keydown', function (e) {\n if (e.which === 13) {\n e.preventDefault();\n $addCoupon.click();\n }\n });\n $giftCertCode.on('keydown', function (e) {\n if (e.which === 13) {\n e.preventDefault();\n $addGiftCert.click();\n }\n });\n stickySummaryBlock();\n};\n\nfunction clairesRecaptchaCallback() {\n\n $('#dwfrm_billing_reCaptcha').val('on')\n $('input[name$=\"billing_reCaptcha\"]').trigger('change');\n\n}\n\nfunction clairesRecaptchaCallbackExpired() {\n\n $('input[name$=\"billing_reCaptcha\"]').val('');\n $('input[name$=\"billing_reCaptcha\"]').trigger('change');\n\n}\n\nexports.initGiftCardCheckBalance = initGiftCardCheckBalance\nwindow.clairesRecaptchaCallback = clairesRecaptchaCallback;\nwindow.clairesRecaptchaCallbackExpired = clairesRecaptchaCallbackExpired;\n","'use strict';\n\nvar _ = require('lodash');\n\nvar ajax = require('base/ajax');\nvar formPrepare = require('./formPrepareV2');\nvar progress = require('base/progress');\nvar tooltip = require('org/tooltip');\nvar util = require('org/util');\nvar giftcard = require('org/giftcard');\nvar clickcollect = require('org/clickcollect/shiptostoreV2');\nvar klarna = require('org/pages/checkout/klarna');\n\nvar shippingMethods;\nvar saveEmailCounter = 0;\n\nvar domStrings = {\n codeError : $('.checkout-shipping').find('.giftcertCode-error'),\n pinError: $('.checkout-shipping').find('.giftcertPin-error'),\n giftcertCodeInputId: $('.checkout-shipping').find('#dwfrm_ceridiangiftcard_giftCertCode'),\n giftcertPinInputId : $('.checkout-shipping').find('#dwfrm_ceridiangiftcard_giftCertPin'),\n isError : null,\n balance : $('.balance-v2'),\n error : $('.checkout-shipping').find('.giftcert-error-v2'),\n giftCertPin : $('input[name$=\"_giftCertPin\"]'),\n giftCertCode : $('input[name$=\"_giftCertCode\"]'),\n giftcardBalanceWrapper : $('.checkout-shipping').find('.giftcard-balance-wrapper'),\n itemSummary : $('.item-summary-wrapper'),\n itemSummaryMultiship : $('.item-summary-wrapper-multiship'),\n itemSummaryBox : $('.order-summary-wrapper .item-summary-wrapper.box1'),\n titleArea : $('.title-area img'),\n firstImage : $('.title-area img.first'),\n box1 : $('.item-summary-wrapper.box1'),\n secondImage : $('.title-area img.second'),\n box2 : $('.item-summary-wrapper.box2'),\n orderSummary : $('.order-totals-container'),\n //giftcertCost: $('#gift-card-cost'),\n giftcardPaymentInstruments : $('#gift-card-payment-instruments-v2'),\n container : $('.item-name'),\n productName : $('.item-name p')\n}\n\n/**\n *\n * @param container\n * @param productName\n * @returns\n */\n/*function truncateProductTitle() {\n $('.item-name').each(function() {\n var containerHeight = $(this).outerHeight();\n var productNameHeight = $(this).find('p').outerHeight();\n if (productNameHeight > containerHeight) {\n $(this).css({'display': '-webkit-box', '-webkit-line-clamp': '2', '-webkit-box-orient': 'vertical', 'text-overflow': 'ellipsis', 'overflow': 'hidden'});\n $(this).siblings('.item-price-v2').css('padding-top', '10px');\n }\n}\n*/\n\n/**\n * @function\n * @description Toggle Item summary section\n */\nfunction itemSummaryDisplay() {\n domStrings.itemSummary.show();\n if ($('.item-summary-wrapper').hasClass('item-summary-wrapper-multiship'))\n domStrings.itemSummaryMultiship.hide();\n if ($(window).width() >= 767) { //desktop\n domStrings.firstImage.click(function() {\n if ($(this).parent().parent().next().css('display') == 'block') {\n $(this).parent().parent().next().hide();\n $(this).css('transform', 'rotate(180deg)');\n } else {\n $(this).parent().parent().next().show();\n $(this).css('transform', 'rotate(0deg)');\n }\n stickySummaryBlockReinit();\n });\n domStrings.secondImage.click(function() {\n if ($(this).parent().parent().next().css('display') == 'block') {\n $(this).parent().parent().next().hide();\n $(this).css('transform', 'rotate(180deg)');\n } else {\n $(this).parent().parent().next().show();\n $(this).css('transform', 'rotate(0deg)');\n }\n stickySummaryBlockReinit();\n });\n } else { //mobile\n domStrings.firstImage.click(function() {\n if ($(this).parent().parent().next().css('display') == 'none') {\n $(this).parent().parent().next().show();\n $(this).css('transform', 'rotate(0deg)');\n } else {\n $(this).parent().parent().next().hide();\n $(this).css('transform', 'rotate(180deg)');\n }\n });\n domStrings.secondImage.click(function() {\n if ($(this).parent().parent().next().css('display') == 'none') {\n $(this).parent().parent().next().show();\n $(this).css('transform', 'rotate(0deg)');\n } else {\n $(this).parent().parent().next().hide();\n $(this).css('transform', 'rotate(180deg)');\n }\n });\n }\n}\n\n\n/**\n * @function\n * @description Initializes gift message box, if shipment is gift\n */\nfunction giftMessageBox() {\n // show gift message box, if shipment is gift\n $('.gift-message-text').toggleClass('hidden', $('input[name$=\"_shippingAddress_isGift\"]:checked').val() !== 'true');\n\n if ($('.gift-message-text').hasClass('hidden')) {\n $('.gift-message-text').attr('aria-hidden', 'true');\n } else {\n $('.gift-message-text').removeAttr('aria-hidden');\n }\n}\n\n/**\n * @function\n * @description refresh ordersummaryV2\n */\nfunction orderSummaryRefresh() {\n // load giftcard cost area\n domStrings.itemSummaryBox.load(Urls.itemSummaryRefresh);\n domStrings.orderSummary.load(Urls.orderSummaryRefresh, function() {\n // refresh klarna placement\n window.KlarnaOnsiteService = window.KlarnaOnsiteService || [];\n window.KlarnaOnsiteService.push({eventName: 'refresh-placements'});\n });\n}\n\n/**\n * @function\n * @description Helper method which constructs a URL for an AJAX request using the\n * entered address information as URL request parameters.\n */\nfunction getShippingMethodURL(url, extraParams) {\n var $form = $('.address');\n var countryCode = $form.find('select[id$=\"_country\"]:enabled').val() !== undefined ? $form.find('select[id$=\"_country\"]:enabled').val() : $form.find('#sl-country-selector:enabled').val();\n var params;\n if (extraParams.getAddressFromCart === true) {\n params = {\n getAddressFromCart: extraParams.getAddressFromCart\n };\n } else {\n params = {\n address1: $form.find('input[name$=\"_address1\"]').val(),\n address2: $form.find('input[name$=\"_address2\"]').val(),\n countryCode: countryCode,\n stateCode: $form.find('select[id$=\"_state\"]').val(),\n postalCode: $form.find('input[name$=\"_postal\"]').val(),\n city: $form.find('input[name$=\"_city\"]').val(),\n deliveryType: $('.op-item-delivery-options').find('input:checked').val()\n };\n }\n return util.appendParamsToUrl(url, $.extend(params, extraParams));\n}\n\n/**\n * @function\n * @description selects a shipping method for the default shipment and updates the summary section on the right hand side\n * @param\n */\nfunction selectShippingMethod(shippingMethodID) {\n // nothing entered\n if (!shippingMethodID) {\n return;\n }\n // attempt to set shipping method\n var url = getShippingMethodURL(Urls.selectShippingMethodsList, {\n shippingMethodID: shippingMethodID\n });\n ajax.getJson({\n url: url,\n callback: function(data) {\n orderSummaryRefresh();\n if (!data || !data.shippingMethodID) {\n window.alert('Couldn\\'t select shipping method.');\n return false;\n }\n // display promotion in UI and update the summary section,\n // if some promotions were applied\n $('.shippingpromotions').empty();\n\n updatePaymentMethodsSection();\n }\n });\n}\n\n/**\n * @function\n * @description Make an AJAX request to the server to retrieve the list of applicable shipping methods\n * based on the merchandise in the cart and the currently entered shipping address\n * (the address may be only partially entered). If the list of applicable shipping methods\n * has changed because new address information has been entered, then issue another AJAX\n * request which updates the currently selected shipping method (if needed) and also updates\n * the UI.\n */\nfunction updateShippingMethodList(getAddressFromCart) {\n var $shippingMethodList = $('#shipping-method-list');\n if (!$shippingMethodList || $shippingMethodList.length === 0) {\n return;\n }\n var url = getShippingMethodURL(Urls.getShippingMethods, {getAddressFromCart: getAddressFromCart});\n\n ajax.getJson({\n url: url,\n callback: function(data) {\n if (!data) {\n window.alert('Couldn\\'t get list of applicable shipping methods.');\n return false;\n }\n if (data.toString() === '') {\n $('[name$=\"shippingAddress_save\"]').attr('disabled', 'disabled');\n }\n\n if (shippingMethods && shippingMethods.toString() === data.toString()) {\n // No need to update the UI. The list has not changed.\n return true;\n }\n // We need to update the UI. The list has changed.\n // Cache the array of returned shipping methods.\n shippingMethods = data;\n // indicate progress\n progress.show($shippingMethodList);\n loadShippingMethods(getAddressFromCart);\n var $placeOrderButton = $('.checkout-place-order-wrapper .billing-form-row .button-fancy-large');\n $placeOrderButton.removeClass('disabled');\n initLegalRequirements();\n }\n });\n}\n\n\nfunction loadShippingMethods(getAddressFromCart) {\n var $shippingMethodList = $('#shipping-method-list');\n // load the shipping method form\n var smlUrl = getShippingMethodURL(Urls.updateShippingMethods, {getAddressFromCart: getAddressFromCart});\n $shippingMethodList.load(smlUrl, function() {\n $shippingMethodList.fadeIn('fast');\n // rebind the radio buttons onclick function to a handler.\n $shippingMethodList.find('[name$=\"_shippingMethodID\"]').click(function() {\n selectShippingMethod($(this).val());\n });\n // update the summary\n updatePaymentMethodsSection();\n orderSummaryRefresh();\n progress.hide();\n tooltip.init();\n //if nothing is selected in the shipping methods select the first one\n if ($shippingMethodList.find('.input-radio:checked').length === 0) {\n $shippingMethodList.find('.input-radio:first').prop('checked', 'checked');\n }\n $shippingMethodList.find('.input-radio:checked').click();\n });\n}\n\nfunction updateShippingAddress() {\n var address = {};\n address.address1 = $('input[name$=\"_shippingAddress_addressFields_address1\"]').val();\n address.address2 = $('input[name$=\"_shippingAddress_addressFields_address2\"]').val();\n address.state = $('select[name$=\"_shippingAddress_addressFields_states_state\"]').val();\n address.city = $('input[name$=\"_shippingAddress_addressFields_city\"]').val();\n address.country = $('select[name$=\"_shippingAddress_addressFields_country\"]').val();\n\n if ($('input[name$=\"_shippingAddress_addressFields_postal\"]').length) {\n address.postal = $('input[name$=\"_shippingAddress_addressFields_postal\"]').val();\n } else {\n address.postal = $('input[name$=\"_shippingAddress_addressFields_zip\"]').val();\n }\n\n var url = util.appendParamToURL(Urls.updateShippingAddress, 'address', JSON.stringify(address));\n\n ajax.getJson({\n url: url,\n callback: function() {\n if (domStrings.giftCertCode.val().length && domStrings.giftCertPin.val().length) {\n $('#add-giftcert').trigger('click');\n domStrings.orderSummary.load(Urls.orderSummaryRefresh);\n } else {\n orderSummaryRefresh();\n }\n updatePaymentMethodsSection();\n }\n });\n}\n\n/* billing */\n\n/**\n * @function\n * @description Fills the Credit Card form with the passed data-parameter and clears the former cvn input\n * @param {Object} data The Credit Card data (holder, type, masked number, expiration month/year)\n */\nfunction setCCFields(data) {\n var $selectPaymentMethod = $('.op-payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n var $creditCard = $('[data-method=\"' + selectedPaymentMethod + '\"]');\n\n $creditCard.find('input[name$=\"creditCard_owner\"]').val(data.holder).trigger('change');\n $creditCard.find('select[name$=\"_type\"]').val(data.type).trigger('change');\n $creditCard.find('input[name*=\"_creditCard_number\"]').val(data.maskedNumber).trigger('change');\n $creditCard.find('[name$=\"_month\"]').val(data.expirationMonth).trigger('change');\n $creditCard.find('[name$=\"_year\"]').val(data.expirationYear).trigger('change');\n\n if (data.showCvvField) {\n // hide CVV\n $creditCard.find('input[name*=\"_cvn\"]').removeClass('required');\n $creditCard.find('div.cvn').hide();\n } else {\n // show CVV\n $creditCard.find('input[name*=\"_cvn\"]').addClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').val('').trigger('change');\n }\n}\n\n/**\n * @function\n * @description Updates the credit card form with the attributes of a given card\n * @param {String} cardID the credit card ID of a given card\n */\nfunction populateCreditCardForm(cardID) {\n // load card details\n var url = util.appendParamToURL(Urls.checkoutSelectCC, 'creditCardUUID', cardID);\n ajax.getJson({\n url: url,\n callback: function(data) {\n if (!data) {\n window.alert(Resources.CC_LOAD_ERROR);\n return false;\n }\n setCCFields(data);\n }\n });\n}\n\n/**\n * @function\n * @description show/hide fields for Brazil(CPF, installment)\n * @param {String} payMethodID, countryCode\n */\nfunction fieldForBrazil(paymentMethodID, countryCode) {\n if (countryCode == 'BR') {\n if (paymentMethodID == 'BOLETO-SSL') {\n $('#Payment_Brazil').show();\n var $fieldsSection = $('#Payment_Brazil');\n $fieldsSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $fieldsSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else if (paymentMethodID == 'CREDIT_CARD') {\n $('#Payment_Brazil').show();\n $('#Payment_Brazil').children().show();\n var $creditCardSection = $('#Payment_Brazil');\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else {\n $('#Payment_Brazil').hide();\n }\n } else {\n $('#Payment_Brazil').hide();\n }\n}\n\nfunction initDetectCardType() {\n $('input[name$=\"_creditCard_number\"]').on('keyup paste', function() {\n var $cardNumbValue = $(this).val();\n var $cardTypeEl = $('input[name$=\"_creditCard_type\"]');\n var $creditCardImgEl = $('#checkout-credit-card-type-icon');\n\n if ($cardNumbValue.match(/^3/)) {\n $cardTypeEl.val('Amex');\n $creditCardImgEl.attr('src', Urls.amexIcon);\n } else if ($cardNumbValue.match(/^4/)) {\n $cardTypeEl.val('Visa');\n $creditCardImgEl.attr('src', Urls.visaIcon);\n } else if ($cardNumbValue.match(/^5/)) {\n $cardTypeEl.val('MasterCard');\n $creditCardImgEl.attr('src', Urls.masterCardIcon);\n } else {\n $cardTypeEl.val('');\n $creditCardImgEl.attr('src', Urls.defaultCardIcon);\n }\n });\n}\n\nfunction getCreditCardDetails(section, UUID) {\n $.ajax({\n url: Urls.getCreditCardDetails,\n data: {\n section: section,\n UUID: UUID\n },\n success: function(response) {\n $('#credit-card-section').html(response);\n initDetectCardType();\n formPrepare.init({\n continueSelector: '[name$=\"checkout_save\"]',\n formSelector: '[id$=\"_checkout\"]',\n skipDisabled: true,\n shippingListSelector: '#shipping-method-list'\n });\n\n if (section === 'summary') {\n $('#edit-card-details').on('click', function(e) {\n e.preventDefault();\n getCreditCardDetails('edit', $('#creditCardList').val());\n });\n $('#new-card-details').on('click', function(e) {\n e.preventDefault();\n getCreditCardDetails('new', null);\n });\n } else if (section === 'edit') {\n $('input[name$=\"_number\"]').val('');\n $('input[name$=\"_expiration_month\"]').val('');\n $('input[name$=\"_expiration_year\"]').val('');\n\n formPrepare.init({\n continueSelector: $('#update-card-details'),\n formSelector: $('#credit-card-section'),\n skipDisabled: true\n });\n $('#update-card-details').on('click', function(e) {\n e.preventDefault();\n if (!$(this).hasClass('disabled')) {\n var card = {\n owner: $('input[name$=\"_owner\"]').val(),\n number: $('input[name$=\"_number\"]').val(),\n expirationMonth: $('input[name$=\"_expiration_month\"]').val(),\n expirationYear: $('input[name$=\"_expiration_year\"]').val(),\n type: $('[name$=\"_creditCard_type\"]').val()\n };\n $.ajax({\n url: Urls.saveCreditCardJSON,\n data: {\n saveCard: $('select[name$=\"_saveCard\"]').val(),\n card: JSON.stringify(card)\n },\n success: function(response) {\n var result = response.result;\n if (result.isSuccess) {\n var UUID = response.UUID;\n getCreditCardDetails('summary', UUID);\n initPaymentMethods();\n } else {\n alert(result.error);\n }\n }\n });\n }\n return false;\n });\n } else if (section === 'new') {\n $('#credit-card-section input:not([type=hidden])').each(function() {\n $(this).val('');\n });\n $('.payment-method select').each(function() {\n $(this).prop('selectedIndex', 0);\n });\n }\n\n initChangeSaveCard();\n initLegalRequirements();\n }\n });\n}\n\n/**\n * @function\n * @description Changes the payment method form depending on the passed paymentMethodID\n * @param {String} paymentMethodID the ID of the payment method, to which the payment method form should be changed to\n * @param {string} elementId id of the HTML element, used by Klarna to make selection for corresponding payment method\n */\nfunction updatePaymentMethod(paymentMethodID, elementId) {\n $('body').find('.op-payment-details-section').removeClass('payment-method-expanded').empty();\n var browserSize = null;\n if(paymentMethodID == 'CREDIT_CARD'){\n browserSize = JSON.stringify({width : $(window).width(), height: $(window).height()});\n }\n $.ajax({\n url: Urls.loadPaymentMethodsV2,\n type: 'Post',\n data: {\n 'selectedPaymentMethodId': paymentMethodID,\n 'browserSize' : browserSize\n },\n success: function(response) {\n var paymentMethodContainer = '.payment-method-content-' + paymentMethodID;\n if (elementId) {\n paymentMethodContainer += '[data-klarna-payment-id=' + elementId + ']';\n }\n\n // Update expanded payment method section with content\n $(paymentMethodContainer).addClass('payment-method-expanded').html(response);\n\n if (paymentMethodID === 'KLARNA_PAYMENTS') {\n klarna.init();\n var containerName = '#klarna_payments_' + elementId + '_container';\n klarna.loadPaymentData(containerName, elementId);\n }\n\n if (paymentMethodID == 'WorldPay') {\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input,select').val('');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n }\n\n var $form = $('.address');\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n fieldForBrazil(paymentMethodID, countryCode);\n\n\n // clean cvv field\n $('[data-method=\"CREDIT_CARD\"]').find('input[name*=\"_cvn\"]').val('').trigger('change');\n\n resetCaptcha();\n paypalSaveCheckboxInit();\n moveTooltipsAndCaptions();\n\n $('select[name$=\"_creditCardList\"] option[value=\"\"]:selected').prop('disabled','disabled');\n\n if ($('select[name$=\"_creditCardList\"]').length > 0) {\n $('select[name$=\"_creditCardList\"] option:eq(1)').prop('selected', true).trigger('change');\n } else {\n getCreditCardDetails('summary', null);\n }\n }\n });\n\n $('input[name$=\"_selectedPaymentMethodID\"]').removeAttr('checked');\n if ($('body').find('[data-sfcc-payment-name=' + paymentMethodID + ']') &&\n $('body').find('[data-sfcc-payment-name=' + paymentMethodID + ']').length) {\n if (elementId) {\n $('body').find('[data-sfcc-payment-name=' + paymentMethodID + '][id=' + elementId + ']').prop('checked', 'checked');\n } else {\n $('body').find('[data-sfcc-payment-name=' + paymentMethodID + ']').prop('checked', 'checked');\n }\n } else {\n $('input[value=' + paymentMethodID + ']').prop('checked', 'checked');\n }\n}\n\n/**\n * @function\n * @description PayPal checkbox manipulations\n */\nfunction paypalSaveCheckboxInit() {\n if ($('input[id$=_paypal_paypalSaveAgreement]').length > 0) {\n\n // Put content asset as a label for checkbox\n $('label[for$=_paypal_paypalSaveAgreement]').html($('#paypal-sale-message-label').html()).addClass('paypal-save-agreement');\n $('#paypal-sale-message-label').remove();\n $('.billing-form-row .button-fancy-large').addClass('button-fancy-large-paypal');\n\n // Remove required error message for paypal save checkbox\n $('input[id$=_paypal_paypalSaveAgreement]').on('change', function() {\n resetCaptcha();\n if ($('span[id$=_paypal_paypalSaveAgreement-error]').length > 0) {\n $('span[id$=_paypal_paypalSaveAgreement-error]').remove();\n }\n if (this && !$(this).is(':checked')) {\n $('[name$=\"checkout_save\"]').attr('disabled', 'disabled');\n } else {\n $('[name$=\"checkout_save\"]').removeAttr('disabled');\n }\n });\n if ($('span[id$=_paypal_paypalSaveAgreement-error]').length > 0) {\n $('span[id$=_paypal_paypalSaveAgreement-error]').remove();\n }\n if ($('input[id$=_paypal_paypalSaveAgreement]').is(':checked')) {\n $('[name$=\"checkout_save\"]').removeAttr('disabled');\n } else {\n $('[name$=\"checkout_save\"]').attr('disabled', 'disabled');\n }\n } else {\n $('[name$=\"checkout_save\"]').removeAttr('disabled');\n }\n}\n\nfunction resetCaptcha() {\n $('input[name$=\"billing_reCaptcha\"]').val('');\n if ($('#g-recaptcha-response').length) {\n if ($('#g-recaptcha-response').val()) {\n $('#dwfrm_billing_reCaptcha').val('on');\n $('input[name$=\"billing_reCaptcha\"]').trigger('change');\n }\n }\n}\n\n/**\n * @function\n * @description populates WorldPay APM payment methods.\n */\nfunction populateApmMethods() {\n // load APM details\n var countryCode = $('select[id$=\"_country\"]').val();\n\n if (countryCode == undefined || countryCode == '') {\n return;\n }\n\n var url = Urls.apmLookUp + '?billingCountry=' + countryCode;\n\n $.ajax({\n url: url,\n method: 'POST',\n dataType: 'json'\n }).done(function(data) {\n var result = new Array();\n for (var i = 0; i < data.paymentMethod.length; i++) {\n result.push(data.paymentMethod[i].apm);\n }\n var paymentMethods = $('.op-payment-method-options').find('input[type=\"radio\"]');\n $(paymentMethods).each(function(index, value) {\n if ($.inArray($(value).val(), result) == -1 && $(value).val() != 'CREDIT_CARD' && $(value).val() != 'WorldPay') {\n $(value).parent().parent('div').hide();\n } else {\n $(value).parent().parent('div').show();\n }\n });\n });\n}\n\nvar countrycodelist = {\n 'US': '+1',\n 'CA': '+1',\n 'AL': '+355',\n 'DZ': '+213',\n 'AD': '+376',\n 'AO': '+244',\n 'AI': '+1264',\n 'AG': '+1268',\n 'AR': '+54',\n 'AM': '+374',\n 'AW': '+297',\n 'AU': '+61',\n 'AT': '+43',\n 'AZ': '+994',\n 'BS': '+1242',\n 'BD': '+880',\n 'BB': '+1246',\n 'BE': '+32',\n 'BZ': '+501',\n 'BJ': '+229',\n 'BM': '+1441',\n 'BT': '+975',\n 'BO': '+591',\n 'BA': '+387',\n 'BW': '+267',\n 'BR': '+55',\n 'BN': '+673',\n 'BG': '+359',\n 'BF': '+226',\n 'BI': '+257',\n 'KH': '+855',\n 'CM': '+237',\n 'ES': '+34',\n 'IC': '+34',\n 'CV': '+238',\n 'KY': '+345',\n 'CF': '+236',\n 'TD': '+235',\n 'CL': '+56',\n 'CN': '+86',\n 'CO': '+57',\n 'KM': '+269',\n 'CK': '+682',\n 'CR': '+506',\n 'HR': '+385',\n 'CY': '+537',\n 'CZ': '+420',\n 'DK': '+45',\n 'DJ': '+253',\n 'DM': '+1767',\n 'DO': '+1809',\n 'TL': '+670',\n 'EC': '+593',\n 'EG': '+20',\n 'SV': '+503',\n 'GQ': '+240',\n 'ER': '+291',\n 'EE': '+372',\n 'ET': '+251',\n 'FO': '+298',\n 'FJ': '+679',\n 'FI': '+358',\n 'FR': '+33',\n 'GF': '+594',\n 'PF': '+689',\n 'GA': '+241',\n 'GM': '+220',\n 'GE': '+995',\n 'DE': '+49',\n 'GH': '+233',\n 'GI': '+350',\n 'GR': '+30',\n 'GL': '+299',\n 'GD': '+1473',\n 'GU': '+1671',\n 'GT': '+502',\n 'GG': '+44',\n 'IM': '+44',\n 'JE': '+44',\n 'GB': '+44',\n 'GN': '+224',\n 'HT': '+509',\n 'HN': '+504',\n 'HK': '+852',\n 'HU': '+36',\n 'IS': '+354',\n 'IN': '+91',\n 'ID': '+62',\n 'IE': '+353',\n 'IL': '+972',\n 'IT': '+39',\n 'JM': '+1876',\n 'JP': '+81',\n 'JO': '+962',\n 'KZ': '+7',\n 'RU': '+7',\n 'KE': '+254',\n 'KG': '+996',\n 'LV': '+371',\n 'LS': '+266',\n 'LI': '+423',\n 'LT': '+370',\n 'LU': '+352',\n 'MO': '+853',\n 'MG': '+261',\n 'MW': '+265',\n 'MY': '+60',\n 'MV': '+960',\n 'ML': '+223',\n 'MT': '+356',\n 'MH': '+692',\n 'MQ': '+596',\n 'MR': '+222',\n 'MU': '+230',\n 'YT': '+262',\n 'RE': '+262',\n 'MX': '+52',\n 'FM': '+691',\n 'MD': '+373',\n 'MC': '+377',\n 'MN': '+976',\n 'ME': '+382',\n 'MS': '+1664',\n 'MA': '+212',\n 'MZ': '+258',\n 'NA': '+264',\n 'NP': '+977',\n 'NL': '+31',\n 'NC': '+687',\n 'NZ': '+64',\n 'NI': '+505',\n 'NE': '+227',\n 'NG': '+234',\n 'NO': '+47',\n 'OM': '+968',\n 'PK': '+92',\n 'PW': '+680',\n 'PS': '+970',\n 'PA': '+507',\n 'PG': '+675',\n 'PY': '+595',\n 'PE': '+51',\n 'PH': '+63',\n 'PL': '+48',\n 'PT': '+351',\n 'PR': '+1 787',\n 'QA': '+974',\n 'CG': '+242',\n 'RO': '+40',\n 'RW': '+250',\n 'SM': '+378',\n 'SA': '+966',\n 'SN': '+221',\n 'SC': '+248',\n 'SG': '+65',\n 'SK': '+421',\n 'SI': '+386',\n 'SB': '+677',\n 'ZA': '+27',\n 'LK': '+94',\n 'KN': '+1 869',\n 'LC': '+1 758',\n 'VC': '+1 784',\n 'MF': '+590',\n 'GP': '+590',\n 'SR': '+597',\n 'SZ': '+268',\n 'SE': '+46',\n 'CH': '+41',\n 'TW': '+886',\n 'TZ': '+255',\n 'TH': '+66',\n 'TG': '+228',\n 'TT': '+1 868',\n 'TN': '+216',\n 'TR': '+90',\n 'AE': '+971',\n 'UY': '+598',\n 'ZM': '+260',\n 'ZW': '+263'\n};\n\nfunction chekingCountryFill() {\n var el = $('.form-row.selectbox select.country');\n if (el.val() != '') {\n el.closest('.form-row').addClass('complete');\n } else {\n el.closest('.form-row').removeClass('complete');\n }\n \n $('#dwfrm_profile_customer_phoneNumberCountryCode option').each(function() {\n if ($(this).text() === countrycodelist[el.val()]) {\n $(this).prop('selected', true); // Select the option with the matching label\n }\n });\n}\n$('body').on('change', '.form-row select.country', chekingCountryFill);\n\n$('.login-form-nav #register-tab').on('click', function() {\n var el = $('.form-row.selectbox select.country');\n $('#dwfrm_profile_customer_phoneNumberCountryCode option').each(function() {\n if ($(this).text() === countrycodelist[el.val()]) {\n $(this).prop('selected', true); // Select the option with the matching label\n }\n });\n})\n\nif ($('.account-registration-body #dwfrm_profile_customer_phone').val() == '') {\n $('.account-registration-body #consent-sms').attr('disabled', 'disabled');\n}\n\n$('body').on('change', '.account-registration-body', enableConsent);\n\nfunction enableConsent() {\n if ($('.account-registration-body #dwfrm_profile_customer_phone').val() == '') {\n $('.account-registration-body #consent-sms').attr('disabled', 'disabled');\n var checkbox = document.getElementById(\"consent-sms\");\n checkbox.checked = false;\n }\n\n if ($('.account-registration-body #dwfrm_profile_customer_phone').val() != '') {\n $('.account-registration-body #consent-sms').removeAttr('disabled');\n }\n}\n\n$('body').on('change', '.account-registration-body', enableCreateAccount);\n\nfunction enableCreateAccount() {\nif($('.account-registration-body .form-row.required input').toArray().every(function(input) {\n return $(input).val() !== ''; // Check if all required fields have values\n}) && ($('.account-registration-body #dwfrm_profile_customer_phone').val() == '' || $('.account-registration-body #dwfrm_profile_customer_phone').val() != '')) {\n $('.signup-checkout').removeAttr('disabled');\n }\n}\n\nfunction initGiftCardCheckBalance() {\n $('#check-giftcert').on('click', function(e) {\n e.preventDefault();\n domStrings.codeError.html('');\n domStrings.pinError.html('');\n domStrings.isError = false;\n domStrings.error.html('');\n\n if (domStrings.giftCertCode.length === 0 || domStrings.giftCertCode.val().length === 0) {\n domStrings.codeError.html(Resources.GIFT_CERT_MISSING);\n domStrings.isError = true;\n domStrings.balance.html('');\n }\n if (domStrings.giftCertPin.length === 0 || domStrings.giftCertPin.val().length === 0) {\n domStrings.pinError.html(Resources.GIFT_CERT_PIN_MISSING);\n domStrings.isError = true;\n domStrings.balance.html('');\n }\n\n restyleInputField(domStrings.codeError, domStrings.giftcertCodeInputId);\n restyleInputField(domStrings.pinError, domStrings.giftcertPinInputId);\n\n if (!domStrings.isError) {\n giftcard.checkBalance(domStrings.giftCertCode.val(), domStrings.giftCertPin.val(), function(data) {\n if (!data || !data.giftCertificate) {\n if (data && data.error) {\n domStrings.error.html(data.error);\n } else {\n domStrings.error.html(Resources.GIFT_CERT_INVALID);\n restyleInputField(domStrings.error, domStrings.giftcertCodeInputId);\n restyleInputField(domStrings.error, domStrings.giftcertPinInputId);\n }\n return;\n }\n domStrings.balance.html(Resources.GIFT_CERT_BALANCE + ' ' + data.giftCertificate.balance);\n domStrings.giftcardBalanceWrapper.show();\n domStrings.error.html('');\n });\n }\n });\n}\n\nfunction updateGiftCardInfo() {\n domStrings.giftcardPaymentInstruments.load(Urls.updateGiftCardInfo, function() {\n initRemoveGiftCard();\n });\n}\n\nvar updatePayments = _.debounce(function () {\n var url = window.location.search.indexOf('returnToForm=true') >= 0 ? Urls.updatePaymentSection +'?returnToForm=true' : Urls.updatePaymentSection;\n $('#checkout-payment-methods-section').load(url , function() {\n initPaymentMethods();\n progress.hide('.checkout-payment-methods-section');\n $('[name$=\"checkout_save\"]').text(Resources.PLACE_ORDER);\n });\n}, 800);\n\nvar updateKlarnaSession = _.debounce(klarna.updateKlarnaSession, 400);\n\nvar updatePaymentMethodsSection = function () {\n progress.show('.checkout-payment-methods-section');\n updateKlarnaSession(); // 400 ms\n updatePayments(); // 800 ms\n}\n\nfunction initPaymentMethods() {\n var $selectPaymentMethod = $('.op-payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n\n // default payment method to 'CREDIT_CARD'\n if ($selectPaymentMethod.find(':checked').attr('data-sfcc-payment-name') &&\n $selectPaymentMethod.find(':checked').attr('data-sfcc-payment-name').length) {\n selectedPaymentMethod = $selectPaymentMethod.find(':checked').attr('data-sfcc-payment-name');\n }\n klarna.submitPaymentMethod($('.op-payment-method-options').find(':checked.input-radio'),\n function (selectedPaymentMethod) {\n updatePaymentMethod((selectedPaymentMethod) ? selectedPaymentMethod : 'CREDIT_CARD')\n });\n\n\n $selectPaymentMethod.on('click', 'input[type=\"radio\"]', function() {\n var paymentMethodName = $('.op-payment-method-options').find(':checked.input-radio').val();\n var $paymentMethodElement = $('.op-payment-method-options').find(':checked.input-radio');\n var isKlarna;\n if ($(this).attr('data-sfcc-payment-name') && $(this).attr('data-sfcc-payment-name').length) {\n paymentMethodName = $('.op-payment-method-options').find(':checked.input-radio').attr('data-sfcc-payment-name');\n isKlarna = true;\n }\n\n klarna.submitPaymentMethod($paymentMethodElement, function () {\n updatePaymentMethod(paymentMethodName, isKlarna ? $paymentMethodElement.attr('id') : null)\n });\n // Change button text if PayPal\n if ($(this).val() === 'PayPal') {\n $('[name$=\"checkout_save\"]').text(Resources.KEEP_ON_GOING);\n } else {\n $('[name$=\"checkout_save\"]').text(Resources.PLACE_ORDER);\n }\n // remove server side error\n if ($(this).val() !== selectedPaymentMethod) {\n $('.error-form.payment').remove();\n }\n });\n}\n\nfunction initRemoveGiftCard() {\n $('.remove-gift-card').on('click', function(e) {\n e.preventDefault();\n\n removeValidation(domStrings.giftCertCode, domStrings.codeError);\n removeValidation(domStrings.giftCertPin, domStrings.codeError);\n\n var $checkoutForm = $('.checkout-billing');\n var $error = $checkoutForm.find('.giftcert-error');\n $error.html('');\n $('.balance').html('');\n $.getJSON(this.href, function(data) {\n var fail = false;\n var msg = '';\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n $error.html(msg);\n return;\n } else {\n orderSummaryRefresh();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n }\n });\n });\n}\n\nfunction stickySummaryBlock() {\n if (util.isDesktopViewport() && $('.order-summary-wrapper').outerHeight() < $('.checkout-box-col1').outerHeight()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').trigger('sticky_kit:detach');\n $('.order-summary-wrapper').stick_in_parent();\n }\n\n // updating sticky sidebar onResize (recalculating position and events) and destroy on mobile VP\n util.smartResize(function() {\n if (!util.isDesktopViewport()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').trigger('sticky_kit:detach');\n } else {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').stick_in_parent();\n }\n })\n}\n\nfunction stickySummaryBlockReinit() {\n if (util.isDesktopViewport()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').trigger('sticky_kit:detach');\n $('.order-summary-wrapper').stick_in_parent();\n }\n}\n\nfunction initBlockFolding() {\n $('.main-content-container').off().on('click', '.coupon-code-header, .gift-cert-header', function() {\n $(this).toggleClass('collapsed');\n });\n}\n\nfunction createAccountFolding() {\n $('.create-account .title-area img.login-arrow-up').css('transform', 'rotate(0deg)');\n $('.create-account').click(function() {\n $(this).toggleClass('collapsed');\n if ($(this).hasClass('collapsed')) {\n $('.create-account-box').hide();\n $('.create-account .title-area img.login-arrow-up').css('transform', 'rotate(180deg)');\n } else {\n $('.create-account-box').show();\n $('.create-account .title-area img.login-arrow-up').css('transform', 'rotate(0deg)');\n }\n\n });\n}\n\nfunction toggleAriaExpanded() {\n var arrowUp = document.querySelector('.login-arrow-up');\n var itemSummaryWrapper = document.querySelector('.item-summary-wrapper.box1.item-summary-wrapper-multiship.item-summary-multiship');\n var promoCodeWrapper = document.getElementById('cart-v2-coupon-body');\n arrowUp.addEventListener('click', function() {\n var isExpanded = itemSummaryWrapper.getAttribute('aria-expanded') === 'true';\n itemSummaryWrapper.setAttribute('aria-expanded', !isExpanded);\n\n var isPromoCodeExpanded = promoCodeWrapper.getAttribute('aria-expanded') === 'true';\n promoCodeWrapper.setAttribute('aria-expanded', !isPromoCodeExpanded);\n\n });\n var legend = document.querySelector('.gift-cert-header');\n var giftCertWrapper = document.querySelector('.gift-cert-wrapper');\n legend.addEventListener('click', function() {\n var giftCertWrapperElements = document.getElementsByClassName('gift-cert-wrapper gift-cert-wrapper-v2');\n if (giftCertWrapperElements.length > 0) {\n var isGiftCertExpanded = giftCertWrapperElements[0].getAttribute('aria-expanded') === 'true';\n giftCertWrapperElements[0].setAttribute('aria-expanded', !isGiftCertExpanded);\n console.log(\"isGiftCertExpanded\", isGiftCertExpanded);\n }\n });\n }\n \n document.addEventListener('DOMContentLoaded', function() {\n toggleAriaExpanded();\n });\n\nfunction moveTooltipsAndCaptions() {\n var $checkoutForm = $('.checkout-shipping');\n $checkoutForm.find('.form-row .inputfield-caption').each(function() {\n var $caption = $(this);\n $caption.insertBefore($caption.closest('.form-row').children().first());\n });\n $checkoutForm.find('.form-row .form-field-tooltip').each(function() {\n var $tooltip = $(this);\n $tooltip.insertBefore($tooltip.closest('.form-row').children().first());\n });\n}\n\nfunction updateStateOptions(form, $countryField, $stateField) {\n var $form = $(form),\n arrHtml = [],\n $country = $countryField,\n country = window.Countries[$country.val().toUpperCase()],\n $postalField = $country.data('postalField') ? $country.data('postalField') : $form.find('input[name$=\"_postal\"]'),\n $stateLabel = ($stateField.length > 0) ? $form.find('label[for=\"' + $stateField[0].id + '\"] span').not('.required-indicator') : undefined,\n $postalLabel = ($postalField.length > 0) ? $form.find('label[for=\"' + $postalField[0].id + '\"] span').not('.required-indicator') : undefined,\n prevStateValue = $stateField.val() || $stateField.val();\n // set the label text\n if ($postalLabel && country) {\n $postalLabel.html(country.postalLabel);\n }\n if ($stateLabel && country) {\n $stateLabel.html(country.regionLabel);\n } else {\n return;\n }\n var s, i = 0;\n for (s in country.regions) {\n arrHtml.push('');\n i++;\n }\n // if a state was selected previously, save that selection\n if (prevStateValue && typeof country.regions[prevStateValue] !== 'undefined') {\n $stateField.val(prevStateValue).trigger('change');\n } else if (i === 1) {\n // if there is only one region, make that region selected\n $stateField[0].selectedIndex = 1;\n } else {\n $stateField[0].selectedIndex = 0;\n }\n}\n\nfunction updateStateField(form, countryField, stateField) {\n var $form = $(form);\n var $countryField = countryField;\n var country = window.Countries[$countryField.val().toUpperCase()];\n var $stateField = $countryField.data('stateField') ? $countryField.data('stateField') : $countryField.find('select[name$=\"_state\"]');\n var data;\n\n if ($countryField.length === 0 || !country) {\n if ($stateField.length === 0) {\n data = {'formField': $countryField.attr('name'), 'fieldType': 'input', 'country': $countryField.val(), 'rowClass': 'visible hidden'};\n } else {\n return;\n }\n } else {\n if ($stateField.length === 0) {\n data = {'formField': $countryField.attr('name'), 'fieldType': 'select', 'country': $countryField.val()}\n } else {\n data = {'formField': $countryField.attr('name')};\n }\n }\n\n if (data !== undefined) {\n $.ajax({\n url: Urls.getStateField,\n type: 'GET',\n data: data,\n success: function (response) {\n var $stateContainer = $(stateField.selector).parents('.form-row');\n if ($stateContainer) {\n if ($stateContainer.length > 0) {\n $stateContainer.replaceWith(response);\n }\n\n if (stateField.selector === '#dwfrm_checkout_shippingAddress_addressFields_states_state') {\n $stateField = $('select[name$=\"_checkout_shippingAddress_addressFields_states_state\"]');\n } else if (stateField.selector === '#dwfrm_checkout_billingAddress_addressFields_states_state') {\n $stateField = $('select[name$=\"_checkout_billingAddress_addressFields_states_state\"]');\n }\n\n updateStateOptions($form, $countryField, $stateField);\n }\n }\n });\n } else {\n return;\n }\n}\n\nfunction initCountryState($form, countrySelect, stateSelect, $addressSection) {\n $(countrySelect, $form).off().on('change', function() {\n lookupHandle($addressSection, countrySelect);\n if (Constants.ENABLE_APMLOOKUP) {\n populateApmMethods();\n }\n var $selectPaymentMethod = $('.op-payment-method-options');\n var selectedPaymentMethod = $selectPaymentMethod.find(':checked').val();\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n fieldForBrazil((selectedPaymentMethod) ? selectedPaymentMethod : 'CREDIT_CARD', countryCode);\n updateStateField($form, countrySelect, stateSelect);\n });\n updateStateField($form, countrySelect, stateSelect);\n}\n\nfunction lookupHandle ($addressSection, countrySelect) {\n var $lookupSection = $addressSection ? $addressSection.find('.address-lookup-section') : '';\n if ($lookupSection.length == 0) {\n return;\n }\n if (window.SitePreferences.QAS_COUNTRIES_ENABLED.indexOf(countrySelect[0].value) == -1) {\n $lookupSection.hide();\n $lookupSection.closest('.op-checkout-address-container').find('.visible').removeClass('hidden');\n } else {\n $lookupSection.show();\n $lookupSection.closest('.op-checkout-address-container').find('.visible').addClass('hidden');\n }\n}\n\nfunction restyleInputField($error, $inputId) {\n if ($error.text().length > 0) {\n $($inputId).css('border-color', '#e0142c');\n $($inputId).parent().parent().parent().children('.img-wrapper').addClass('giftcard-error-empty-field');\n $($inputId).parent().parent().parent().children('.img-wrapper').removeClass('giftcard-valid');\n } else {\n $($inputId).css('border-color', '#e2e2e2'); // '#3ecf22'\n $($inputId).parent().parent().parent().children('.img-wrapper').addClass('giftcard-valid');\n $($inputId).parent().parent().parent().children('.img-wrapper').removeClass('giftcard-error-empty-field');\n }\n}\n\nfunction removeValidation($input, $div) {\n $input.val('');\n $input.css('border-color', '#e2e2e2');\n $div.html('');\n $input.parent().parent().siblings('.img-wrapper').removeClass('giftcard-valid');\n $input.parent().parent().siblings('.img-wrapper').removeClass('giftcard-error-empty-field');\n}\n\nfunction updatePostcodePlaceholder(country) {\n var countryField = $(country)\n var $postalCodeField = $(countryField).closest('fieldset').find('.postCode-field').length ? $(countryField).closest('fieldset').find('.postCode-field') : $(countryField).closest('form').find('.postCode-field').first();\n if (countryField.val() === 'IE') {\n $postalCodeField.attr('placeholder', 'Please enter at least 4 characters or ‘none’');\n }\n}\n\n// method to check shipping address against PO rules\nfunction validatePOAddress() {\n // check if PO selected and address 1 populated\n var $addressForm = $('#shipping-address-container');\n var $stateField = $addressForm.find('select[name$=\"_shippingAddress_addressFields_states_state\"]');\n var $addressField1 = $addressForm.find('input[name$=\"_shippingAddress_addressFields_address1\"]');\n var $addressField2 = $addressForm.find('input[name$=\"_shippingAddress_addressFields_address2\"]');\n var poStates = ['AA', 'AE', 'AP'];\n if (!$stateField.is(':visible') || $stateField.length && poStates.indexOf($stateField.val()) < 0 || !$addressField1.val()) {\n return true;\n }\n\n // pull combined text from both inputs and convert to upper case for the next checks\n var addressText = ($addressField1.val() + ' ' + $addressField2.val()).trim().toUpperCase();\n\n // checking unit related key words\n if (addressText.indexOf('UNIT') < 0 && addressText.indexOf('CMR') < 0 && addressText.indexOf('PSC') < 0) {\n return false;\n }\n\n // checking box related key words\n if (addressText.indexOf('BOX') < 0) {\n return false;\n }\n\n return true;\n}\n\n/*\n * Check shipping address fields against PO address and validate them\n * append error message and invalidate related inputs\n*/\nfunction checkPOAddress() {\n\n // remove message and unblock submit if passed\n if (validatePOAddress()){\n $('.po-address-error').hide();\n $('input[name*=\"_shippingAddress_addressFields_address\"]').removeClass('invalid');\n return;\n }\n // show or add message and block submit, but adding invalid class to related inputs\n if ($('.po-address-error').length) {\n $('.po-address-error').show();\n } else {\n $('')\n .addClass('po-address-error form-row error')\n .text(Resources.INVALID_PO_ADDRESS)\n .insertBefore(\n $('input[name$=\"_shippingAddress_addressFields_address2\"]')\n .closest('.form-row')\n );\n }\n $('input[name*=\"_shippingAddress_addressFields_address\"]').addClass('invalid').valid();\n}\n\nfunction applyGiftCardCall() {\n\n var url = Urls.applyGiftCard;\n var csrfToken = $('#csrf_token').val();\n var $recaptchatoken = $('.g-recaptcha-response');\n var data = {'giftCertID':domStrings.giftCertCode.val(), 'giftCertPin' : domStrings.giftCertPin.val(), 'csrf_token' : csrfToken, 'recaptchatoken' : $recaptchatoken.val()};\n $.ajax({\n url: url,\n type:'POST',\n data: data,\n success: function(response){\n var fail = false;\n var msg = '';\n if (!response) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!response.success) {\n msg = response.error.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n if (response.error =='Recaptcha is required' && !data.recaptchatoken){\n $('#add-giftcert').addClass('g-recaptcha-custom').click();\n return;\n }\n domStrings.error.html(msg);\n restyleInputField(domStrings.error, domStrings.giftcertCodeInputId);\n restyleInputField(domStrings.error, domStrings.giftcertPinInputId);\n $(domStrings.error).trigger('checkout.tagerror');\n return;\n } else {\n domStrings.error.html('');\n restyleInputField(domStrings.error, domStrings.giftcertCodeInputId);\n restyleInputField(domStrings.error, domStrings.giftcertPinInputId);\n orderSummaryRefresh();\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n stickySummaryBlock();\n }\n }\n });\n}\n\nexports.init = function() {\n // domStrings.giftcertCodeInputId.placeholder = '1111 2222 3333 4444 5555';\n // domStrings.giftcertPinInputId.placeholder = '1234';\n var $form = $('form.address');\n\n /*\n Move lookup container from above form to be inside it, after phone field (shipping and billing)\n */\n if ($('form.checkout-shipping').find('#shipping-address-lookup-container').length > 0) {\n var inputDataS = $('.op-checkout-section-content .address-lookup-section.delivery-shipping').detach();\n inputDataS.insertAfter('#shipping-address-container .form-row.phone.required');\n lookupHandle($('#shipping-address-container'), $('select[name$=\"_shippingAddress_addressFields_country\"]'))\n }\n if ($('form.checkout-shipping').find('#billing-address-lookup-container').length > 0) {\n var inputDataB = $('.op-checkout-section-content .address-lookup-section.delivery-billing').detach();\n inputDataB.insertAfter('#billing-address-container .form-row.phone.required');\n lookupHandle($('#billing-address-container'), $('select[name$=\"_billingAddress_addressFields_country\"]'))\n }\n\n // toggle class on address specific inputs, to allow manuall address input\n $form.on('click', '.op-checkout-enter-address', function (e){\n e.preventDefault();\n $(this).closest('.op-checkout-address-container').find('.visible').toggleClass('hidden');\n });\n domStrings.giftcardBalanceWrapper.hide();\n\n $('input[name$=\"_shippingAddress_isGift\"]').on('click', giftMessageBox);\n $form.on('change', 'select[name$=\"_shippingAddress_addressFields_country\"]:enabled', updateShippingMethodList);\n\n $('.address').on('change', 'select[name$=\"_shippingAddress_addressFields_country\"]:enabled', function () {\n var country = $(this);\n if (!country) {return;}\n updatePostcodePlaceholder(country);\n });\n $('.address').on('change', 'select[name$=\"_billingAddress_addressFields_country\"]:enabled', function () {\n var country = $(this);\n if (!country) {return;}\n updatePostcodePlaceholder(country);\n });\n $('.address').on('change',\n 'input[name$=\"_shippingAddress_addressFields_address1\"], input[name$=\"_shippingAddress_addressFields_address2\"], select[name$=\"_shippingAddress_addressFields_states_state\"], input[name$=\"_shippingAddress_addressFields_city\"], input[name$=\"_shippingAddress_addressFields_zip\"], input[name$=\"_shippingAddress_addressFields_postal\"]',\n updateShippingAddress\n );\n\n // bind check event with address and state fields update\n $form.on('change',\n 'input[name$=\"_shippingAddress_addressFields_address1\"], input[name$=\"_shippingAddress_addressFields_address2\"], select[name$=\"_shippingAddress_addressFields_states_state\"]',\n checkPOAddress\n );\n\n // klarna custom code changes\n $('body').on('change', '.billing-section', function () {\n if ($('.op-payment-method-options').find(':checked').attr('data-sfcc-payment-name') &&\n $('.op-payment-method-options').find(':checked').attr('data-sfcc-payment-name').length) {\n var billingAddressElements = $('#billing-address-container').find('input[name*=_billingAddress_addressFields_], select[name*=_billingAddress_addressFields_]');\n var formValid = true;\n for (var i = 0; i < billingAddressElements.length; i++) {\n if (billingAddressElements[i].getAttribute('aria-invalid') === 'true' || (billingAddressElements[i].getAttribute('aria-required') === 'true' && billingAddressElements[i].value.length === 0)) {\n formValid = false;\n break;\n }\n }\n if (formValid) {\n klarna.submitPaymentMethod($('.op-payment-method-options').find(':checked'), klarna.updateBillingAddress($('.op-payment-method-options').find(':checked').attr('id')));\n }\n }\n });\n /*\n Handle checkbox use-shipping-for-billing-address, as part of init, then as part of interaction\n */\n var billingSection = $('.billing-section');\n if ($('input[name$=\"_shippingAddress_useAsBillingAddress\"]').prop('checked')) {\n billingSection.hide();\n } else {\n billingSection.show();\n }\n $form.on('click', 'input[name$=\"_shippingAddress_useAsBillingAddress\"]', function() {\n if ($(this).prop('checked')) {\n billingSection.hide();\n var $shippingFields = $('input[name*=\"_shippingAddress_addressFields_\"]');\n $shippingFields.each(function() {\n var billingName = this.name.replace('shipping', 'billing');\n $('[name=\"' + billingName + '\"]').val(this.value);\n });\n var shipCountry = $('select[name$=\"_checkout_shippingAddress_addressFields_country\"]');\n var fieldName = shipCountry.attr('name').replace('shipping', 'billing');\n $('[name=\"' + fieldName + '\"]').val(shipCountry.val()).trigger('change');\n } else {\n var $billingFields = $('input[name*=\"_billingAddress_addressFields_\"]');\n $billingFields.each(function() {\n $(this).val('');\n });\n billingSection.show();\n }\n });\n\n if ($form.find('select[id$=\"_shippingAddress_addressFields_country\"]:enabled').length > 0) {\n var shipCountry = $('select[name$=\"_checkout_shippingAddress_addressFields_country\"]');\n var shipState = $('#dwfrm_checkout_shippingAddress_addressFields_states_state');\n var $sippingAddressSection = $('#shipping-address-container');\n initCountryState($form, shipCountry, shipState, $sippingAddressSection);\n }\n\n if ($form.find('select[id$=\"_billingAddress_addressFields_country\"]:enabled').length > 0) {\n var billCountry = $('select[name$=\"_checkout_billingAddress_addressFields_country\"]');\n var billState = $('#dwfrm_checkout_billingAddress_addressFields_states_state');\n var $billingAddressSection = $('#billing-address-container');\n initCountryState($form, billCountry, billState, $billingAddressSection);\n }\n\n $form.find('.form-row .inputfield-caption').each(function() {\n var $caption = $(this);\n $caption.insertBefore($caption.closest('.form-row').children().first());\n });\n\n $form.find('.form-row .form-field-tooltip').each(function() {\n var $tooltip = $(this);\n $tooltip.insertBefore($tooltip.closest('.form-row').children().first());\n });\n\n var $checkoutForm = $('#dwfrm_checkout');\n var $addGiftCert = $('#add-giftcert');\n var $addCoupon = $('#add-coupon');\n var $couponCode = $('input[name$=\"_couponCode\"]');\n var $payPalBtn = $('.billing-form-row #form-submit');\n\n if (Constants.ENABLE_APMLOOKUP) {\n populateApmMethods();\n }\n\n /*\n Shipping address handler to copy field value into billing address fields\n */\n $form.on('change', '#shipping-address-container select, #shipping-address-container input', function(e) {\n var shippingField = e.target;\n if ($('[name$=\"shippingAddress_useAsBillingAddress\"]').get(0).checked) {\n var deliveryFieldSelector = '#billing-address-container [name$=\"_' + shippingField.name.split('_').pop() + '\"]';\n $(deliveryFieldSelector).val(shippingField.value);\n }\n var shippingWrapers = $(shippingField).parents('.form-row');\n if (shippingWrapers.length && !shippingWrapers.hasClass('profile') && !shippingWrapers.hasClass('phone')) {\n updateShippingMethodList();\n }\n });\n\n $('button[name$=\"checkout_save\"]').on('click', function(e) {\n e.preventDefault();\n\n // indicate progress\n var $placeOrderButton = $('.checkout-place-order-wrapper .billing-form-row .button-fancy-large');\n if ($form.valid()){\n $placeOrderButton.removeClass('disabled');\n progress.show($form);\n $placeOrderButton.addClass('disabled');\n } else {\n // Hide loader image\n progress.hide();\n $placeOrderButton.removeClass('disabled');\n }\n\n if ($('input[name*=\"_expiration_month\"]').val() == '' || $('input[name*=\"_expiration_year\"]').val() == '') {\n $('.expired-field').removeClass('valid').addClass('error');\n }\n\n if ($('input[name*=\"_createAccount_password\"]').val() != '') {\n $('input[name*=\"_createAccount_privacypolicy\"]').addClass('required');\n } else {\n $('input[name*=\"_createAccount_privacypolicy\"]').removeClass('required');\n }\n\n // check of shipping method being selected\n if ($('.shipping-method-row input:checked').length < 1) {\n var messageNoSelectShippingMethod = $('.messageNoSelectShippingMethod');\n messageNoSelectShippingMethod.removeClass('v2-hide');\n progress.hide();\n $placeOrderButton.removeClass('disabled');\n util.scrollBrowser(messageNoSelectShippingMethod.offset().top);\n e.stopPropagation();\n return false;\n }\n\n // collect fields with validation error and reveal them if required\n if (!$form.validate().form()) {\n var $errorFields = $('input.error, select.error, .op-shipping-method-list .error-form:visible');\n $errorFields.trigger('checkout.tagerror');\n\n if ($('input[name*=\"_expiration_month\"]').val() == '' || $('input[name*=\"_expiration_year\"]').val() == '') {\n $('.expired-field').removeClass('valid').addClass('error');\n }\n\n // reveal shipping details area\n if ($errorFields.parents('.form-row.visible.hidden').length > 0) {\n $errorFields.parents('.op-checkout-address-container').find('.op-checkout-enter-address').trigger('click');\n }\n\n // reaveal billing address details, if hidden\n var $errorBillingFields = $errorFields.filter('[name*=\"billingAddress_\"]');\n if ($errorBillingFields.length && !$errorBillingFields.is(':visible')) {\n $errorBillingFields.closest('.billing-section').show()\n }\n\n progress.hide();\n $placeOrderButton.removeClass('disabled');\n\n // scroll to first invalid field, or payment error\n if ($errorFields.length) {\n util.scrollBrowser($errorFields.first().offset().top - 30);\n } else if($('.error-form.payment').length) {\n util.scrollBrowser($('.error-form.payment').offset().top);\n }\n return false;\n }\n\n // Klarna custom code changes\n if ($('.op-payment-method-options').find(':checked').attr('data-sfcc-payment-name') &&\n $('.op-payment-method-options').find(':checked').attr('data-sfcc-payment-name').length &&\n $('.op-payment-method-options').find(':checked').attr('data-sfcc-payment-name') === 'KLARNA_PAYMENTS') {\n klarna.klarnaAuthFlow($('button[name$=\"checkout_save\"]'), $('.op-payment-method-options').find(':checked'));\n return;\n }\n\n if ($('.op-payment-method-options').find(':checked').val() == 'CREDIT_CARD') {\n if (window.SitePreferences.IS3DSV2_ENABLED && !$('#sessionIDEP').val()) {\n var ddcMessageRecieved = false;\n var ddcMessageTimeout = window.SitePreferences.DDC_MESSAGE_TIMEOUT;\n var cardNumber = $('[name$=\"_creditCard_number\"]').val();\n var iframeurl = $('#card-iframe').data('url');\n var ccnum2;\n if (cardNumber) {\n ccnum2 = iframeurl + '?bin=' + cardNumber.toString().substring(0, 9);\n } else {\n ccnum2 = iframeurl;\n }\n $('#card-iframe').attr('src', ccnum2);\n window.addEventListener('message', function (event) { // eslint-disable-line\n window.console.log('message recieved');\n var data = JSON.parse(event.data);\n var dataSessionId = data.SessionId;\n $('#sessionIDEP').val(dataSessionId);\n // prevent duplicate submits\n if (!ddcMessageRecieved) {\n ddcMessageRecieved = true;\n $('button[name$=\"checkout_save\"]').click();\n }\n \n }, false);\n if (ddcMessageTimeout > 0) {\n setTimeout(function() {\n if(!ddcMessageRecieved) {\n // prevent duplicate submits\n ddcMessageRecieved = true;\n $('#dwfrm_checkout').submit();\n }\n }, ddcMessageTimeout);\n }\n return;\n }\n var $creditCard = $('[data-method=\"CREDIT_CARD\"]');\n $creditCard.find('[name$=\"_encryptedData\"]').val('');\n var data = {\n cvc: $creditCard.find('input[name*=\"_cvn\"]').val(),\n cardHolderName: $creditCard.find('input[name$=\"creditCard_owner\"]').val(),\n cardNumber: $creditCard.find('input[name*=\"_creditCard_number\"]').val(),\n expiryMonth: $creditCard.find('[name$=\"_month\"]').val() < 10 ? '0' + $creditCard.find('[name$=\"_month\"]').val() : $creditCard.find('[name$=\"_month\"]').val(),\n expiryYear: $creditCard.find('[name$=\"_year\"]').val()\n }\n if (window.SitePreferences.WP_ENABLE_CLIENT_SIDE_ENCRYPTION) {\n window.Worldpay.setPublicKey(window.SitePreferences.WP_CSE_PUBLIC_KEY);\n var encryptedData = window.Worldpay.encrypt(data, function(e) {\n window.console.log('Worldpay Client Side Encryption validation error: ' + e);\n });\n if (encryptedData) {\n $creditCard.find('[name$=\"_encryptedData\"]').val(encryptedData);\n }\n }\n }\n\n //$checkoutForm.setAttrib('action', 'sale');\n if (!$('#dwfrm_checkout').hasClass('QAS-enabled')) {\n $('#dwfrm_checkout').submit();\n }\n //$placeOrderButton.attr('disabled', 'disabled');\n });\n\n // Remove Giftcard field validation and input value\n domStrings.giftCertCode.on('click', function() {\n removeValidation($(this), domStrings.codeError);\n });\n domStrings.giftCertPin.on('click', function() {\n removeValidation($(this), domStrings.pinError);\n });\n\n // css styling\n if ($(window).width() >= 767) { //desktop\n if ($checkoutForm.find('.giftcertCode-error').text().length < 0) {\n $('.img-wrapper-pin').css('top', '128px');\n }\n } else { //mobile\n if ($checkoutForm.find('.giftcertCode-error').text().length < 0) {\n $('.img-wrapper-pin').css('top', '208px');\n }\n }\n\n $addGiftCert.on('click', function(e) {\n e.preventDefault();\n domStrings.codeError.html('');\n domStrings.pinError.html('');\n domStrings.isError = false;\n domStrings.error.html('');\n // domStrings.balance.html('');\n\n if (domStrings.giftCertCode.val().length === 0) {\n domStrings.codeError.html(Resources.GIFT_CERT_MISSING);\n restyleInputField(domStrings.codeError, domStrings.giftcertCodeInputId);\n $(domStrings.codeError).trigger('checkout.tagerror');\n domStrings.isError = true;\n }\n\n if (domStrings.giftCertPin.val().length === 0) {\n domStrings.pinError.html(Resources.GIFT_CERT_PIN_MISSING);\n restyleInputField(domStrings.pinError, domStrings.giftcertPinInputId);\n $(domStrings.pinError).trigger('checkout.tagerror');\n domStrings.isError = true;\n }\n\n if (!domStrings.isError) {\n if ($addGiftCert.hasClass('g-recaptcha-custom')) {\n window.grecaptcha.execute(window.SitePreferences.RECAPTCHA_SITE_KEY, {action: 'submit'}).then(applyGiftCardCall);\n } else {\n applyGiftCardCall();\n }\n }\n });\n\n $addCoupon.on('click', function(e) {\n e.preventDefault();\n var $error = $checkoutForm.find('.coupon-error'),\n $success = $checkoutForm.find('.redemption.coupon'),\n code = $couponCode.val();\n if (code.length === 0) {\n $error.html(Resources.COUPON_CODE_MISSING);\n return;\n }\n\n var url = util.appendParamsToUrl(Urls.addCoupon, {\n couponCode: code,\n format: 'ajax'\n });\n $.getJSON(url, function(data) {\n var fail = false;\n var msg = '';\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n if (fail) {\n $error.html(msg);\n return;\n }\n\n //basket check for displaying the payment section, if the adjusted total of the basket is 0 after applying the coupon\n //this will force a page refresh to display the coupon message based on a parameter message\n if (data.success && data.baskettotal === 0) {\n window.location.assign(Urls.checkout);\n }\n\n if (data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n var $sucessContainer = $success.find('span.success');\n if ($sucessContainer.length > 0) {\n $sucessContainer.html(msg);\n } else {\n var successMsg = '' + msg + '';\n $success.html(successMsg);\n }\n updateGiftCardInfo();\n updatePaymentMethodsSection();\n stickySummaryBlock();\n }\n });\n });\n\n // trigger events on enter\n $couponCode.on('keydown', function(e) {\n if (e.which === 13) {\n e.preventDefault();\n $addCoupon.click();\n }\n });\n domStrings.giftCertCode.on('keydown', function(e) {\n if (e.which === 13) {\n e.preventDefault();\n $addGiftCert.click();\n }\n });\n\n if ($('[name*=\"_checkout_shippingAddress_addressFields\"]').length > 0 || $('[name*=\"_checkout_billingAddress_addressFields\"]').length > 0) {\n initUpdateForm();\n }\n\n $payPalBtn.on('click', function(e) {\n e.preventDefault();\n if ($('.op-payment-method-options').find(':checked').val() === 'PayPal') {\n $('.paypal-save-agreement').next('.error').remove();\n if($('#paypal-sale-message-label').attr('currentsiteid') != Resources.CLAIRES_NA && $('#paypal-sale-message-label').attr('currentsiteid') != Resources.ICING_NA){\n if (!$('input[id$=_paypal_paypalSaveAgreement]').is(':checked')) {\n $('.paypal-save-agreement').after('' + Resources.VALIDATE_REQUIRED + '');\n }\n }\n }\n });\n\n clickcollect.init();\n giftMessageBox();\n\n updateShippingMethodList();\n updatePostcodePlaceholder('select[name$=\"_shippingAddress_addressFields_country\"]:enabled');\n updatePostcodePlaceholder('select[name$=\"_billingAddress_addressFields_country\"]:enabled');\n initPaymentMethods();\n initGiftCardCheckBalance();\n initRemoveGiftCard();\n initBlockFolding();\n createAccountFolding();\n stickySummaryBlock();\n itemSummaryDisplay();\n // truncateProductTitle();\n\n if ($('select[id$=\"_shippingAddress_addressFields_country\"]:enabled').parents('.country-submitted').length == 0) {\n $.ajax({\n url: Urls.getCountryCode,\n success: function(response) {\n var countryCode = response.countryCode;\n var shipCountryDropDown = $('select[id$=\"_shippingAddress_addressFields_country\"]:enabled');\n var shipCountryDropDownOpt = $('select[id$=\"_shippingAddress_addressFields_country\"]:enabled option');\n shipCountryDropDownOpt.each(function() {\n if (this.value === countryCode) {\n if ($('input[name$=\"_shippingAddress_useAsBillingAddress\"]').prop('checked')) {\n $('select[id$=\"_billingAddress_addressFields_country\"]:enabled').val(countryCode).trigger('change');\n }\n shipCountryDropDown.val(countryCode).trigger('change');\n }\n });\n }\n });\n // if we have only one country we should save it to basket to pass this data into klarna\n } else if ($('select[id$=\"_shippingAddress_addressFields_country\"]:enabled option').length == 1) {\n var countryCode = $('select[id$=\"_shippingAddress_addressFields_country\"]:enabled option')[0].value;\n if ($('input[name$=\"_shippingAddress_useAsBillingAddress\"]').prop('checked')) {\n $('select[id$=\"_billingAddress_addressFields_country\"]:enabled').val(countryCode).trigger('change');\n }\n $('select[id$=\"_shippingAddress_addressFields_country\"]:enabled').val(countryCode).trigger('change');\n }\n\n if ($('#email-address-section #dwfrm_checkout_emailAddress').length > 0){\n $('#email-address-section #dwfrm_checkout_emailAddress').addClass('email');\n }\n\n $('#email-address-section').off().on('click', '.op-checkout-edit-email', function(e) {\n e.preventDefault();\n var emailSectionID = $('#email-address-section');\n var payload = {}\n if($('#dwfrm_checkout_subscribe').length){\n \tpayload.subscribe = $('#dwfrm_checkout_subscribe').prop('checked')\n }\n $.ajax({\n url: Urls.editPreferredEmail,\n data: payload,\n success: function(response) {\n emailSectionID.html(response);\n $('#email-address-section #dwfrm_checkout_emailAddress').addClass('email');\n var $requiredFields = $(emailSectionID).find('.required:input:enabled');\n var callback = function() {\n $requiredFields.map(function () {\n return $(this).val();\n });\n };\n\n formPrepare.init({\n continueSelector: '[name$=\"checkout_save\"]',\n formSelector: '[id$=\"_checkout\"]',\n skipDisabled: true,\n callback: callback\n });\n callback();\n }\n });\n });\n\n $('#email-address-section').on('blur', 'input', function(e) {\n e.stopPropagation();\n var $emailInput = $('#email-address-section #dwfrm_checkout_emailAddress');\n if ($emailInput.length && $emailInput.val() && !$emailInput.hasClass('error')) {\n $emailInput.attr('disabled', 'disabled');\n }\n if ($emailInput.length && $emailInput.val() && !$emailInput.hasClass('error') && (saveEmailCounter<1)){\n e.preventDefault();\n saveEmailCounter ++;\n var emailSectionID = $('#email-address-section');\n var formName = $('#email-address-section #dwfrm_checkout_emailAddress').attr('name');\n var formValue = $('#email-address-section #dwfrm_checkout_emailAddress').val();\n var payload = {formName: formName,formValue: formValue}\n if($('#dwfrm_checkout_subscribe').length){\n \tpayload.subscribe = $('#dwfrm_checkout_subscribe').prop('checked')\n }\n window.console.log(formName, formValue);\n $.ajax({\n url: Urls.saveEmailAddress,\n method: 'POST',\n data: payload,\n success: function(response) {\n emailSectionID.html(response);\n saveEmailCounter --;\n }\n });\n }\n }).on('keydown', 'input', function (e) {\n if (e.which == 13) {\n e.preventDefault();\n if (!$(this).hasClass('error')) {\n $(this).trigger('blur');\n }\n }\n });\n\n // coupon code apply ajax\n $('#cart-v2-add-coupon').on('click', function(e) {\n e.preventDefault();\n var $applyPromoCodeBtn = $(this);\n var $promoCodeFieild = $('input[name$=\"_couponCode\"]');\n if ($promoCodeFieild.val()) {\n $applyPromoCodeBtn.prop('disabled', true);\n } else {\n $applyPromoCodeBtn.prop('disabled', false);\n }\n checkCoupon($promoCodeFieild);\n });\n \n //remove disable class when click on promo code option \n $('#cart-v2-add-coupon').on('click', function(e) {\n if ($('#form-submit').hasClass('disabled-button')) {\n $('#form-submit').removeClass('disabled-button');\n }\n if ($('#form-submit-EU').hasClass('disabled-button')) {\n $('#form-submit-EU').removeClass('disabled-button');\n }\n });\n // handle reward apply ajax\n $('#checkout-rewards-section').on('click', '#cart-v2-apply-reward', function(e) {\n e.preventDefault();\n var url = $(this).data('href');\n var $applyBtn = $(this);\n var $updateRewardsBtn = $('#cart-v2-show-all-rewards');\n var $rewardItems = $('.show-rewards-container .reward-item');\n var rewardsMarkedForRedemption = [];\n var allUserRewards = [];\n $rewardItems.find('input[type=\"checkbox\"]').each(function() {\n var $rewardCheckbox = $(this);\n if ($rewardCheckbox.prop('checked')) {\n rewardsMarkedForRedemption.push($rewardCheckbox.prop('id'));\n }\n allUserRewards.push($rewardCheckbox.prop('id'));\n });\n\n if (rewardsMarkedForRedemption.length > 0) {\n $applyBtn.prop('disabled', true);\n }\n\n var data = {\n allUserRewards: allUserRewards,\n rewardsMarkedForRedemption: rewardsMarkedForRedemption\n }\n $.ajax({\n type: 'POST',\n contentType: 'application/json',\n dataType: 'json',\n url: url,\n data: JSON.stringify(data),\n success: function (response) {\n var $rewardItems = $('.show-rewards-container .reward-item');\n var $error = $('#reward-redemption-error');\n var $errorMessage = $error.find('span.v2-medium').text('');\n $error.hide();\n // update error messages\n for (var reward in response.result) {\n var result = response.result[reward]\n if (!result.success) {\n $error.find('span.v2-medium').text($errorMessage.text() + '\\n' + result.message);\n $error.show();\n $applyBtn.prop('disabled', false);\n }\n }\n\n if (response.activeRewardsData.hasAppliedRewards) {\n if (response.activeRewardsData.hasAvailableRewardsForApplication) {\n $('#apply-another-reward').removeClass('visually-hidden');\n } else {\n $('#apply-another-reward').addClass('visually-hidden');\n }\n $updateRewardsBtn.removeClass('visually-hidden');\n $updateRewardsBtn.prop('disabled', false)\n $('#apply-rewards-caption').addClass('visually-hidden');\n $applyBtn.addClass('visually-hidden');\n $('#update-rewards-caption').removeClass('visually-hidden');\n\n $rewardItems.each(function() {\n $(this).addClass('visually-hidden');\n $(this).find('input[type=\"checkbox\"]').prop('checked', false);\n });\n var rewardAdded = false;\n response.activeRewardsData.appliedRewards.forEach(function(reward){\n var result = response.result[reward]\n if (result.success) {\n $rewardItems.find('input[id=' + reward + ']').prop('checked', true);\n $rewardItems.find('input[id=' + reward + ']').parent('.reward-item').removeClass('visually-hidden');\n rewardAdded = true;\n } else {\n $error.find('span.v2-medium').text(result.message);\n $error.show();\n }\n });\n\n if (rewardAdded) {\n $('.rewards-list-container').css('pointer-events', 'none');\n }\n }\n checkoutCouponsRefresh();\n },\n error: function () {\n $applyBtn.prop('disabled', false)\n }\n });\n });\n\n $('#checkout-rewards-section').on('click', '#cart-v2-show-all-rewards', function(e) {\n e.preventDefault();\n var $rewardItems = $('.show-rewards-container .reward-item');\n var $applyBtn = $('#cart-v2-apply-reward');\n var $updateRewardsBtn = $(this);\n $rewardItems.each(function() {\n $(this).removeClass('visually-hidden');\n });\n $('.rewards-list-container').css('pointer-events', 'auto');\n $('#apply-rewards-caption').removeClass('visually-hidden');\n $('#update-rewards-caption').addClass('visually-hidden');\n $('#apply-another-reward').addClass('visually-hidden');\n $applyBtn.removeClass('visually-hidden');\n $applyBtn.prop('disabled', false);\n $updateRewardsBtn.addClass('visually-hidden');\n $updateRewardsBtn.prop('disabled', true);\n });\n\n window.setTimeout(function(){\n if ($('#shipping-address-section').length > 0) {\n var shippingURL = util.appendParamsToUrl(Urls.handleSavedAddress, {'source': 'shipping', 'format': 'ajax'});\n $.ajax({\n url: shippingURL,\n success: function(response) {\n if (!response.isShippingAddressFullFilled) {\n $('#edit-shipping-address').trigger('click');\n }\n }\n });\n $('select.shipping-address-list').on('change', function() {\n var shippingURL = util.appendParamsToUrl(Urls.handleSavedAddress, {'source': 'shipping', 'format': 'ajax'});\n $.ajax({\n url: shippingURL,\n success: function(response) {\n if (!response.isShippingAddressFullFilled) {\n $('#edit-shipping-address').trigger('click');\n }\n }\n });\n });\n }\n\n if ($('#billing-address-section').length > 0) {\n var billingURL = util.appendParamsToUrl(Urls.handleSavedAddress, {'source': 'billing', 'format': 'ajax'});\n $.ajax({\n url: billingURL,\n success: function(response) {\n if (!response.isShippingAddressFullFilled) {\n $('#edit-billing-address').trigger('click');\n }\n }\n });\n $('select.billing-address-list').on('change', function() {\n var billingURL = util.appendParamsToUrl(Urls.handleSavedAddress, {'source': 'billing', 'format': 'ajax'});\n $.ajax({\n url: billingURL,\n success: function(response) {\n if (!response.isShippingAddressFullFilled) {\n $('#edit-billing-address').trigger('click');\n }\n }\n });\n });\n }\n\n if ($('#checkout-selected-store').length > 0) {\n var storePostalCode = $('.hidden-store-postalCode input').val();\n $('.valid-store-checkout').val(storePostalCode);\n }\n\n }, 500);\n if (!$('.op-checkout-address-container .add-address2').length) {\n $('.op-checkout-address-container .address2').before('' + Resources.ADD_ADDRESS2 + '');\n }\n\n $(document).on('click', '.add-address2', function(e) {\n e.preventDefault();\n $(this).toggleClass('active');\n });\n\n $(document).on('keyup change', '.full-name', function() {\n fullNameSplit($(this));\n });\n\n initChangeSaveCard();\n initRemoveCoupon();\n initDataLayerHandleStep();\n initLegalRequirements();\n // cart.init();\n\n $(document).on('keyup', '#expired-field', function(event) {\n expiryMask();\n });\n\n $(document).on('focusout', '#expired-field', function(){\n $(this).removeClass('error');\n $(this).parent('.form-row').find('.error').hide();\n if (validateExpDate($(this).val())) {\n splitDate($(this), $(this).val());\n $(this).parent('.expired').find('.error-message').hide();\n } else {\n $('input[name$=\"_expiration_month\"]').val('');\n $('input[name$=\"_expiration_year\"]').val('');\n $(this).addClass('error');\n $(this).parent('.expired').find('.error-message').show();\n }\n });\n\n $(document).on('change', 'input[name$=\"_expiration_month\"], input[name$=\"_expiration_year\"]', function(){\n var month = ($('input[name$=\"_expiration_month\"]').val().length == 1) ? '0' + $('input[name$=\"_expiration_month\"]').val() : $('input[name$=\"_expiration_month\"]').val();\n var year = $('input[name$=\"_expiration_year\"]').val().slice(2);\n $('#expired-field').val(month + '/' + year);\n });\n\n $('body').on('change', '.creditCardList', function() {\n var cardUUID = $(this).val();\n if (!cardUUID) {\n var creditCardIndex = $('.creditCardList option:selected').index();\n if (creditCardIndex == '0') {\n if (SitePreferences.WP_DISABLE_CVV === true) {\n var paymentDetailsSection = $('.op-payment-details-section');\n paymentDetailsSection.find('div.cvn').show();\n paymentDetailsSection.find('div.cvn').addClass('required');\n paymentDetailsSection.find('div.cvn .input-text').addClass('required');\n }\n\n }\n return;\n }\n\n // remove server side error\n $('.required.error').removeClass('error');\n $('.error-message').remove();\n var selectedPaymentMethod = $('.op-payment-method-options').find(':checked').val();\n if (selectedPaymentMethod == 'WorldPay') {\n if (cardUUID != '') {\n $('.card-block').show();\n } else {\n $('.card-block').hide();\n $('.save-card-block').show();\n }\n }\n\n var $creditCardSection = $('[data-method=\"WorldPay\"]');\n $creditCardSection.find('[name$=\"_cards\"]').val('');\n var $form = $('.address');\n var countryCode = $('select[id$=\"_country\"]', $form).val();\n if (selectedPaymentMethod == 'WorldPay' && countryCode == 'BR') {\n $('#Payment_Brazil').show();\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n } else if (selectedPaymentMethod == 'CREDIT_CARD' && countryCode == 'BR') {\n $creditCardSection = $('[data-method=\"CREDIT_CARD\"]');\n $creditCardSection.find('input[name$=\"creditCard_cpf\"]').addClass('required');\n $creditCardSection.find('input[name$=\"creditCard_installments\"]').addClass('required');\n }\n });\n\n // select prefered credit card from list\n $('body').on('change', '#preferedCreditCardList', function(e) {\n e.preventDefault();\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('input[name$=\"creditCard_owner\"]').val('');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').val('');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n $('#Payment_Brazil').hide();\n });\n\n $('body').on('click', '#clearpaymentform', function(e) {\n e.preventDefault();\n var $creditCard = $('[data-method=\"WorldPay\"]');\n $creditCard.find('input,select').val('');\n $creditCard.find('input[name$=\"creditCard_owner\"]').removeClass('required');\n $creditCard.find('select[name$=\"_type\"]').removeClass('required');\n $creditCard.find('input[name*=\"_creditCard_number\"]').removeClass('required');\n $creditCard.find('[name$=\"_month\"]').removeClass('required');\n $creditCard.find('[name$=\"_year\"]').removeClass('required');\n $creditCard.find('input[name$=\"_cvn\"]').removeClass('required');\n $('.creditCardList').val('');\n $creditCard.find('input[name$=\"_saveCard\"]').val('true');\n $('.card-block').hide();\n $('.save-card-block').show();\n $('#Payment_Brazil').hide();\n });\n\n $('body').on('change', 'select[name$=\"_creditCardList\"]', function(e) {\n e.preventDefault();\n var selected = $(this).children(':selected').first();\n var UUID = $(selected).val();\n\n // handle case when selected payment card token expired\n // trigger different view\n var viewType = 'summary';\n if (selected.data('token-expired')) {\n viewType = 'edit';\n }\n\n getCreditCardDetails(viewType, UUID);\n });\n};\n\nfunction expiryMask() {\n var inputChar = String.fromCharCode(event.keyCode);\n var code = event.keyCode;\n var allowedKeys = [8];\n if (allowedKeys.indexOf(code) !== -1) {\n return;\n }\n\n event.target.value = event.target.value.replace(\n /^([1-9]\\/|[2-9])$/g, '0$1/'\n ).replace(\n /^(0[1-9]|1[0-2])$/g, '$1/'\n ).replace(\n /^([0-1])([3-9])$/g, '0$1/$2'\n ).replace(\n /^(0?[1-9]|1[0-2])([0-9]{2})$/g, '$1/$2'\n ).replace(\n /^([0]+)\\/|[0]+$/g, '0'\n ).replace(\n /[^\\d\\/]|^[\\/]*$/g, ''\n ).replace(\n /\\/\\//g, '/'\n );\n\n if (event.target.value.indexOf('/') == 1 && event.target.value.length == 5) {\n event.target.value = event.target.value.replace('/', '');\n event.target.value = event.target.value.substring(0, 2) + '/' + event.target.value.substring(2);\n }\n}\n\nfunction splitDate($domobj, value) {\n var regExp = /(1[0-2]|0[1-9]|\\d)\\/(20\\d{2}|19\\d{2}|0(?!0)\\d|[1-9]\\d)/;\n var matches = regExp.exec(value);\n var expirationMonth = $('input[name$=\"_expiration_month\"]');\n var expirationYear = $('input[name$=\"_expiration_year\"]');\n expirationMonth.val(matches[1].replace(/^0/, ''));\n expirationYear.val(\"20\" + matches[2]);\n}\n\nfunction validateExpDate(value) {\n var regExp = /(1[0-2]|0[1-9]|\\d)\\/(20\\d{2}|19\\d{2}|0(?!0)\\d|[1-9]\\d)/;\n var matches = regExp.exec(value);\n var minYear = new Date().getFullYear();\n var minMonth = new Date().getMonth();\n var maxYear = minYear + 15;\n\n if (!matches) {\n return false;\n } else {\n var month = matches[1];\n var year = matches[2];\n var monthValue = matches['input'].substr(0, matches['input'].indexOf('/'));\n\n if (month.length == 1) {\n expiryMask();\n }\n\n if (parseInt(\"20\" + year) < minYear || parseInt(\"20\" + year) > maxYear) {\n return false;\n }\n\n if (parseInt(month) <= minMonth && parseInt(\"20\" + year) == minYear) {\n return false;\n }\n\n if (parseInt(monthValue) > 12) {\n return false;\n }\n }\n\n return true;\n}\n\n/*\n* Function to disable order plasement button if customer didn't check box with legal statement\n*/\nfunction initLegalRequirements() {\n var handleOrderButton = $('.checkout-place-order-wrapper .billing-form-row .button-fancy-large');\n var legalRequirements = $('input[name$=\"_creditCard_terms\"]');\n if (legalRequirements.length > 0 && !legalRequirements.prop('checked')){\n handleOrderButton.prop('disabled', true);\n }\n\n legalRequirements.on('change', function() {\n if (legalRequirements.prop('checked')){\n handleOrderButton.prop('disabled', false);\n } else {\n handleOrderButton.prop('disabled', true);\n }\n });\n}\n\nfunction initChangeSaveCard () {\n if ($('input[name$=\"_saveCard\"]').is(':checked')) {\n $('input[name$=\"_saveCard\"]').val(true);\n } else {\n $('input[name$=\"_saveCard\"]').val(false);\n }\n\n $('input[name$=\"_saveCard\"]').change(function() {\n if (this.checked) {\n $('input[name$=\"_saveCard\"]').val(true);\n } else {\n $('input[name$=\"_saveCard\"]').val(false);\n }\n });\n}\n\nfunction initDataLayerHandleStep () {\n var checkoutElement;\n var busy = false;\n var scrollCounter = 0;\n for (var el in window.dataLayer) {\n if (window.dataLayer[el].event == 'checkout') {\n checkoutElement = window.dataLayer[el];\n checkoutElement.ecommerce.checkout.actionField.step = '3';\n }\n }\n\n var scrollHandler = function() {\n scrollCounter++;\n if (busy || scrollCounter % 20 != 0) {\n return;\n }\n busy = true;\n var docViewTop = $(window).scrollTop();\n var docViewBottom = docViewTop + $(window).height();\n var elemCenter = $('#checkout-payment-methods-section').offset().top + $('#checkout-payment-methods-section').height() / 2;\n var newCheckoutElement = {};\n var stepValue;\n\n if ((elemCenter <= docViewBottom) && (elemCenter >= docViewTop) && checkoutElement.ecommerce.checkout.actionField.step == '3') {\n stepValue = '4';\n }\n\n if (stepValue) {\n $.ajax({\n url: Urls.getCheckoutDatalayerData,\n success: function(response) {\n document.removeEventListener('scroll', scrollHandler, false);\n newCheckoutElement = response;\n newCheckoutElement.ecommerce.checkout.actionField.step = stepValue;\n window.dataLayer.push(newCheckoutElement);\n checkoutElement = newCheckoutElement;\n }\n });\n }\n busy = false;\n };\n document.addEventListener('scroll', scrollHandler, false);\n}\n\nfunction initUpdateForm () {\n $('form.checkout-shipping').on('change', '[name*=\"_checkout_shippingAddress_addressFields\"], [name*=\"_checkout_billingAddress_addressFields\"]', function () {\n var useForBilling = false;\n var isCountryChangeEvent = this.name.indexOf('_addressFields_country') > -1;\n if (this.name.indexOf('shipping') > -1 && $('input[name$=\"_shippingAddress_useAsBillingAddress\"]').prop('checked')) {\n useForBilling = true;\n }\n $.ajax({\n url: Urls.saveAddressField,\n data: {\n formUseForBilling: useForBilling,\n formName: this.name,\n formValue: this.value\n },\n success: function() {\n if (isCountryChangeEvent) {\n updatePaymentMethodsSection();\n }\n }\n });\n });\n}\n\nfunction initRemoveCoupon () {\n $('.cart-v2-coupon-remove').on('click', function () {\n var couponId = $(this).data('couponid');\n var url = util.appendParamsToUrl(Urls.checkoutCouponsRefresh, {\n couponId: couponId,\n format: 'ajax'\n });\n ajax.load({\n url: url,\n target: $('#one-page-checkou-coupons'),\n callback: function () {\n loadShippingMethods();\n orderSummaryRefresh();\n initRemoveCoupon();\n }\n });\n });\n}\n\nfunction checkCoupon($couponCode) {\n var code = $couponCode.val();\n if (code.length == 0) {\n $('#no-active-promotion span.v2-medium').html(Resources.COUPON_CODE_MISSING);\n $('#no-active-promotion').show();\n $('#no-active-promotion div.coupon-apply').hide();\n $couponCode.trigger('checkout.tagerror');\n\n return;\n }\n\n var url = util.appendParamsToUrl(Urls.checkCoupon, {\n couponCode: code,\n format: 'ajax'\n });\n\n $.getJSON(url, function (data) {\n if (data.status == 'NO_CART') {\n window.location.reload(true);\n }\n\n var fail = false;\n var msg = '';\n\n if (!data) {\n msg = Resources.BAD_RESPONSE;\n fail = true;\n } else if (!data.success) {\n msg = data.message.split('<').join('<').split('>').join('>');\n fail = true;\n }\n\n if (fail) {\n $('#cart-v2-add-coupon').prop('disabled', false);\n $('#no-active-promotion span.v2-medium').html(msg);\n $('#no-active-promotion').show();\n if (data.guestLoginUrl && !$('.linkToLogin').length) {\n var linkToLoginHtml = 'Login';\n $('#no-active-promotion').append(linkToLoginHtml);\n }\n $couponCode.trigger('checkout.tagerror');\n\n if (data.status == 'SHOW_NOTIFICATION') {\n $('#dropdown-coupon-apply').data('couponcode', code);\n $('#no-active-promotion div.v2-medium').hide();\n $('#no-active-promotion div.coupon-apply').show();\n } else {\n $('#no-active-promotion div.coupon-apply').hide();\n }\n\n $('#dropdown-coupon-cancel').on('click', function () {\n $('#no-active-promotion').hide();\n });\n\n $('#dropdown-coupon-apply').on('click', function () {\n var code = $(this).data('couponcode');\n var url = util.appendParamsToUrl(Urls.addCoupon, {\n couponCode: code,\n format: 'ajax',\n needRemoveOldCoupons: 'true'\n });\n\n ajax.load({\n url: url,\n callback: function (data) {\n data = JSON.parse(data);\n\n if (data.status == 'APPLIED') {\n $('#no-active-promotion').hide();\n checkoutCouponsRefresh();\n }\n }\n });\n });\n\n return;\n }\n\n if (data.success) {\n $('#cart-v2-add-coupon').prop('disabled', false);\n checkoutCouponsRefresh();\n }\n });\n}\n\nfunction checkoutCouponsRefresh() {\n var url = util.appendParamsToUrl(Urls.checkoutCouponsRefresh, {\n format: 'ajax'\n });\n ajax.load({\n url: url,\n target: $('#one-page-checkou-coupons'),\n callback: function () {\n orderSummaryRefresh();\n initRemoveCoupon();\n loadShippingMethods();\n $('input[name$=\"_couponCode\"]').val('');\n $('#no-active-promotion').hide();\n }\n });\n}\n\nfunction fullNameSplit(fullNameInput) {\n var firstNameInput = fullNameInput.parents('.op-checkout-address-container').find('input[name$=\"_firstName\"]')\n var lastNameInput = fullNameInput.parents('.op-checkout-address-container').find('input[name$=\"_lastName\"]')\n var fullName = fullNameInput.val();\n var firstName = fullName.substring(0, fullName.indexOf(' '));\n var lastName = fullName.substring(fullName.indexOf(' ') + 1);\n\n firstNameInput.val(firstName).change();\n lastNameInput.val(lastName).change();\n}\n\nif ($('.error-form.payment').length > 0){\n $(window).scrollTop($('.error-form.payment').offset().top);\n}\n\nexports.updateShippingMethodList = updateShippingMethodList; // this gets called by address.js\nexports.initCountryState = initCountryState;\nexports.lookupHandle = lookupHandle;\nexports.orderSummaryRefresh = orderSummaryRefresh;\nexports.itemSummaryDisplay = itemSummaryDisplay; // this gets called by login.js\n// exports.truncateProductTitle = truncateProductTitle; // this gets called by login.js\n","'use strict';\n\nvar checkout = require('./checkout');\nvar util = require('org/util');\n\nfunction stickySummaryBlock() {\n if (util.isDesktopViewport() && $('.order-summary-wrapper').outerHeight() < $('.checkout-box-col1').outerHeight()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').trigger('sticky_kit:detach');\n $('.order-summary-wrapper').stick_in_parent();\n }\n\n // updating sticky sidebar onResize (recalculating position and events) and destroy on mobile VP\n util.smartResize(function() {\n if (!util.isDesktopViewport()) {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').trigger('sticky_kit:detach');\n } else {\n $(document.body).trigger('sticky_kit:recalc');\n $('.order-summary-wrapper').stick_in_parent();\n }\n })\n}\n\nfunction checkFormComplete() {\n $('.icing-checkout-login-wrapper input').keyup(function() {\n var form = $(this).parents('form');\n var empty = false;\n\n $(form).find('input').each(function() {\n if ($(this).val() == '') {\n empty = true;\n }\n });\n\n setSubmitState(empty, form);\n });\n}\n\nfunction setSubmitState(status, form) {\n if (status) {\n $(form).find('button[type=\"submit\"]').attr('disabled', 'disabled');\n } else {\n $(form).find('button[type=\"submit\"]').removeAttr('disabled');\n }\n}\n\nexports.init = function init() {\n checkout.itemSummaryDisplay();\n stickySummaryBlock();\n checkFormComplete();\n setSubmitState();\n};\n","'use strict';\n\nvar _ = require('lodash');\n\nvar $form, $continue, $requiredInputs, validator;\n\nvar hasEmptyRequired = function () {\n // filter out only the visible fields\n var requiredValues = $requiredInputs.filter(':visible').map(function () {\n return $(this).val();\n });\n return _(requiredValues).contains('');\n};\n\nvar validateForm = function () {\n // only validate form when all required fields are filled to avoid\n // throwing errors on empty form\n if (!validator) {\n return;\n }\n if (!hasEmptyRequired()) {\n if (validator.form()) {\n $continue.removeAttr('disabled');\n }\n } else {\n $continue.attr('disabled', 'disabled');\n }\n};\n\nvar validateEl = function () {\n if ($(this).val() === '') {\n $continue.attr('disabled', 'disabled');\n } else {\n // enable continue button on last required field that is valid\n // only validate single field\n if (validator.element(this) && !hasEmptyRequired()) {\n $continue.removeAttr('disabled');\n if ($form.attr('id') === 'dwfrm_singleshipping_shippingAddress') {\n var $checkoutStoreAddress = $form.find('#checkout-selected-store');\n if ($checkoutStoreAddress.length > 0) {\n validateCheckoutSelectedStoreAndEmail();\n }\n }\n } else {\n $continue.attr('disabled', 'disabled');\n }\n }\n};\n\nvar validateCheckoutSelectedStoreAndEmail = function() {\n var $emailInput = $('#dwfrm_singleshipping_shippingAddress_addressFields_email');\n var storeId = $('.selected-store-address').data('store-id');\n var isEmailValid = false;\n var isStoreSelected = false;\n\n if (storeId !== undefined) {\n isStoreSelected = true;\n }\n if (!$emailInput.hasClass('error')) {\n isEmailValid = true;\n }\n\n if (isEmailValid && isStoreSelected) {\n $continue.removeAttr('disabled');\n } else {\n $continue.attr('disabled', 'disabled');\n }\n};\n\nvar validateCardNumber = function(e) {\n if (e.which >= 48 && e.which <= 57) { // if e.which is a number return true\n return true;\n }\n return false;\n}\n\nvar init = function (opts) {\n if (!opts.formSelector || !opts.continueSelector) {\n throw new Error('Missing form and continue action selectors.');\n }\n $form = $(opts.formSelector);\n $continue = $(opts.continueSelector);\n validator = $form.validate();\n $requiredInputs = (opts.skipDisabled) ? $('.required', $form).find(':input:enabled') : $('.required', $form).find(':input');\n validateForm();\n // start listening\n $requiredInputs.on('change', validateEl);\n $requiredInputs.filter('input').on('keyup', _.debounce(validateEl, 200));\n\n if ($form.attr('id') === 'dwfrm_billing') {\n var $cardNumber = $('#dwfrm_billing_paymentMethods_creditCard_number');\n if ($cardNumber.length > 0) {\n $cardNumber.on('change keypress paste', validateCardNumber);\n }\n }\n\n if ($form.attr('id') === 'dwfrm_singleshipping_shippingAddress') {\n var $checkoutStoreAddress = $form.find('#checkout-selected-store');\n if ($checkoutStoreAddress.length > 0) {\n validateCheckoutSelectedStoreAndEmail();\n var $checkoutEmailInput = $('#dwfrm_singleshipping_shippingAddress_addressFields_email');\n $checkoutEmailInput.on('change', validateCheckoutSelectedStoreAndEmail);\n $checkoutEmailInput.on('keyup', _.debounce(validateCheckoutSelectedStoreAndEmail, 200));\n $checkoutStoreAddress.bind('DOMSubtreeModified', validateCheckoutSelectedStoreAndEmail);\n }\n }\n};\n\nexports.init = init;\nexports.validateForm = validateForm;\nexports.validateEl = validateEl;\n","'use strict';\n\nvar _ = require('lodash');\n// var modulValidator = require('../../validator');\n\nvar $form, /*$continue,*/ $requiredInputs, validator, $passwordsInputs, callbackFunction, $shippingMethodList;\n\nvar hasEmptyRequired = function () {\n // checked all required fields\n var requiredValues = $requiredInputs.map(function () {\n if ($(this)[0].type == 'checkbox' && !$(this)[0].checked) {\n return '';\n }\n return $(this).val();\n });\n return Object.values(requiredValues).indexOf('') > -1;\n};\n\nvar hasFilledPassword = function () {\n var passwordValues = $passwordsInputs.map(function () {\n return $(this).val();\n });\n return !_(passwordValues).contains('');\n};\n\nvar comparedEachOthers = function () {\n var passwordValue = $passwordsInputs[0].value;\n var compareResults = $passwordsInputs.map(function () {\n return $(this).val() === passwordValue;\n });\n return !_(compareResults).contains(false);\n};\n\nvar validateForm = function () {\n // only validate form when all required fields are filled to avoid\n // throwing errors on empty form\n if (!validator) {\n return;\n }\n//remove disable attribute when click on klarna/paypal payment option \nfunction enableButtonIfDisabledforUKEU() {\n if ($('#form-submit-EU').hasClass('disabled-button')) {\n $('#form-submit-EU').removeClass('disabled-button');\n }\n}\nfunction enableButtonIfDisabledforNaIcingFR() {\n if ($('#form-submit').hasClass('disabled-button')) {\n $('#form-submit').removeClass('disabled-button');\n }\n}\n\n$('#is-PayPal, #pay_later, #klarna, #add-giftcert').click(enableButtonIfDisabledforUKEU);\n$('#is-PayPal, #pay_later, #klarna, #add-giftcert').click(enableButtonIfDisabledforNaIcingFR);\n\n\n var shippingAvailable = true;\n if ($shippingMethodList && $shippingMethodList.find('[name$=\"_shippingMethodID\"][checked=\"checked\"]').length == 0) {\n shippingAvailable = false;\n }\n var buttonClicked = false;\n if ($('.add-giftcert-btn').length > 0) {\n $('.add-giftcert-btn').on('click', function () {\n buttonClicked = true;\n });\n }\n\n if (buttonClicked) {\n if (!hasEmptyRequired() && $form.find('.form-row.error [type=\"password\"]').length == 0 && shippingAvailable) {\n validator.form();\n }\n }\n if ($('#pay_later, #dwfrm_checkout_paymentMethods_creditCard_cvn').prop('checked')) {\n console.log(\"Yes2\");\n $('#terms-and-conditions-checkbox-error').removeClass('error').hide();\n $('input#terms-and-conditions-checkbox').removeClass('error');\n } \n};\n\n\nvar validatePassword = function () {\n var $formRow = $form.find('[name$=\"_createAccount_passwordconfirm\"]').parents('.form-row');\n\n if ($(this).val() === '' && !hasFilledPassword()) {\n $formRow.removeClass('error');\n $formRow.find('.form-caption').removeClass('error');\n $formRow.find('.form-caption')[0].innerHTML = '';\n validateForm();\n } else if (!validator.element(this)) {\n //$continue.addClass('disabled');\n $formRow.find('.form-caption').removeClass('error');\n $formRow.find('.form-caption')[0].innerHTML = '';\n } else if (!comparedEachOthers()) {\n $formRow.addClass('error');\n $formRow.find('.form-caption')[0].innerHTML = Resources.VALIDATE_COMPARE;\n $formRow.find('.form-caption').addClass('error');\n } else {\n $formRow.removeClass('error');\n $formRow.find('.form-caption').removeClass('error');\n $formRow.find('.form-caption')[0].innerHTML = '';\n validateForm();\n }\n};\n\nvar validateEl = function () {\n if ($(this).val() !== '') {\n // enable continue button on last required field that is valid\n // only validate single field\n // ED-1661 when it is card-number field not use default validation only get validation results\n if ($(this).hasClass('card-number')) { // && !modulValidator.methods.validateCardNumber($(this).val())) {\n if (callbackFunction) {\n callbackFunction();\n }\n return;\n }\n if (validator.element(this)) {\n var paypalSaveCheckbox = false;\n if($('#paypal-sale-message-label').attr('currentsiteid') != Resources.CLAIRES_NA && $('#paypal-sale-message-label').attr('currentsiteid') != Resources.ICING_NA){\n paypalSaveCheckbox = !$('#dwfrm_billing_paymentMethods_paypal_paypalSaveAgreement').prop('checked');\n }\n if (paypalSaveCheckbox && $('#dwfrm_checkout_paymentMethods_creditCard_cvn').val().trim() === '') {\n validateForm(); \n } \n if ($form.attr('id') === 'dwfrm_singleshipping_shippingAddress') {\n var $checkoutStoreAddress = $form.find('#checkout-selected-store');\n if ($checkoutStoreAddress.length > 0) {\n validateCheckoutSelectedStoreAndEmail();\n }\n }\n }\n }\n if (callbackFunction) {\n callbackFunction();\n }\n};\n\nvar validateCheckoutSelectedStoreAndEmail = function() {\n var $emailInput = $('#dwfrm_singleshipping_shippingAddress_addressFields_email');\n var storeId = $('.selected-store-address').data('store-id');\n var isEmailValid = false;\n var isStoreSelected = false;\n\n if (storeId !== undefined) {\n isStoreSelected = true;\n }\n if (!$emailInput.hasClass('error')) {\n isEmailValid = true;\n }\n\n if (isEmailValid && isStoreSelected) {\n //$continue.removeClass('disabled');\n } else {\n //$continue.addClass('disabled');\n }\n};\n\nvar validateCardNumber = function(e) {\n if (e.which >= 48 && e.which <= 57) { // if e.which is a number return true\n return true;\n }\n return false;\n}\n\nvar init = function (opts) {\n if (!opts.formSelector || !opts.continueSelector) {\n throw new Error('Missing form and continue action selectors.');\n }\n $form = $(opts.formSelector);\n callbackFunction = opts.callback;\n //$continue = $(opts.continueSelector);\n validator = $form.validate({onkeyup: false});\n $requiredInputs = (opts.skipDisabled) ? $('.required', $form).find(':input:enabled') : $('.required', $form).find(':input');\n $passwordsInputs = $form.find('[name$=\"_createAccount_password\"], [name$=\"_createAccount_passwordconfirm\"]');\n\n if (opts.shippingListSelector) {\n $shippingMethodList = $(opts.shippingListSelector);\n }\n\n validateForm();\n // start listening\n if ($passwordsInputs.length > 0) {\n $passwordsInputs.on('change', validatePassword);\n }\n $requiredInputs.on('change', validateEl);\n\n\n if ($form.attr('id') === 'dwfrm_billing') {\n var $cardNumber = $('#dwfrm_billing_paymentMethods_creditCard_number');\n if ($cardNumber.length > 0) {\n $cardNumber.on('change keypress paste', validateCardNumber);\n }\n }\n\n if ($form.attr('id') === 'dwfrm_singleshipping_shippingAddress') {\n var $checkoutStoreAddress = $form.find('#checkout-selected-store');\n if ($checkoutStoreAddress.length > 0) {\n validateCheckoutSelectedStoreAndEmail();\n var $checkoutEmailInput = $('#dwfrm_singleshipping_shippingAddress_addressFields_email');\n $checkoutEmailInput.on('change', validateCheckoutSelectedStoreAndEmail);\n $checkoutEmailInput.on('keyup', _.debounce(validateCheckoutSelectedStoreAndEmail, 200));\n $checkoutStoreAddress.bind('DOMSubtreeModified', validateCheckoutSelectedStoreAndEmail);\n }\n }\n};\n\nexports.init = init;\nexports.validateForm = validateForm;\nexports.validateEl = validateEl;\n","'use strict';\n\n// one page checkout\nvar address = require('./addressV2');\nvar checkout = require('./checkout');\n\n// original checkout\nvar billing = require('./billing');\nvar shipping = require('./shipping');\n\n/**\n * @function\n * @description Initializes the page events depending on the checkout stage (shipping / billing)\n * @abdul @edward might not be needed for onepage checkout?\n */\nexports.init = function() {\n address.init();\n\n // one page checkout\n if ($('#dwfrm_checkout').length > 0) {\n checkout.init();\n }\n\n // original checkout\n if ($('#dwfrm_singleshipping_shippingAddress').length > 0) {\n shipping.init();\n } else if ($('#dwfrm_billing').length > 0) {\n billing.init();\n }\n\n // if on the order review page and there are products that are not available diable the submit order button\n if ($('.order-summary-footer').length > 0) {\n if ($('.notavailable').length > 0) {\n $('.order-summary-footer .submit-order .button-fancy-large').attr('disabled', 'disabled');\n }\n }\n};\n","'use scrict';\n\nvar progress = require('base/progress');\n\nfunction getKlarnaPaymentSelector(paymentCategory) {\n return document.querySelectorAll('.klarna-payment-categories#' + paymentCategory)[0].closest('.form-row');\n}\n\nfunction getSelectedKlarnaPaymentContainer(paymentCategory) {\n return $('body').find('[data-klarna-payment-id=' + paymentCategory + ']')[0];\n}\n\nfunction hidePaymentCategory(paymentCategory) {\n var $klarnaTab = getKlarnaPaymentSelector(paymentCategory);\n var $klarnaContainer = getSelectedKlarnaPaymentContainer(paymentCategory);\n\n addClass($klarnaTab, 'hide');\n addClass($klarnaContainer, 'hide');\n}\n\nfunction greyoutPaymentCategory(klarnaPaymentMethod, flag) {\n var $klarnaTab = getKlarnaPaymentSelector(klarnaPaymentMethod);\n var $klarnaContainer = getSelectedKlarnaPaymentContainer(klarnaPaymentMethod);\n\n toggleClass($klarnaTab, 'klarna-grayed-tab', flag);\n toggleClass($klarnaContainer, 'klarna-grayed-content', flag);\n}\n\n/**\n * Enable or disable klarna payment options\n *\n * @param {Boolean} flag enable or disable klarna payment options\n */\nfunction togglePaymentAvailability(flag) {\n $('.op-payment-method-options').find('[data-sfcc-payment-name=\"KLARNA_PAYMENTS\"]').each(function () {\n var $klarnaPaymentInput = $(this);\n var $klarnaTab = getKlarnaPaymentSelector($(this)[0].id);\n if (window.KlarnaPaymentsObjects.hideRejectedPayments === 'hide') {\n addClass($klarnaTab, 'hidden');\n selectFirstPayment();\n } else if (window.KlarnaPaymentsObjects.hideRejectedPayments === 'greyout') {\n $klarnaPaymentInput.attr('disabled', !!flag);\n\n if (flag) {\n addClass($klarnaTab, 'klarna-grayed-tab');\n } else {\n removeClass($klarnaTab, 'klarna-grayed-tab');\n }\n }\n });\n}\n\nfunction selectFirstPayment() {\n var $firstItem = $('.op-payment-method-options .form-row:not(.hide) input[type=\"radio\"]').first();\n $firstItem.prop('checked', 'checked').trigger('click');\n}\n\nfunction hasClass(el, className) {\n var re = new RegExp('(^|\\\\s)' + className + '(\\\\s|$)' , 'g');\n if (el.className !== null && className !== '') {\n return re.test(el.className);\n }\n return false;\n}\n\nfunction addClass(el, className) {\n var re = new RegExp('(^|\\\\s)' + className + '(\\\\s|$)', 'g');\n if (el.className !== null) {\n if (re.test(el.className)) {\n return;\n }\n el.className = (el.className + ' ' + className).replace(/\\s+/g, ' ').replace(/(^ | $)/g, '');\n }\n}\n\nfunction removeClass(el, className) {\n var re = new RegExp('(^|\\\\s)' + className + '(\\\\s|$)', 'g');\n el.className = el.className.replace(re, '$1').replace(/\\s+/g, ' ').replace(/(^ | $)/g, '');\n}\n\nfunction toggleClass(el, className) {\n var re = new RegExp('(^|\\\\s)' + className + '(\\\\s|$)', 'g');\n if (re.test(el.className)) {\n removeClass(el, className);\n } else {\n addClass(el, className);\n }\n}\n\n/**\n* @function\n* @description checks if the given payment method ID is Klarna payment category.\n* @param {string} paymentCategory the payment category ID\n* @returns {void}\n*/\nfunction isKlarnaPaymentCategory(paymentCategory) {\n for (var i = 0; i < $('.klarna-payment-categories').length; ++i) {\n var $klarnaPaymentCategory = $('.klarna-payment-categories')[i];\n\n if (paymentCategory === $klarnaPaymentCategory.id) {\n return true;\n }\n }\n return false;\n}\n\n\n/**\n * Get address object based on form inputs or saved address\n *\n * @param {Object} $parentSelector address element in DOM\n * @returns {Object} address object\n */\nfunction obtainAddressData($parentSelector) {\n var $addressDiv = $parentSelector.find('.address-details-for-javascript');\n if ($addressDiv.length) {\n return {\n title: $addressDiv.data('title'),\n firstName: $addressDiv.data('firstname'),\n lastName: $addressDiv.data('lastname'),\n address1: $addressDiv.data('address1'),\n address2: $addressDiv.data('address2'),\n city: $addressDiv.data('city'),\n postalCode: $addressDiv.data('postalcode'),\n stateCode: $addressDiv.data('statecode'),\n countryCode: $addressDiv.data('countrycode'),\n phone: $addressDiv.data('phone')\n }\n }\n return {\n title: '',\n firstName: $parentSelector.find('input[name$=\"_firstName\"]').val(),\n lastName: $parentSelector.find('input[name$=\"_lastName\"]').val(),\n address1: $parentSelector.find('input[name$=\"_address1\"]').val(),\n address2: $parentSelector.find('input[name$=\"_address2\"]').val(),\n city: $parentSelector.find('input[name$=\"_city\"]').val(),\n postalCode: $parentSelector.find('input[name$=\"_postal\"]').val(),\n stateCode: $parentSelector.find('select[name$=\"_states_state\"]').val(),\n countryCode: $parentSelector.find('select[name$=\"_country\"]').val(),\n phone: $parentSelector.find('input[name$=\"_phone\"]').val()\n }\n}\n\n/**\n * Get email address value\n *\n * @returns {string} email address value\n */\nfunction getEmailAddress() {\n if ($('#email-address-lookup-container').length) {\n return $('#email-address-lookup-container').find('input').val();\n }\n\n return $('body').find('span.checkout-info').text();\n}\n\n/**\n * Get billing address\n *\n * @returns {Object} billing address details\n */\nfunction obtainBillingAddressData() {\n var addressFromForm = obtainAddressData($('.billing-section'));\n if ($('body').find('#dwfrm_checkout_shippingAddress_useAsBillingAddress').prop('checked')) {\n addressFromForm = obtainAddressData($('#shipping-address-area'));\n }\n var address = {\n given_name: addressFromForm.firstName || '',\n family_name: addressFromForm.lastName || '',\n email: getEmailAddress(),\n title: addressFromForm.title || '',\n street_address: addressFromForm.address1 || '',\n street_address2: addressFromForm.address2 || '',\n postal_code: addressFromForm.postalCode || '',\n city: addressFromForm.city || '',\n region: addressFromForm.stateCode || '',\n phone: addressFromForm.phone || '',\n country: addressFromForm.countryCode || ($('body').find('#dwfrm_checkout_shippingAddress_useAsBillingAddress').prop('checked') ? $('#dwfrm_checkout_shippingAddress_addressFields_country').val() : '')\n };\n\n return address;\n}\n\n/**\n * Get shipping address\n *\n * @param {Object} billingAddress billing address\n * @returns {Object} shipping address details\n */\nfunction obtainShippingAddressData(billingAddress) {\n var addressForm = obtainAddressData($('#shipping-address-area'));\n var address = {\n given_name: addressForm.firstName || '',\n family_name: addressForm.lastName || '',\n title: addressForm.title || '',\n street_address: addressForm.address1 || '',\n street_address2: addressForm.address2 || '',\n postal_code: addressForm.postalCode || '',\n city: addressForm.city || '',\n region: addressForm.stateCode || '',\n phone: addressForm.phone || '',\n country: addressForm.countryCode || $('#dwfrm_checkout_shippingAddress_addressFields_country').val(),\n email: getEmailAddress()\n };\n\n // check if first shipment is store one and if so - update first & lastName\n if ($('#shipping-address-area').find('#selected-store-container-bopis').length) {\n if (billingAddress) {\n address.given_name = billingAddress.given_name;\n address.family_name = billingAddress.family_name;\n }\n }\n\n\n return address;\n}\n\n/**\n * Get Bopis address\n * Due to klarna docs we should always send store address in other_delivery_address section\n *\n * @param {Object} billingAddress billing address\n * @returns {Array} bopis address details\n */\nfunction getBopisAddress(billingAddress) {\n var addressesArr = [];\n var addressForm = obtainAddressData($('#shipping-address-area'));\n var address = {\n shipping_method: window.KPConstants.SHIPPING_METHOD_TYPE.STORE,\n shipping_type: window.KPConstants.SHIPPING_TYPE.NORMAL,\n first_name: billingAddress.given_name || '',\n last_name: billingAddress.family_name || '',\n street_address: addressForm.address1 || '',\n street_number: addressForm.address2 || '',\n city: addressForm.city || '',\n postal_code: addressForm.postalCode || '',\n country: addressForm.countryCode || $('#dwfrm_checkout_shippingAddress_addressFields_country').val(),\n };\n\n addressesArr.push(address);\n return addressesArr;\n}\n\n\n/**\n * @function\n * @description updates the billing and shipping address with the billing address form\n * @param {string} paymentCategory the payment category ID\n * @returns {void}\n */\nfunction updateBillingAddress(paymentCategory) {\n var hasShippingAddress = $('#shipping-address-area').length;\n var containerName = '#klarna_payments_' + paymentCategory + '_container';\n var $klarnaTab = getKlarnaPaymentSelector( paymentCategory );\n\n if (hasClass($klarnaTab, 'klarna-grayed-tab')) {\n return;\n }\n\n var klarnaRequestData = {\n billing_address: obtainBillingAddressData()\n };\n\n if (hasShippingAddress) {\n klarnaRequestData.shipping_address = obtainShippingAddressData(klarnaRequestData.billing_address);\n }\n\n loadPaymentData($('body').find(containerName)[0], paymentCategory, klarnaRequestData);\n}\n\n/**\n * Update klarna payment iframe container\n *\n * @param {Object} containerName klarna container element\n * @param {string} paymentCategory klarna payment method(not SFCC)\n * @param {Object} requestData request data\n * @param {Function|null} callback callback function\n */\nfunction loadPaymentData(containerName, paymentCategory, requestData, callback) {\n Klarna.Payments.load({\n container: containerName,\n payment_method_category: paymentCategory\n }, requestData || {} , function(res) {\n\n if (!res.show_form) {\n togglePaymentAvailability(true);\n } else {\n togglePaymentAvailability(false);\n }\n\n if (typeof callback === 'function') {\n callback(res);\n }\n });\n}\n\n/**\n * Make service side call to update klarna session\n */\nfunction updateKlarnaSession() {\n $.ajax({\n url: window.KlarnaPaymentsUrls.updateKlarnaSessionPayments,\n type: 'GET',\n asycn: false,\n success: function (response) {\n // if (response.error) {\n // togglePaymentAvailability(true);\n // } else if (response.updateSessionResponse.payment_method_categories && response.updateSessionResponse.payment_method_categories.length == 0) {\n // togglePaymentAvailability(true);\n // } else if (response.updateSessionResponse.payment_method_categories && response.updateSessionResponse.payment_method_categories.length > 0) {\n // togglePaymentAvailability(false);\n // }\n }\n });\n}\n\n/**\n * Update payment method\n *\n * @param {Object} $paymentMethod payment method element\n * @param {Function|null} callback callback function\n */\nfunction submitPaymentMethod($paymentMethod, callback) {\n var isKlarnaCategory = isKlarnaPaymentCategory($paymentMethod.attr('id'));\n var formData = 'dwfrm_billing_paymentMethods_selectedPaymentMethodID=';\n\n if (isKlarnaCategory) {\n document.cookie = 'selectedKlarnaPaymentCategory=' + $paymentMethod.attr('id') + '; SameSite=Strict; path=/';\n if ($($paymentMethod).attr('data-sfcc-payment-name').length) {\n formData += $paymentMethod.attr('data-sfcc-payment-name');\n } else {\n formData += 'Klarna';\n }\n } else {\n formData += $paymentMethod.val();\n }\n\n var xhr = new XMLHttpRequest();\n xhr.open('POST', window.KlarnaPaymentsUrls.selectPaymentMethod, false);\n xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n\n xhr.onreadystatechange = function() {\n // always execute the KP options loading\n if (typeof callback === 'function') {\n callback();\n }\n };\n\n xhr.send(formData);\n}\n\n/**\n * Trigger klarna modal display and auth flow.\n *\n * @param {Object} continueBtn continue button element\n * @param {Object} selectedPaymentMethod selected payment method element\n */\nfunction klarnaAuthFlow(continueBtn, selectedPaymentMethod) {\n var $selectedPaymentMethod = $(selectedPaymentMethod)[0];\n var $continueBtn = $(continueBtn)[0];\n if (isKlarnaPaymentCategory($selectedPaymentMethod.id)) {\n $continueBtn.disabled = true;\n\n var klarnaContainer = '#klarna_payments_' + $selectedPaymentMethod.id + '_container';\n var klarnaPaymentMethodId = $selectedPaymentMethod.id;\n submitPaymentMethod(selectedPaymentMethod, function () {\n\n loadPaymentData(klarnaContainer, klarnaPaymentMethodId);\n var hasShippingAddress = $('#shipping-address-area').length;\n\n var klarnaRequestData = {\n billing_address: obtainBillingAddressData()\n };\n\n if (hasShippingAddress) {\n klarnaRequestData.shipping_address = obtainShippingAddressData(klarnaRequestData.billing_address);\n }\n\n if (window.KPCustomerInfo && window.KPCustomerInfo.attachment) {\n var kpAttachment = window.KPCustomerInfo.attachment;\n\n var kpAttachmentBody = JSON.parse(kpAttachment.body);\n var otherAddresses = [] // getBopisAddress(klarnaRequestData.billing_address);\n //var otherAddresses = getBopisAddress(klarnaRequestData.billing_address);\n\n kpAttachmentBody.other_delivery_address = otherAddresses;\n kpAttachment.body = JSON.stringify(kpAttachmentBody);\n\n klarnaRequestData.attachment = kpAttachment;\n }\n\n Klarna.Payments.authorize({\n payment_method_category: $selectedPaymentMethod.id,\n auto_finalize: SitePreferences.AUTO_FINALIZE\n }, klarnaRequestData , function(res) {\n if (res.approved) {\n var xhr = new XMLHttpRequest();\n xhr.open('GET', window.KlarnaPaymentsUrls.saveAuth, true);\n\n xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');\n xhr.setRequestHeader('X-Auth', res.authorization_token);\n xhr.setRequestHeader('Finalize-Required', res.finalize_required);\n\n xhr.onreadystatechange = function() {\n if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {\n document.cookie = 'selectedKlarnaPaymentCategory=' + $selectedPaymentMethod.id + '; SameSite=Strict; path=/';\n $selectedPaymentMethod.value = $($selectedPaymentMethod).attr('data-sfcc-payment-name');\n\n //submit billing form when Klarna Payments authorization is successfully finished\n $continueBtn.disabled = false;\n $('.checkout-place-order-wrapper .billing-form-row .button-fancy-large').removeClass('disabled');\n $('#dwfrm_checkout').submit();\n }\n //progress.hide();\n };\n xhr.send();\n } else if (!res.show_form) {\n togglePaymentAvailability(true);\n $continueBtn.disabled = true;\n progress.hide();\n } else {\n togglePaymentAvailability(false);\n $continueBtn.disabled = false;\n $('.checkout-place-order-wrapper .billing-form-row .button-fancy-large').removeClass('disabled');\n progress.hide();\n }\n });\n });\n\n }\n}\n\n/**\n * Init Klarna\n */\nfunction init() {\n Klarna.Payments.init({\n client_token: window.KlarnaPaymentsObjects.clientToken\n });\n}\n\nexports.init = init;\nexports.loadPaymentData = loadPaymentData;\nexports.klarnaAuthFlow = klarnaAuthFlow;\nexports.submitPaymentMethod = submitPaymentMethod;\nexports.updateBillingAddress = updateBillingAddress;\nexports.isKlarnaPaymentCategory = isKlarnaPaymentCategory;\nexports.updateKlarnaSession = updateKlarnaSession;\nexports.togglePaymentAvailability = togglePaymentAvailability;\n","'use strict';\n\nvar ajax = require('base/ajax'),\n formPrepare = require('./formPrepare'),\n progress = require('base/progress'),\n tooltip = require('org/tooltip'),\n util = require('org/util'),\n clickcollect = require('org/clickcollect/shiptostore'),\n qas = require('int_QASc/qas');\n\nvar shippingMethods;\n/**\n * @function\n * @description Initializes gift message box, if shipment is gift\n */\nfunction giftMessageBox() {\n // show gift message box, if shipment is gift\n $('.gift-message-text').toggleClass('hidden', $('input[name$=\"_shippingAddress_isGift\"]:checked').val() !== 'true');\n\n if ($('.gift-message-text').hasClass('hidden')) {\n $('.gift-message-text').attr('aria-hidden', 'true');\n } else {\n $('.gift-message-text').removeAttr('aria-hidden');\n }\n}\n\n/**\n * @function\n * @description updates the order summary based on a possibly recalculated basket after a shipping promotion has been applied\n */\nfunction updateSummary() {\n var $summary = $('#secondary.summary');\n // indicate progress\n progress.show($summary);\n\n // load the updated summary area\n $summary.load(Urls.summaryRefreshURL, function () {\n // hide edit shipping method link\n $summary.fadeIn('fast');\n $summary.find('.checkout-mini-cart .minishipment .header a').hide();\n $summary.find('.order-totals-table .order-shipping .label a').hide();\n });\n}\n\n/**\n * @function\n * @description Helper method which constructs a URL for an AJAX request using the\n * entered address information as URL request parameters.\n */\nfunction getShippingMethodURL(url, extraParams) {\n var $form = $('.address');\n var countryCode = $form.find('select[id$=\"_country\"]:enabled').val() !== undefined ? $form.find('select[id$=\"_country\"]:enabled').val() : $form.find('#sl-country-selector:enabled').val();\n var params = {\n address1: $form.find('input[name$=\"_address1\"]').val(),\n address2: $form.find('input[name$=\"_address2\"]').val(),\n countryCode: countryCode,\n stateCode: $form.find('select[id$=\"_state\"]').val(),\n postalCode: $form.find('input[name$=\"_postal\"]').val(),\n city: $form.find('input[name$=\"_city\"]').val(),\n deliveryType: $('.item-delivery-options').find('input:checked').val()\n };\n return util.appendParamsToUrl(url, $.extend(params, extraParams));\n}\n\n/**\n * @function\n * @description selects a shipping method for the default shipment and updates the summary section on the right hand side\n * @param\n */\nfunction selectShippingMethod(shippingMethodID) {\n // nothing entered\n if (!shippingMethodID) {\n return;\n }\n // attempt to set shipping method\n var url = getShippingMethodURL(Urls.selectShippingMethodsList, {shippingMethodID: shippingMethodID});\n ajax.getJson({\n url: url,\n callback: function (data) {\n updateSummary();\n if (!data || !data.shippingMethodID) {\n window.alert('Couldn\\'t select shipping method.');\n return false;\n }\n // display promotion in UI and update the summary section,\n // if some promotions were applied\n $('.shippingpromotions').empty();\n\n // TODO the for loop below isn't doing anything?\n // if (data.shippingPriceAdjustments && data.shippingPriceAdjustments.length > 0) {\n // var len = data.shippingPriceAdjustments.length;\n // for (var i=0; i < len; i++) {\n // var spa = data.shippingPriceAdjustments[i];\n // }\n // }\n }\n });\n}\n\n/**\n * @function\n * @description Make an AJAX request to the server to retrieve the list of applicable shipping methods\n * based on the merchandise in the cart and the currently entered shipping address\n * (the address may be only partially entered). If the list of applicable shipping methods\n * has changed because new address information has been entered, then issue another AJAX\n * request which updates the currently selected shipping method (if needed) and also updates\n * the UI.\n */\nfunction updateShippingMethodList() {\n var $shippingMethodList = $('#shipping-method-list');\n if (!$shippingMethodList || $shippingMethodList.length === 0) { return; }\n var url = getShippingMethodURL(Urls.shippingMethodsJSON);\n\n ajax.getJson({\n url: url,\n callback: function (data) {\n if (!data) {\n window.alert('Couldn\\'t get list of applicable shipping methods.');\n return false;\n }\n if (data.toString() === '') {\n $('[name$=\"shippingAddress_save\"]').attr('disabled', 'disabled');\n } else {\n formPrepare.init({\n continueSelector: '[name$=\"shippingAddress_save\"]',\n formSelector:'[id$=\"singleshipping_shippingAddress\"]',\n skipDisabled: true\n });\n }\n if (shippingMethods && shippingMethods.toString() === data.toString()) {\n // No need to update the UI. The list has not changed.\n return true;\n }\n\n // We need to update the UI. The list has changed.\n // Cache the array of returned shipping methods.\n shippingMethods = data;\n // indicate progress\n progress.show($shippingMethodList);\n\n // load the shipping method form\n var smlUrl = getShippingMethodURL(Urls.shippingMethodsList);\n $shippingMethodList.load(smlUrl, function () {\n $shippingMethodList.fadeIn('fast');\n // rebind the radio buttons onclick function to a handler.\n $shippingMethodList.find('[name$=\"_shippingMethodID\"]').click(function () {\n selectShippingMethod($(this).val());\n });\n\n // update the summary\n updateSummary();\n progress.hide();\n tooltip.init();\n //if nothing is selected in the shipping methods select the first one\n if ($shippingMethodList.find('.input-radio:checked').length === 0) {\n $shippingMethodList.find('.input-radio:first').prop('checked', 'checked');\n }\n });\n }\n });\n}\n\nexports.init = function () {\n clickcollect.init();\n\n formPrepare.init({\n continueSelector: '[name$=\"shippingAddress_save\"]',\n formSelector:'[id$=\"singleshipping_shippingAddress\"]',\n skipDisabled: true\n });\n $('input[name$=\"_shippingAddress_isGift\"]').on('click', giftMessageBox);\n\n $('.address').on('change',\n 'input[name$=\"_addressFields_address1\"], input[name$=\"_addressFields_address2\"], select[name$=\"_addressFields_states_state\"], input[name$=\"_addressFields_city\"], input[name$=\"_addressFields_postal\"], select[name$=\"_addressFields_country\"]:enabled',\n updateShippingMethodList\n );\n\n giftMessageBox();\n updateShippingMethodList();\n\n var $form = $('.address');\n if ($form.find('select[id$=\"_country\"]:enabled').length > 0) {\n util.updateStateField($form);\n $('select[name$=\"_country\"]:enabled', $form).on('change', function () {\n util.updateStateField($form);\n });\n }\n $form.find('.form-row .inputfield-caption').each(function () {\n var $caption = $(this);\n $caption.insertBefore($caption.closest('.form-row').children().first());\n });\n $form.find('.form-row .form-field-tooltip').each(function () {\n var $tooltip = $(this);\n $tooltip.insertBefore($tooltip.closest('.form-row').children().first());\n });\n\n qas.init();\n};\n\nexports.updateShippingMethodList = updateShippingMethodList;\nexports.updateSummary = updateSummary;\n","'use strict';\n\nvar addProductToCart = require('org/pages/product/addToCart'),\n ajax = require('base/ajax'),\n page = require('base/page'),\n productTile = require('org/product-tile'),\n quickview = require('org/quickview');\n\n/**\n * @private\n * @function\n * @description Binds the click events to the remove-link and quick-view button\n */\nfunction initializeEvents() {\n $('#compare-table').on('click', '.remove-link', function (e) {\n e.preventDefault();\n ajax.getJson({\n url: this.href,\n callback: function () {\n page.refresh();\n }\n });\n })\n .on('click', '.open-quick-view', function (e) {\n e.preventDefault();\n var url = $(this).closest('.product').find('.thumb-link').attr('href');\n quickview.show({\n url: url,\n source: 'quickview'\n });\n });\n\n $('#compare-category-list').on('change', function () {\n $(this).closest('form').submit();\n });\n}\n\nexports.init = function () {\n productTile.init();\n initializeEvents();\n addProductToCart();\n};\n","'use strict';\n\nvar dialog = require('../../dialog'),\n minicart = require('../../minicart'),\n page = require('base/page'),\n util = require('../../util'),\n Promise = require('promise'),\n bonusProductsView = require('../../bonus-products-view'),\n _ = require('lodash');\n\n/**\n * @description Make the AJAX request to add an item to cart\n * @param {Element} form The form element that contains the item quantity and ID data\n * @returns {Promise}\n */\nvar addItemToCart = function (form) {\n var $form = $(form),\n $qty = $form.find('input[name=\"Quantity\"]');\n if ($qty.length === 0 || isNaN($qty.val()) || parseInt($qty.val(), 10) === 0) {\n $qty.val('1');\n }\n return Promise.resolve($.ajax({\n type: 'POST',\n url: util.ajaxUrl(Urls.addProduct),\n data: $form.serialize()\n })).then(function (response) {\n // handle error in the response\n if (response.error) {\n $('.error-wrapper').remove();\n var parentDiv = $($form.context.parentElement);\n var errorMessage = response.errorMessage;\n var errorDiv = '
' + errorMessage + '
';\n parentDiv.append(errorDiv);\n throw new Error(response.error);\n } else {\n return response;\n }\n });\n};\n\n/**\n * @description Handler to handle the add to cart event\n */\nvar addToCart = function (e) {\n e.preventDefault();\n var $form = $(this).closest('form');\n\n addItemToCart($form).then(function (response) {\n var $uuid = $form.find('input[name=\"uuid\"]');\n var formData = $form.serialize();\n var shipToAddValue =$form.find('input[name=\"ship-to-add\"]').val();\n var pickUpInStoreValue =$form.find('input[name=\"pick-up-instore\"]').val();\n var storeIdValue =$form.find('input[name=\"store-id\"]').val();\n \n if ($uuid.length > 0 && $uuid.val().length > 0) {\n page.refresh();\n } else {\n // do not close quickview if adding individual item that is part of product set\n // @TODO should notify the user some other way that the add action has completed successfully\n if (!$(this).hasClass('sub-product-item')) {\n dialog.close();\n }\n if (util.isMobile() && $(document).width() < 768) {\n var actualPrice = $form.parent().siblings().find('.actual-price').attr('data-pricing');\n var priceField = $('').attr('type', 'hidden').attr('name', 'actualPrice').val(actualPrice);\n $form.append(priceField);\n minicart.updateQty(response);\n $('.basket-link.claires-basket').load(Urls.miniCartMobile);\n dialog.open({\n url:Urls.addProductMobileIntercept,\n data: $form.serialize(),\n options: {\n dialogClass: 'mobile-cart-dialog-intercept',\n title: Resources.CART_SUCCESS,\n buttons: [{\n text: Resources.CART_GO_TO_BAG,\n click: function() {\n window.location.replace(Urls.cartShow);\n }\n },\n {\n class: 'simple',\n text: Resources.CART_CONTINUE_SHOPPING,\n click: function() {\n $(this).dialog('close');\n }\n }],\n close: function () {\n bonusProductsView.loadBonusOption();\n }\n },\n callback: function () {\n //updates shipping message in minicart\n $('body').trigger('basketUpdate');\n }\n });\n } else {\n minicart.show(response);\n\n //updates shipping message in minicart\n $('body').trigger('basketUpdate');\n }\n }\n }.bind(this));\n};\n\n/**\n * @description Handler to handle the add all items to cart event\n */\nvar addAllToCart = function (e) {\n e.preventDefault();\n var $productForms = $('#product-set-list').find('form').toArray();\n Promise.all(_.map($productForms, addItemToCart))\n .then(function (responses) {\n dialog.close();\n // show the final response only, which would include all the other items\n\n if (!util.isMobile() && $(document).width() > 767) {\n minicart.show(responses[responses.length - 1]);\n } else {\n minicart.updateQty(responses[responses.length - 1]);\n dialog.open({\n url:Urls.addProductMobileIntercept,\n options: {\n dialogClass: 'mobile-cart-dialog-intercept',\n title: Resources.CART_SUCCESS,\n buttons: [{\n text: Resources.CART_CONTINUE_SHOPPING,\n click: function() {\n $(this).dialog('close');\n }\n }]\n }\n });\n }\n });\n};\n\n/**\n * @function\n * @description Binds the click event to a given target for the add-to-cart handling\n */\nmodule.exports = function () {\n $('.add-to-cart[disabled]').attr('title', $('.availability-msg').text());\n $('.product-detail').on('click', '.add-to-cart', addToCart);\n $('#add-all-to-cart').on('click', addAllToCart);\n $('.option-add-to-cart').on('click', '.add-to-cart', addToCart);\n};\nmodule.exports.addToCart = addToCart;\n","'use strict';\n\nvar ajax = require('base/ajax'),\n util = require('org/util');\n\nvar updateContainer = function (data) {\n var $availabilityMsg = $('#pdpMain .availability .availability-msg');\n var message; // this should be lexically scoped, when `let` is supported (ES6)\n if (!data) {\n $availabilityMsg.html(Resources.ITEM_STATUS_NOTAVAILABLE);\n return;\n }\n $availabilityMsg.empty();\n // Look through levels ... if msg is not empty, then create span el\n if (data.levels.IN_STOCK > 0) {\n if (data.levels.PREORDER === 0 && data.levels.BACKORDER === 0 && data.levels.NOT_AVAILABLE === 0) {\n // Just in stock\n message = Resources.IN_STOCK;\n } else {\n // In stock with conditions ...\n message = data.inStockMsg;\n }\n $availabilityMsg.append('
\n */\nfunction initializeEvents() {\n var $main = $('#main');\n\n wireFilterSortButtons();\n\n // compare checked\n $main.on('click', 'input[type=\"checkbox\"].compare-check', function () {\n var cb = $(this);\n var tile = cb.closest('.product-tile');\n\n var func = this.checked ? compareWidget.addProduct : compareWidget.removeProduct;\n var itemImg = tile.find('.product-image a img').first();\n func({\n itemid: tile.data('itemid'),\n uuid: tile[0].id,\n img: itemImg,\n cb: cb\n });\n\n });\n\n // handle toggle refinement blocks\n $main.on('click keypress', '.refinement > a.toggle, .refinement > h3', function (e) {\n e.preventDefault();\n $(this).toggleClass('expanded');\n $(this).siblings('.ref-pane').toggle();\n if ($(this).hasClass('expanded')) {\n $(this).attr('aria-expanded', 'true');\n } else {\n $(this).attr('aria-expanded', 'false');\n }\n filterScrollCheck($('.ref-block-overflow'));\n });\n\n // handle events for updating grid\n $main.on('click', '.refinements li a, .pagination a, .breadcrumb-refinement-value a', function (e) {\n // don't intercept for category and folder refinements, as well as unselectable\n if ($(this).parents('.category-refinement').length > 0 || $(this).parents('.folder-refinement').length > 0 || $(this).parent().hasClass('unselectable')) {\n return;\n }\n if ($('.top-ref-nav').css('justify-content') == 'center') {\n $('.top-ref-nav').css('justify-content', 'normal');\n $('.top-ref-nav').css('margin-left', '2px');\n }\n e.preventDefault();\n\n if ($(this).find('i.checkbox').length) {\n $(this).find('i.checkbox').toggleClass('check');\n $(this).off('click');\n e.stopPropagation();\n }\n\n if ($(this).parents('.refinement').length) {\n showFilterLoader();\n }\n\n var focusElement = $(this).attr('id');\n\n if (focusElement) {\n focusElement = '#' + focusElement;\n }\n var url = new URL(window.location.href);\n var params = url.searchParams;\n var selectedshipment = (params.get('selectedshipment'));\n\n (selectedshipment) ? updateProductListing(this.href, focusElement,selectedshipment) : updateProductListing(this.href, focusElement);\n });\n\n // handle events item click. append params.\n $main.on('click', '.product-tile a:not(\"#quickviewbutton\")', function () {\n var a = $(this);\n // get current page refinement values\n var wl = window.location;\n\n var qsParams = (wl.search.length > 1) ? util.getQueryStringParams(wl.search.substr(1)) : {};\n var hashParams = (wl.hash.length > 1) ? util.getQueryStringParams(wl.hash.substr(1)) : {};\n\n // merge hash params with querystring params\n var params = $.extend(hashParams, qsParams);\n if (!params.start) {\n params.start = 0;\n }\n // get the index of the selected item and save as start parameter\n var tile = a.closest('.product-tile');\n var idx = tile.data('idx') ? + tile.data('idx') : 0;\n\n // convert params.start to integer and add index\n params.start = (+params.start) + (idx + 1);\n // set the hash and allow normal action to continue\n //a[0].hash = $.param(params);\n });\n //update toggle aria attribute status dynamically for sortby\n $(document).ready(function() {\n var gridSortHeader = $(\"#grid-sort-header\");\n gridSortHeader.on('click', function() {\n var isSelected = gridSortHeader.attr('aria-expanded') === 'false';\n gridSortHeader.attr('aria-expanded', String(isSelected));\n });\n });\n // handle sorting change\n $main.on('change', '.sort-by select', function (e) {\n e.preventDefault();\n updateProductListing($(this).find('option:selected').val(), '.sort-by select');\n })\n .on('change', '.items-per-page select', function () {\n var refineUrl = $(this).find('option:selected').val();\n if (refineUrl === 'INFINITE_SCROLL') {\n $('html').addClass('infinite-scroll').removeClass('disable-infinite-scroll');\n } else {\n $('html').addClass('disable-infinite-scroll').removeClass('infinite-scroll');\n updateProductListing(refineUrl, '.items-per-page select');\n }\n });\n \n //handle bopis sorting change\n\n if ($('.top-ref-nav').css('justify-content') == 'center') {\n $('.top-ref-nav').css('justify-content', 'normal');\n $('.top-ref-nav').css('margin-left', '2px');\n }\n $(document).on('change', '#sth-bopis-toggle', function () {\n var selectedshipment = $(this).is(':checked'); // Check if the toggle is checked\n localStorage.setItem('selectedshipment', selectedshipment);\n updateBopisListing(selectedshipment);\n });\n\n function updateBopisListing(selectedshipment) {\n var storeId = $('.toggle-switch').attr('data-store-id');\n const currenturl = new URL(window.location.href);\n const currentparams = new URLSearchParams(currenturl.search);\n var action;\n if(currentparams.get('start') !== 'undefined'&& currentparams.get('start') !== null && currentparams.get('sz') !== null && currentparams.get('sz') !== 'undefined') {\n currentparams.delete('start');\n currentparams.delete('sz');\n var requiredUrl = currenturl.origin + currenturl.pathname + '?' + currentparams.toString();\n action = requiredUrl;\n } else {\n action = currenturl;\n }\n var url = new URL(action);\n var params = url.searchParams;\n var selectedshipment = params.get('selectedshipment');\n if ((storeId != null)) {\n if (selectedshipment === 'pickup_in_store') {\n selectedshipment = 'ship_to_address';\n } else {\n selectedshipment = 'pickup_in_store';\n }\n } else {\n selectedshipment = 'ship_to_address';\n }\n params.set('selectedshipment', selectedshipment);\n url.search = params.toString();\n updateProductListing(url.href, '#sth-bopis-toggle', selectedshipment);\n }\n // Close Refinements on tab out\n $('.clear-all-refinements').focusout(function (){\n if ($(window).innerWidth() < util.getViewports('large')) {\n $('#secondary.refinements').hide()\n }\n });\n\n $('.ref-block-overflow').on('scroll', function(){\n filterScrollCheck($(this));\n });\n\n // Close Refinements on escape keyup\n $(document).keyup(function(e) {\n if (e.keyCode == 27) {\n if ($(window).innerWidth() < util.getViewports('large')) {\n $('#secondary.refinements').hide();\n }\n if ($('body').find('.refinement-overlay').hasClass('open')) {\n $('.close-refinement-overlay').trigger('click');\n }\n }\n });\n readMore();\n util.smartResize(function () {\n readMore();\n })\n}\n\n// checks to keep filterUI from scrolling up too far from bottom of screen\nfunction filterScrollCheck($el) {\n var Scrollheight = 0;\n $el.children().each(function(){ Scrollheight+=$(this).height() });\n if ($el.scrollTop() > Scrollheight + $(window).height()) {\n $el.scrollTop(Scrollheight + $(window).height());\n }\n}\n\nfunction showFilterLoader() {\n var loader = $('').addClass('filter-loader').append($('').addClass('indicator'));\n $('.refinement-overlay').append(loader);\n}\n\nfunction wireFilterSortButtons() {\n var mainRef = $('body');\n var filterUI = mainRef.find('.refinement-overlay');\n\n if ($('.top-ref-nav').css('justify-content') == 'center') {\n $('.top-ref-nav').css('justify-content', 'normal');\n $('.top-ref-nav').css('margin-left', '2px');\n }\n mainRef.on('click keypress', '.filter, .close-refinement-overlay, .refinement-overlay-bg, .refinements-footer .apply-btn', function (e) {\n e.preventDefault();\n //mainRef.find('.refinement-overlay').animate({width:'toggle'}, 350);\n if (filterUI.hasClass('open')) {\n filterUI.animate({'left':'-100%'}, 500, function(){\n $(this).removeClass('open');\n $('.filter').focus();\n });\n } else {\n filterUI.css('display', 'block');\n filterUI.animate({'left':0}, 500, function(){\n $(this).addClass('open');\n $('.refinements-header').focus();\n });\n }\n });\n}\n/**\n * Adds read more functionality for category banner on mobile\n */\n\nfunction readMore() {\n if (($('.category-banner').length || $('.cat-banner').length) && $(document).width() < 768) {\n var $paragraph = $('.category-banner-paragraph');\n var maxLength = 100;\n\n if ($paragraph.html().length > maxLength){\n\n var shortContent \t= $paragraph.html().substr(0, maxLength);\n var longContent\t= $paragraph.html().substr(maxLength);\n var readMore = 'Read More';\n var readLess = 'Read Less';\n var hiddenText = ''+longContent+'';\n\n $paragraph.html(shortContent + hiddenText + readMore);\n\n $paragraph.on('click', 'a.read-more', function (e) {\n e.preventDefault();\n $(this).parents('.category-banner-paragraph').find('.hidden-text').show();\n $(this).replaceWith(readLess);\n });\n\n $paragraph.on('click', 'a.read-less', function (e) {\n e.preventDefault();\n $(this).parents('.category-banner-paragraph').find('.hidden-text').hide();\n $(this).replaceWith(readMore);\n });\n\n }\n }\n}\n/**\n * Scroll To Top After Pagination Click\n */\nvar paginationBackToTop = function () {\n var $refinements = $('.refinements');\n var $main = $('#main');\n var $indent = $main.offset().top;\n if ($refinements.length === 0) {\n return;\n }\n var offsetTop = ($indent) ? $indent - $('header').height() : $refinements.offset().top;\n $('.pagination a').on('click', function(){\n $('html, body').animate({\n scrollTop: offsetTop\n }, 500);\n });\n};\n\n\n/**\n * Update refinements state (opened or closed)\n */\nfunction updateRefinementsState() {\n var $secondary = $('#secondary');\n\n $secondary.find('.refinement ul').each(function() {\n $(this).find('li').each(function() {\n if ($(this).hasClass('selected') && $(window).innerWidth() >= util.getViewports('large')) {\n $(this).parents('.refinement').find('a.toggle').addClass('expanded').attr('aria-expanded', 'true');\n $(this).parents('.refinement').find('h3').addClass('expanded').attr('aria-expanded', 'true');\n $(this).parents('.refinement ul').show();\n }\n });\n });\n}\n\nfunction initPriceSlider() {\n $('#slider-range').slider({\n range: true,\n min: $('#slider-minval').data('val'),\n max: $('#slider-maxval').data('val'),\n values: [$('#slider-amount').data('lo'), $('#slider-amount').data('hi')],\n slide: function(event, ui) {\n var newRange = window.Constants.CURRENCY_SYMBOL + ui.values[0] + ' - ' + window.Constants.CURRENCY_SYMBOL + ui.values[1];\n if (ui.values[1] == $('#slider-maxval').data('val')) {\n newRange += '+';\n }\n $('#slider-amount').html(newRange);\n },\n stop: function(event, ui) {\n if (ui.values[0] == ui.values[1]) ui.values[1]++;\n var url = $('#priceSliderURL').val();\n if (url.indexOf('?') == -1) {\n url += '?';\n } else {\n url += '&';\n }\n url += 'pmin='+ ui.values[0];\n if (ui.values[1] != $('#slider-maxval').data('val')) {\n url += '&pmax=' + ui.values[1];\n }\n\n showFilterLoader();\n updateProductListing(url, $('#slider-amount'));\n }\n });\n var range = window.Constants.CURRENCY_SYMBOL + $('#slider-range').slider('values', 0) + ' - ' + window.Constants.CURRENCY_SYMBOL + $('#slider-range').slider('values', 1);\n if ($('#slider-range').slider('values', 1) == $('#slider-maxval').data('val')) {\n range += '+';\n }\n $('#slider-amount').html(range);\n}\n\nfunction initSortingSelector() {\n var searchRule = new URL(location.href).searchParams.get('srule');\n if (!searchRule) {\n return;\n }\n\n $('#grid-sort-header option').each(function() {\n var optionUrl = $(this).val();\n if (optionUrl.indexOf('srule=' + searchRule) >= 0) {\n $('#grid-sort-header').val(optionUrl);\n return false;\n }\n });\n}\n\nfunction findGetParameter(parameterName) {\n var result = null,\n tmp = [];\n location.search\n .substr(1)\n .split(\"&\")\n .forEach(function (item) {\n tmp = item.split(\"=\");\n if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);\n });\n return result;\n}\n\nfunction toggleText(t1, t2) {\n if (this.text() == t1) this.text(t2);\n else this.text(t1);\n return this;\n}\n\nfunction hybridInit() {\n // Hybrid PLP\n var hybridCatCnt = $('.hybrid-categories .subcategory').length;\n var textLess = Resources.HYBRID_PLP_VIEWLESS;\n var textMore = Resources.HYBRID_PLP_VIEWMORE;\n\n if (hybridCatCnt > 4 && $(window).width() < 768) {\n $('.hybrid-btn-more').show();\n }\n\n if (hybridCatCnt > 8 && $(window).width() >= 768) {\n $('.hybrid-btn-more').show();\n }\n\n $(document).on('click', '.hybrid-btn-more', function (e) {\n e.preventDefault();\n $('.hybrid-categories').toggleClass('active');\n $(this).toggleClass('active');\n if ($(this).find('.hybrid-categories-more').text() == textLess) {\n $(this).find('.hybrid-categories-more').text(textMore).attr('data-visual-nav', textMore.toLowerCase());\n } else {\n $(this).find('.hybrid-categories-more').text(textLess).attr('data-visual-nav', textLess.toLowerCase());\n }\n });\n}\n\nexports.init = function () {\n compareWidget.init();\n if (SitePreferences.LISTING_INFINITE_SCROLL) {\n $(window).on('scroll', infiniteScroll);\n }\n if (SitePreferences.LISTING_CATEGORY_LAZY_LOAD) {\n categoryLazyLoad();\n }\n updateProductAmountSection();\n\n if ($('.h1-navigation').length && $(window).width() > 1024) {\n var $h1NavUl, sw, w;\n $h1NavUl = $('.h1-navigation ul');\n $h1NavUl.removeClass('mobile-tablet');\n sw = $('.h1-navigation').width();\n $h1NavUl.css('position', 'absolute');\n w = $h1NavUl.width();\n $h1NavUl.css('position', 'relative');\n\n $h1NavUl.draggable({\n axis: 'x',\n start: function () {\n if (sw >= w) return false;\n },\n stop: function (e, ui) {\n if (ui.offset.left > 0) $h1NavUl.css('left', '0px');\n if (ui.offset.left < sw - w) $h1NavUl.css('left', (sw-w)+'px');\n }\n });\n }\n\n productTile.init();\n initPriceSlider();\n paginationBackToTop();\n initializeEvents();\n initSortingSelector();\n //customOrdergroove.disableDefaultOption();\n hybridInit();\n};","'use strict';\nvar util = require('../util'),\n imagesLoaded = require('imagesloaded'),\n addToCart = require('org/pages/product/addToCart');\n\nexports.init = function () {\n $('.product-add-to-cart-plp').on('click', '.add-to-cart', addToCart.addToCart);\n $('#homepage-main-carousel .carousel-elements').slick({\n speed: 300,\n dots: true,\n arrows: false,\n slidesToShow: 1,\n slidesToScroll: 1,\n centerMode: false\n });\n\n $('#homepage-loved-content .carousel-elements').slick({\n speed: 300,\n dots: false,\n arrows: true,\n slidesToShow: 3,\n slidesToScroll: 1,\n centerMode: true,\n centerPadding: '12.5%',\n prevArrow: $('.go-left'),\n nextArrow: $('.go-right'),\n responsive: [\n {\n breakpoint: 767,\n settings: {\n arrows: false,\n slidesToShow: 1,\n centerPadding: '15%'\n }\n }\n ]\n });\n $('#homepage-popular-brands .carousel-elements').slick({\n speed: 300,\n dots: false,\n arrows: true,\n slidesToShow: 6,\n slidesToScroll: 1,\n responsive: [\n {\n breakpoint: 767,\n settings: {\n arrows: false,\n centerMode: true,\n centerPadding: '30%',\n slidesToShow: 1\n }\n }\n ]\n });\n //New arrivals section functionality\n function createNewArrivals($selector) {\n var newArrivalsOptions = {\n speed: 300,\n dots: false,\n arrows: true,\n slidesToShow: 5,\n slidesToScroll: 1,\n responsive: [\n {\n breakpoint: 767,\n settings: {\n arrows: false,\n centerMode: true,\n centerPadding: '25%',\n slidesToShow: 1\n }\n }\n ]\n };\n $selector.addClass('active').slick(newArrivalsOptions);\n equalTileHeight();\n }\n\n util.smartResize(function () {\n equalTileHeight();\n });\n\n /**\n * @private\n * @function\n * @description set equal tile height\n */\n function equalTileHeight() {\n var $tiles = $('#homepage-new-arrivals .product-tile');\n if ($tiles.length === 0) { return; }\n if ($tiles.length > 1 && $tiles.is('[style]')) {\n $tiles.removeAttr('style');\n }\n imagesLoaded('.tiles-container').on('always', function () {\n $tiles.syncHeight()\n .each(function (idx) {\n $(this).data('idx', idx);\n });\n });\n }\n\n /**\n * @private\n * @function\n * @description initialize new arrivals carousel\n */\n createNewArrivals($('#homepage-new-arrivals .carousel-elements.active'));\n //New arrivals section tabs handling\n $('.new-arrivals-wrapper .category-links li').on('click', function () {\n if (!$(this).hasClass('active')) {\n $(this).siblings().removeClass('active');\n $('#homepage-new-arrivals .carousel-elements.active').slick('unslick').removeClass('active');\n $(this).addClass('active');\n var carouselNumber = '#carousel-' + $(this).data('number');\n createNewArrivals($('#homepage-new-arrivals ' + carouselNumber));\n }\n })\n\n};\n","'use strict';\nvar dialog = require('../dialog');\n\n/**\n * Set Default Country in country search dropdown\n */\nfunction setDefaultCountry () {\n var country = $('#dwfrm_storelocator').data('defaultcountry');\n var ddlValue = $('#dwfrm_storelocator_country option[value='+ country +']');\n\n if (ddlValue.length > 0) {\n $('#dwfrm_storelocator_country').val(ddlValue.val());\n }\n}\n\n$('.local-store-options-container').find('a').bind('click', function(e) {\n e.preventDefault(); \n var storeId;\n \n if ($(this).attr('data-store-id')) {\n storeId = $(this).attr('data-store-id');\n } else {\n storeId = '';\n }\n \n $.ajax({\n url: Urls.isAuthenticated,\n success: function (response) {\n var authenticated = response.isAuthenticated;\n if (authenticated) {\n $.ajax({\n url: Urls.setLocalStore,\n type: 'POST',\n data: {\n storeId: storeId,\n page: 'store-details'\n },\n success: function(response) {\n var status = response.status;\n if (status === 'OK') {\n location.reload();\n } \n }\n });\n } else {\n var setStoreDialog = $(document.createElement('div'));\n $(setStoreDialog).html(Resources.REGISTERED_CUSTOMER);\n $(setStoreDialog).dialog({\n title: Resources.LOGIN_REGISTER,\n autoResize: false,\n height: 'auto',\n width: 400,\n modal: true,\n buttons: [\n {\n text: Resources.LOGIN,\n click: function() {\n window.location.replace(Urls.loginShow);\n }\n },\n {\n text: Resources.REGISTER,\n click: function() {\n window.location.replace(Urls.register);\n }\n }\n ]\n });\n }\n }\n });\n});\n\nexports.init = function () {\n $('.store-details-link').on('click', function (e) {\n e.preventDefault();\n dialog.open({\n url: $(e.target).attr('href')\n });\n });\n\n setDefaultCountry();\n\n};\n","'use strict';\n\nvar addProductToCart = require('org/pages/product/addToCart'),\n page = require('base/page'),\n login = require('org/login'),\n util = require('org/util');\n\nexports.init = function () {\n addProductToCart();\n $('#editAddress').on('change', function () {\n page.redirect(util.appendParamToURL(Urls.wishlistAddress, 'AddressID', $(this).val()));\n });\n\n //add js logic to remove the , from the qty feild to pass regex expression on client side\n $('.option-quantity-desired input').on('focusout', function () {\n $(this).val($(this).val().replace(',', ''));\n });\n\n login.init();\n\n};\n","'use strict';\n\nvar imagesLoaded = require('imagesloaded'),\n quickview = require('org/quickview'),\n einstein = require('org/einstein'),\n addToCart = require('org/pages/product/addToCart'),\n util = require('org/util'),\n tooltip = require('org/tooltip'),\n tagmanager = require('int_googletags/tagmanager');\n\n/* ++++++++ BOPIS CODE STARTS ++++++++ */\n// var dialog = require('./dialog');\n/* ++++++++ BOPIS CODE ENDS ++++++++ */\n\n/**\n * @function\n * @description Sets up the quick view button when called with the appropriate values\n * @param {jQuery} $link - Link that is used to define the quick view button\n * @param {boolean} focus - Mark this as true when the focus event is used\n */\nfunction initQuickView() {\n $('.quickview').on('click', function (e) {\n e.preventDefault();\n var link = $(this).attr('href')\n quickview.show({\n url: link,\n source: 'quickview'\n });\n });\n}\n\n/**\n * @function\n * @description Sets up the product tile so that when the mouse cursor enters the tile the quick view button appears\n */\nfunction initQuickViewButtons() {\n var $qvContainer = $('.tiles-container .product-tile');\n $qvContainer.on('mouseenter', function () {\n var $link = $(this).find('a.link-wrap');\n initQuickView($link, true);\n });\n $qvContainer.on('mouseleave', function () {\n var $link = $(this).find('a.link-wrap');\n initQuickView($link);\n });\n}\n\nfunction gridViewToggle() {\n $('.toggle-grid').on('click', function () {\n $('.search-result-content').toggleClass('wide-tiles');\n $(this).toggleClass('wide');\n });\n}\n\n/**\n * @private\n * @function\n * @description set equal tile height\n */\nfunction equalTileHeight() {\n var $tiles = $('.tiles-container .product-tile');\n if ($tiles.length === 0) { return; }\n if ($tiles.length > 1 && $tiles.is('[style]')) {\n $tiles.removeAttr('style');\n }\n imagesLoaded('.tiles-container').on('always', function () {\n $tiles.syncHeight()\n .each(function (idx) {\n $(this).data('idx', idx);\n });\n });\n}\n\n/**\n * @private\n * @function\n * @description Initializes events on the product-tile for the following elements:\n * - swatches\n * - thumbnails\n */\nfunction initializeEvents() {\n $('#d6-clrs-container').on('click', '.add-to-cart', addToCart.addToCart); // 'spring-gift-guide' page\n $('#search-result-items').on('click', '.add-to-cart', addToCart.addToCart);\n $('#new-arrivals-slot').on('click', '.add-to-cart', addToCart.addToCart); \n tagmanager.initAddToCart();\n initQuickView();\n gridViewToggle();\n $('.swatch-list').on('mouseleave', function () {\n // Restore current thumb image\n var $tile = $(this).closest('.product-tile'),\n $thumb = $tile.find('.product-image .thumb-link img').eq(0),\n data = $thumb.data('current');\n\n $thumb.attr({\n src: data.src,\n alt: data.alt,\n title: data.title\n });\n });\n $('.swatch-list .swatch').on('click', function (e) {\n e.preventDefault();\n if ($(this).hasClass('selected')) { return; }\n\n var $tile = $(this).closest('.product-tile');\n $(this).closest('.swatch-list').find('.swatch.selected').removeClass('selected');\n $(this).addClass('selected');\n $tile.find('.thumb-link').attr('href', $(this).attr('href'));\n $tile.find('name-link').attr('href', $(this).attr('href'));\n\n var data = $(this).children('img').filter(':first').data('thumb');\n var $thumb = $tile.find('.product-image .thumb-link img').eq(0);\n var currentAttrs = {\n src: data.src,\n alt: data.alt,\n title: data.title\n };\n $thumb.attr(currentAttrs);\n $thumb.data('current', currentAttrs);\n }).on('mouseenter', function () {\n // get current thumb details\n var $tile = $(this).closest('.product-tile'),\n $thumb = $tile.find('.product-image .thumb-link img').eq(0),\n data = $(this).children('img').filter(':first').data('thumb'),\n current = $thumb.data('current');\n\n // If this is the first time, then record the current img\n if (!current) {\n $thumb.data('current', {\n src: $thumb[0].src,\n alt: $thumb[0].alt,\n title: $thumb[0].title\n });\n }\n\n // Set the tile image to the values provided on the swatch data attributes\n $thumb.attr({\n src: data.src,\n alt: data.alt,\n title: data.title\n });\n });\n $('#carousel-recommendations:not(.slick-initialized)').on('init', function() {\n $('.product-tile-info', $(this)).syncHeight();\n });\n $('#carousel-recommendations:not(.slick-initialized)').slick({\n speed: 300,\n dots: (util.isIcingSite() && $('.pt_product-search-noresult').length) ? true : false, // nav dots on for Icing No Results search page\n arrows: true,\n slidesToShow: 4,\n slidesToScroll: 1,\n responsive: [\n {\n breakpoint: util.getViewports('desktop'),\n settings: {\n slidesToShow: 2,\n arrows: false,\n centerMode: true\n }\n },\n {\n breakpoint: util.getViewports('large'),\n settings: {\n slidesToShow: 1,\n arrows: false,\n centerMode: true,\n centerPadding: '24.5%'\n }\n }\n ]\n });\n\n // this event specific to some pages and carousels\n einstein.onEinstainContentAdded('.carousel-recommendations-einstein', function () {\n $('.carousel-recommendations-einstein:not(.carousel-inited)').each(function () {\n $(this).parents('einstain-inited').addClass('einstain-clear');\n $(this).addClass('carousel-inited');\n $(this).slick({\n infinite: false,\n speed: 300,\n slidesToShow: 4,\n dots: true,\n responsive: [\n {\n breakpoint: util.getViewports('desktop'),\n settings: {\n slidesToShow: 4,\n infinite: true,\n dots: true\n }\n },\n {\n breakpoint: util.getViewports('large'),\n settings: {\n slidesToShow: 1,\n infinite: true,\n centerMode: true,\n dots: true,\n centerPadding: '20%'\n }\n }\n ]\n });\n\n $('.carousel-recommendations-einstein .product-tile-info').syncHeight();\n $('.add-to-cart-recommend').on('click', addToCart.addToCart);\n\n });\n });\n\n}\n\n/* ++++++++ BOPIS CODE STARTS ++++++++ */\n/**\n * @private\n * @function\n * @description Handle space for bopis messages inside tiles\n */\n\nfunction handleBopisMessagesSpace() {\n var $bopisMessages = $('div.product-tile').find('.product-delivery');\n var messagesMaximumHeight = 0;\n var messagesMaximumOuterHeight = 0;\n if ($bopisMessages.length > 0) {\n $bopisMessages.each(function() {\n if ($(this).outerHeight() > messagesMaximumOuterHeight) {\n messagesMaximumHeight = $(this).height();\n messagesMaximumOuterHeight = $(this).outerHeight();\n }\n })\n\n // Set all bopis messages blocks the same height\n $bopisMessages.css('height', messagesMaximumHeight + 'px');\n\n // Set all tiles same extra padding to make room for bopis messages\n var productTilePB = parseInt($('div.product-tile').css('padding-bottom'), 10);\n var productTileNewPB = productTilePB + messagesMaximumOuterHeight;\n $('div.product-tile').css('padding-bottom', productTileNewPB + 'px');\n }\n}\n\n/* ++++++++ BOPIS CODE ENDS ++++++++ */\n\n\nexports.init = function () {\n tooltip.init();\n equalTileHeight();\n /* ++++++++ BOPIS CODE STARTS ++++++++ */\n handleBopisMessagesSpace();\n /* ++++++++ BOPIS CODE ENDS ++++++++ */\n util.smartResize(function () {\n if (util.isDesktopViewport()) {\n equalTileHeight();\n /* ++++++++ BOPIS CODE STARTS ++++++++ */\n handleBopisMessagesSpace();\n }\n /* ++++++++ BOPIS CODE ENDS ++++++++ */\n });\n initializeEvents();\n};\n","'use strict';\n\nvar dialog = require('org/dialog'),\n product = require('org/pages/product/index'),\n util = require('org/util'),\n _ = require('lodash'),\n tagmanager = require('int_googletags/tagmanager');\n\n\nvar makeUrl = function (url, source, productListID) {\n if (source) {\n url = util.appendParamToURL(url, 'source', source);\n }\n if (productListID) {\n url = util.appendParamToURL(url, 'productlistid', productListID);\n }\n return url;\n};\n\nvar removeParam = function (url) {\n if (url.indexOf('?') !== -1) {\n return url.substring(0, url.indexOf('?'));\n } else {\n return url;\n }\n};\n\nvar quickview = {\n init: function () {\n if (!this.exists()) {\n this.$container = $('').attr('id', 'QuickViewDialog').appendTo(document.body);\n }\n this.productLinks = $('#search-result-items .thumb-link').map(function (index, thumbLink) {\n return $(thumbLink).attr('href');\n });\n },\n\n setup: function (qvUrl) {\n var $btnNext = $('.quickview-next'),\n $btnPrev = $('.quickview-prev');\n\n product.initializeEvents();\n tagmanager.initAddToCart();\n\n this.productLinkIndex = _(this.productLinks).findIndex(function (url) {\n return removeParam(url) === removeParam(qvUrl);\n });\n\n // hide the buttons on the compare page or when there are no other products\n if (this.productLinks.length <= 1 || $('.compareremovecell').length > 0) {\n $btnNext.hide();\n $btnPrev.hide();\n return;\n }\n\n if (this.productLinkIndex === this.productLinks.length - 1) {\n $btnNext.attr('disabled', 'disabled');\n }\n if (this.productLinkIndex === 0) {\n $btnPrev.attr('disabled', 'disabled');\n }\n\n $btnNext.on('click', function (e) {\n e.preventDefault();\n this.navigateQuickview(1);\n }.bind(this));\n $btnPrev.on('click', function (e) {\n e.preventDefault();\n this.navigateQuickview(-1);\n }.bind(this));\n },\n\n /**\n * @param {Number} step - How many products away from current product to navigate to. Negative number means navigate backward\n */\n navigateQuickview: function (step) {\n // default step to 0\n this.productLinkIndex += (step ? step : 0);\n var url = makeUrl(this.productLinks[this.productLinkIndex], 'quickview');\n dialog.replace({\n url: url,\n callback: this.setup.bind(this, url)\n });\n },\n\n /**\n * @description show quick view dialog\n * @param {Object} options\n * @param {String} options.url - url of the product details\n * @param {String} options.source - source of the dialog to be appended to URL\n * @param {String} options.productlistid - to be appended to URL\n * @param {Function} options.callback - callback once the dialog is opened\n */\n show: function (options) {\n var url;\n if (!this.exists()) {\n this.init();\n }\n url = makeUrl(options.url, options.source, options.productlistid);\n\n dialog.open({\n target: this.$container,\n url: url,\n options: {\n width: 920,\n open: function () {\n //close window when the user clicks off the screen\n var overlayArea = $('.ui-widget-overlay');\n var buttonClose = $('.ui-dialog-titlebar-close');\n overlayArea.on('click', function() {\n buttonClose.trigger('click');\n });\n buttonClose.focus();\n this.setup(url);\n if (typeof options.callback === 'function') { options.callback(); }\n }.bind(this)\n }\n });\n },\n exists: function () {\n return this.$container && (this.$container.length > 0);\n }\n};\n\nmodule.exports = quickview;\n","'use strict';\n\nvar recaptcha = {\n noToken: 'NaN',\n checksTriggered: {}, // map action to callback, to wait for script load\n timer: null,\n timeoutBase: 500,\n timeoutTotal: 0,\n maxTimeout: 2500,\n loadFailed: false // flag reflecting grecaptcha script being loaded\n};\n\n/*\n Method to wait for grecaptcha script and then \n*/\nrecaptcha.loadAndRun = function () {\n // verify grecaptcha to be loaded and ready\n if (!window.grecaptcha || !window.grecaptcha.ready) {\n if (recaptcha.timer) {\n window.clearTimeout(recaptcha.timer);\n }\n if (recaptcha.timeoutTotal >= recaptcha.maxTimeout) {\n window.console.warn('Recaptcha load failed in', recaptcha.timeoutTotal);\n recaptcha.loadFailed = true;\n recaptcha.resolveCallbacks();\n return;\n }\n recaptcha.timer = window.setTimeout(recaptcha.loadAndRun, recaptcha.timeoutBase);\n recaptcha.timeoutTotal += recaptcha.timeoutBase;\n return;\n }\n\n window.grecaptcha.ready(recaptcha.resolveCallbacks);\n}\n\n/*\n Fire all saved callbacks and clean up saved object\n if recaptcha available it will be called from recaptcha.execute\n otherwise run with empty token value\n*/\nrecaptcha.resolveCallbacks = function () {\n for (var action in recaptcha.checksTriggered) {\n var callback = recaptcha.checksTriggered[action];\n delete recaptcha.checksTriggered[action];\n if (recaptcha.loadFailed) {\n callback(recaptcha.noToken);\n } else {\n window.grecaptcha.execute(SitePreferences.RECAPTCHA_V3_SITE_KEY, {action: action}).then(callback);\n }\n }\n}\n\n/*\n Verify grecaptcha enabled and expected for given action\n*/\nrecaptcha.enabled = function () {\n // currently no action specific checks, so just verify site key\n if (!SitePreferences.RECAPTCHA_V3_SITE_KEY) {\n return false;\n }\n\n return true;\n}\n\n/*\n Update provided $form html with recaptcha token\n*/\nrecaptcha.updateFormTokenInput = function ($form, token) {\n // handle input search or append\n var $tokenInput = $form.find('input[name=\"recaptchatoken\"]');\n if (!$tokenInput.length) {\n $tokenInput = $('').attr({\n type: 'hidden',\n name: 'recaptchatoken'\n }).appendTo($form);\n }\n\n // update value and trigger change event that might be used\n $tokenInput.val(token).change();\n}\n\n/*\n Primary method to run recaptcha verification\n provided callback will be executed as part of recaptcha or with pre set value\n*/\nrecaptcha.check = function (action, callback) {\n // check if recaptcha enabled for given action\n if (!recaptcha.enabled(action)) {\n callback(recaptcha.noToken);\n return;\n }\n\n recaptcha.checksTriggered[action] = callback;\n recaptcha.loadAndRun();\n}\n\nmodule.exports = recaptcha;\n","exports.init = function init () {\n if ($(\".award-container-carousel\").length > 0) {\n $(\".swiper-wrapper\").slick({\n slidesToShow: 3,\n slidesToScroll: 1,\n arrows: true,\n dots: false,\n infinite: false,\n centerMode: false,\n variableWidth: false,\n prevArrow: $(\".prev-btn\"),\n nextArrow: $(\".next-btn\"),\n responsive: [\n {\n breakpoint: 1200,\n settings: {\n slidesToShow: 3,\n }\n },\n {\n breakpoint: 991,\n settings: {\n slidesToShow: 2,\n }\n },\n {\n breakpoint: 767,\n settings: {\n slidesToShow: 1,\n centerMode: true,\n }\n }\n ]\n });\n }\n};\n","'use strict';\n\nvar util = require('./util');\n\nvar currentQuery = null,\n lastQuery = null,\n runningQuery = null,\n listTotal = -1,\n listCurrent = -1,\n delay = 30,\n $resultsContainer;\n/**\n * @function\n * @description Handles keyboard's arrow keys\n * @param keyCode Code of an arrow key to be handled\n */\nfunction handleArrowKeys(keyCode) {\n switch (keyCode) {\n case 38:\n // keyUp\n listCurrent = (listCurrent <= 0) ? (listTotal - 1) : (listCurrent - 1);\n break;\n case 40:\n // keyDown\n listCurrent = (listCurrent >= listTotal - 1) ? 0 : listCurrent + 1;\n break;\n default:\n // reset\n listCurrent = -1;\n return false;\n }\n\n $resultsContainer.children().removeClass('selected').eq(listCurrent).addClass('selected');\n $('input[name=\"q\"]').val($resultsContainer.find('.selected .suggestionterm').first().text());\n return true;\n}\n\n/**\n * Push impressions into DataLayer\n */\nfunction getSuggestedImpressionData (suggestedProducts) {\n\n var obj = {\n 'event': 'ecom.impression',\n 'currencyCode': window.Constants.CURRENCY_CODE,\n 'productCount': suggestedProducts.length,\n 'ecommerce': {\n 'impressions': []\n }\n };\n\n for (var i = 0; i < suggestedProducts.length; i++) {\n obj.ecommerce.impressions.push(suggestedProducts[i]);\n }\n\n dataLayer.push(obj);\n}\n\nvar searchsuggest = {\n /**\n * @function\n * @description Configures parameters and required object instances\n */\n init: function (container, defaultValue) {\n var $searchContainer = $(container);\n var $searchForm = $searchContainer.find('form[name=\"simpleSearch\"]');\n var $searchField = $searchForm.find('input[name=\"q\"]');\n\n // disable browser auto complete\n $searchField.attr('autocomplete', 'off');\n\n // on focus listener (clear default value)\n $searchField.focus(function () {\n if (!$resultsContainer) {\n // create results container if needed\n $resultsContainer = $('').attr('id', 'search-suggestions').appendTo($searchContainer);\n }\n if ($searchField.val() === defaultValue) {\n $searchField.val('');\n }\n });\n\n $(document).on('click', function (e) {\n if (!$searchContainer.is(e.target)) {\n setTimeout(this.clearResults, 200);\n }\n }.bind(this));\n // on key up listener\n $searchField.keyup(function (e) {\n\n // get keyCode (window.event is for IE)\n var keyCode = e.keyCode || window.event.keyCode;\n\n // check and treat up and down arrows\n if (handleArrowKeys(keyCode)) {\n return;\n }\n // check for an ENTER or ESC\n if (keyCode === 13 || keyCode === 27) {\n this.clearResults();\n return;\n }\n\n currentQuery = $searchField.val().trim();\n\n // no query currently running, init an update\n if (!runningQuery) {\n runningQuery = currentQuery;\n setTimeout(this.suggest.bind(this), delay);\n }\n }.bind(this));\n },\n\n /**\n * @function\n * @description trigger suggest action\n */\n suggest: function () {\n // check whether query to execute (runningQuery) is still up to date and had not changed in the meanwhile\n // (we had a little delay)\n if (runningQuery !== currentQuery) {\n // update running query to the most recent search phrase\n runningQuery = currentQuery;\n }\n\n // if it's empty clear the results box and return\n if (runningQuery.length === 0) {\n this.clearResults();\n runningQuery = null;\n return;\n }\n\n // if the current search phrase is the same as for the last suggestion call, just return\n if (lastQuery === runningQuery) {\n runningQuery = null;\n return;\n }\n\n // build the request url\n var reqUrl = util.appendParamToURL(Urls.searchsuggest, 'q', runningQuery);\n\n // execute server call\n $.get(reqUrl, function (data) {\n var suggestionHTML = data,\n ansLength = suggestionHTML.trim().length,\n suggestedProducts = [];\n\n // if there are results populate the results div\n if (ansLength === 0) {\n this.clearResults();\n } else {\n // update the results div\n $resultsContainer.html(suggestionHTML).fadeIn(200);\n //Pass suggested product data to DataLayer\n var productSuggestions = $('body').find('.product-link');\n productSuggestions.each(function() {\n var impressionObj = $.parseJSON($(this).attr('data-gtmdata'));\n suggestedProducts.push(impressionObj);\n });\n }\n getSuggestedImpressionData(suggestedProducts);\n // record the query that has been executed\n lastQuery = runningQuery;\n // reset currently running query\n runningQuery = null;\n\n // check for another required update (if current search phrase is different from just executed call)\n if (currentQuery !== lastQuery) {\n // ... and execute immediately if search has changed while this server call was in transit\n runningQuery = currentQuery;\n setTimeout(this.suggest.bind(this), delay);\n }\n this.hideLeftPanel();\n }.bind(this));\n },\n\n /**\n * @function\n * @description\n */\n clearResults: function () {\n if (!$resultsContainer) { return; }\n $resultsContainer.fadeOut(200, function () {$resultsContainer.empty();});\n },\n /**\n * @function\n * @description\n */\n hideLeftPanel: function () {\n //hide left panel if there is only a matching suggested custom phrase\n if ($('.search-suggestion-left-panel-hit').length === 1 && $('.search-phrase-suggestion a').text().replace(/(^[\\s]+|[\\s]+$)/g, '').toUpperCase() === $('.search-suggestion-left-panel-hit a').text().toUpperCase()) {\n $('.search-suggestion-left-panel').css('display', 'none');\n $('.search-suggestion-wrapper-full').addClass('search-suggestion-wrapper');\n $('.search-suggestion-wrapper').removeClass('search-suggestion-wrapper-full');\n }\n }\n};\n\nmodule.exports = searchsuggest;\n","'use strict';\n\n/**\n * @function\n * @description Initializes the tooltip-content and layout\n */\nvar dialog = require('./dialog'),\n util = require('./util');\n\nexports.init = function () {\n if (util.isMobile() && $(document).width() < 768) {\n $(document).on('click', '.tooltip:not(.product-tile)', function (e) {\n e.preventDefault();\n dialog.open({\n html: $(e.target).find('.tooltip-content').html(),\n options: {\n title: 'Details'\n }\n });\n });\n } else {\n $(document).tooltip({\n items: '.tooltip',\n track: true,\n content: function () {\n return $(this).find('.tooltip-content').html();\n }\n });\n }\n\n $('.share-link').on('click', function (e) {\n e.preventDefault();\n var target = $(this).data('target');\n if (!target) {\n return;\n }\n $(target).toggleClass('active');\n });\n};\n","'use strict';\n\nvar _ = require('lodash'),\n smallBreakpoint = 320,\n mediumBreakpoint = 480,\n largeBreakpoint = 768,\n desktopBreakpoint = 1024;\n\nvar util = {\n /**\n * @function\n * @description Returns either an object with all of the available viewports or a specific viewport based on the given size\n * @param {String} size The viewport to return\n */\n getViewports: function (size) {\n var viewports = {\n small: smallBreakpoint,\n medium: mediumBreakpoint,\n large: largeBreakpoint,\n desktop: desktopBreakpoint\n };\n\n if (typeof size !== 'undefined') {\n var viewport = viewports[size];\n\n if (viewport) {\n return viewport;\n } else {\n window.console.error('Unexpected viewport size given in util.getViewports');\n throw 'Unexpected viewport size given in util.getViewports';\n }\n } else {\n return viewports;\n }\n },\n\n /**\n * @function\n * @description Returns the current viewport name (ex: 'medium') or 'max' if the current window is larger than any defined viewport width\n */\n getCurrentViewport: function () {\n var w = window.innerWidth;\n var viewports = util.getViewports();\n //traverse the object from small up to desktop, and return the first match\n _.each(viewports, function (value, name) {\n if (w <= value) {\n return name;\n }\n });\n return 'max';\n },\n\n /**\n * @function\n * @description appends the parameter with the given name and value to the given url and returns the changed url\n * @param {String} url the url to which the parameter will be added\n * @param {String} name the name of the parameter\n * @param {String} value the value of the parameter\n */\n appendParamToURL: function (url, name, value) {\n // quit if the param already exists\n if (url.indexOf(name + '=') !== -1) {\n return url;\n }\n var separator = url.indexOf('?') !== -1 ? '&' : '?';\n return url + separator + name + '=' + encodeURIComponent(value);\n },\n\n /**\n * @function\n * @description remove the parameter and its value from the given url and returns the changed url\n * @param {String} url the url from which the parameter will be removed\n * @param {String} name the name of parameter that will be removed from url\n */\n removeParamFromURL: function (url, name) {\n if (url.indexOf('?') === -1 || url.indexOf(name + '=') === -1) {\n return url;\n }\n var hash;\n var params;\n var domain = url.split('?')[0];\n var paramUrl = url.split('?')[1];\n var newParams = [];\n // if there is a hash at the end, store the hash\n if (paramUrl.indexOf('#') > -1) {\n hash = paramUrl.split('#')[1] || '';\n paramUrl = paramUrl.split('#')[0];\n }\n params = paramUrl.split('&');\n for (var i = 0; i < params.length; i++) {\n // put back param to newParams array if it is not the one to be removed\n if (params[i].split('=')[0] !== name) {\n newParams.push(params[i]);\n }\n }\n return domain + '?' + newParams.join('&') + (hash ? '#' + hash : '');\n },\n\n /**\n * @function\n * @description appends the parameters to the given url and returns the changed url\n * @param {String} url the url to which the parameters will be added\n * @param {Object} params\n */\n appendParamsToUrl: function (url, params) {\n var _url = url;\n _.each(params, function (value, name) {\n _url = this.appendParamToURL(_url, name, value);\n }.bind(this));\n return _url;\n },\n /**\n * @function\n * @description extract the query string from URL\n * @param {String} url the url to extra query string from\n **/\n getQueryString: function (url) {\n var qs;\n if (!_.isString(url)) { return; }\n var a = document.createElement('a');\n a.href = url;\n if (a.search) {\n qs = a.search.substr(1); // remove the leading ?\n }\n return qs;\n },\n /**\n * @function\n * @description\n * @param {String}\n * @param {String}\n */\n elementInViewport: function (el, offsetToTop) {\n var top = el.offsetTop,\n left = el.offsetLeft,\n width = el.offsetWidth,\n height = el.offsetHeight;\n\n while (el.offsetParent) {\n el = el.offsetParent;\n top += el.offsetTop;\n left += el.offsetLeft;\n }\n\n if (typeof(offsetToTop) !== 'undefined') {\n top -= offsetToTop;\n }\n\n if (window.pageXOffset !== null) {\n return (\n top < (window.pageYOffset + window.innerHeight) &&\n left < (window.pageXOffset + window.innerWidth) &&\n (top + height) > window.pageYOffset &&\n (left + width) > window.pageXOffset\n );\n }\n\n if (document.compatMode === 'CSS1Compat') {\n return (\n top < (window.document.documentElement.scrollTop + window.document.documentElement.clientHeight) &&\n left < (window.document.documentElement.scrollLeft + window.document.documentElement.clientWidth) &&\n (top + height) > window.document.documentElement.scrollTop &&\n (left + width) > window.document.documentElement.scrollLeft\n );\n }\n },\n\n /**\n * @function\n * @description Appends the parameter 'format=ajax' to a given path\n * @param {String} path the relative path\n */\n ajaxUrl: function (path) {\n return this.appendParamToURL(path, 'format', 'ajax');\n },\n\n /**\n * @function\n * @description\n * @param {String} url\n */\n toAbsoluteUrl: function (url) {\n if (url.indexOf('http') !== 0 && url.charAt(0) !== '/') {\n url = '/' + url;\n }\n return url;\n },\n /**\n * @function\n * @description Loads css dynamically from given urls\n * @param {Array} urls Array of urls from which css will be dynamically loaded.\n */\n loadDynamicCss: function (urls) {\n var i, len = urls.length;\n for (i = 0; i < len; i++) {\n this.loadedCssFiles.push(this.loadCssFile(urls[i]));\n }\n },\n\n /**\n * @function\n * @description Loads css file dynamically from given url\n * @param {String} url The url from which css file will be dynamically loaded.\n */\n loadCssFile: function (url) {\n return $('').appendTo($('head')).attr({\n type: 'text/css',\n rel: 'stylesheet'\n }).attr('href', url); // for i.e. <9, href must be added after link has been appended to head\n },\n // array to keep track of the dynamically loaded CSS files\n loadedCssFiles: [],\n\n /**\n * @function\n * @description Removes all css files which were dynamically loaded\n */\n clearDynamicCss: function () {\n var i = this.loadedCssFiles.length;\n while (0 > i--) {\n $(this.loadedCssFiles[i]).remove();\n }\n this.loadedCssFiles = [];\n },\n /**\n * @function\n * @description Extracts all parameters from a given query string into an object\n * @param {String} qs The query string from which the parameters will be extracted\n */\n getQueryStringParams: function (qs) {\n if (!qs || qs.length === 0) { return {}; }\n var params = {},\n unescapedQS = decodeURIComponent(qs);\n // Use the String::replace method to iterate over each\n // name-value pair in the string.\n unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'),\n function ($0, $1, $2, $3) {\n params[$1] = $3;\n }\n );\n return params;\n },\n\n fillAddressFields: function (address, $form) {\n for (var field in address) {\n if (field === 'ID' || field === 'UUID' || field === 'key') {\n continue;\n }\n //check for not to find cardtype input\n // if the key in address object ends with 'Code', remove that suffix\n // keys that ends with 'Code' are postalCode, stateCode and countryCode\n if (field.replace('Code', '') !== 'type') {\n $form.find('[name$=\"' + field.replace('Code', '') + '\"]').val(address[field]).trigger('change');\n }\n // update the state fields\n if (field === 'countryCode') {\n $form.find('[name$=\"country\"]').trigger('change');\n // retrigger state selection after country has changed\n // this results in duplication of the state code, but is a necessary evil\n // for now because sometimes countryCode comes after stateCode\n $form.find('[name$=\"state\"]').val(address.stateCode);\n }\n }\n },\n /**\n * @function\n * @description Updates the number of the remaining character\n * based on the character limit in a text area\n */\n limitCharacters: function () {\n $('form').find('textarea[data-character-limit]').each(function () {\n var characterLimit = $(this).data('character-limit');\n var charCountHtml = String.format(Resources.CHAR_LIMIT_MSG,\n '' + characterLimit + '',\n '' + characterLimit + '');\n var charCountContainer = $(this).next('div.char-count');\n if (charCountContainer.length === 0) {\n charCountContainer = $('
').insertAfter($(this));\n }\n charCountContainer.html(charCountHtml);\n // trigger the keydown event so that any existing character data is calculated\n $(this).change();\n });\n },\n /**\n * @function\n * @description Binds the onclick-event to a delete button on a given container,\n * which opens a confirmation box with a given message\n * @param {String} container The name of element to which the function will be bind\n * @param {String} message The message the will be shown upon a click\n */\n setDeleteConfirmation: function (container, message) {\n $(container).on('click', '.delete', function () {\n return window.confirm(message);\n });\n },\n /**\n * @function\n * @description Scrolls a browser window to a given x point\n * @param {String} The x coordinate\n */\n scrollBrowser: function (xLocation) {\n $('html, body').animate({scrollTop: xLocation}, 500);\n },\n\n isMobile: function () {\n var mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];\n var idx = 0;\n var isMobile = false;\n var userAgent = (navigator.userAgent).toLowerCase();\n\n while (mobileAgentHash[idx] && !isMobile) {\n isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);\n idx++;\n }\n return isMobile;\n },\n\n /**\n * @function\n * @description Updates the states options to a given country\n * @param {String} form The form to update states options\n */\n updateStateOptions: function(form, stateValue) {\n var $form = $(form),\n arrHtml = [],\n $country = $form.find('select[id$=\"_country\"]:enabled'),\n country = window.Countries[$country.val().toUpperCase()],\n $stateField = $country.data('stateField') ? $country.data('stateField') : $form.find('select[name$=\"_state\"]'),\n $postalField = $country.data('postalField') ? $country.data('postalField') : $form.find('input[name$=\"_postal\"]'),\n $stateLabel = ($stateField.length > 0) ? $form.find('label[for=\"' + $stateField[0].id + '\"] span').not('.required-indicator') : undefined,\n $postalLabel = ($postalField.length > 0) ? $form.find('label[for=\"' + $postalField[0].id + '\"] span').not('.required-indicator') : undefined,\n prevStateValue = $stateField.val() || stateValue;\n // set the label text\n if ($postalLabel) {\n $postalLabel.html(country.postalLabel);\n }\n if ($stateLabel) {\n $stateLabel.html(country.regionLabel);\n } else {\n return;\n }\n var s, i = 0;\n for (s in country.regions) {\n arrHtml.push('');\n i++;\n }\n $stateField.html(arrHtml.join('')).removeAttr('disabled');\n // if a state was selected previously, save that selection\n if (prevStateValue && typeof country.regions[prevStateValue] !== 'undefined') {\n $stateField.val(prevStateValue).trigger('change');\n } else if (i === 1) {\n // if there is only one region, make that region selected\n $stateField[0].selectedIndex = 1;\n } else {\n $stateField[0].selectedIndex = 0;\n }\n },\n /**\n * Execute callback function when the user has stopped resizing the screen.\n * @param callback {Function} The callback function to execute.\n */\n smartResize: function (callback) {\n var timeout;\n\n window.addEventListener('resize', function () {\n clearTimeout(timeout);\n timeout = setTimeout(callback, 100);\n });\n\n return callback;\n },\n /**\n * Return boolean indicating whether the browser is currently being displayed in the \"mobile\" viewport (below 768)\n * Ex: if (util.isMobileViewport()) //do something\n */\n isMobileViewport: function () {\n return $(window).innerWidth() < this.getViewports('large');\n },\n\n /**\n * Return boolean indicating whether the browser is currently being displayed in the \"desktop\" viewport (above or equal to 1024)\n * Ex: if (util.isDesktopViewport()) //do something\n */\n isDesktopViewport: function () {\n return $(window).innerWidth() >= this.getViewports('desktop');\n },\n\n /**\n * @function\n * @description Updates the state field to a given country\n * @param {String} form The form to update state field\n */\n updateStateField: function (form) {\n var $form = $(form),\n $country = $form.find('select[id$=\"_country\"]:enabled'),\n country = window.Countries[$country.val().toUpperCase()],\n url, options, $stateContainer;\n var $stateField = $country.data('stateField') ? $country.data('stateField') : $form.find('select[name$=\"_state\"]');\n if ($country.length === 0 || !country) {\n if ($stateField.length > 0) {\n $stateContainer = $stateField.parents('.form-row');\n url = this.appendParamsToUrl(Urls.getStateField, {'formField': $country.attr('name'), 'fieldType': 'input', 'country': $country.val()});\n options = {\n url: url,\n type: 'GET',\n target: $stateContainer\n };\n $.ajax(options).done(function (response) {\n if (options.target) {\n $(options.target).replaceWith(response);\n }\n });\n } else {\n return;\n }\n } else {\n if ($stateField.length === 0) {\n $stateField = $form.find('input[name$=\"_state\"]');\n $stateContainer = $stateField.parents('.form-row');\n url = this.appendParamsToUrl(Urls.getStateField, {'formField': $country.attr('name'), 'fieldType': 'select', 'country': $country.val()});\n options = {\n url: url,\n type: 'GET',\n target: $stateContainer\n };\n $.ajax(options).done(function (response) {\n if (options.target) {\n var stateValue = $form.find('[name$=\"_state\"]').val();\n if (options.target.length > 0) {\n $(options.target).replaceWith(response);\n } else {\n $(response).insertAfter($country.parents('.form-row'));\n }\n util.updateStateOptions($form, stateValue);\n }\n });\n } else {\n url = this.appendParamsToUrl(Urls.getStateValue, {'formField': $country.attr('name')});\n options = {\n url: url,\n dataType: 'json'\n };\n $.ajax(options).done(function (response) {\n if (response && response.state) {\n util.updateStateOptions($form, response.state);\n } else {\n util.updateStateOptions($form);\n }\n });\n }\n }\n },\n\n displayLookupButton: function(form) {\n var $form = $(form),\n $country = $form.find('select[id$=\"_country\"]:enabled'),\n $lookupButton = $('.lookup-button'),\n $formRow = $('input[name$=\"_lookup\"]').closest('.form-row');\n if ($country.length !== 0) {\n if ($country.val() === 'GB') {\n $lookupButton.show();\n $formRow.show();\n return true;\n } else {\n if ($form.find('input[name$=\"_lookup\"]').hasClass('ui-autocomplete-input')) {\n $form.find('input[name$=\"_lookup\"]').autocomplete('close');\n $form.find('input[name$=\"_lookup\"]').autocomplete('destroy');\n }\n $lookupButton.hide();\n $formRow.hide();\n }\n }\n return false;\n },\n\n isIcingSite: function() {\n return($('body.icingNA').length);\n },\n isClairesNASite: function() {\n return($('body.clairesNA').length);\n },\n isClairesEUSite: function() {\n return($('body.clairesEU').length);\n },\n isClairesFRSite: function() {\n return($('body.clairesFR').length);\n }\n};\n\nmodule.exports = util;\n","'use strict';\n\nvar allPhone = /^[0-9-() +]{0,50}$/;\nvar regex = {\n phone: /(^[2-9]\\d{2}-?\\d{3}-?\\d{4}$)|(^[2-9]\\d{2}\\s\\d{3}\\s\\d{4}$)/,\n postal: /^\\d{5}(-\\d{4})?$/,\n notCC: /^(?!(([0-9 -]){13,19})).*$/,\n CC: /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/,\n cvn: /^[0-9]{3,4}$/,\n name: /^[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF-\\s]{1,50}$/,\n fullName: /^[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF]+(?:[-][a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF]+)?(?:\\s[a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF]+(?:[-][a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF]+)?){1,3}$/,\n address: {\n address1: /(?=(^[ A-Za-z0-9_@./#',+-]*$))/,\n default: /^[ A-Za-z0-9_@./#',+-]*$/\n },\n city: /^[ A-Za-z0-9_@./#',+-]*$/,\n orderNum: /^[0-9]+$/\n};\nvar filters = {\n phone: {\n us: phoneFilter,\n ca: phoneFilter,\n clairesNA: phoneFilter,\n default: function() {\n return true;\n }\n }\n}\n\nconst shippingPrefix = 'dwfrm_checkout_shippingAddress_addressFields_';\nconst billingPrefix = 'dwfrm_checkout_billingAddress_addressFields_';\nconst emailPrefix = 'dwfrm_checkout_';\nconst pickupPrefix = 'dwfrm_pickuppersonform_';\n\nvar formMessages = {\n [shippingPrefix + 'fullName']: {\n required: Resources.INVALID_FULL_NAME\n },\n [shippingPrefix + 'phone']: {\n required: Resources.INVALID_PHONE\n },\n [shippingPrefix + 'address1']: {\n required: Resources.INVALID_ADDRES\n },\n [shippingPrefix + 'city']: {\n required: Resources.INVALID_CITY_NAME\n },\n [shippingPrefix + 'states_state']: {\n required: Resources.INVALID_STATE\n },\n [shippingPrefix + 'postal']: {\n required: Resources.INVALID_ZIP_CODE\n },\n [billingPrefix + 'fullName']: {\n required: Resources.INVALID_FULL_NAME\n },\n [billingPrefix + 'phone']: {\n required: Resources.INVALID_PHONE\n },\n [billingPrefix + 'address1']: {\n required: Resources.INVALID_ADDRES\n },\n [billingPrefix + 'city']: {\n required: Resources.INVALID_CITY_NAME\n },\n [billingPrefix + 'states_state']: {\n required: Resources.INVALID_STATE\n },\n [billingPrefix + 'postal']: {\n required: Resources.INVALID_ZIP_CODE\n },\n [emailPrefix + 'emailAddress']: {\n required: Resources.VALIDATE_EMAIL\n },\n [pickupPrefix + 'name']: {\n required: Resources.INVALID_FULL_NAME\n },\n [pickupPrefix + 'phoneNumber']: {\n required: Resources.INVALID_PHONE\n }\n}\n\n// global form validator settings\nvar settings = {\n errorClass: 'error',\n errorElement: 'span',\n errorPlacement: function ($error, $element) {\n var $selectStyle = $element.parent('.select-style');\n if ($selectStyle.length) {\n $selectStyle.after($error);\n } else if ($element.attr('type') === 'checkbox' || $element.attr('type') === 'radio') {\n var $label = $element.next('label');\n\n if ($label.length) {\n $label.after($error);\n } else {\n $element.after($error);\n }\n } else {\n $element.after($error);\n }\n },\n ignore: '.suppress',\n onkeyup: function(element, event) {\n if ($(element).hasClass('phone') && !this.checkable(element)) {\n filterPhone(element, event);\n }\n },\n onfocusout: function(element) {\n if (!this.checkable(element)) {\n this.element(element);\n }\n },\n messages : formMessages\n};\n\nfunction filterPhone(element, event) {\n element.value = element.value.replace(/[^0-9]/g,'');\n var country = $(element).closest('form').find('.country:enabled');\n var filterCase = Resources.LOCALE;\n if (country.length !== 0 && country.val().length !== 0) {\n filterCase = country.val().toLowerCase();\n }\n var filter = filters.phone[filterCase] || filters.phone.default;\n filter(element, event);\n}\n\nfunction phoneFilter(element, event) {\n var deletKeyCodes = [46, 8];\n var navKeyCodes = [37, 38, 39, 40];\n window.console.log(event.keyCode);\n if (navKeyCodes.indexOf(event.keyCode) >= 0) {\n return;\n }\n var numberLength = element.value.replace(/[^0-9]/g,'').length;\n if (event.keyCode > 47 && event.keyCode < 58 && numberLength <= 10\n ||event.keyCode > 95 && event.keyCode < 106 && numberLength <= 10\n || deletKeyCodes.indexOf(event.keyCode) >= 0 || !event.keyCode) {\n element.oldValue = element.value;\n element.oldSelectionStart = element.selectionStart;\n element.oldSelectionEnd = element.selectionEnd;\n } else if (element.hasOwnProperty('oldValue')) {\n element.value = element.oldValue;\n element.setSelectionRange(element.oldSelectionStart, element.oldSelectionEnd);\n } else {\n element.value = '';\n }\n if ((element.value.length == 3 || element.value.length == 7) && deletKeyCodes.indexOf(event.keyCode) < 0) {\n element.value = element.value + ' ';\n }\n if (numberLength == 10) {\n var value = element.value.replace(/[^0-9]/g,'');\n element.value = value.substring(0,3) + ' ' + value.substring(3,6) + ' ' + value.substring(6);\n element.oldValue = element.value;\n element.oldSelectionStart = element.selectionStart;\n element.oldSelectionEnd = element.selectionEnd;\n }\n}\n/**\n * @function\n * @description Validates a given phone number against the countries phone regex\n * @param {String} value The phone number which will be validated\n * @param {String} el The input field\n */\nvar validatePhone = function (value, el) {\n var country = $(el).closest('form').find('.country:enabled');\n if (country.length === 0 || country.val().length === 0) {\n return true;\n }\n\n var rgx = regex.phone;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n\n return isOptional || isValid;\n};\n\nvar validateStore = function (value) {\n var storeID = $('.hidden-store-ID input').val();\n if (!value || !storeID) {\n return false;\n }\n return true;\n}\n\n/**\n * @function\n * @description Validates a name against alphabet regex\n * @param {String} value The name which will be validated\n * @param {String} el The input field\n */\nvar validateName = function (value, el) {\n var rgx = regex.name;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n var isNotUndefined = $.trim(value).toLowerCase().indexOf('undefined') < 0;\n\n return (isOptional || isValid) && isNotUndefined;\n};\n\n/**\n * @function\n * @description Validates a name against alphabet regex\n * @param {String} value The name which will be validated\n * @param {String} el The input field\n */\nvar validateFullName = function (value, el) {\n var rgx = regex.fullName;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n if(value[0] === ' ' || value[value.length-1] === ' '){\n isValid = false;\n }\n var isNotUndefined = $.trim(value).toLowerCase().indexOf('undefined') < 0;\n return (isOptional || isValid) && isNotUndefined;\n};\n\nvar validateUndefined = function (value) {\n return $.trim(value).toLowerCase().indexOf('undefined') < 0;\n};\n\n/** @description Validates email against regex\n * @param {String} value The name which will be validated\n * @param {String} el The input field\n */\nvar validateEmail = function (value, el) {\n var regex = /^[a-zA-Z0-9_-]+(?:[\\.\\+][a-zA-Z0-9_-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$|\\[(?:\\d{1,3}\\.){3}\\d{1,3}\\]$/;\n var isOptional = this.optional(el);\n var isValid = regex.test($.trim(value));\n return isOptional || isValid;\n};\n\n/**\n * @function\n * @description Validates a given postal code against the countries postal regex\n * @param {String} value The postal code which will be validated\n * @param {String} el The input field\n */\nvar validatePostal = function (value, el) {\n var isBopisFlow = $(el).closest('form').attr('data-bopis-selected') === 'true';\n var country = $(el).closest('form').find('.country:enabled');\n if (isBopisFlow) {\n country = $(el).closest('fieldset').find('.country:enabled');\n }\n if (country.length === 0 || country.val().length === 0) {\n return true;\n }\n\n var rgx = regex.postal;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n\n return isOptional || isValid;\n};\n\n/**\n * @function\n * @description Validates a given address against regex\n * @param {String} value The address which will be validated\n * @param {String} el The input field\n */\nvar validateAddress = function (value, el) {\n var rgx = el.name.indexOf('address1') > 0 ? regex.address.address1 : regex.address.default;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n\n return isOptional || isValid;\n};\n\nvar validateCity = function (value, el) {\n var rgx = regex.city;\n var isOptional = this.optional(el);\n var isValid = rgx.test($.trim(value));\n\n return isOptional || isValid;\n}\n\n/**\n * @function\n * @description Validates length of card number\n * @param {String} value The card number which will be validated\n */\n\nvar validateCardNumber = function (value, element) {\n var trimmedValue = $.trim(value);\n var discoverCardFlag = false;\n var isDiscover = /^(6011|622(12[6-9]|1[3-9][0-9]|2[0-9][0-9]|92[0-5])|64[4-9]|65)/.test(trimmedValue.substring(0, 4));\n\n if (isDiscover) {\n discoverCardFlag = true;\n $(element).data('card-error', 'Discover card is not accepted');\n $('#form-submit').addClass('disabled-button');\n return false;\n }\n\n var isValid = regex.CC.test(trimmedValue);\n if (!isValid) {\n $(element).data('card-error', Resources.INVALID_CARD_NUMBER);\n }\n $('#form-submit').removeClass('disabled-button');\n return isValid;\n};\n//remove disable attribute when click on promocode and GC option\n$('#cart-v2-coupon-head').click(function () {\n if ($('#form-submit').hasClass('disabled-button')) {\n $('#form-submit').removeClass('disabled-button');\n }\n});\n$('.gift-cert-header').click(function () {\n if ($('#form-submit').hasClass('disabled-button')) {\n $('#form-submit').removeClass('disabled-button');\n }\n\n});\n\n$('#form-submit').on('click', function (event) {\n if ($('#form-submit').hasClass('disabled-button')) {\n document.getElementById('dwfrm_checkout_paymentMethods_creditCard_number').scrollIntoView({ behavior: \"smooth\" });\n return false;\n }\n});\n\n\n/**\n * @function\n * @description Validates length of card number\n * @param {String} value The card number which will be validated\n */\nvar validateCardCVN = function (value) {\n var isValid = regex.cvn.test($.trim(value));\n\n return isValid;\n};\n\n\n/**\n * @function\n * @description Validates that a credit card owner is not a Credit card number\n * @param {String} value The owner field which will be validated\n * @param {String} el The input field\n */\nvar validateOwner = function (value) {\n var isValid = regex.notCC.test($.trim(value));\n return isValid;\n};\n\n/**\n * @function\n * @description Verifies that 'Verify Age' checkbox is checked\n * @param {String} value The verify age field value which will be validated\n * @param {String} el The input field\n */\nvar verifyAge = function (value, el) {\n return value || $(el).is(':checked');\n};\n/**\n * @function\n * @description Verifies that day/month/year select is checked\n * @param {String} value The verify age field value which will be validated\n */\nvar validateAge = function (value) {\n if (!value) {\n return false;\n }\n return true;\n}\n/**\n * @function\n * @description Verifies that date of birth hidden input has value and customers age is more than 13\n * @param {String} value The verify age field value which will be validated\n */\nvar validateDateOfBirth = function (value) {\n var minAge = SitePreferences.MIN_AGE;\n var minAgePerCountry = SitePreferences.MIN_AGE_PER_COUNTRY;\n var customerCountry = $('[name=\"dwfrm_profile_address_country\"]');\n //if minAgePerCountry is not null and country filed is present on the form, validate age based on country-specific settings\n if (minAgePerCountry && customerCountry.length > 0 && minAgePerCountry[customerCountry.val()]) {\n minAge = minAgePerCountry[customerCountry.val()];\n }\n\n var customersData = value.split('/');\n var day = customersData[0];\n var month = customersData[1];\n var year = customersData[2];\n var customersDate = new Date();\n customersDate.setFullYear(year, month - 1, day);\n var demandDate = new Date();\n demandDate.setFullYear(demandDate.getFullYear() - minAge);\n if (demandDate > customersDate) {\n return true;\n }\n return false;\n}\n\n/**\n * @function\n * @description Validates that a product quantity is less then quantity limit\n * @param {String} value The quantity field value which will be validated\n * @param {String} el The input field\n */\nvar verifyQtyLimit = function (value, el) {\n var limit = $(el).attr('qtylimit');\n if (limit == undefined) {\n return true;\n }\n\n var isValid = +value <= +limit;\n return isValid;\n};\n\n/**\n * @function\n * @description Validates that a Mobile Phone input is required for sms\n * @param {String} value The Mobile Phone field value which will be validated\n * @param {String} el The input field\n */\nvar verifyMobileSmsCheckbox = function (value , el) {\n if (!$(el).hasClass('verify-mobile-sms-checkbox')) {\n return true;\n }\n if (value !== '') {\n return true;\n }\n return false;\n}\n/**\n * @function\n * @description Generates quantity limit error message based on 'qtylimit' attribute of the qty field\n * @param {Object} params\n * @param {Object} el The input field\n */\nvar getQtyLimitMessage = function (params, el) {\n var qtyLimit = $(el).attr('qtylimit');\n return Resources.VALIDATE_QTYLIMIT.replace('{0}', +qtyLimit);\n};\n\n/**\n * @function\n * @description Validates that order number contains only numbers\n * @param {String} value The owner field which will be validated\n */\nvar validateOrderNumber = function (value) {\n if(value ===''){\n\t\treturn true;\n\t}\n var isValid = regex.orderNum.test($.trim(value));\n return isValid;\n};\n/**\n * Force input to be invalid via class to disable form submission\n */\n$.validator.addMethod('invalid', function(){ return false }, '');\n\n/**\n * Add age validation method to jQuery validation plugin.\n * Text fields must have 'customer-day'/'customer-month'/'customer-year' css class to be validated as age\n */\n$.validator.addMethod('customer-day', validateAge, Resources.VALIDATE_REQUIRED);\n$.validator.addMethod('customer-month', validateAge, Resources.VALIDATE_REQUIRED);\n$.validator.addMethod('customer-year', validateAge, Resources.VALIDATE_REQUIRED);\n/**\n * Add date-of-birth validation method to jQuery validation plugin.\n * Text fields must have 'date-of-birth' css class to be validated as date of birth\n */\n$.validator.addMethod('date-of-birth', validateDateOfBirth, Resources.VALIDATE_AGE);\n/**\n * Add phone validation method to jQuery validation plugin.\n * Text fields must have 'phone' css class to be validated as phone\n */\n$.validator.addMethod('phone', validatePhone, Resources.INVALID_PHONE);\n\n/**\n * Add store validation method to jQuery validation plugin.\n * Text fields must have 'valid-store-checkout' css class to be validated as store\n */\n$.validator.addMethod('valid-store-checkout', validateStore, Resources.INVALID_STORE);\n\n/**\n * Add name validation method to jQuery validation plugin.\n * Text fields must have 'name' css class to be validated as name\n */\n$.validator.addMethod('first-last-name', validateName, Resources.INVALID_NAME);\n\n/**\n * Add name validation method to jQuery validation plugin.\n * Text fields must have 'name' css class to be validated as name\n */\n$.validator.addMethod('full-name', validateFullName, Resources.INVALID_FULL_NAME);\n\n/**\n * Add address-field validation method to jQuery validation plugin.\n * Text fields must have 'address' css class to be validated as name\n */\n$.validator.addMethod('address-field', validateUndefined, Resources.INVALID_ADDRES);\n\n/**\n * Add city-field validation method to jQuery validation plugin.\n * Text fields must have 'city' css class to be validated as name\n */\n$.validator.addMethod('city-field', validateCity, Resources.INVALID_CITY);\n\n/**\n * Add postCode-field validation method to jQuery validation plugin.\n *Text fields must have 'postCode-field' css class to be validated as postal\n */\n$.validator.addMethod('postCode-field', validateUndefined, Resources.INVALID_ZIP);\n\n/**\n* Add email verification validation method to jQuery validation plugin.\n* Text fields must have 'email' css class to be validated as phone\n*/\n$.validator.addMethod('email', validateEmail, Resources.VALIDATE_EMAIL);\n\n/**\n * Add CCOwner validation method to jQuery validation plugin.\n * Text fields must have 'owner' css class to be validated as not a credit card\n */\n$.validator.addMethod('owner', validateOwner, Resources.INVALID_OWNER);\n\n/**\n * Add postal code validation method to jQuery validation plugin.\n * Text fields must have 'postal' css class to be validated as postal\n */\n$.validator.addMethod('postal', validatePostal, Resources.INVALID_ZIP);\n\n/**\n * Add address validation method to jQuery validation plugin.\n * Text fields must have 'address-field' css class to be validated as address\n */\n$.validator.addMethod('address-field', validateAddress, Resources.INVALID_ADDRES);\n\n\n/**\n* Add card number validation method to jQuery validation plugin.\n* Text fields must have 'card-number' css class to be validated as card number\n*/\n$.validator.addMethod('card-number', function(value, element) {\n var isValid = validateCardNumber(value, element);\n var errorMessage = $(element).data('card-error');\n $.validator.messages['card-number'] = errorMessage;\n return isValid;\n}, 'Resources.INVALID_CARD_NUMBER');\n\n/**\n* Add cvn validation method to jQuery validation plugin.\n* Text fields must have 'cvn' css class to be validated as cvn\n*/\n$.validator.addMethod('cvn', validateCardCVN, Resources.INVALID_CARD_CVN);\n\n/**\n * Add gift cert amount validation method to jQuery validation plugin.\n * Text fields must have 'gift-cert-amont' css class to be validated\n */\n$.validator.addMethod('gift-cert-amount', function (value, el) {\n var isOptional = this.optional(el);\n var isValid = (!isNaN(value)) && (parseFloat(value) >= 5) && (parseFloat(value) <= 5000);\n return isOptional || isValid;\n}, Resources.GIFT_CERT_AMOUNT_INVALID);\n\n/**\n * Add positive number validation method to jQuery validation plugin.\n * Text fields must have 'positivenumber' css class to be validated as positivenumber\n */\n$.validator.addMethod('positivenumber', function (value) {\n if ($.trim(value).length === 0) { return true; }\n return (!isNaN(value) && Number(value) >= 0);\n}, ''); // '' should be replaced with error message if needed\n\n/**\n * Add age verification validation method to jQuery validation plugin.\n * Text fields must have 'age-verify' css class to be validated as phone\n */\n$.validator.addMethod('verify-age', verifyAge, Resources.VERIFY_AGE);\n\n/**\n * Add qty limit verification validation method to jQuery validation plugin.\n * Input fields must have 'verify-qty-limit' css class to be validated as quantity field\n */\n$.validator.addMethod('verify-qty-limit', verifyQtyLimit, getQtyLimitMessage);\n//$.validator.addMethod('verify-mobile-sms-checkbox', verifyMobileSmsCheckbox, Resources.VALIDATE_REQUIRED_MOBILE);\n$.validator.addMethod('order-number', validateOrderNumber, Resources.VALIDATE_ORDER_NUMBER);\n\n$.extend($.validator.messages, {\n required: Resources.VALIDATE_REQUIRED,\n remote: Resources.VALIDATE_REMOTE,\n email: Resources.VALIDATE_EMAIL,\n url: Resources.VALIDATE_URL,\n date: Resources.VALIDATE_DATE,\n dateISO: Resources.VALIDATE_DATEISO,\n number: Resources.VALIDATE_NUMBER,\n digits: Resources.VALIDATE_DIGITS,\n creditcard: Resources.VALIDATE_CREDITCARD,\n equalTo: Resources.VALIDATE_EQUALTO,\n maxlength: $.validator.format(Resources.VALIDATE_MAXLENGTH),\n minlength: $.validator.format(Resources.VALIDATE_MINLENGTH),\n rangelength: $.validator.format(Resources.VALIDATE_RANGELENGTH),\n range: $.validator.format(Resources.VALIDATE_RANGE),\n max: $.validator.format(Resources.VALIDATE_MAX),\n min: $.validator.format(Resources.VALIDATE_MIN)\n});\n\nvar validator = {\n regex: regex,\n settings: settings,\n init: function () {\n var self = this;\n $('form:not(.suppress)').each(function () {\n $(this).validate(self.settings);\n });\n\n var removeCharsForEmail = function(e) {\n e.preventDefault();\n this.value = this.value.replace(/[^a-zA-Z0-9\\.\\-\\@]/g, '');\n };\n\n var removeNonIntegers = function(e) {\n e.preventDefault();\n this.value = this.value.replace(/[^0-9\\-]/g, '');\n };\n\n // zip code validation format xxxxx-xxxx\n var formatzip = function(e) {\n e.preventDefault();\n if (this.value.length > 5 && this.value != '') {\n var regex = /^\\d{5}(-\\d{4})?$/;\n if (!regex.test(this.value) && this.value.charAt(5) != '-') {\n this.value = this.value.substring(0, 5) + '-' + this.value.substring(5, this.value.length);\n }\n }\n };\n\n // phone number validation format xxx-xxx-xxxx\n var formatPhone = function(e) {\n e.preventDefault();\n this.value = this.value.replace(/[^0-9]/g, '');\n if (this.value.length >= 10 && this.value != '') {\n var regex = /^\\(?\\d{3}\\)?[-\\.\\s]?\\d{3}[-\\.\\s]?\\d{4}$/;\n if (regex.test(this.value)) {\n this.value = this.value.substring(0, 3) + '-' + this.value.substring(3, 6) + '-' + this.value.substring(6, 10);\n }\n }\n };\n\n var $giftCertPin = $('input[name$=\"_giftCertPin\"]');\n var $giftCertCode = $('input[name$=\"_giftCertCode\"]');\n $giftCertPin.on('keyup', removeNonIntegers);\n $giftCertCode.on('keyup', removeNonIntegers);\n\n // validate gift card cash refund form\n if ($('#GiftCardRefundForm').length > 0) {\n $('#GiftCardRefundForm').validate();\n $('.gcRefundNo').on('keyup', removeNonIntegers);\n $('.gcRefundPin').on('keyup', removeNonIntegers);\n $('.gcrefundemail').on('keyup', removeCharsForEmail);\n $('.gcrefundphone').on('keyup', formatPhone);\n $('.gcrefundpostal').on('keyup', formatzip);\n }\n\n // validate ccpa form\n if ($('#cppaForm').length > 0) {\n $('#cppaForm').validate();\n $('.cppaemail').on('keyup', removeCharsForEmail);\n $('.cppaphone').on('keyup', formatPhone);\n $('.cppapostal').on('keyup', formatzip);\n }\n\n },\n initForm: function (f) {\n $(f).validate(this.settings);\n },\n methods: {\n validateCardNumber: validateCardNumber,\n validateCardCVN: validateCardCVN\n }\n};\n\nmodule.exports = validator;\n","\"use strict\";\n\n// rawAsap provides everything we need except exception management.\nvar rawAsap = require(\"./raw\");\n// RawTasks are recycled to reduce GC churn.\nvar freeTasks = [];\n// We queue errors to ensure they are thrown in right order (FIFO).\n// Array-as-queue is good enough here, since we are just dealing with exceptions.\nvar pendingErrors = [];\nvar requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);\n\nfunction throwFirstError() {\n if (pendingErrors.length) {\n throw pendingErrors.shift();\n }\n}\n\n/**\n * Calls a task as soon as possible after returning, in its own event, with priority\n * over other events like animation, reflow, and repaint. An error thrown from an\n * event will not interrupt, nor even substantially slow down the processing of\n * other events, but will be rather postponed to a lower priority event.\n * @param {{call}} task A callable object, typically a function that takes no\n * arguments.\n */\nmodule.exports = asap;\nfunction asap(task) {\n var rawTask;\n if (freeTasks.length) {\n rawTask = freeTasks.pop();\n } else {\n rawTask = new RawTask();\n }\n rawTask.task = task;\n rawAsap(rawTask);\n}\n\n// We wrap tasks with recyclable task objects. A task object implements\n// `call`, just like a function.\nfunction RawTask() {\n this.task = null;\n}\n\n// The sole purpose of wrapping the task is to catch the exception and recycle\n// the task object after its single use.\nRawTask.prototype.call = function () {\n try {\n this.task.call();\n } catch (error) {\n if (asap.onerror) {\n // This hook exists purely for testing purposes.\n // Its name will be periodically randomized to break any code that\n // depends on its existence.\n asap.onerror(error);\n } else {\n // In a web browser, exceptions are not fatal. However, to avoid\n // slowing down the queue of pending tasks, we rethrow the error in a\n // lower priority turn.\n pendingErrors.push(error);\n requestErrorThrow();\n }\n } finally {\n this.task = null;\n freeTasks[freeTasks.length] = this;\n }\n};\n","\"use strict\";\n\n// Use the fastest means possible to execute a task in its own turn, with\n// priority over other events including IO, animation, reflow, and redraw\n// events in browsers.\n//\n// An exception thrown by a task will permanently interrupt the processing of\n// subsequent tasks. The higher level `asap` function ensures that if an\n// exception is thrown by a task, that the task queue will continue flushing as\n// soon as possible, but if you use `rawAsap` directly, you are responsible to\n// either ensure that no exceptions are thrown from your task, or to manually\n// call `rawAsap.requestFlush` if an exception is thrown.\nmodule.exports = rawAsap;\nfunction rawAsap(task) {\n if (!queue.length) {\n requestFlush();\n flushing = true;\n }\n // Equivalent to push, but avoids a function call.\n queue[queue.length] = task;\n}\n\nvar queue = [];\n// Once a flush has been requested, no further calls to `requestFlush` are\n// necessary until the next `flush` completes.\nvar flushing = false;\n// `requestFlush` is an implementation-specific method that attempts to kick\n// off a `flush` event as quickly as possible. `flush` will attempt to exhaust\n// the event queue before yielding to the browser's own event loop.\nvar requestFlush;\n// The position of the next task to execute in the task queue. This is\n// preserved between calls to `flush` so that it can be resumed if\n// a task throws an exception.\nvar index = 0;\n// If a task schedules additional tasks recursively, the task queue can grow\n// unbounded. To prevent memory exhaustion, the task queue will periodically\n// truncate already-completed tasks.\nvar capacity = 1024;\n\n// The flush function processes all tasks that have been scheduled with\n// `rawAsap` unless and until one of those tasks throws an exception.\n// If a task throws an exception, `flush` ensures that its state will remain\n// consistent and will resume where it left off when called again.\n// However, `flush` does not make any arrangements to be called again if an\n// exception is thrown.\nfunction flush() {\n while (index < queue.length) {\n var currentIndex = index;\n // Advance the index before calling the task. This ensures that we will\n // begin flushing on the next task the task throws an error.\n index = index + 1;\n queue[currentIndex].call();\n // Prevent leaking memory for long chains of recursive calls to `asap`.\n // If we call `asap` within tasks scheduled by `asap`, the queue will\n // grow, but to avoid an O(n) walk for every task we execute, we don't\n // shift tasks off the queue after they have been executed.\n // Instead, we periodically shift 1024 tasks off the queue.\n if (index > capacity) {\n // Manually shift all values starting at the index back to the\n // beginning of the queue.\n for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {\n queue[scan] = queue[scan + index];\n }\n queue.length -= index;\n index = 0;\n }\n }\n queue.length = 0;\n index = 0;\n flushing = false;\n}\n\n// `requestFlush` is implemented using a strategy based on data collected from\n// every available SauceLabs Selenium web driver worker at time of writing.\n// https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593\n\n// Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that\n// have WebKitMutationObserver but not un-prefixed MutationObserver.\n// Must use `global` or `self` instead of `window` to work in both frames and web\n// workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.\n\n/* globals self */\nvar scope = typeof global !== \"undefined\" ? global : self;\nvar BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver;\n\n// MutationObservers are desirable because they have high priority and work\n// reliably everywhere they are implemented.\n// They are implemented in all modern browsers.\n//\n// - Android 4-4.3\n// - Chrome 26-34\n// - Firefox 14-29\n// - Internet Explorer 11\n// - iPad Safari 6-7.1\n// - iPhone Safari 7-7.1\n// - Safari 6-7\nif (typeof BrowserMutationObserver === \"function\") {\n requestFlush = makeRequestCallFromMutationObserver(flush);\n\n// MessageChannels are desirable because they give direct access to the HTML\n// task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera\n// 11-12, and in web workers in many engines.\n// Although message channels yield to any queued rendering and IO tasks, they\n// would be better than imposing the 4ms delay of timers.\n// However, they do not work reliably in Internet Explorer or Safari.\n\n// Internet Explorer 10 is the only browser that has setImmediate but does\n// not have MutationObservers.\n// Although setImmediate yields to the browser's renderer, it would be\n// preferrable to falling back to setTimeout since it does not have\n// the minimum 4ms penalty.\n// Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and\n// Desktop to a lesser extent) that renders both setImmediate and\n// MessageChannel useless for the purposes of ASAP.\n// https://github.com/kriskowal/q/issues/396\n\n// Timers are implemented universally.\n// We fall back to timers in workers in most engines, and in foreground\n// contexts in the following browsers.\n// However, note that even this simple case requires nuances to operate in a\n// broad spectrum of browsers.\n//\n// - Firefox 3-13\n// - Internet Explorer 6-9\n// - iPad Safari 4.3\n// - Lynx 2.8.7\n} else {\n requestFlush = makeRequestCallFromTimer(flush);\n}\n\n// `requestFlush` requests that the high priority event queue be flushed as\n// soon as possible.\n// This is useful to prevent an error thrown in a task from stalling the event\n// queue if the exception handled by Node.js’s\n// `process.on(\"uncaughtException\")` or by a domain.\nrawAsap.requestFlush = requestFlush;\n\n// To request a high priority event, we induce a mutation observer by toggling\n// the text of a text node between \"1\" and \"-1\".\nfunction makeRequestCallFromMutationObserver(callback) {\n var toggle = 1;\n var observer = new BrowserMutationObserver(callback);\n var node = document.createTextNode(\"\");\n observer.observe(node, {characterData: true});\n return function requestCall() {\n toggle = -toggle;\n node.data = toggle;\n };\n}\n\n// The message channel technique was discovered by Malte Ubl and was the\n// original foundation for this library.\n// http://www.nonblocking.io/2011/06/windownexttick.html\n\n// Safari 6.0.5 (at least) intermittently fails to create message ports on a\n// page's first load. Thankfully, this version of Safari supports\n// MutationObservers, so we don't need to fall back in that case.\n\n// function makeRequestCallFromMessageChannel(callback) {\n// var channel = new MessageChannel();\n// channel.port1.onmessage = callback;\n// return function requestCall() {\n// channel.port2.postMessage(0);\n// };\n// }\n\n// For reasons explained above, we are also unable to use `setImmediate`\n// under any circumstances.\n// Even if we were, there is another bug in Internet Explorer 10.\n// It is not sufficient to assign `setImmediate` to `requestFlush` because\n// `setImmediate` must be called *by name* and therefore must be wrapped in a\n// closure.\n// Never forget.\n\n// function makeRequestCallFromSetImmediate(callback) {\n// return function requestCall() {\n// setImmediate(callback);\n// };\n// }\n\n// Safari 6.0 has a problem where timers will get lost while the user is\n// scrolling. This problem does not impact ASAP because Safari 6.0 supports\n// mutation observers, so that implementation is used instead.\n// However, if we ever elect to use timers in Safari, the prevalent work-around\n// is to add a scroll event listener that calls for a flush.\n\n// `setTimeout` does not call the passed callback if the delay is less than\n// approximately 7 in web workers in Firefox 8 through 18, and sometimes not\n// even then.\n\nfunction makeRequestCallFromTimer(callback) {\n return function requestCall() {\n // We dispatch a timeout with a specified delay of 0 for engines that\n // can reliably accommodate that request. This will usually be snapped\n // to a 4 milisecond delay, but once we're flushing, there's no delay\n // between events.\n var timeoutHandle = setTimeout(handleTimer, 0);\n // However, since this timer gets frequently dropped in Firefox\n // workers, we enlist an interval handle that will try to fire\n // an event 20 times per second until it succeeds.\n var intervalHandle = setInterval(handleTimer, 50);\n\n function handleTimer() {\n // Whichever timer succeeds will cancel both timers and\n // execute the callback.\n clearTimeout(timeoutHandle);\n clearInterval(intervalHandle);\n callback();\n }\n };\n}\n\n// This is for `asap.js` only.\n// Its name will be periodically randomized to break any code that depends on\n// its existence.\nrawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;\n\n// ASAP was originally a nextTick shim included in Q. This was factored out\n// into this ASAP package. It was later adapted to RSVP which made further\n// amendments. These decisions, particularly to marginalize MessageChannel and\n// to capture the MutationObserver implementation in a closure, were integrated\n// back into ASAP proper.\n// https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js\n","'use strict';\n\nvar GetIntrinsic = require('get-intrinsic');\n\nvar callBind = require('./');\n\nvar $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));\n\nmodule.exports = function callBoundIntrinsic(name, allowMissing) {\n\tvar intrinsic = GetIntrinsic(name, !!allowMissing);\n\tif (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {\n\t\treturn callBind(intrinsic);\n\t}\n\treturn intrinsic;\n};\n","'use strict';\n\nvar bind = require('function-bind');\nvar GetIntrinsic = require('get-intrinsic');\n\nvar $apply = GetIntrinsic('%Function.prototype.apply%');\nvar $call = GetIntrinsic('%Function.prototype.call%');\nvar $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply);\n\nvar $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);\nvar $defineProperty = GetIntrinsic('%Object.defineProperty%', true);\nvar $max = GetIntrinsic('%Math.max%');\n\nif ($defineProperty) {\n\ttry {\n\t\t$defineProperty({}, 'a', { value: 1 });\n\t} catch (e) {\n\t\t// IE 8 has a broken defineProperty\n\t\t$defineProperty = null;\n\t}\n}\n\nmodule.exports = function callBind(originalFunction) {\n\tvar func = $reflectApply(bind, $call, arguments);\n\tif ($gOPD && $defineProperty) {\n\t\tvar desc = $gOPD(func, 'length');\n\t\tif (desc.configurable) {\n\t\t\t// original length, plus the receiver, minus any additional arguments (after the receiver)\n\t\t\t$defineProperty(\n\t\t\t\tfunc,\n\t\t\t\t'length',\n\t\t\t\t{ value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) }\n\t\t\t);\n\t\t}\n\t}\n\treturn func;\n};\n\nvar applyBind = function applyBind() {\n\treturn $reflectApply(bind, $apply, arguments);\n};\n\nif ($defineProperty) {\n\t$defineProperty(module.exports, 'apply', { value: applyBind });\n} else {\n\tmodule.exports.apply = applyBind;\n}\n","/**\n * EvEmitter v1.1.0\n * Lil' event emitter\n * MIT License\n */\n\n/* jshint unused: true, undef: true, strict: true */\n\n( function( global, factory ) {\n // universal module definition\n /* jshint strict: false */ /* globals define, module, window */\n if ( typeof define == 'function' && define.amd ) {\n // AMD - RequireJS\n define( factory );\n } else if ( typeof module == 'object' && module.exports ) {\n // CommonJS - Browserify, Webpack\n module.exports = factory();\n } else {\n // Browser globals\n global.EvEmitter = factory();\n }\n\n}( typeof window != 'undefined' ? window : this, function() {\n\n\"use strict\";\n\nfunction EvEmitter() {}\n\nvar proto = EvEmitter.prototype;\n\nproto.on = function( eventName, listener ) {\n if ( !eventName || !listener ) {\n return;\n }\n // set events hash\n var events = this._events = this._events || {};\n // set listeners array\n var listeners = events[ eventName ] = events[ eventName ] || [];\n // only add once\n if ( listeners.indexOf( listener ) == -1 ) {\n listeners.push( listener );\n }\n\n return this;\n};\n\nproto.once = function( eventName, listener ) {\n if ( !eventName || !listener ) {\n return;\n }\n // add event\n this.on( eventName, listener );\n // set once flag\n // set onceEvents hash\n var onceEvents = this._onceEvents = this._onceEvents || {};\n // set onceListeners object\n var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};\n // set flag\n onceListeners[ listener ] = true;\n\n return this;\n};\n\nproto.off = function( eventName, listener ) {\n var listeners = this._events && this._events[ eventName ];\n if ( !listeners || !listeners.length ) {\n return;\n }\n var index = listeners.indexOf( listener );\n if ( index != -1 ) {\n listeners.splice( index, 1 );\n }\n\n return this;\n};\n\nproto.emitEvent = function( eventName, args ) {\n var listeners = this._events && this._events[ eventName ];\n if ( !listeners || !listeners.length ) {\n return;\n }\n // copy over to avoid interference if .off() in listener\n listeners = listeners.slice(0);\n args = args || [];\n // once stuff\n var onceListeners = this._onceEvents && this._onceEvents[ eventName ];\n\n for ( var i=0; i < listeners.length; i++ ) {\n var listener = listeners[i]\n var isOnce = onceListeners && onceListeners[ listener ];\n if ( isOnce ) {\n // remove listener\n // remove before trigger to prevent recursion\n this.off( eventName, listener );\n // unset once flag\n delete onceListeners[ listener ];\n }\n // trigger listener\n listener.apply( this, args );\n }\n\n return this;\n};\n\nproto.allOff = function() {\n delete this._events;\n delete this._onceEvents;\n};\n\nreturn EvEmitter;\n\n}));\n","'use strict';\n\n/* eslint no-invalid-this: 1 */\n\nvar ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';\nvar slice = Array.prototype.slice;\nvar toStr = Object.prototype.toString;\nvar funcType = '[object Function]';\n\nmodule.exports = function bind(that) {\n var target = this;\n if (typeof target !== 'function' || toStr.call(target) !== funcType) {\n throw new TypeError(ERROR_MESSAGE + target);\n }\n var args = slice.call(arguments, 1);\n\n var bound;\n var binder = function () {\n if (this instanceof bound) {\n var result = target.apply(\n this,\n args.concat(slice.call(arguments))\n );\n if (Object(result) === result) {\n return result;\n }\n return this;\n } else {\n return target.apply(\n that,\n args.concat(slice.call(arguments))\n );\n }\n };\n\n var boundLength = Math.max(0, target.length - args.length);\n var boundArgs = [];\n for (var i = 0; i < boundLength; i++) {\n boundArgs.push('$' + i);\n }\n\n bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);\n\n if (target.prototype) {\n var Empty = function Empty() {};\n Empty.prototype = target.prototype;\n bound.prototype = new Empty();\n Empty.prototype = null;\n }\n\n return bound;\n};\n","'use strict';\n\nvar implementation = require('./implementation');\n\nmodule.exports = Function.prototype.bind || implementation;\n","'use strict';\n\nvar undefined;\n\nvar $SyntaxError = SyntaxError;\nvar $Function = Function;\nvar $TypeError = TypeError;\n\n// eslint-disable-next-line consistent-return\nvar getEvalledConstructor = function (expressionSyntax) {\n\ttry {\n\t\treturn $Function('\"use strict\"; return (' + expressionSyntax + ').constructor;')();\n\t} catch (e) {}\n};\n\nvar $gOPD = Object.getOwnPropertyDescriptor;\nif ($gOPD) {\n\ttry {\n\t\t$gOPD({}, '');\n\t} catch (e) {\n\t\t$gOPD = null; // this is IE 8, which has a broken gOPD\n\t}\n}\n\nvar throwTypeError = function () {\n\tthrow new $TypeError();\n};\nvar ThrowTypeError = $gOPD\n\t? (function () {\n\t\ttry {\n\t\t\t// eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties\n\t\t\targuments.callee; // IE 8 does not throw here\n\t\t\treturn throwTypeError;\n\t\t} catch (calleeThrows) {\n\t\t\ttry {\n\t\t\t\t// IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')\n\t\t\t\treturn $gOPD(arguments, 'callee').get;\n\t\t\t} catch (gOPDthrows) {\n\t\t\t\treturn throwTypeError;\n\t\t\t}\n\t\t}\n\t}())\n\t: throwTypeError;\n\nvar hasSymbols = require('has-symbols')();\n\nvar getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto\n\nvar needsEval = {};\n\nvar TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array);\n\nvar INTRINSICS = {\n\t'%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,\n\t'%Array%': Array,\n\t'%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,\n\t'%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined,\n\t'%AsyncFromSyncIteratorPrototype%': undefined,\n\t'%AsyncFunction%': needsEval,\n\t'%AsyncGenerator%': needsEval,\n\t'%AsyncGeneratorFunction%': needsEval,\n\t'%AsyncIteratorPrototype%': needsEval,\n\t'%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics,\n\t'%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt,\n\t'%Boolean%': Boolean,\n\t'%DataView%': typeof DataView === 'undefined' ? undefined : DataView,\n\t'%Date%': Date,\n\t'%decodeURI%': decodeURI,\n\t'%decodeURIComponent%': decodeURIComponent,\n\t'%encodeURI%': encodeURI,\n\t'%encodeURIComponent%': encodeURIComponent,\n\t'%Error%': Error,\n\t'%eval%': eval, // eslint-disable-line no-eval\n\t'%EvalError%': EvalError,\n\t'%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array,\n\t'%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array,\n\t'%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry,\n\t'%Function%': $Function,\n\t'%GeneratorFunction%': needsEval,\n\t'%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array,\n\t'%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array,\n\t'%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,\n\t'%isFinite%': isFinite,\n\t'%isNaN%': isNaN,\n\t'%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined,\n\t'%JSON%': typeof JSON === 'object' ? JSON : undefined,\n\t'%Map%': typeof Map === 'undefined' ? undefined : Map,\n\t'%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()),\n\t'%Math%': Math,\n\t'%Number%': Number,\n\t'%Object%': Object,\n\t'%parseFloat%': parseFloat,\n\t'%parseInt%': parseInt,\n\t'%Promise%': typeof Promise === 'undefined' ? undefined : Promise,\n\t'%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy,\n\t'%RangeError%': RangeError,\n\t'%ReferenceError%': ReferenceError,\n\t'%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,\n\t'%RegExp%': RegExp,\n\t'%Set%': typeof Set === 'undefined' ? undefined : Set,\n\t'%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()),\n\t'%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,\n\t'%String%': String,\n\t'%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined,\n\t'%Symbol%': hasSymbols ? Symbol : undefined,\n\t'%SyntaxError%': $SyntaxError,\n\t'%ThrowTypeError%': ThrowTypeError,\n\t'%TypedArray%': TypedArray,\n\t'%TypeError%': $TypeError,\n\t'%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array,\n\t'%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray,\n\t'%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array,\n\t'%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array,\n\t'%URIError%': URIError,\n\t'%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap,\n\t'%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef,\n\t'%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet\n};\n\nvar doEval = function doEval(name) {\n\tvar value;\n\tif (name === '%AsyncFunction%') {\n\t\tvalue = getEvalledConstructor('async function () {}');\n\t} else if (name === '%GeneratorFunction%') {\n\t\tvalue = getEvalledConstructor('function* () {}');\n\t} else if (name === '%AsyncGeneratorFunction%') {\n\t\tvalue = getEvalledConstructor('async function* () {}');\n\t} else if (name === '%AsyncGenerator%') {\n\t\tvar fn = doEval('%AsyncGeneratorFunction%');\n\t\tif (fn) {\n\t\t\tvalue = fn.prototype;\n\t\t}\n\t} else if (name === '%AsyncIteratorPrototype%') {\n\t\tvar gen = doEval('%AsyncGenerator%');\n\t\tif (gen) {\n\t\t\tvalue = getProto(gen.prototype);\n\t\t}\n\t}\n\n\tINTRINSICS[name] = value;\n\n\treturn value;\n};\n\nvar LEGACY_ALIASES = {\n\t'%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'],\n\t'%ArrayPrototype%': ['Array', 'prototype'],\n\t'%ArrayProto_entries%': ['Array', 'prototype', 'entries'],\n\t'%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'],\n\t'%ArrayProto_keys%': ['Array', 'prototype', 'keys'],\n\t'%ArrayProto_values%': ['Array', 'prototype', 'values'],\n\t'%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'],\n\t'%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'],\n\t'%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'],\n\t'%BooleanPrototype%': ['Boolean', 'prototype'],\n\t'%DataViewPrototype%': ['DataView', 'prototype'],\n\t'%DatePrototype%': ['Date', 'prototype'],\n\t'%ErrorPrototype%': ['Error', 'prototype'],\n\t'%EvalErrorPrototype%': ['EvalError', 'prototype'],\n\t'%Float32ArrayPrototype%': ['Float32Array', 'prototype'],\n\t'%Float64ArrayPrototype%': ['Float64Array', 'prototype'],\n\t'%FunctionPrototype%': ['Function', 'prototype'],\n\t'%Generator%': ['GeneratorFunction', 'prototype'],\n\t'%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'],\n\t'%Int8ArrayPrototype%': ['Int8Array', 'prototype'],\n\t'%Int16ArrayPrototype%': ['Int16Array', 'prototype'],\n\t'%Int32ArrayPrototype%': ['Int32Array', 'prototype'],\n\t'%JSONParse%': ['JSON', 'parse'],\n\t'%JSONStringify%': ['JSON', 'stringify'],\n\t'%MapPrototype%': ['Map', 'prototype'],\n\t'%NumberPrototype%': ['Number', 'prototype'],\n\t'%ObjectPrototype%': ['Object', 'prototype'],\n\t'%ObjProto_toString%': ['Object', 'prototype', 'toString'],\n\t'%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'],\n\t'%PromisePrototype%': ['Promise', 'prototype'],\n\t'%PromiseProto_then%': ['Promise', 'prototype', 'then'],\n\t'%Promise_all%': ['Promise', 'all'],\n\t'%Promise_reject%': ['Promise', 'reject'],\n\t'%Promise_resolve%': ['Promise', 'resolve'],\n\t'%RangeErrorPrototype%': ['RangeError', 'prototype'],\n\t'%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'],\n\t'%RegExpPrototype%': ['RegExp', 'prototype'],\n\t'%SetPrototype%': ['Set', 'prototype'],\n\t'%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'],\n\t'%StringPrototype%': ['String', 'prototype'],\n\t'%SymbolPrototype%': ['Symbol', 'prototype'],\n\t'%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'],\n\t'%TypedArrayPrototype%': ['TypedArray', 'prototype'],\n\t'%TypeErrorPrototype%': ['TypeError', 'prototype'],\n\t'%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'],\n\t'%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'],\n\t'%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'],\n\t'%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'],\n\t'%URIErrorPrototype%': ['URIError', 'prototype'],\n\t'%WeakMapPrototype%': ['WeakMap', 'prototype'],\n\t'%WeakSetPrototype%': ['WeakSet', 'prototype']\n};\n\nvar bind = require('function-bind');\nvar hasOwn = require('has');\nvar $concat = bind.call(Function.call, Array.prototype.concat);\nvar $spliceApply = bind.call(Function.apply, Array.prototype.splice);\nvar $replace = bind.call(Function.call, String.prototype.replace);\nvar $strSlice = bind.call(Function.call, String.prototype.slice);\n\n/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */\nvar rePropName = /[^%.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|%$))/g;\nvar reEscapeChar = /\\\\(\\\\)?/g; /** Used to match backslashes in property paths. */\nvar stringToPath = function stringToPath(string) {\n\tvar first = $strSlice(string, 0, 1);\n\tvar last = $strSlice(string, -1);\n\tif (first === '%' && last !== '%') {\n\t\tthrow new $SyntaxError('invalid intrinsic syntax, expected closing `%`');\n\t} else if (last === '%' && first !== '%') {\n\t\tthrow new $SyntaxError('invalid intrinsic syntax, expected opening `%`');\n\t}\n\tvar result = [];\n\t$replace(string, rePropName, function (match, number, quote, subString) {\n\t\tresult[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match;\n\t});\n\treturn result;\n};\n/* end adaptation */\n\nvar getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {\n\tvar intrinsicName = name;\n\tvar alias;\n\tif (hasOwn(LEGACY_ALIASES, intrinsicName)) {\n\t\talias = LEGACY_ALIASES[intrinsicName];\n\t\tintrinsicName = '%' + alias[0] + '%';\n\t}\n\n\tif (hasOwn(INTRINSICS, intrinsicName)) {\n\t\tvar value = INTRINSICS[intrinsicName];\n\t\tif (value === needsEval) {\n\t\t\tvalue = doEval(intrinsicName);\n\t\t}\n\t\tif (typeof value === 'undefined' && !allowMissing) {\n\t\t\tthrow new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');\n\t\t}\n\n\t\treturn {\n\t\t\talias: alias,\n\t\t\tname: intrinsicName,\n\t\t\tvalue: value\n\t\t};\n\t}\n\n\tthrow new $SyntaxError('intrinsic ' + name + ' does not exist!');\n};\n\nmodule.exports = function GetIntrinsic(name, allowMissing) {\n\tif (typeof name !== 'string' || name.length === 0) {\n\t\tthrow new $TypeError('intrinsic name must be a non-empty string');\n\t}\n\tif (arguments.length > 1 && typeof allowMissing !== 'boolean') {\n\t\tthrow new $TypeError('\"allowMissing\" argument must be a boolean');\n\t}\n\n\tvar parts = stringToPath(name);\n\tvar intrinsicBaseName = parts.length > 0 ? parts[0] : '';\n\n\tvar intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing);\n\tvar intrinsicRealName = intrinsic.name;\n\tvar value = intrinsic.value;\n\tvar skipFurtherCaching = false;\n\n\tvar alias = intrinsic.alias;\n\tif (alias) {\n\t\tintrinsicBaseName = alias[0];\n\t\t$spliceApply(parts, $concat([0, 1], alias));\n\t}\n\n\tfor (var i = 1, isOwn = true; i < parts.length; i += 1) {\n\t\tvar part = parts[i];\n\t\tvar first = $strSlice(part, 0, 1);\n\t\tvar last = $strSlice(part, -1);\n\t\tif (\n\t\t\t(\n\t\t\t\t(first === '\"' || first === \"'\" || first === '`')\n\t\t\t\t|| (last === '\"' || last === \"'\" || last === '`')\n\t\t\t)\n\t\t\t&& first !== last\n\t\t) {\n\t\t\tthrow new $SyntaxError('property names with quotes must have matching quotes');\n\t\t}\n\t\tif (part === 'constructor' || !isOwn) {\n\t\t\tskipFurtherCaching = true;\n\t\t}\n\n\t\tintrinsicBaseName += '.' + part;\n\t\tintrinsicRealName = '%' + intrinsicBaseName + '%';\n\n\t\tif (hasOwn(INTRINSICS, intrinsicRealName)) {\n\t\t\tvalue = INTRINSICS[intrinsicRealName];\n\t\t} else if (value != null) {\n\t\t\tif (!(part in value)) {\n\t\t\t\tif (!allowMissing) {\n\t\t\t\t\tthrow new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');\n\t\t\t\t}\n\t\t\t\treturn void undefined;\n\t\t\t}\n\t\t\tif ($gOPD && (i + 1) >= parts.length) {\n\t\t\t\tvar desc = $gOPD(value, part);\n\t\t\t\tisOwn = !!desc;\n\n\t\t\t\t// By convention, when a data property is converted to an accessor\n\t\t\t\t// property to emulate a data property that does not suffer from\n\t\t\t\t// the override mistake, that accessor's getter is marked with\n\t\t\t\t// an `originalValue` property. Here, when we detect this, we\n\t\t\t\t// uphold the illusion by pretending to see that original data\n\t\t\t\t// property, i.e., returning the value rather than the getter\n\t\t\t\t// itself.\n\t\t\t\tif (isOwn && 'get' in desc && !('originalValue' in desc.get)) {\n\t\t\t\t\tvalue = desc.get;\n\t\t\t\t} else {\n\t\t\t\t\tvalue = value[part];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tisOwn = hasOwn(value, part);\n\t\t\t\tvalue = value[part];\n\t\t\t}\n\n\t\t\tif (isOwn && !skipFurtherCaching) {\n\t\t\t\tINTRINSICS[intrinsicRealName] = value;\n\t\t\t}\n\t\t}\n\t}\n\treturn value;\n};\n","'use strict';\n\nvar origSymbol = typeof Symbol !== 'undefined' && Symbol;\nvar hasSymbolSham = require('./shams');\n\nmodule.exports = function hasNativeSymbols() {\n\tif (typeof origSymbol !== 'function') { return false; }\n\tif (typeof Symbol !== 'function') { return false; }\n\tif (typeof origSymbol('foo') !== 'symbol') { return false; }\n\tif (typeof Symbol('bar') !== 'symbol') { return false; }\n\n\treturn hasSymbolSham();\n};\n","'use strict';\n\n/* eslint complexity: [2, 18], max-statements: [2, 33] */\nmodule.exports = function hasSymbols() {\n\tif (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }\n\tif (typeof Symbol.iterator === 'symbol') { return true; }\n\n\tvar obj = {};\n\tvar sym = Symbol('test');\n\tvar symObj = Object(sym);\n\tif (typeof sym === 'string') { return false; }\n\n\tif (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }\n\tif (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }\n\n\t// temp disabled per https://github.com/ljharb/object.assign/issues/17\n\t// if (sym instanceof Symbol) { return false; }\n\t// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4\n\t// if (!(symObj instanceof Symbol)) { return false; }\n\n\t// if (typeof Symbol.prototype.toString !== 'function') { return false; }\n\t// if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }\n\n\tvar symVal = 42;\n\tobj[sym] = symVal;\n\tfor (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop\n\tif (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }\n\n\tif (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }\n\n\tvar syms = Object.getOwnPropertySymbols(obj);\n\tif (syms.length !== 1 || syms[0] !== sym) { return false; }\n\n\tif (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }\n\n\tif (typeof Object.getOwnPropertyDescriptor === 'function') {\n\t\tvar descriptor = Object.getOwnPropertyDescriptor(obj, sym);\n\t\tif (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }\n\t}\n\n\treturn true;\n};\n","'use strict';\n\nvar bind = require('function-bind');\n\nmodule.exports = bind.call(Function.call, Object.prototype.hasOwnProperty);\n","/*!\n * imagesLoaded v4.1.4\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n\n( function( window, factory ) { 'use strict';\n // universal module definition\n\n /*global define: false, module: false, require: false */\n\n if ( typeof define == 'function' && define.amd ) {\n // AMD\n define( [\n 'ev-emitter/ev-emitter'\n ], function( EvEmitter ) {\n return factory( window, EvEmitter );\n });\n } else if ( typeof module == 'object' && module.exports ) {\n // CommonJS\n module.exports = factory(\n window,\n require('ev-emitter')\n );\n } else {\n // browser global\n window.imagesLoaded = factory(\n window,\n window.EvEmitter\n );\n }\n\n})( typeof window !== 'undefined' ? window : this,\n\n// -------------------------- factory -------------------------- //\n\nfunction factory( window, EvEmitter ) {\n\n'use strict';\n\nvar $ = window.jQuery;\nvar console = window.console;\n\n// -------------------------- helpers -------------------------- //\n\n// extend objects\nfunction extend( a, b ) {\n for ( var prop in b ) {\n a[ prop ] = b[ prop ];\n }\n return a;\n}\n\nvar arraySlice = Array.prototype.slice;\n\n// turn element or nodeList into an array\nfunction makeArray( obj ) {\n if ( Array.isArray( obj ) ) {\n // use object if already an array\n return obj;\n }\n\n var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';\n if ( isArrayLike ) {\n // convert nodeList to array\n return arraySlice.call( obj );\n }\n\n // array of single index\n return [ obj ];\n}\n\n// -------------------------- imagesLoaded -------------------------- //\n\n/**\n * @param {Array, Element, NodeList, String} elem\n * @param {Object or Function} options - if function, use as callback\n * @param {Function} onAlways - callback function\n */\nfunction ImagesLoaded( elem, options, onAlways ) {\n // coerce ImagesLoaded() without new, to be new ImagesLoaded()\n if ( !( this instanceof ImagesLoaded ) ) {\n return new ImagesLoaded( elem, options, onAlways );\n }\n // use elem as selector string\n var queryElem = elem;\n if ( typeof elem == 'string' ) {\n queryElem = document.querySelectorAll( elem );\n }\n // bail if bad element\n if ( !queryElem ) {\n console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) );\n return;\n }\n\n this.elements = makeArray( queryElem );\n this.options = extend( {}, this.options );\n // shift arguments if no options set\n if ( typeof options == 'function' ) {\n onAlways = options;\n } else {\n extend( this.options, options );\n }\n\n if ( onAlways ) {\n this.on( 'always', onAlways );\n }\n\n this.getImages();\n\n if ( $ ) {\n // add jQuery Deferred object\n this.jqDeferred = new $.Deferred();\n }\n\n // HACK check async to allow time to bind listeners\n setTimeout( this.check.bind( this ) );\n}\n\nImagesLoaded.prototype = Object.create( EvEmitter.prototype );\n\nImagesLoaded.prototype.options = {};\n\nImagesLoaded.prototype.getImages = function() {\n this.images = [];\n\n // filter & find items if we have an item selector\n this.elements.forEach( this.addElementImages, this );\n};\n\n/**\n * @param {Node} element\n */\nImagesLoaded.prototype.addElementImages = function( elem ) {\n // filter siblings\n if ( elem.nodeName == 'IMG' ) {\n this.addImage( elem );\n }\n // get background image on element\n if ( this.options.background === true ) {\n this.addElementBackgroundImages( elem );\n }\n\n // find children\n // no non-element nodes, #143\n var nodeType = elem.nodeType;\n if ( !nodeType || !elementNodeTypes[ nodeType ] ) {\n return;\n }\n var childImgs = elem.querySelectorAll('img');\n // concat childElems to filterFound array\n for ( var i=0; i < childImgs.length; i++ ) {\n var img = childImgs[i];\n this.addImage( img );\n }\n\n // get child background images\n if ( typeof this.options.background == 'string' ) {\n var children = elem.querySelectorAll( this.options.background );\n for ( i=0; i < children.length; i++ ) {\n var child = children[i];\n this.addElementBackgroundImages( child );\n }\n }\n};\n\nvar elementNodeTypes = {\n 1: true,\n 9: true,\n 11: true\n};\n\nImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {\n var style = getComputedStyle( elem );\n if ( !style ) {\n // Firefox returns null if in a hidden iframe https://bugzil.la/548397\n return;\n }\n // get url inside url(\"...\")\n var reURL = /url\\((['\"])?(.*?)\\1\\)/gi;\n var matches = reURL.exec( style.backgroundImage );\n while ( matches !== null ) {\n var url = matches && matches[2];\n if ( url ) {\n this.addBackground( url, elem );\n }\n matches = reURL.exec( style.backgroundImage );\n }\n};\n\n/**\n * @param {Image} img\n */\nImagesLoaded.prototype.addImage = function( img ) {\n var loadingImage = new LoadingImage( img );\n this.images.push( loadingImage );\n};\n\nImagesLoaded.prototype.addBackground = function( url, elem ) {\n var background = new Background( url, elem );\n this.images.push( background );\n};\n\nImagesLoaded.prototype.check = function() {\n var _this = this;\n this.progressedCount = 0;\n this.hasAnyBroken = false;\n // complete if no images\n if ( !this.images.length ) {\n this.complete();\n return;\n }\n\n function onProgress( image, elem, message ) {\n // HACK - Chrome triggers event before object properties have changed. #83\n setTimeout( function() {\n _this.progress( image, elem, message );\n });\n }\n\n this.images.forEach( function( loadingImage ) {\n loadingImage.once( 'progress', onProgress );\n loadingImage.check();\n });\n};\n\nImagesLoaded.prototype.progress = function( image, elem, message ) {\n this.progressedCount++;\n this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;\n // progress event\n this.emitEvent( 'progress', [ this, image, elem ] );\n if ( this.jqDeferred && this.jqDeferred.notify ) {\n this.jqDeferred.notify( this, image );\n }\n // check if completed\n if ( this.progressedCount == this.images.length ) {\n this.complete();\n }\n\n if ( this.options.debug && console ) {\n console.log( 'progress: ' + message, image, elem );\n }\n};\n\nImagesLoaded.prototype.complete = function() {\n var eventName = this.hasAnyBroken ? 'fail' : 'done';\n this.isComplete = true;\n this.emitEvent( eventName, [ this ] );\n this.emitEvent( 'always', [ this ] );\n if ( this.jqDeferred ) {\n var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';\n this.jqDeferred[ jqMethod ]( this );\n }\n};\n\n// -------------------------- -------------------------- //\n\nfunction LoadingImage( img ) {\n this.img = img;\n}\n\nLoadingImage.prototype = Object.create( EvEmitter.prototype );\n\nLoadingImage.prototype.check = function() {\n // If complete is true and browser supports natural sizes,\n // try to check for image status manually.\n var isComplete = this.getIsImageComplete();\n if ( isComplete ) {\n // report based on naturalWidth\n this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );\n return;\n }\n\n // If none of the checks above matched, simulate loading on detached element.\n this.proxyImage = new Image();\n this.proxyImage.addEventListener( 'load', this );\n this.proxyImage.addEventListener( 'error', this );\n // bind to image as well for Firefox. #191\n this.img.addEventListener( 'load', this );\n this.img.addEventListener( 'error', this );\n this.proxyImage.src = this.img.src;\n};\n\nLoadingImage.prototype.getIsImageComplete = function() {\n // check for non-zero, non-undefined naturalWidth\n // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671\n return this.img.complete && this.img.naturalWidth;\n};\n\nLoadingImage.prototype.confirm = function( isLoaded, message ) {\n this.isLoaded = isLoaded;\n this.emitEvent( 'progress', [ this, this.img, message ] );\n};\n\n// ----- events ----- //\n\n// trigger specified handler for event type\nLoadingImage.prototype.handleEvent = function( event ) {\n var method = 'on' + event.type;\n if ( this[ method ] ) {\n this[ method ]( event );\n }\n};\n\nLoadingImage.prototype.onload = function() {\n this.confirm( true, 'onload' );\n this.unbindEvents();\n};\n\nLoadingImage.prototype.onerror = function() {\n this.confirm( false, 'onerror' );\n this.unbindEvents();\n};\n\nLoadingImage.prototype.unbindEvents = function() {\n this.proxyImage.removeEventListener( 'load', this );\n this.proxyImage.removeEventListener( 'error', this );\n this.img.removeEventListener( 'load', this );\n this.img.removeEventListener( 'error', this );\n};\n\n// -------------------------- Background -------------------------- //\n\nfunction Background( url, element ) {\n this.url = url;\n this.element = element;\n this.img = new Image();\n}\n\n// inherit LoadingImage prototype\nBackground.prototype = Object.create( LoadingImage.prototype );\n\nBackground.prototype.check = function() {\n this.img.addEventListener( 'load', this );\n this.img.addEventListener( 'error', this );\n this.img.src = this.url;\n // check if image is already complete\n var isComplete = this.getIsImageComplete();\n if ( isComplete ) {\n this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );\n this.unbindEvents();\n }\n};\n\nBackground.prototype.unbindEvents = function() {\n this.img.removeEventListener( 'load', this );\n this.img.removeEventListener( 'error', this );\n};\n\nBackground.prototype.confirm = function( isLoaded, message ) {\n this.isLoaded = isLoaded;\n this.emitEvent( 'progress', [ this, this.element, message ] );\n};\n\n// -------------------------- jQuery -------------------------- //\n\nImagesLoaded.makeJQueryPlugin = function( jQuery ) {\n jQuery = jQuery || window.jQuery;\n if ( !jQuery ) {\n return;\n }\n // set local variable\n $ = jQuery;\n // $().imagesLoaded()\n $.fn.imagesLoaded = function( options, callback ) {\n var instance = new ImagesLoaded( this, options, callback );\n return instance.jqDeferred.promise( $(this) );\n };\n};\n// try making plugin\nImagesLoaded.makeJQueryPlugin();\n\n// -------------------------- -------------------------- //\n\nreturn ImagesLoaded;\n\n});\n","/*! lazysizes - v5.3.2 */\n\n!function(e){var t=function(u,D,f){\"use strict\";var k,H;if(function(){var e;var t={lazyClass:\"lazyload\",loadedClass:\"lazyloaded\",loadingClass:\"lazyloading\",preloadClass:\"lazypreload\",errorClass:\"lazyerror\",autosizesClass:\"lazyautosizes\",fastLoadedClass:\"ls-is-cached\",iframeLoadMode:0,srcAttr:\"data-src\",srcsetAttr:\"data-srcset\",sizesAttr:\"data-sizes\",minSize:40,customMedia:{},init:true,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:true,ricTimeout:0,throttleDelay:125};H=u.lazySizesConfig||u.lazysizesConfig||{};for(e in t){if(!(e in H)){H[e]=t[e]}}}(),!D||!D.getElementsByClassName){return{init:function(){},cfg:H,noSupport:true}}var O=D.documentElement,i=u.HTMLPictureElement,P=\"addEventListener\",$=\"getAttribute\",q=u[P].bind(u),I=u.setTimeout,U=u.requestAnimationFrame||I,o=u.requestIdleCallback,j=/^picture$/i,r=[\"load\",\"error\",\"lazyincluded\",\"_lazyloaded\"],a={},G=Array.prototype.forEach,J=function(e,t){if(!a[t]){a[t]=new RegExp(\"(\\\\s|^)\"+t+\"(\\\\s|$)\")}return a[t].test(e[$](\"class\")||\"\")&&a[t]},K=function(e,t){if(!J(e,t)){e.setAttribute(\"class\",(e[$](\"class\")||\"\").trim()+\" \"+t)}},Q=function(e,t){var a;if(a=J(e,t)){e.setAttribute(\"class\",(e[$](\"class\")||\"\").replace(a,\" \"))}},V=function(t,a,e){var i=e?P:\"removeEventListener\";if(e){V(t,a)}r.forEach(function(e){t[i](e,a)})},X=function(e,t,a,i,r){var n=D.createEvent(\"Event\");if(!a){a={}}a.instance=k;n.initEvent(t,!i,!r);n.detail=a;e.dispatchEvent(n);return n},Y=function(e,t){var a;if(!i&&(a=u.picturefill||H.pf)){if(t&&t.src&&!e[$](\"srcset\")){e.setAttribute(\"srcset\",t.src)}a({reevaluate:true,elements:[e]})}else if(t&&t.src){e.src=t.src}},Z=function(e,t){return(getComputedStyle(e,null)||{})[t]},s=function(e,t,a){a=a||e.offsetWidth;while(a49?function(){o(t,{timeout:n});if(n!==H.ricTimeout){n=H.ricTimeout}}:te(function(){I(t)},true);return function(e){var t;if(e=e===true){n=33}if(a){return}a=true;t=r-(f.now()-i);if(t<0){t=0}if(e||t<9){s()}else{I(s,t)}}},ie=function(e){var t,a;var i=99;var r=function(){t=null;e()};var n=function(){var e=f.now()-a;if(e0;if(r&&Z(i,\"overflow\")!=\"visible\"){a=i.getBoundingClientRect();r=C>a.left&&pa.top-1&&g500&&O.clientWidth>500?500:370:H.expand;k._defEx=u;f=u*H.expFactor;c=H.hFac;A=null;if(w2&&h>2&&!D.hidden){w=f;N=0}else if(h>1&&N>1&&M<6){w=u}else{w=_}}if(l!==n){y=innerWidth+n*c;z=innerHeight+n;s=n*-1;l=n}a=d[t].getBoundingClientRect();if((b=a.bottom)>=s&&(g=a.top)<=z&&(C=a.right)>=s*c&&(p=a.left)<=y&&(b||C||p||g)&&(H.loadHidden||x(d[t]))&&(m&&M<3&&!o&&(h<3||N<4)||W(d[t],n))){R(d[t]);r=true;if(M>9){break}}else if(!r&&m&&!i&&M<4&&N<4&&h>2&&(v[0]||H.preloadAfterLoad)&&(v[0]||!o&&(b||C||p||g||d[t][$](H.sizesAttr)!=\"auto\"))){i=v[0]||d[t]}}if(i&&!r){R(i)}}};var a=ae(t);var S=function(e){var t=e.target;if(t._lazyCache){delete t._lazyCache;return}L(e);K(t,H.loadedClass);Q(t,H.loadingClass);V(t,B);X(t,\"lazyloaded\")};var i=te(S);var B=function(e){i({target:e.target})};var T=function(e,t){var a=e.getAttribute(\"data-load-mode\")||H.iframeLoadMode;if(a==0){e.contentWindow.location.replace(t)}else if(a==1){e.src=t}};var F=function(e){var t;var a=e[$](H.srcsetAttr);if(t=H.customMedia[e[$](\"data-media\")||e[$](\"media\")]){e.setAttribute(\"media\",t)}if(a){e.setAttribute(\"srcset\",a)}};var s=te(function(t,e,a,i,r){var n,s,o,l,u,f;if(!(u=X(t,\"lazybeforeunveil\",e)).defaultPrevented){if(i){if(a){K(t,H.autosizesClass)}else{t.setAttribute(\"sizes\",i)}}s=t[$](H.srcsetAttr);n=t[$](H.srcAttr);if(r){o=t.parentNode;l=o&&j.test(o.nodeName||\"\")}f=e.firesLoad||\"src\"in t&&(s||n||l);u={target:t};K(t,H.loadingClass);if(f){clearTimeout(c);c=I(L,2500);V(t,B,true)}if(l){G.call(o.getElementsByTagName(\"source\"),F)}if(s){t.setAttribute(\"srcset\",s)}else if(n&&!l){if(d.test(t.nodeName)){T(t,n)}else{t.src=n}}if(r&&(s||l)){Y(t,{src:n})}}if(t._lazyRace){delete t._lazyRace}Q(t,H.lazyClass);ee(function(){var e=t.complete&&t.naturalWidth>1;if(!f||e){if(e){K(t,H.fastLoadedClass)}S(u);t._lazyCache=true;I(function(){if(\"_lazyCache\"in t){delete t._lazyCache}},9)}if(t.loading==\"lazy\"){M--}},true)});var R=function(e){if(e._lazyRace){return}var t;var a=n.test(e.nodeName);var i=a&&(e[$](H.sizesAttr)||e[$](\"sizes\"));var r=i==\"auto\";if((r||!m)&&a&&(e[$](\"src\")||e.srcset)&&!e.complete&&!J(e,H.errorClass)&&J(e,H.lazyClass)){return}t=X(e,\"lazyunveilread\").detail;if(r){re.updateElem(e,true,e.offsetWidth)}e._lazyRace=true;M++;s(e,t,r,i,a)};var r=ie(function(){H.loadMode=3;a()});var o=function(){if(H.loadMode==3){H.loadMode=2}r()};var l=function(){if(m){return}if(f.now()-e<999){I(l,999);return}m=true;H.loadMode=3;a();q(\"scroll\",o,true)};return{_:function(){e=f.now();k.elements=D.getElementsByClassName(H.lazyClass);v=D.getElementsByClassName(H.lazyClass+\" \"+H.preloadClass);q(\"scroll\",a,true);q(\"resize\",a,true);q(\"pageshow\",function(e){if(e.persisted){var t=D.querySelectorAll(\".\"+H.loadingClass);if(t.length&&t.forEach){U(function(){t.forEach(function(e){if(e.complete){R(e)}})})}}});if(u.MutationObserver){new MutationObserver(a).observe(O,{childList:true,subtree:true,attributes:true})}else{O[P](\"DOMNodeInserted\",a,true);O[P](\"DOMAttrModified\",a,true);setInterval(a,999)}q(\"hashchange\",a,true);[\"focus\",\"mouseover\",\"click\",\"load\",\"transitionend\",\"animationend\"].forEach(function(e){D[P](e,a,true)});if(/d$|^c/.test(D.readyState)){l()}else{q(\"load\",l);D[P](\"DOMContentLoaded\",a);I(l,2e4)}if(k.elements.length){t();ee._lsFlush()}else{a()}},checkElems:a,unveil:R,_aLSL:o}}(),re=function(){var a;var n=te(function(e,t,a,i){var r,n,s;e._lazysizesWidth=i;i+=\"px\";e.setAttribute(\"sizes\",i);if(j.test(t.nodeName||\"\")){r=t.getElementsByTagName(\"source\");for(n=0,s=r.length;n\n * Copyright OpenJS Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n;(function() {\n\n /** Used as a safe reference for `undefined` in pre-ES5 environments. */\n var undefined;\n\n /** Used as the semantic version number. */\n var VERSION = '4.17.21';\n\n /** Used as the size to enable large array optimizations. */\n var LARGE_ARRAY_SIZE = 200;\n\n /** Error message constants. */\n var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',\n FUNC_ERROR_TEXT = 'Expected a function',\n INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`';\n\n /** Used to stand-in for `undefined` hash values. */\n var HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n /** Used as the maximum memoize cache size. */\n var MAX_MEMOIZE_SIZE = 500;\n\n /** Used as the internal argument placeholder. */\n var PLACEHOLDER = '__lodash_placeholder__';\n\n /** Used to compose bitmasks for cloning. */\n var CLONE_DEEP_FLAG = 1,\n CLONE_FLAT_FLAG = 2,\n CLONE_SYMBOLS_FLAG = 4;\n\n /** Used to compose bitmasks for value comparisons. */\n var COMPARE_PARTIAL_FLAG = 1,\n COMPARE_UNORDERED_FLAG = 2;\n\n /** Used to compose bitmasks for function metadata. */\n var WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_BOUND_FLAG = 4,\n WRAP_CURRY_FLAG = 8,\n WRAP_CURRY_RIGHT_FLAG = 16,\n WRAP_PARTIAL_FLAG = 32,\n WRAP_PARTIAL_RIGHT_FLAG = 64,\n WRAP_ARY_FLAG = 128,\n WRAP_REARG_FLAG = 256,\n WRAP_FLIP_FLAG = 512;\n\n /** Used as default options for `_.truncate`. */\n var DEFAULT_TRUNC_LENGTH = 30,\n DEFAULT_TRUNC_OMISSION = '...';\n\n /** Used to detect hot functions by number of calls within a span of milliseconds. */\n var HOT_COUNT = 800,\n HOT_SPAN = 16;\n\n /** Used to indicate the type of lazy iteratees. */\n var LAZY_FILTER_FLAG = 1,\n LAZY_MAP_FLAG = 2,\n LAZY_WHILE_FLAG = 3;\n\n /** Used as references for various `Number` constants. */\n var INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991,\n MAX_INTEGER = 1.7976931348623157e+308,\n NAN = 0 / 0;\n\n /** Used as references for the maximum length and index of an array. */\n var MAX_ARRAY_LENGTH = 4294967295,\n MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,\n HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;\n\n /** Used to associate wrap methods with their bit flags. */\n var wrapFlags = [\n ['ary', WRAP_ARY_FLAG],\n ['bind', WRAP_BIND_FLAG],\n ['bindKey', WRAP_BIND_KEY_FLAG],\n ['curry', WRAP_CURRY_FLAG],\n ['curryRight', WRAP_CURRY_RIGHT_FLAG],\n ['flip', WRAP_FLIP_FLAG],\n ['partial', WRAP_PARTIAL_FLAG],\n ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],\n ['rearg', WRAP_REARG_FLAG]\n ];\n\n /** `Object#toString` result references. */\n var argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n asyncTag = '[object AsyncFunction]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n domExcTag = '[object DOMException]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n nullTag = '[object Null]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n proxyTag = '[object Proxy]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n undefinedTag = '[object Undefined]',\n weakMapTag = '[object WeakMap]',\n weakSetTag = '[object WeakSet]';\n\n var arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n /** Used to match empty string literals in compiled template source. */\n var reEmptyStringLeading = /\\b__p \\+= '';/g,\n reEmptyStringMiddle = /\\b(__p \\+=) '' \\+/g,\n reEmptyStringTrailing = /(__e\\(.*?\\)|\\b__t\\)) \\+\\n'';/g;\n\n /** Used to match HTML entities and HTML characters. */\n var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,\n reUnescapedHtml = /[&<>\"']/g,\n reHasEscapedHtml = RegExp(reEscapedHtml.source),\n reHasUnescapedHtml = RegExp(reUnescapedHtml.source);\n\n /** Used to match template delimiters. */\n var reEscape = /<%-([\\s\\S]+?)%>/g,\n reEvaluate = /<%([\\s\\S]+?)%>/g,\n reInterpolate = /<%=([\\s\\S]+?)%>/g;\n\n /** Used to match property names within property paths. */\n var reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n reIsPlainProp = /^\\w*$/,\n rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n\n /**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\n var reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g,\n reHasRegExpChar = RegExp(reRegExpChar.source);\n\n /** Used to match leading whitespace. */\n var reTrimStart = /^\\s+/;\n\n /** Used to match a single whitespace character. */\n var reWhitespace = /\\s/;\n\n /** Used to match wrap detail comments. */\n var reWrapComment = /\\{(?:\\n\\/\\* \\[wrapped with .+\\] \\*\\/)?\\n?/,\n reWrapDetails = /\\{\\n\\/\\* \\[wrapped with (.+)\\] \\*/,\n reSplitDetails = /,? & /;\n\n /** Used to match words composed of alphanumeric characters. */\n var reAsciiWord = /[^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]+/g;\n\n /**\n * Used to validate the `validate` option in `_.template` variable.\n *\n * Forbids characters which could potentially change the meaning of the function argument definition:\n * - \"(),\" (modification of function parameters)\n * - \"=\" (default value)\n * - \"[]{}\" (destructuring of function parameters)\n * - \"/\" (beginning of a comment)\n * - whitespace\n */\n var reForbiddenIdentifierChars = /[()=,{}\\[\\]\\/\\s]/;\n\n /** Used to match backslashes in property paths. */\n var reEscapeChar = /\\\\(\\\\)?/g;\n\n /**\n * Used to match\n * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).\n */\n var reEsTemplate = /\\$\\{([^\\\\}]*(?:\\\\.[^\\\\}]*)*)\\}/g;\n\n /** Used to match `RegExp` flags from their coerced string values. */\n var reFlags = /\\w*$/;\n\n /** Used to detect bad signed hexadecimal string values. */\n var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n /** Used to detect binary string values. */\n var reIsBinary = /^0b[01]+$/i;\n\n /** Used to detect host constructors (Safari). */\n var reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n /** Used to detect octal string values. */\n var reIsOctal = /^0o[0-7]+$/i;\n\n /** Used to detect unsigned integer values. */\n var reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n /** Used to match Latin Unicode letters (excluding mathematical operators). */\n var reLatin = /[\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\xff\\u0100-\\u017f]/g;\n\n /** Used to ensure capturing order of template delimiters. */\n var reNoMatch = /($^)/;\n\n /** Used to match unescaped characters in compiled string literals. */\n var reUnescapedString = /['\\n\\r\\u2028\\u2029\\\\]/g;\n\n /** Used to compose unicode character classes. */\n var rsAstralRange = '\\\\ud800-\\\\udfff',\n rsComboMarksRange = '\\\\u0300-\\\\u036f',\n reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\n rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\n rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,\n rsDingbatRange = '\\\\u2700-\\\\u27bf',\n rsLowerRange = 'a-z\\\\xdf-\\\\xf6\\\\xf8-\\\\xff',\n rsMathOpRange = '\\\\xac\\\\xb1\\\\xd7\\\\xf7',\n rsNonCharRange = '\\\\x00-\\\\x2f\\\\x3a-\\\\x40\\\\x5b-\\\\x60\\\\x7b-\\\\xbf',\n rsPunctuationRange = '\\\\u2000-\\\\u206f',\n rsSpaceRange = ' \\\\t\\\\x0b\\\\f\\\\xa0\\\\ufeff\\\\n\\\\r\\\\u2028\\\\u2029\\\\u1680\\\\u180e\\\\u2000\\\\u2001\\\\u2002\\\\u2003\\\\u2004\\\\u2005\\\\u2006\\\\u2007\\\\u2008\\\\u2009\\\\u200a\\\\u202f\\\\u205f\\\\u3000',\n rsUpperRange = 'A-Z\\\\xc0-\\\\xd6\\\\xd8-\\\\xde',\n rsVarRange = '\\\\ufe0e\\\\ufe0f',\n rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;\n\n /** Used to compose unicode capture groups. */\n var rsApos = \"['\\u2019]\",\n rsAstral = '[' + rsAstralRange + ']',\n rsBreak = '[' + rsBreakRange + ']',\n rsCombo = '[' + rsComboRange + ']',\n rsDigits = '\\\\d+',\n rsDingbat = '[' + rsDingbatRange + ']',\n rsLower = '[' + rsLowerRange + ']',\n rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',\n rsFitz = '\\\\ud83c[\\\\udffb-\\\\udfff]',\n rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',\n rsNonAstral = '[^' + rsAstralRange + ']',\n rsRegional = '(?:\\\\ud83c[\\\\udde6-\\\\uddff]){2}',\n rsSurrPair = '[\\\\ud800-\\\\udbff][\\\\udc00-\\\\udfff]',\n rsUpper = '[' + rsUpperRange + ']',\n rsZWJ = '\\\\u200d';\n\n /** Used to compose unicode regexes. */\n var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',\n rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',\n rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',\n rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',\n reOptMod = rsModifier + '?',\n rsOptVar = '[' + rsVarRange + ']?',\n rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',\n rsOrdLower = '\\\\d*(?:1st|2nd|3rd|(?![123])\\\\dth)(?=\\\\b|[A-Z_])',\n rsOrdUpper = '\\\\d*(?:1ST|2ND|3RD|(?![123])\\\\dTH)(?=\\\\b|[a-z_])',\n rsSeq = rsOptVar + reOptMod + rsOptJoin,\n rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,\n rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';\n\n /** Used to match apostrophes. */\n var reApos = RegExp(rsApos, 'g');\n\n /**\n * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\n * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\n */\n var reComboMark = RegExp(rsCombo, 'g');\n\n /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */\n var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');\n\n /** Used to match complex or compound words. */\n var reUnicodeWord = RegExp([\n rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',\n rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',\n rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,\n rsUpper + '+' + rsOptContrUpper,\n rsOrdUpper,\n rsOrdLower,\n rsDigits,\n rsEmoji\n ].join('|'), 'g');\n\n /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */\n var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');\n\n /** Used to detect strings that need a more robust regexp to match words. */\n var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;\n\n /** Used to assign default `context` object properties. */\n var contextProps = [\n 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',\n 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',\n 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array',\n 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',\n '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'\n ];\n\n /** Used to make template sourceURLs easier to identify. */\n var templateCounter = -1;\n\n /** Used to identify `toStringTag` values of typed arrays. */\n var typedArrayTags = {};\n typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\n typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\n typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\n typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\n typedArrayTags[uint32Tag] = true;\n typedArrayTags[argsTag] = typedArrayTags[arrayTag] =\n typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\n typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\n typedArrayTags[errorTag] = typedArrayTags[funcTag] =\n typedArrayTags[mapTag] = typedArrayTags[numberTag] =\n typedArrayTags[objectTag] = typedArrayTags[regexpTag] =\n typedArrayTags[setTag] = typedArrayTags[stringTag] =\n typedArrayTags[weakMapTag] = false;\n\n /** Used to identify `toStringTag` values supported by `_.clone`. */\n var cloneableTags = {};\n cloneableTags[argsTag] = cloneableTags[arrayTag] =\n cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\n cloneableTags[boolTag] = cloneableTags[dateTag] =\n cloneableTags[float32Tag] = cloneableTags[float64Tag] =\n cloneableTags[int8Tag] = cloneableTags[int16Tag] =\n cloneableTags[int32Tag] = cloneableTags[mapTag] =\n cloneableTags[numberTag] = cloneableTags[objectTag] =\n cloneableTags[regexpTag] = cloneableTags[setTag] =\n cloneableTags[stringTag] = cloneableTags[symbolTag] =\n cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\n cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\n cloneableTags[errorTag] = cloneableTags[funcTag] =\n cloneableTags[weakMapTag] = false;\n\n /** Used to map Latin Unicode letters to basic Latin letters. */\n var deburredLetters = {\n // Latin-1 Supplement block.\n '\\xc0': 'A', '\\xc1': 'A', '\\xc2': 'A', '\\xc3': 'A', '\\xc4': 'A', '\\xc5': 'A',\n '\\xe0': 'a', '\\xe1': 'a', '\\xe2': 'a', '\\xe3': 'a', '\\xe4': 'a', '\\xe5': 'a',\n '\\xc7': 'C', '\\xe7': 'c',\n '\\xd0': 'D', '\\xf0': 'd',\n '\\xc8': 'E', '\\xc9': 'E', '\\xca': 'E', '\\xcb': 'E',\n '\\xe8': 'e', '\\xe9': 'e', '\\xea': 'e', '\\xeb': 'e',\n '\\xcc': 'I', '\\xcd': 'I', '\\xce': 'I', '\\xcf': 'I',\n '\\xec': 'i', '\\xed': 'i', '\\xee': 'i', '\\xef': 'i',\n '\\xd1': 'N', '\\xf1': 'n',\n '\\xd2': 'O', '\\xd3': 'O', '\\xd4': 'O', '\\xd5': 'O', '\\xd6': 'O', '\\xd8': 'O',\n '\\xf2': 'o', '\\xf3': 'o', '\\xf4': 'o', '\\xf5': 'o', '\\xf6': 'o', '\\xf8': 'o',\n '\\xd9': 'U', '\\xda': 'U', '\\xdb': 'U', '\\xdc': 'U',\n '\\xf9': 'u', '\\xfa': 'u', '\\xfb': 'u', '\\xfc': 'u',\n '\\xdd': 'Y', '\\xfd': 'y', '\\xff': 'y',\n '\\xc6': 'Ae', '\\xe6': 'ae',\n '\\xde': 'Th', '\\xfe': 'th',\n '\\xdf': 'ss',\n // Latin Extended-A block.\n '\\u0100': 'A', '\\u0102': 'A', '\\u0104': 'A',\n '\\u0101': 'a', '\\u0103': 'a', '\\u0105': 'a',\n '\\u0106': 'C', '\\u0108': 'C', '\\u010a': 'C', '\\u010c': 'C',\n '\\u0107': 'c', '\\u0109': 'c', '\\u010b': 'c', '\\u010d': 'c',\n '\\u010e': 'D', '\\u0110': 'D', '\\u010f': 'd', '\\u0111': 'd',\n '\\u0112': 'E', '\\u0114': 'E', '\\u0116': 'E', '\\u0118': 'E', '\\u011a': 'E',\n '\\u0113': 'e', '\\u0115': 'e', '\\u0117': 'e', '\\u0119': 'e', '\\u011b': 'e',\n '\\u011c': 'G', '\\u011e': 'G', '\\u0120': 'G', '\\u0122': 'G',\n '\\u011d': 'g', '\\u011f': 'g', '\\u0121': 'g', '\\u0123': 'g',\n '\\u0124': 'H', '\\u0126': 'H', '\\u0125': 'h', '\\u0127': 'h',\n '\\u0128': 'I', '\\u012a': 'I', '\\u012c': 'I', '\\u012e': 'I', '\\u0130': 'I',\n '\\u0129': 'i', '\\u012b': 'i', '\\u012d': 'i', '\\u012f': 'i', '\\u0131': 'i',\n '\\u0134': 'J', '\\u0135': 'j',\n '\\u0136': 'K', '\\u0137': 'k', '\\u0138': 'k',\n '\\u0139': 'L', '\\u013b': 'L', '\\u013d': 'L', '\\u013f': 'L', '\\u0141': 'L',\n '\\u013a': 'l', '\\u013c': 'l', '\\u013e': 'l', '\\u0140': 'l', '\\u0142': 'l',\n '\\u0143': 'N', '\\u0145': 'N', '\\u0147': 'N', '\\u014a': 'N',\n '\\u0144': 'n', '\\u0146': 'n', '\\u0148': 'n', '\\u014b': 'n',\n '\\u014c': 'O', '\\u014e': 'O', '\\u0150': 'O',\n '\\u014d': 'o', '\\u014f': 'o', '\\u0151': 'o',\n '\\u0154': 'R', '\\u0156': 'R', '\\u0158': 'R',\n '\\u0155': 'r', '\\u0157': 'r', '\\u0159': 'r',\n '\\u015a': 'S', '\\u015c': 'S', '\\u015e': 'S', '\\u0160': 'S',\n '\\u015b': 's', '\\u015d': 's', '\\u015f': 's', '\\u0161': 's',\n '\\u0162': 'T', '\\u0164': 'T', '\\u0166': 'T',\n '\\u0163': 't', '\\u0165': 't', '\\u0167': 't',\n '\\u0168': 'U', '\\u016a': 'U', '\\u016c': 'U', '\\u016e': 'U', '\\u0170': 'U', '\\u0172': 'U',\n '\\u0169': 'u', '\\u016b': 'u', '\\u016d': 'u', '\\u016f': 'u', '\\u0171': 'u', '\\u0173': 'u',\n '\\u0174': 'W', '\\u0175': 'w',\n '\\u0176': 'Y', '\\u0177': 'y', '\\u0178': 'Y',\n '\\u0179': 'Z', '\\u017b': 'Z', '\\u017d': 'Z',\n '\\u017a': 'z', '\\u017c': 'z', '\\u017e': 'z',\n '\\u0132': 'IJ', '\\u0133': 'ij',\n '\\u0152': 'Oe', '\\u0153': 'oe',\n '\\u0149': \"'n\", '\\u017f': 's'\n };\n\n /** Used to map characters to HTML entities. */\n var htmlEscapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n };\n\n /** Used to map HTML entities to characters. */\n var htmlUnescapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\"\n };\n\n /** Used to escape characters for inclusion in compiled string literals. */\n var stringEscapes = {\n '\\\\': '\\\\',\n \"'\": \"'\",\n '\\n': 'n',\n '\\r': 'r',\n '\\u2028': 'u2028',\n '\\u2029': 'u2029'\n };\n\n /** Built-in method references without a dependency on `root`. */\n var freeParseFloat = parseFloat,\n freeParseInt = parseInt;\n\n /** Detect free variable `global` from Node.js. */\n var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n /** Detect free variable `self`. */\n var freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n /** Used as a reference to the global object. */\n var root = freeGlobal || freeSelf || Function('return this')();\n\n /** Detect free variable `exports`. */\n var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n /** Detect free variable `module`. */\n var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n /** Detect the popular CommonJS extension `module.exports`. */\n var moduleExports = freeModule && freeModule.exports === freeExports;\n\n /** Detect free variable `process` from Node.js. */\n var freeProcess = moduleExports && freeGlobal.process;\n\n /** Used to access faster Node.js helpers. */\n var nodeUtil = (function() {\n try {\n // Use `util.types` for Node.js 10+.\n var types = freeModule && freeModule.require && freeModule.require('util').types;\n\n if (types) {\n return types;\n }\n\n // Legacy `process.binding('util')` for Node.js < 10.\n return freeProcess && freeProcess.binding && freeProcess.binding('util');\n } catch (e) {}\n }());\n\n /* Node.js helper references. */\n var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,\n nodeIsDate = nodeUtil && nodeUtil.isDate,\n nodeIsMap = nodeUtil && nodeUtil.isMap,\n nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,\n nodeIsSet = nodeUtil && nodeUtil.isSet,\n nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\n function apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n }\n\n /**\n * A specialized version of `baseAggregator` for arrays.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function arrayAggregator(array, setter, iteratee, accumulator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n var value = array[index];\n setter(accumulator, value, iteratee(value), array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEach(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.forEachRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEachRight(array, iteratee) {\n var length = array == null ? 0 : array.length;\n\n while (length--) {\n if (iteratee(array[length], length, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.every` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n */\n function arrayEvery(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (!predicate(array[index], index, array)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * A specialized version of `_.filter` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function arrayFilter(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludes(array, value) {\n var length = array == null ? 0 : array.length;\n return !!length && baseIndexOf(array, value, 0) > -1;\n }\n\n /**\n * This function is like `arrayIncludes` except that it accepts a comparator.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludesWith(array, value, comparator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (comparator(value, array[index])) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `_.map` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function arrayMap(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length,\n result = Array(length);\n\n while (++index < length) {\n result[index] = iteratee(array[index], index, array);\n }\n return result;\n }\n\n /**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\n function arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n }\n\n /**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduce(array, iteratee, accumulator, initAccum) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n if (initAccum && length) {\n accumulator = array[++index];\n }\n while (++index < length) {\n accumulator = iteratee(accumulator, array[index], index, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.reduceRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the last element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduceRight(array, iteratee, accumulator, initAccum) {\n var length = array == null ? 0 : array.length;\n if (initAccum && length) {\n accumulator = array[--length];\n }\n while (length--) {\n accumulator = iteratee(accumulator, array[length], length, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function arraySome(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Gets the size of an ASCII `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n var asciiSize = baseProperty('length');\n\n /**\n * Converts an ASCII `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function asciiToArray(string) {\n return string.split('');\n }\n\n /**\n * Splits an ASCII `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function asciiWords(string) {\n return string.match(reAsciiWord) || [];\n }\n\n /**\n * The base implementation of methods like `_.findKey` and `_.findLastKey`,\n * without support for iteratee shorthands, which iterates over `collection`\n * using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the found element or its key, else `undefined`.\n */\n function baseFindKey(collection, predicate, eachFunc) {\n var result;\n eachFunc(collection, function(value, key, collection) {\n if (predicate(value, key, collection)) {\n result = key;\n return false;\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOf(array, value, fromIndex) {\n return value === value\n ? strictIndexOf(array, value, fromIndex)\n : baseFindIndex(array, baseIsNaN, fromIndex);\n }\n\n /**\n * This function is like `baseIndexOf` except that it accepts a comparator.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOfWith(array, value, fromIndex, comparator) {\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (comparator(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.isNaN` without support for number objects.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n */\n function baseIsNaN(value) {\n return value !== value;\n }\n\n /**\n * The base implementation of `_.mean` and `_.meanBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the mean.\n */\n function baseMean(array, iteratee) {\n var length = array == null ? 0 : array.length;\n return length ? (baseSum(array, iteratee) / length) : NAN;\n }\n\n /**\n * The base implementation of `_.property` without support for deep paths.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function baseProperty(key) {\n return function(object) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.propertyOf` without support for deep paths.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyOf(object) {\n return function(key) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.reduce` and `_.reduceRight`, without support\n * for iteratee shorthands, which iterates over `collection` using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} accumulator The initial value.\n * @param {boolean} initAccum Specify using the first or last element of\n * `collection` as the initial value.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the accumulated value.\n */\n function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {\n eachFunc(collection, function(value, index, collection) {\n accumulator = initAccum\n ? (initAccum = false, value)\n : iteratee(accumulator, value, index, collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.sortBy` which uses `comparer` to define the\n * sort order of `array` and replaces criteria objects with their corresponding\n * values.\n *\n * @private\n * @param {Array} array The array to sort.\n * @param {Function} comparer The function to define sort order.\n * @returns {Array} Returns `array`.\n */\n function baseSortBy(array, comparer) {\n var length = array.length;\n\n array.sort(comparer);\n while (length--) {\n array[length] = array[length].value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.sum` and `_.sumBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the sum.\n */\n function baseSum(array, iteratee) {\n var result,\n index = -1,\n length = array.length;\n\n while (++index < length) {\n var current = iteratee(array[index]);\n if (current !== undefined) {\n result = result === undefined ? current : (result + current);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\n function baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array\n * of key-value pairs for `object` corresponding to the property names of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the key-value pairs.\n */\n function baseToPairs(object, props) {\n return arrayMap(props, function(key) {\n return [key, object[key]];\n });\n }\n\n /**\n * The base implementation of `_.trim`.\n *\n * @private\n * @param {string} string The string to trim.\n * @returns {string} Returns the trimmed string.\n */\n function baseTrim(string) {\n return string\n ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')\n : string;\n }\n\n /**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\n function baseUnary(func) {\n return function(value) {\n return func(value);\n };\n }\n\n /**\n * The base implementation of `_.values` and `_.valuesIn` which creates an\n * array of `object` property values corresponding to the property names\n * of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the array of property values.\n */\n function baseValues(object, props) {\n return arrayMap(props, function(key) {\n return object[key];\n });\n }\n\n /**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function cacheHas(cache, key) {\n return cache.has(key);\n }\n\n /**\n * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the first unmatched string symbol.\n */\n function charsStartIndex(strSymbols, chrSymbols) {\n var index = -1,\n length = strSymbols.length;\n\n while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the last unmatched string symbol.\n */\n function charsEndIndex(strSymbols, chrSymbols) {\n var index = strSymbols.length;\n\n while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Gets the number of `placeholder` occurrences in `array`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} placeholder The placeholder to search for.\n * @returns {number} Returns the placeholder count.\n */\n function countHolders(array, placeholder) {\n var length = array.length,\n result = 0;\n\n while (length--) {\n if (array[length] === placeholder) {\n ++result;\n }\n }\n return result;\n }\n\n /**\n * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A\n * letters to basic Latin letters.\n *\n * @private\n * @param {string} letter The matched letter to deburr.\n * @returns {string} Returns the deburred letter.\n */\n var deburrLetter = basePropertyOf(deburredLetters);\n\n /**\n * Used by `_.escape` to convert characters to HTML entities.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n var escapeHtmlChar = basePropertyOf(htmlEscapes);\n\n /**\n * Used by `_.template` to escape characters for inclusion in compiled string literals.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n function escapeStringChar(chr) {\n return '\\\\' + stringEscapes[chr];\n }\n\n /**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function getValue(object, key) {\n return object == null ? undefined : object[key];\n }\n\n /**\n * Checks if `string` contains Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a symbol is found, else `false`.\n */\n function hasUnicode(string) {\n return reHasUnicode.test(string);\n }\n\n /**\n * Checks if `string` contains a word composed of Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a word is found, else `false`.\n */\n function hasUnicodeWord(string) {\n return reHasUnicodeWord.test(string);\n }\n\n /**\n * Converts `iterator` to an array.\n *\n * @private\n * @param {Object} iterator The iterator to convert.\n * @returns {Array} Returns the converted array.\n */\n function iteratorToArray(iterator) {\n var data,\n result = [];\n\n while (!(data = iterator.next()).done) {\n result.push(data.value);\n }\n return result;\n }\n\n /**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\n function mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n }\n\n /**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\n function overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n }\n\n /**\n * Replaces all `placeholder` elements in `array` with an internal placeholder\n * and returns an array of their indexes.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {*} placeholder The placeholder to replace.\n * @returns {Array} Returns the new array of placeholder indexes.\n */\n function replaceHolders(array, placeholder) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value === placeholder || value === PLACEHOLDER) {\n array[index] = PLACEHOLDER;\n result[resIndex++] = index;\n }\n }\n return result;\n }\n\n /**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\n function setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n }\n\n /**\n * Converts `set` to its value-value pairs.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the value-value pairs.\n */\n function setToPairs(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = [value, value];\n });\n return result;\n }\n\n /**\n * A specialized version of `_.indexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictIndexOf(array, value, fromIndex) {\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (array[index] === value) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * A specialized version of `_.lastIndexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictLastIndexOf(array, value, fromIndex) {\n var index = fromIndex + 1;\n while (index--) {\n if (array[index] === value) {\n return index;\n }\n }\n return index;\n }\n\n /**\n * Gets the number of symbols in `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the string size.\n */\n function stringSize(string) {\n return hasUnicode(string)\n ? unicodeSize(string)\n : asciiSize(string);\n }\n\n /**\n * Converts `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function stringToArray(string) {\n return hasUnicode(string)\n ? unicodeToArray(string)\n : asciiToArray(string);\n }\n\n /**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace\n * character of `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the index of the last non-whitespace character.\n */\n function trimmedEndIndex(string) {\n var index = string.length;\n\n while (index-- && reWhitespace.test(string.charAt(index))) {}\n return index;\n }\n\n /**\n * Used by `_.unescape` to convert HTML entities to characters.\n *\n * @private\n * @param {string} chr The matched character to unescape.\n * @returns {string} Returns the unescaped character.\n */\n var unescapeHtmlChar = basePropertyOf(htmlUnescapes);\n\n /**\n * Gets the size of a Unicode `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n function unicodeSize(string) {\n var result = reUnicode.lastIndex = 0;\n while (reUnicode.test(string)) {\n ++result;\n }\n return result;\n }\n\n /**\n * Converts a Unicode `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function unicodeToArray(string) {\n return string.match(reUnicode) || [];\n }\n\n /**\n * Splits a Unicode `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function unicodeWords(string) {\n return string.match(reUnicodeWord) || [];\n }\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * Create a new pristine `lodash` function using the `context` object.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Util\n * @param {Object} [context=root] The context object.\n * @returns {Function} Returns a new `lodash` function.\n * @example\n *\n * _.mixin({ 'foo': _.constant('foo') });\n *\n * var lodash = _.runInContext();\n * lodash.mixin({ 'bar': lodash.constant('bar') });\n *\n * _.isFunction(_.foo);\n * // => true\n * _.isFunction(_.bar);\n * // => false\n *\n * lodash.isFunction(lodash.foo);\n * // => false\n * lodash.isFunction(lodash.bar);\n * // => true\n *\n * // Create a suped-up `defer` in Node.js.\n * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;\n */\n var runInContext = (function runInContext(context) {\n context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));\n\n /** Built-in constructor references. */\n var Array = context.Array,\n Date = context.Date,\n Error = context.Error,\n Function = context.Function,\n Math = context.Math,\n Object = context.Object,\n RegExp = context.RegExp,\n String = context.String,\n TypeError = context.TypeError;\n\n /** Used for built-in method references. */\n var arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n /** Used to detect overreaching core-js shims. */\n var coreJsData = context['__core-js_shared__'];\n\n /** Used to resolve the decompiled source of functions. */\n var funcToString = funcProto.toString;\n\n /** Used to check objects for own properties. */\n var hasOwnProperty = objectProto.hasOwnProperty;\n\n /** Used to generate unique IDs. */\n var idCounter = 0;\n\n /** Used to detect methods masquerading as native. */\n var maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n }());\n\n /**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\n var nativeObjectToString = objectProto.toString;\n\n /** Used to infer the `Object` constructor. */\n var objectCtorString = funcToString.call(Object);\n\n /** Used to restore the original `_` reference in `_.noConflict`. */\n var oldDash = root._;\n\n /** Used to detect if a method is native. */\n var reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n );\n\n /** Built-in value references. */\n var Buffer = moduleExports ? context.Buffer : undefined,\n Symbol = context.Symbol,\n Uint8Array = context.Uint8Array,\n allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,\n getPrototype = overArg(Object.getPrototypeOf, Object),\n objectCreate = Object.create,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice,\n spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,\n symIterator = Symbol ? Symbol.iterator : undefined,\n symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n var defineProperty = (function() {\n try {\n var func = getNative(Object, 'defineProperty');\n func({}, '', {});\n return func;\n } catch (e) {}\n }());\n\n /** Mocked built-ins. */\n var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,\n ctxNow = Date && Date.now !== root.Date.now && Date.now,\n ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;\n\n /* Built-in method references for those with the same name as other `lodash` methods. */\n var nativeCeil = Math.ceil,\n nativeFloor = Math.floor,\n nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n nativeIsFinite = context.isFinite,\n nativeJoin = arrayProto.join,\n nativeKeys = overArg(Object.keys, Object),\n nativeMax = Math.max,\n nativeMin = Math.min,\n nativeNow = Date.now,\n nativeParseInt = context.parseInt,\n nativeRandom = Math.random,\n nativeReverse = arrayProto.reverse;\n\n /* Built-in method references that are verified to be native. */\n var DataView = getNative(context, 'DataView'),\n Map = getNative(context, 'Map'),\n Promise = getNative(context, 'Promise'),\n Set = getNative(context, 'Set'),\n WeakMap = getNative(context, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n /** Used to store function metadata. */\n var metaMap = WeakMap && new WeakMap;\n\n /** Used to lookup unminified function names. */\n var realNames = {};\n\n /** Used to detect maps, sets, and weakmaps. */\n var dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n /** Used to convert symbols to primitives and strings. */\n var symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,\n symbolToString = symbolProto ? symbolProto.toString : undefined;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` object which wraps `value` to enable implicit method\n * chain sequences. Methods that operate on and return arrays, collections,\n * and functions can be chained together. Methods that retrieve a single value\n * or may return a primitive value will automatically end the chain sequence\n * and return the unwrapped value. Otherwise, the value must be unwrapped\n * with `_#value`.\n *\n * Explicit chain sequences, which must be unwrapped with `_#value`, may be\n * enabled using `_.chain`.\n *\n * The execution of chained methods is lazy, that is, it's deferred until\n * `_#value` is implicitly or explicitly called.\n *\n * Lazy evaluation allows several methods to support shortcut fusion.\n * Shortcut fusion is an optimization to merge iteratee calls; this avoids\n * the creation of intermediate arrays and can greatly reduce the number of\n * iteratee executions. Sections of a chain sequence qualify for shortcut\n * fusion if the section is applied to an array and iteratees accept only\n * one argument. The heuristic for whether a section qualifies for shortcut\n * fusion is subject to change.\n *\n * Chaining is supported in custom builds as long as the `_#value` method is\n * directly or indirectly included in the build.\n *\n * In addition to lodash methods, wrappers have `Array` and `String` methods.\n *\n * The wrapper `Array` methods are:\n * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`\n *\n * The wrapper `String` methods are:\n * `replace` and `split`\n *\n * The wrapper methods that support shortcut fusion are:\n * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,\n * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,\n * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`\n *\n * The chainable wrapper methods are:\n * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,\n * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,\n * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,\n * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,\n * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,\n * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,\n * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,\n * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,\n * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,\n * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,\n * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,\n * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,\n * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,\n * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,\n * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,\n * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,\n * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,\n * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,\n * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,\n * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,\n * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,\n * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,\n * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,\n * `zipObject`, `zipObjectDeep`, and `zipWith`\n *\n * The wrapper methods that are **not** chainable by default are:\n * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,\n * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,\n * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,\n * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,\n * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,\n * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,\n * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,\n * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,\n * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,\n * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,\n * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,\n * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,\n * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,\n * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,\n * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,\n * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,\n * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,\n * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,\n * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,\n * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,\n * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,\n * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,\n * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,\n * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,\n * `upperFirst`, `value`, and `words`\n *\n * @name _\n * @constructor\n * @category Seq\n * @param {*} value The value to wrap in a `lodash` instance.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2, 3]);\n *\n * // Returns an unwrapped value.\n * wrapped.reduce(_.add);\n * // => 6\n *\n * // Returns a wrapped value.\n * var squares = wrapped.map(square);\n *\n * _.isArray(squares);\n * // => false\n *\n * _.isArray(squares.value());\n * // => true\n */\n function lodash(value) {\n if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {\n if (value instanceof LodashWrapper) {\n return value;\n }\n if (hasOwnProperty.call(value, '__wrapped__')) {\n return wrapperClone(value);\n }\n }\n return new LodashWrapper(value);\n }\n\n /**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} proto The object to inherit from.\n * @returns {Object} Returns the new object.\n */\n var baseCreate = (function() {\n function object() {}\n return function(proto) {\n if (!isObject(proto)) {\n return {};\n }\n if (objectCreate) {\n return objectCreate(proto);\n }\n object.prototype = proto;\n var result = new object;\n object.prototype = undefined;\n return result;\n };\n }());\n\n /**\n * The function whose prototype chain sequence wrappers inherit from.\n *\n * @private\n */\n function baseLodash() {\n // No operation performed.\n }\n\n /**\n * The base constructor for creating `lodash` wrapper objects.\n *\n * @private\n * @param {*} value The value to wrap.\n * @param {boolean} [chainAll] Enable explicit method chain sequences.\n */\n function LodashWrapper(value, chainAll) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__chain__ = !!chainAll;\n this.__index__ = 0;\n this.__values__ = undefined;\n }\n\n /**\n * By default, the template delimiters used by lodash are like those in\n * embedded Ruby (ERB) as well as ES2015 template strings. Change the\n * following template settings to use alternative delimiters.\n *\n * @static\n * @memberOf _\n * @type {Object}\n */\n lodash.templateSettings = {\n\n /**\n * Used to detect `data` property values to be HTML-escaped.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'escape': reEscape,\n\n /**\n * Used to detect code to be evaluated.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'evaluate': reEvaluate,\n\n /**\n * Used to detect `data` property values to inject.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'interpolate': reInterpolate,\n\n /**\n * Used to reference the data object in the template text.\n *\n * @memberOf _.templateSettings\n * @type {string}\n */\n 'variable': '',\n\n /**\n * Used to import variables into the compiled template.\n *\n * @memberOf _.templateSettings\n * @type {Object}\n */\n 'imports': {\n\n /**\n * A reference to the `lodash` function.\n *\n * @memberOf _.templateSettings.imports\n * @type {Function}\n */\n '_': lodash\n }\n };\n\n // Ensure wrappers are instances of `baseLodash`.\n lodash.prototype = baseLodash.prototype;\n lodash.prototype.constructor = lodash;\n\n LodashWrapper.prototype = baseCreate(baseLodash.prototype);\n LodashWrapper.prototype.constructor = LodashWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.\n *\n * @private\n * @constructor\n * @param {*} value The value to wrap.\n */\n function LazyWrapper(value) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__dir__ = 1;\n this.__filtered__ = false;\n this.__iteratees__ = [];\n this.__takeCount__ = MAX_ARRAY_LENGTH;\n this.__views__ = [];\n }\n\n /**\n * Creates a clone of the lazy wrapper object.\n *\n * @private\n * @name clone\n * @memberOf LazyWrapper\n * @returns {Object} Returns the cloned `LazyWrapper` object.\n */\n function lazyClone() {\n var result = new LazyWrapper(this.__wrapped__);\n result.__actions__ = copyArray(this.__actions__);\n result.__dir__ = this.__dir__;\n result.__filtered__ = this.__filtered__;\n result.__iteratees__ = copyArray(this.__iteratees__);\n result.__takeCount__ = this.__takeCount__;\n result.__views__ = copyArray(this.__views__);\n return result;\n }\n\n /**\n * Reverses the direction of lazy iteration.\n *\n * @private\n * @name reverse\n * @memberOf LazyWrapper\n * @returns {Object} Returns the new reversed `LazyWrapper` object.\n */\n function lazyReverse() {\n if (this.__filtered__) {\n var result = new LazyWrapper(this);\n result.__dir__ = -1;\n result.__filtered__ = true;\n } else {\n result = this.clone();\n result.__dir__ *= -1;\n }\n return result;\n }\n\n /**\n * Extracts the unwrapped value from its lazy wrapper.\n *\n * @private\n * @name value\n * @memberOf LazyWrapper\n * @returns {*} Returns the unwrapped value.\n */\n function lazyValue() {\n var array = this.__wrapped__.value(),\n dir = this.__dir__,\n isArr = isArray(array),\n isRight = dir < 0,\n arrLength = isArr ? array.length : 0,\n view = getView(0, arrLength, this.__views__),\n start = view.start,\n end = view.end,\n length = end - start,\n index = isRight ? end : (start - 1),\n iteratees = this.__iteratees__,\n iterLength = iteratees.length,\n resIndex = 0,\n takeCount = nativeMin(length, this.__takeCount__);\n\n if (!isArr || (!isRight && arrLength == length && takeCount == length)) {\n return baseWrapperValue(array, this.__actions__);\n }\n var result = [];\n\n outer:\n while (length-- && resIndex < takeCount) {\n index += dir;\n\n var iterIndex = -1,\n value = array[index];\n\n while (++iterIndex < iterLength) {\n var data = iteratees[iterIndex],\n iteratee = data.iteratee,\n type = data.type,\n computed = iteratee(value);\n\n if (type == LAZY_MAP_FLAG) {\n value = computed;\n } else if (!computed) {\n if (type == LAZY_FILTER_FLAG) {\n continue outer;\n } else {\n break outer;\n }\n }\n }\n result[resIndex++] = value;\n }\n return result;\n }\n\n // Ensure `LazyWrapper` is an instance of `baseLodash`.\n LazyWrapper.prototype = baseCreate(baseLodash.prototype);\n LazyWrapper.prototype.constructor = LazyWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Hash(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\n function hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function hashDelete(key) {\n var result = this.has(key) && delete this.__data__[key];\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n }\n\n /**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);\n }\n\n /**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\n function hashSet(key, value) {\n var data = this.__data__;\n this.size += this.has(key) ? 0 : 1;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n }\n\n // Add methods to `Hash`.\n Hash.prototype.clear = hashClear;\n Hash.prototype['delete'] = hashDelete;\n Hash.prototype.get = hashGet;\n Hash.prototype.has = hashHas;\n Hash.prototype.set = hashSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function ListCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\n function listCacheClear() {\n this.__data__ = [];\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n --this.size;\n return true;\n }\n\n /**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n }\n\n /**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n }\n\n /**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\n function listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n ++this.size;\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n }\n\n // Add methods to `ListCache`.\n ListCache.prototype.clear = listCacheClear;\n ListCache.prototype['delete'] = listCacheDelete;\n ListCache.prototype.get = listCacheGet;\n ListCache.prototype.has = listCacheHas;\n ListCache.prototype.set = listCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function MapCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\n function mapCacheClear() {\n this.size = 0;\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n }\n\n /**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function mapCacheDelete(key) {\n var result = getMapData(this, key)['delete'](key);\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function mapCacheGet(key) {\n return getMapData(this, key).get(key);\n }\n\n /**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function mapCacheHas(key) {\n return getMapData(this, key).has(key);\n }\n\n /**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\n function mapCacheSet(key, value) {\n var data = getMapData(this, key),\n size = data.size;\n\n data.set(key, value);\n this.size += data.size == size ? 0 : 1;\n return this;\n }\n\n // Add methods to `MapCache`.\n MapCache.prototype.clear = mapCacheClear;\n MapCache.prototype['delete'] = mapCacheDelete;\n MapCache.prototype.get = mapCacheGet;\n MapCache.prototype.has = mapCacheHas;\n MapCache.prototype.set = mapCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\n function SetCache(values) {\n var index = -1,\n length = values == null ? 0 : values.length;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n }\n\n /**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\n function setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n }\n\n /**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\n function setCacheHas(value) {\n return this.__data__.has(value);\n }\n\n // Add methods to `SetCache`.\n SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\n SetCache.prototype.has = setCacheHas;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Stack(entries) {\n var data = this.__data__ = new ListCache(entries);\n this.size = data.size;\n }\n\n /**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\n function stackClear() {\n this.__data__ = new ListCache;\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function stackDelete(key) {\n var data = this.__data__,\n result = data['delete'](key);\n\n this.size = data.size;\n return result;\n }\n\n /**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function stackGet(key) {\n return this.__data__.get(key);\n }\n\n /**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function stackHas(key) {\n return this.__data__.has(key);\n }\n\n /**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\n function stackSet(key, value) {\n var data = this.__data__;\n if (data instanceof ListCache) {\n var pairs = data.__data__;\n if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n pairs.push([key, value]);\n this.size = ++data.size;\n return this;\n }\n data = this.__data__ = new MapCache(pairs);\n }\n data.set(key, value);\n this.size = data.size;\n return this;\n }\n\n // Add methods to `Stack`.\n Stack.prototype.clear = stackClear;\n Stack.prototype['delete'] = stackDelete;\n Stack.prototype.get = stackGet;\n Stack.prototype.has = stackHas;\n Stack.prototype.set = stackSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\n function arrayLikeKeys(value, inherited) {\n var isArr = isArray(value),\n isArg = !isArr && isArguments(value),\n isBuff = !isArr && !isArg && isBuffer(value),\n isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n skipIndexes = isArr || isArg || isBuff || isType,\n result = skipIndexes ? baseTimes(value.length, String) : [],\n length = result.length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (\n // Safari 9 has enumerable `arguments.length` in strict mode.\n key == 'length' ||\n // Node.js 0.10 has enumerable non-index properties on buffers.\n (isBuff && (key == 'offset' || key == 'parent')) ||\n // PhantomJS 2 has enumerable non-index properties on typed arrays.\n (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||\n // Skip index properties.\n isIndex(key, length)\n ))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.sample` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @returns {*} Returns the random element.\n */\n function arraySample(array) {\n var length = array.length;\n return length ? array[baseRandom(0, length - 1)] : undefined;\n }\n\n /**\n * A specialized version of `_.sampleSize` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function arraySampleSize(array, n) {\n return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));\n }\n\n /**\n * A specialized version of `_.shuffle` for arrays.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function arrayShuffle(array) {\n return shuffleSelf(copyArray(array));\n }\n\n /**\n * This function is like `assignValue` except that it doesn't assign\n * `undefined` values.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignMergeValue(object, key, value) {\n if ((value !== undefined && !eq(object[key], value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n }\n\n /**\n * Aggregates elements of `collection` on `accumulator` with keys transformed\n * by `iteratee` and values set by `setter`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseAggregator(collection, setter, iteratee, accumulator) {\n baseEach(collection, function(value, key, collection) {\n setter(accumulator, value, iteratee(value), collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssign(object, source) {\n return object && copyObject(source, keys(source), object);\n }\n\n /**\n * The base implementation of `_.assignIn` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssignIn(object, source) {\n return object && copyObject(source, keysIn(source), object);\n }\n\n /**\n * The base implementation of `assignValue` and `assignMergeValue` without\n * value checks.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function baseAssignValue(object, key, value) {\n if (key == '__proto__' && defineProperty) {\n defineProperty(object, key, {\n 'configurable': true,\n 'enumerable': true,\n 'value': value,\n 'writable': true\n });\n } else {\n object[key] = value;\n }\n }\n\n /**\n * The base implementation of `_.at` without support for individual paths.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {string[]} paths The property paths to pick.\n * @returns {Array} Returns the picked elements.\n */\n function baseAt(object, paths) {\n var index = -1,\n length = paths.length,\n result = Array(length),\n skip = object == null;\n\n while (++index < length) {\n result[index] = skip ? undefined : get(object, paths[index]);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.clamp` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n */\n function baseClamp(number, lower, upper) {\n if (number === number) {\n if (upper !== undefined) {\n number = number <= upper ? number : upper;\n }\n if (lower !== undefined) {\n number = number >= lower ? number : lower;\n }\n }\n return number;\n }\n\n /**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Deep clone\n * 2 - Flatten inherited properties\n * 4 - Clone symbols\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\n function baseClone(value, bitmask, customizer, key, object, stack) {\n var result,\n isDeep = bitmask & CLONE_DEEP_FLAG,\n isFlat = bitmask & CLONE_FLAT_FLAG,\n isFull = bitmask & CLONE_SYMBOLS_FLAG;\n\n if (customizer) {\n result = object ? customizer(value, key, object, stack) : customizer(value);\n }\n if (result !== undefined) {\n return result;\n }\n if (!isObject(value)) {\n return value;\n }\n var isArr = isArray(value);\n if (isArr) {\n result = initCloneArray(value);\n if (!isDeep) {\n return copyArray(value, result);\n }\n } else {\n var tag = getTag(value),\n isFunc = tag == funcTag || tag == genTag;\n\n if (isBuffer(value)) {\n return cloneBuffer(value, isDeep);\n }\n if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n result = (isFlat || isFunc) ? {} : initCloneObject(value);\n if (!isDeep) {\n return isFlat\n ? copySymbolsIn(value, baseAssignIn(result, value))\n : copySymbols(value, baseAssign(result, value));\n }\n } else {\n if (!cloneableTags[tag]) {\n return object ? value : {};\n }\n result = initCloneByTag(value, tag, isDeep);\n }\n }\n // Check for circular references and return its corresponding clone.\n stack || (stack = new Stack);\n var stacked = stack.get(value);\n if (stacked) {\n return stacked;\n }\n stack.set(value, result);\n\n if (isSet(value)) {\n value.forEach(function(subValue) {\n result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));\n });\n } else if (isMap(value)) {\n value.forEach(function(subValue, key) {\n result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n }\n\n var keysFunc = isFull\n ? (isFlat ? getAllKeysIn : getAllKeys)\n : (isFlat ? keysIn : keys);\n\n var props = isArr ? undefined : keysFunc(value);\n arrayEach(props || value, function(subValue, key) {\n if (props) {\n key = subValue;\n subValue = value[key];\n }\n // Recursively populate clone (susceptible to call stack limits).\n assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n return result;\n }\n\n /**\n * The base implementation of `_.conforms` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property predicates to conform to.\n * @returns {Function} Returns the new spec function.\n */\n function baseConforms(source) {\n var props = keys(source);\n return function(object) {\n return baseConformsTo(object, source, props);\n };\n }\n\n /**\n * The base implementation of `_.conformsTo` which accepts `props` to check.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property predicates to conform to.\n * @returns {boolean} Returns `true` if `object` conforms, else `false`.\n */\n function baseConformsTo(object, source, props) {\n var length = props.length;\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (length--) {\n var key = props[length],\n predicate = source[key],\n value = object[key];\n\n if ((value === undefined && !(key in object)) || !predicate(value)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.delay` and `_.defer` which accepts `args`\n * to provide to `func`.\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {Array} args The arguments to provide to `func`.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n function baseDelay(func, wait, args) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return setTimeout(function() { func.apply(undefined, args); }, wait);\n }\n\n /**\n * The base implementation of methods like `_.difference` without support\n * for excluding multiple arrays or iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Array} values The values to exclude.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n */\n function baseDifference(array, values, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n isCommon = true,\n length = array.length,\n result = [],\n valuesLength = values.length;\n\n if (!length) {\n return result;\n }\n if (iteratee) {\n values = arrayMap(values, baseUnary(iteratee));\n }\n if (comparator) {\n includes = arrayIncludesWith;\n isCommon = false;\n }\n else if (values.length >= LARGE_ARRAY_SIZE) {\n includes = cacheHas;\n isCommon = false;\n values = new SetCache(values);\n }\n outer:\n while (++index < length) {\n var value = array[index],\n computed = iteratee == null ? value : iteratee(value);\n\n value = (comparator || value !== 0) ? value : 0;\n if (isCommon && computed === computed) {\n var valuesIndex = valuesLength;\n while (valuesIndex--) {\n if (values[valuesIndex] === computed) {\n continue outer;\n }\n }\n result.push(value);\n }\n else if (!includes(values, computed, comparator)) {\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.forEach` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEach = createBaseEach(baseForOwn);\n\n /**\n * The base implementation of `_.forEachRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEachRight = createBaseEach(baseForOwnRight, true);\n\n /**\n * The base implementation of `_.every` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`\n */\n function baseEvery(collection, predicate) {\n var result = true;\n baseEach(collection, function(value, index, collection) {\n result = !!predicate(value, index, collection);\n return result;\n });\n return result;\n }\n\n /**\n * The base implementation of methods like `_.max` and `_.min` which accepts a\n * `comparator` to determine the extremum value.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The iteratee invoked per iteration.\n * @param {Function} comparator The comparator used to compare values.\n * @returns {*} Returns the extremum value.\n */\n function baseExtremum(array, iteratee, comparator) {\n var index = -1,\n length = array.length;\n\n while (++index < length) {\n var value = array[index],\n current = iteratee(value);\n\n if (current != null && (computed === undefined\n ? (current === current && !isSymbol(current))\n : comparator(current, computed)\n )) {\n var computed = current,\n result = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.fill` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n */\n function baseFill(array, value, start, end) {\n var length = array.length;\n\n start = toInteger(start);\n if (start < 0) {\n start = -start > length ? 0 : (length + start);\n }\n end = (end === undefined || end > length) ? length : toInteger(end);\n if (end < 0) {\n end += length;\n }\n end = start > end ? 0 : toLength(end);\n while (start < end) {\n array[start++] = value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.filter` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function baseFilter(collection, predicate) {\n var result = [];\n baseEach(collection, function(value, index, collection) {\n if (predicate(value, index, collection)) {\n result.push(value);\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\n function baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n\n predicate || (predicate = isFlattenable);\n result || (result = []);\n\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `baseForOwn` which iterates over `object`\n * properties returned by `keysFunc` and invokes `iteratee` for each property.\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseFor = createBaseFor();\n\n /**\n * This function is like `baseFor` except that it iterates over properties\n * in the opposite order.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseForRight = createBaseFor(true);\n\n /**\n * The base implementation of `_.forOwn` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwn(object, iteratee) {\n return object && baseFor(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.forOwnRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwnRight(object, iteratee) {\n return object && baseForRight(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.functions` which creates an array of\n * `object` function property names filtered from `props`.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Array} props The property names to filter.\n * @returns {Array} Returns the function names.\n */\n function baseFunctions(object, props) {\n return arrayFilter(props, function(key) {\n return isFunction(object[key]);\n });\n }\n\n /**\n * The base implementation of `_.get` without support for default values.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @returns {*} Returns the resolved value.\n */\n function baseGet(object, path) {\n path = castPath(path, object);\n\n var index = 0,\n length = path.length;\n\n while (object != null && index < length) {\n object = object[toKey(path[index++])];\n }\n return (index && index == length) ? object : undefined;\n }\n\n /**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n }\n\n /**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n function baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n }\n\n /**\n * The base implementation of `_.gt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n */\n function baseGt(value, other) {\n return value > other;\n }\n\n /**\n * The base implementation of `_.has` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHas(object, key) {\n return object != null && hasOwnProperty.call(object, key);\n }\n\n /**\n * The base implementation of `_.hasIn` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHasIn(object, key) {\n return object != null && key in Object(object);\n }\n\n /**\n * The base implementation of `_.inRange` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to check.\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n */\n function baseInRange(number, start, end) {\n return number >= nativeMin(start, end) && number < nativeMax(start, end);\n }\n\n /**\n * The base implementation of methods like `_.intersection`, without support\n * for iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of shared values.\n */\n function baseIntersection(arrays, iteratee, comparator) {\n var includes = comparator ? arrayIncludesWith : arrayIncludes,\n length = arrays[0].length,\n othLength = arrays.length,\n othIndex = othLength,\n caches = Array(othLength),\n maxLength = Infinity,\n result = [];\n\n while (othIndex--) {\n var array = arrays[othIndex];\n if (othIndex && iteratee) {\n array = arrayMap(array, baseUnary(iteratee));\n }\n maxLength = nativeMin(array.length, maxLength);\n caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))\n ? new SetCache(othIndex && array)\n : undefined;\n }\n array = arrays[0];\n\n var index = -1,\n seen = caches[0];\n\n outer:\n while (++index < length && result.length < maxLength) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n value = (comparator || value !== 0) ? value : 0;\n if (!(seen\n ? cacheHas(seen, computed)\n : includes(result, computed, comparator)\n )) {\n othIndex = othLength;\n while (--othIndex) {\n var cache = caches[othIndex];\n if (!(cache\n ? cacheHas(cache, computed)\n : includes(arrays[othIndex], computed, comparator))\n ) {\n continue outer;\n }\n }\n if (seen) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.invert` and `_.invertBy` which inverts\n * `object` with values transformed by `iteratee` and set by `setter`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform values.\n * @param {Object} accumulator The initial inverted object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseInverter(object, setter, iteratee, accumulator) {\n baseForOwn(object, function(value, key, object) {\n setter(accumulator, iteratee(value), key, object);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.invoke` without support for individual\n * method arguments.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the method to invoke.\n * @param {Array} args The arguments to invoke the method with.\n * @returns {*} Returns the result of the invoked method.\n */\n function baseInvoke(object, path, args) {\n path = castPath(path, object);\n object = parent(object, path);\n var func = object == null ? object : object[toKey(last(path))];\n return func == null ? undefined : apply(func, object, args);\n }\n\n /**\n * The base implementation of `_.isArguments`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n */\n function baseIsArguments(value) {\n return isObjectLike(value) && baseGetTag(value) == argsTag;\n }\n\n /**\n * The base implementation of `_.isArrayBuffer` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.\n */\n function baseIsArrayBuffer(value) {\n return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;\n }\n\n /**\n * The base implementation of `_.isDate` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a date object, else `false`.\n */\n function baseIsDate(value) {\n return isObjectLike(value) && baseGetTag(value) == dateTag;\n }\n\n /**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\n function baseIsEqual(value, other, bitmask, customizer, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n }\n\n /**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {\n var objIsArr = isArray(object),\n othIsArr = isArray(other),\n objTag = objIsArr ? arrayTag : getTag(object),\n othTag = othIsArr ? arrayTag : getTag(other);\n\n objTag = objTag == argsTag ? objectTag : objTag;\n othTag = othTag == argsTag ? objectTag : othTag;\n\n var objIsObj = objTag == objectTag,\n othIsObj = othTag == objectTag,\n isSameTag = objTag == othTag;\n\n if (isSameTag && isBuffer(object)) {\n if (!isBuffer(other)) {\n return false;\n }\n objIsArr = true;\n objIsObj = false;\n }\n if (isSameTag && !objIsObj) {\n stack || (stack = new Stack);\n return (objIsArr || isTypedArray(object))\n ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)\n : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);\n }\n if (!(bitmask & COMPARE_PARTIAL_FLAG)) {\n var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n if (objIsWrapped || othIsWrapped) {\n var objUnwrapped = objIsWrapped ? object.value() : object,\n othUnwrapped = othIsWrapped ? other.value() : other;\n\n stack || (stack = new Stack);\n return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);\n }\n }\n if (!isSameTag) {\n return false;\n }\n stack || (stack = new Stack);\n return equalObjects(object, other, bitmask, customizer, equalFunc, stack);\n }\n\n /**\n * The base implementation of `_.isMap` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n */\n function baseIsMap(value) {\n return isObjectLike(value) && getTag(value) == mapTag;\n }\n\n /**\n * The base implementation of `_.isMatch` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Array} matchData The property names, values, and compare flags to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n */\n function baseIsMatch(object, source, matchData, customizer) {\n var index = matchData.length,\n length = index,\n noCustomizer = !customizer;\n\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (index--) {\n var data = matchData[index];\n if ((noCustomizer && data[2])\n ? data[1] !== object[data[0]]\n : !(data[0] in object)\n ) {\n return false;\n }\n }\n while (++index < length) {\n data = matchData[index];\n var key = data[0],\n objValue = object[key],\n srcValue = data[1];\n\n if (noCustomizer && data[2]) {\n if (objValue === undefined && !(key in object)) {\n return false;\n }\n } else {\n var stack = new Stack;\n if (customizer) {\n var result = customizer(objValue, srcValue, key, object, source, stack);\n }\n if (!(result === undefined\n ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)\n : result\n )) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\n function baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = isFunction(value) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n }\n\n /**\n * The base implementation of `_.isRegExp` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.\n */\n function baseIsRegExp(value) {\n return isObjectLike(value) && baseGetTag(value) == regexpTag;\n }\n\n /**\n * The base implementation of `_.isSet` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a set, else `false`.\n */\n function baseIsSet(value) {\n return isObjectLike(value) && getTag(value) == setTag;\n }\n\n /**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\n function baseIsTypedArray(value) {\n return isObjectLike(value) &&\n isLength(value.length) && !!typedArrayTags[baseGetTag(value)];\n }\n\n /**\n * The base implementation of `_.iteratee`.\n *\n * @private\n * @param {*} [value=_.identity] The value to convert to an iteratee.\n * @returns {Function} Returns the iteratee.\n */\n function baseIteratee(value) {\n // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n if (typeof value == 'function') {\n return value;\n }\n if (value == null) {\n return identity;\n }\n if (typeof value == 'object') {\n return isArray(value)\n ? baseMatchesProperty(value[0], value[1])\n : baseMatches(value);\n }\n return property(value);\n }\n\n /**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeysIn(object) {\n if (!isObject(object)) {\n return nativeKeysIn(object);\n }\n var isProto = isPrototype(object),\n result = [];\n\n for (var key in object) {\n if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.lt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than `other`,\n * else `false`.\n */\n function baseLt(value, other) {\n return value < other;\n }\n\n /**\n * The base implementation of `_.map` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function baseMap(collection, iteratee) {\n var index = -1,\n result = isArrayLike(collection) ? Array(collection.length) : [];\n\n baseEach(collection, function(value, key, collection) {\n result[++index] = iteratee(value, key, collection);\n });\n return result;\n }\n\n /**\n * The base implementation of `_.matches` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property values to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatches(source) {\n var matchData = getMatchData(source);\n if (matchData.length == 1 && matchData[0][2]) {\n return matchesStrictComparable(matchData[0][0], matchData[0][1]);\n }\n return function(object) {\n return object === source || baseIsMatch(object, source, matchData);\n };\n }\n\n /**\n * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.\n *\n * @private\n * @param {string} path The path of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatchesProperty(path, srcValue) {\n if (isKey(path) && isStrictComparable(srcValue)) {\n return matchesStrictComparable(toKey(path), srcValue);\n }\n return function(object) {\n var objValue = get(object, path);\n return (objValue === undefined && objValue === srcValue)\n ? hasIn(object, path)\n : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);\n };\n }\n\n /**\n * The base implementation of `_.merge` without support for multiple sources.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} [customizer] The function to customize merged values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMerge(object, source, srcIndex, customizer, stack) {\n if (object === source) {\n return;\n }\n baseFor(source, function(srcValue, key) {\n stack || (stack = new Stack);\n if (isObject(srcValue)) {\n baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);\n }\n else {\n var newValue = customizer\n ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)\n : undefined;\n\n if (newValue === undefined) {\n newValue = srcValue;\n }\n assignMergeValue(object, key, newValue);\n }\n }, keysIn);\n }\n\n /**\n * A specialized version of `baseMerge` for arrays and objects which performs\n * deep merges and tracks traversed objects enabling objects with circular\n * references to be merged.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {string} key The key of the value to merge.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} mergeFunc The function to merge values.\n * @param {Function} [customizer] The function to customize assigned values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {\n var objValue = safeGet(object, key),\n srcValue = safeGet(source, key),\n stacked = stack.get(srcValue);\n\n if (stacked) {\n assignMergeValue(object, key, stacked);\n return;\n }\n var newValue = customizer\n ? customizer(objValue, srcValue, (key + ''), object, source, stack)\n : undefined;\n\n var isCommon = newValue === undefined;\n\n if (isCommon) {\n var isArr = isArray(srcValue),\n isBuff = !isArr && isBuffer(srcValue),\n isTyped = !isArr && !isBuff && isTypedArray(srcValue);\n\n newValue = srcValue;\n if (isArr || isBuff || isTyped) {\n if (isArray(objValue)) {\n newValue = objValue;\n }\n else if (isArrayLikeObject(objValue)) {\n newValue = copyArray(objValue);\n }\n else if (isBuff) {\n isCommon = false;\n newValue = cloneBuffer(srcValue, true);\n }\n else if (isTyped) {\n isCommon = false;\n newValue = cloneTypedArray(srcValue, true);\n }\n else {\n newValue = [];\n }\n }\n else if (isPlainObject(srcValue) || isArguments(srcValue)) {\n newValue = objValue;\n if (isArguments(objValue)) {\n newValue = toPlainObject(objValue);\n }\n else if (!isObject(objValue) || isFunction(objValue)) {\n newValue = initCloneObject(srcValue);\n }\n }\n else {\n isCommon = false;\n }\n }\n if (isCommon) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, newValue);\n mergeFunc(newValue, srcValue, srcIndex, customizer, stack);\n stack['delete'](srcValue);\n }\n assignMergeValue(object, key, newValue);\n }\n\n /**\n * The base implementation of `_.nth` which doesn't coerce arguments.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {number} n The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n */\n function baseNth(array, n) {\n var length = array.length;\n if (!length) {\n return;\n }\n n += n < 0 ? length : 0;\n return isIndex(n, length) ? array[n] : undefined;\n }\n\n /**\n * The base implementation of `_.orderBy` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n * @param {string[]} orders The sort orders of `iteratees`.\n * @returns {Array} Returns the new sorted array.\n */\n function baseOrderBy(collection, iteratees, orders) {\n if (iteratees.length) {\n iteratees = arrayMap(iteratees, function(iteratee) {\n if (isArray(iteratee)) {\n return function(value) {\n return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);\n }\n }\n return iteratee;\n });\n } else {\n iteratees = [identity];\n }\n\n var index = -1;\n iteratees = arrayMap(iteratees, baseUnary(getIteratee()));\n\n var result = baseMap(collection, function(value, key, collection) {\n var criteria = arrayMap(iteratees, function(iteratee) {\n return iteratee(value);\n });\n return { 'criteria': criteria, 'index': ++index, 'value': value };\n });\n\n return baseSortBy(result, function(object, other) {\n return compareMultiple(object, other, orders);\n });\n }\n\n /**\n * The base implementation of `_.pick` without support for individual\n * property identifiers.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @returns {Object} Returns the new object.\n */\n function basePick(object, paths) {\n return basePickBy(object, paths, function(value, path) {\n return hasIn(object, path);\n });\n }\n\n /**\n * The base implementation of `_.pickBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @param {Function} predicate The function invoked per property.\n * @returns {Object} Returns the new object.\n */\n function basePickBy(object, paths, predicate) {\n var index = -1,\n length = paths.length,\n result = {};\n\n while (++index < length) {\n var path = paths[index],\n value = baseGet(object, path);\n\n if (predicate(value, path)) {\n baseSet(result, castPath(path, object), value);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `baseProperty` which supports deep paths.\n *\n * @private\n * @param {Array|string} path The path of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyDeep(path) {\n return function(object) {\n return baseGet(object, path);\n };\n }\n\n /**\n * The base implementation of `_.pullAllBy` without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n */\n function basePullAll(array, values, iteratee, comparator) {\n var indexOf = comparator ? baseIndexOfWith : baseIndexOf,\n index = -1,\n length = values.length,\n seen = array;\n\n if (array === values) {\n values = copyArray(values);\n }\n if (iteratee) {\n seen = arrayMap(array, baseUnary(iteratee));\n }\n while (++index < length) {\n var fromIndex = 0,\n value = values[index],\n computed = iteratee ? iteratee(value) : value;\n\n while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {\n if (seen !== array) {\n splice.call(seen, fromIndex, 1);\n }\n splice.call(array, fromIndex, 1);\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.pullAt` without support for individual\n * indexes or capturing the removed elements.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {number[]} indexes The indexes of elements to remove.\n * @returns {Array} Returns `array`.\n */\n function basePullAt(array, indexes) {\n var length = array ? indexes.length : 0,\n lastIndex = length - 1;\n\n while (length--) {\n var index = indexes[length];\n if (length == lastIndex || index !== previous) {\n var previous = index;\n if (isIndex(index)) {\n splice.call(array, index, 1);\n } else {\n baseUnset(array, index);\n }\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.random` without support for returning\n * floating-point numbers.\n *\n * @private\n * @param {number} lower The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the random number.\n */\n function baseRandom(lower, upper) {\n return lower + nativeFloor(nativeRandom() * (upper - lower + 1));\n }\n\n /**\n * The base implementation of `_.range` and `_.rangeRight` which doesn't\n * coerce arguments.\n *\n * @private\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @param {number} step The value to increment or decrement by.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the range of numbers.\n */\n function baseRange(start, end, step, fromRight) {\n var index = -1,\n length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),\n result = Array(length);\n\n while (length--) {\n result[fromRight ? length : ++index] = start;\n start += step;\n }\n return result;\n }\n\n /**\n * The base implementation of `_.repeat` which doesn't coerce arguments.\n *\n * @private\n * @param {string} string The string to repeat.\n * @param {number} n The number of times to repeat the string.\n * @returns {string} Returns the repeated string.\n */\n function baseRepeat(string, n) {\n var result = '';\n if (!string || n < 1 || n > MAX_SAFE_INTEGER) {\n return result;\n }\n // Leverage the exponentiation by squaring algorithm for a faster repeat.\n // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.\n do {\n if (n % 2) {\n result += string;\n }\n n = nativeFloor(n / 2);\n if (n) {\n string += string;\n }\n } while (n);\n\n return result;\n }\n\n /**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\n function baseRest(func, start) {\n return setToString(overRest(func, start, identity), func + '');\n }\n\n /**\n * The base implementation of `_.sample`.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n */\n function baseSample(collection) {\n return arraySample(values(collection));\n }\n\n /**\n * The base implementation of `_.sampleSize` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function baseSampleSize(collection, n) {\n var array = values(collection);\n return shuffleSelf(array, baseClamp(n, 0, array.length));\n }\n\n /**\n * The base implementation of `_.set`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseSet(object, path, value, customizer) {\n if (!isObject(object)) {\n return object;\n }\n path = castPath(path, object);\n\n var index = -1,\n length = path.length,\n lastIndex = length - 1,\n nested = object;\n\n while (nested != null && ++index < length) {\n var key = toKey(path[index]),\n newValue = value;\n\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return object;\n }\n\n if (index != lastIndex) {\n var objValue = nested[key];\n newValue = customizer ? customizer(objValue, key, nested) : undefined;\n if (newValue === undefined) {\n newValue = isObject(objValue)\n ? objValue\n : (isIndex(path[index + 1]) ? [] : {});\n }\n }\n assignValue(nested, key, newValue);\n nested = nested[key];\n }\n return object;\n }\n\n /**\n * The base implementation of `setData` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var baseSetData = !metaMap ? identity : function(func, data) {\n metaMap.set(func, data);\n return func;\n };\n\n /**\n * The base implementation of `setToString` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var baseSetToString = !defineProperty ? identity : function(func, string) {\n return defineProperty(func, 'toString', {\n 'configurable': true,\n 'enumerable': false,\n 'value': constant(string),\n 'writable': true\n });\n };\n\n /**\n * The base implementation of `_.shuffle`.\n *\n * @private\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function baseShuffle(collection) {\n return shuffleSelf(values(collection));\n }\n\n /**\n * The base implementation of `_.slice` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseSlice(array, start, end) {\n var index = -1,\n length = array.length;\n\n if (start < 0) {\n start = -start > length ? 0 : (length + start);\n }\n end = end > length ? length : end;\n if (end < 0) {\n end += length;\n }\n length = start > end ? 0 : ((end - start) >>> 0);\n start >>>= 0;\n\n var result = Array(length);\n while (++index < length) {\n result[index] = array[index + start];\n }\n return result;\n }\n\n /**\n * The base implementation of `_.some` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function baseSome(collection, predicate) {\n var result;\n\n baseEach(collection, function(value, index, collection) {\n result = predicate(value, index, collection);\n return !result;\n });\n return !!result;\n }\n\n /**\n * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which\n * performs a binary search of `array` to determine the index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndex(array, value, retHighest) {\n var low = 0,\n high = array == null ? low : array.length;\n\n if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {\n while (low < high) {\n var mid = (low + high) >>> 1,\n computed = array[mid];\n\n if (computed !== null && !isSymbol(computed) &&\n (retHighest ? (computed <= value) : (computed < value))) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return high;\n }\n return baseSortedIndexBy(array, value, identity, retHighest);\n }\n\n /**\n * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`\n * which invokes `iteratee` for `value` and each element of `array` to compute\n * their sort ranking. The iteratee is invoked with one argument; (value).\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} iteratee The iteratee invoked per element.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndexBy(array, value, iteratee, retHighest) {\n var low = 0,\n high = array == null ? 0 : array.length;\n if (high === 0) {\n return 0;\n }\n\n value = iteratee(value);\n var valIsNaN = value !== value,\n valIsNull = value === null,\n valIsSymbol = isSymbol(value),\n valIsUndefined = value === undefined;\n\n while (low < high) {\n var mid = nativeFloor((low + high) / 2),\n computed = iteratee(array[mid]),\n othIsDefined = computed !== undefined,\n othIsNull = computed === null,\n othIsReflexive = computed === computed,\n othIsSymbol = isSymbol(computed);\n\n if (valIsNaN) {\n var setLow = retHighest || othIsReflexive;\n } else if (valIsUndefined) {\n setLow = othIsReflexive && (retHighest || othIsDefined);\n } else if (valIsNull) {\n setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);\n } else if (valIsSymbol) {\n setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);\n } else if (othIsNull || othIsSymbol) {\n setLow = false;\n } else {\n setLow = retHighest ? (computed <= value) : (computed < value);\n }\n if (setLow) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return nativeMin(high, MAX_ARRAY_INDEX);\n }\n\n /**\n * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseSortedUniq(array, iteratee) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n if (!index || !eq(computed, seen)) {\n var seen = computed;\n result[resIndex++] = value === 0 ? 0 : value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toNumber` which doesn't ensure correct\n * conversions of binary, hexadecimal, or octal string values.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n */\n function baseToNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n return +value;\n }\n\n /**\n * The base implementation of `_.toString` which doesn't convert nullish\n * values to empty strings.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n */\n function baseToString(value) {\n // Exit early for strings to avoid a performance hit in some environments.\n if (typeof value == 'string') {\n return value;\n }\n if (isArray(value)) {\n // Recursively convert values (susceptible to call stack limits).\n return arrayMap(value, baseToString) + '';\n }\n if (isSymbol(value)) {\n return symbolToString ? symbolToString.call(value) : '';\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n }\n\n /**\n * The base implementation of `_.uniqBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseUniq(array, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n length = array.length,\n isCommon = true,\n result = [],\n seen = result;\n\n if (comparator) {\n isCommon = false;\n includes = arrayIncludesWith;\n }\n else if (length >= LARGE_ARRAY_SIZE) {\n var set = iteratee ? null : createSet(array);\n if (set) {\n return setToArray(set);\n }\n isCommon = false;\n includes = cacheHas;\n seen = new SetCache;\n }\n else {\n seen = iteratee ? [] : result;\n }\n outer:\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n value = (comparator || value !== 0) ? value : 0;\n if (isCommon && computed === computed) {\n var seenIndex = seen.length;\n while (seenIndex--) {\n if (seen[seenIndex] === computed) {\n continue outer;\n }\n }\n if (iteratee) {\n seen.push(computed);\n }\n result.push(value);\n }\n else if (!includes(seen, computed, comparator)) {\n if (seen !== result) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.unset`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The property path to unset.\n * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n */\n function baseUnset(object, path) {\n path = castPath(path, object);\n object = parent(object, path);\n return object == null || delete object[toKey(last(path))];\n }\n\n /**\n * The base implementation of `_.update`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to update.\n * @param {Function} updater The function to produce the updated value.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseUpdate(object, path, updater, customizer) {\n return baseSet(object, path, updater(baseGet(object, path)), customizer);\n }\n\n /**\n * The base implementation of methods like `_.dropWhile` and `_.takeWhile`\n * without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {Function} predicate The function invoked per iteration.\n * @param {boolean} [isDrop] Specify dropping elements instead of taking them.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseWhile(array, predicate, isDrop, fromRight) {\n var length = array.length,\n index = fromRight ? length : -1;\n\n while ((fromRight ? index-- : ++index < length) &&\n predicate(array[index], index, array)) {}\n\n return isDrop\n ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))\n : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));\n }\n\n /**\n * The base implementation of `wrapperValue` which returns the result of\n * performing a sequence of actions on the unwrapped `value`, where each\n * successive action is supplied the return value of the previous.\n *\n * @private\n * @param {*} value The unwrapped value.\n * @param {Array} actions Actions to perform to resolve the unwrapped value.\n * @returns {*} Returns the resolved value.\n */\n function baseWrapperValue(value, actions) {\n var result = value;\n if (result instanceof LazyWrapper) {\n result = result.value();\n }\n return arrayReduce(actions, function(result, action) {\n return action.func.apply(action.thisArg, arrayPush([result], action.args));\n }, result);\n }\n\n /**\n * The base implementation of methods like `_.xor`, without support for\n * iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of values.\n */\n function baseXor(arrays, iteratee, comparator) {\n var length = arrays.length;\n if (length < 2) {\n return length ? baseUniq(arrays[0]) : [];\n }\n var index = -1,\n result = Array(length);\n\n while (++index < length) {\n var array = arrays[index],\n othIndex = -1;\n\n while (++othIndex < length) {\n if (othIndex != index) {\n result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);\n }\n }\n }\n return baseUniq(baseFlatten(result, 1), iteratee, comparator);\n }\n\n /**\n * This base implementation of `_.zipObject` which assigns values using `assignFunc`.\n *\n * @private\n * @param {Array} props The property identifiers.\n * @param {Array} values The property values.\n * @param {Function} assignFunc The function to assign values.\n * @returns {Object} Returns the new object.\n */\n function baseZipObject(props, values, assignFunc) {\n var index = -1,\n length = props.length,\n valsLength = values.length,\n result = {};\n\n while (++index < length) {\n var value = index < valsLength ? values[index] : undefined;\n assignFunc(result, props[index], value);\n }\n return result;\n }\n\n /**\n * Casts `value` to an empty array if it's not an array like object.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Array|Object} Returns the cast array-like object.\n */\n function castArrayLikeObject(value) {\n return isArrayLikeObject(value) ? value : [];\n }\n\n /**\n * Casts `value` to `identity` if it's not a function.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Function} Returns cast function.\n */\n function castFunction(value) {\n return typeof value == 'function' ? value : identity;\n }\n\n /**\n * Casts `value` to a path array if it's not one.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {Object} [object] The object to query keys on.\n * @returns {Array} Returns the cast property path array.\n */\n function castPath(value, object) {\n if (isArray(value)) {\n return value;\n }\n return isKey(value, object) ? [value] : stringToPath(toString(value));\n }\n\n /**\n * A `baseRest` alias which can be replaced with `identity` by module\n * replacement plugins.\n *\n * @private\n * @type {Function}\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n var castRest = baseRest;\n\n /**\n * Casts `array` to a slice if it's needed.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {number} start The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the cast slice.\n */\n function castSlice(array, start, end) {\n var length = array.length;\n end = end === undefined ? length : end;\n return (!start && end >= length) ? array : baseSlice(array, start, end);\n }\n\n /**\n * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).\n *\n * @private\n * @param {number|Object} id The timer id or timeout object of the timer to clear.\n */\n var clearTimeout = ctxClearTimeout || function(id) {\n return root.clearTimeout(id);\n };\n\n /**\n * Creates a clone of `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\n function cloneBuffer(buffer, isDeep) {\n if (isDeep) {\n return buffer.slice();\n }\n var length = buffer.length,\n result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);\n\n buffer.copy(result);\n return result;\n }\n\n /**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\n function cloneArrayBuffer(arrayBuffer) {\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n return result;\n }\n\n /**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\n function cloneDataView(dataView, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n }\n\n /**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\n function cloneRegExp(regexp) {\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n result.lastIndex = regexp.lastIndex;\n return result;\n }\n\n /**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\n function cloneSymbol(symbol) {\n return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n }\n\n /**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\n function cloneTypedArray(typedArray, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n }\n\n /**\n * Compares values to sort them in ascending order.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {number} Returns the sort order indicator for `value`.\n */\n function compareAscending(value, other) {\n if (value !== other) {\n var valIsDefined = value !== undefined,\n valIsNull = value === null,\n valIsReflexive = value === value,\n valIsSymbol = isSymbol(value);\n\n var othIsDefined = other !== undefined,\n othIsNull = other === null,\n othIsReflexive = other === other,\n othIsSymbol = isSymbol(other);\n\n if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||\n (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||\n (valIsNull && othIsDefined && othIsReflexive) ||\n (!valIsDefined && othIsReflexive) ||\n !valIsReflexive) {\n return 1;\n }\n if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||\n (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||\n (othIsNull && valIsDefined && valIsReflexive) ||\n (!othIsDefined && valIsReflexive) ||\n !othIsReflexive) {\n return -1;\n }\n }\n return 0;\n }\n\n /**\n * Used by `_.orderBy` to compare multiple properties of a value to another\n * and stable sort them.\n *\n * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,\n * specify an order of \"desc\" for descending or \"asc\" for ascending sort order\n * of corresponding values.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {boolean[]|string[]} orders The order to sort by for each property.\n * @returns {number} Returns the sort order indicator for `object`.\n */\n function compareMultiple(object, other, orders) {\n var index = -1,\n objCriteria = object.criteria,\n othCriteria = other.criteria,\n length = objCriteria.length,\n ordersLength = orders.length;\n\n while (++index < length) {\n var result = compareAscending(objCriteria[index], othCriteria[index]);\n if (result) {\n if (index >= ordersLength) {\n return result;\n }\n var order = orders[index];\n return result * (order == 'desc' ? -1 : 1);\n }\n }\n // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications\n // that causes it, under certain circumstances, to provide the same value for\n // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247\n // for more details.\n //\n // This also ensures a stable sort in V8 and other engines.\n // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.\n return object.index - other.index;\n }\n\n /**\n * Creates an array that is the composition of partially applied arguments,\n * placeholders, and provided arguments into a single array of arguments.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to prepend to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgs(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersLength = holders.length,\n leftIndex = -1,\n leftLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(leftLength + rangeLength),\n isUncurried = !isCurried;\n\n while (++leftIndex < leftLength) {\n result[leftIndex] = partials[leftIndex];\n }\n while (++argsIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[holders[argsIndex]] = args[argsIndex];\n }\n }\n while (rangeLength--) {\n result[leftIndex++] = args[argsIndex++];\n }\n return result;\n }\n\n /**\n * This function is like `composeArgs` except that the arguments composition\n * is tailored for `_.partialRight`.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to append to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgsRight(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersIndex = -1,\n holdersLength = holders.length,\n rightIndex = -1,\n rightLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(rangeLength + rightLength),\n isUncurried = !isCurried;\n\n while (++argsIndex < rangeLength) {\n result[argsIndex] = args[argsIndex];\n }\n var offset = argsIndex;\n while (++rightIndex < rightLength) {\n result[offset + rightIndex] = partials[rightIndex];\n }\n while (++holdersIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[offset + holders[holdersIndex]] = args[argsIndex++];\n }\n }\n return result;\n }\n\n /**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\n function copyArray(source, array) {\n var index = -1,\n length = source.length;\n\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n }\n\n /**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\n function copyObject(source, props, object, customizer) {\n var isNew = !object;\n object || (object = {});\n\n var index = -1,\n length = props.length;\n\n while (++index < length) {\n var key = props[index];\n\n var newValue = customizer\n ? customizer(object[key], source[key], key, object, source)\n : undefined;\n\n if (newValue === undefined) {\n newValue = source[key];\n }\n if (isNew) {\n baseAssignValue(object, key, newValue);\n } else {\n assignValue(object, key, newValue);\n }\n }\n return object;\n }\n\n /**\n * Copies own symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbols(source, object) {\n return copyObject(source, getSymbols(source), object);\n }\n\n /**\n * Copies own and inherited symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbolsIn(source, object) {\n return copyObject(source, getSymbolsIn(source), object);\n }\n\n /**\n * Creates a function like `_.groupBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} [initializer] The accumulator object initializer.\n * @returns {Function} Returns the new aggregator function.\n */\n function createAggregator(setter, initializer) {\n return function(collection, iteratee) {\n var func = isArray(collection) ? arrayAggregator : baseAggregator,\n accumulator = initializer ? initializer() : {};\n\n return func(collection, setter, getIteratee(iteratee, 2), accumulator);\n };\n }\n\n /**\n * Creates a function like `_.assign`.\n *\n * @private\n * @param {Function} assigner The function to assign values.\n * @returns {Function} Returns the new assigner function.\n */\n function createAssigner(assigner) {\n return baseRest(function(object, sources) {\n var index = -1,\n length = sources.length,\n customizer = length > 1 ? sources[length - 1] : undefined,\n guard = length > 2 ? sources[2] : undefined;\n\n customizer = (assigner.length > 3 && typeof customizer == 'function')\n ? (length--, customizer)\n : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n customizer = length < 3 ? undefined : customizer;\n length = 1;\n }\n object = Object(object);\n while (++index < length) {\n var source = sources[index];\n if (source) {\n assigner(object, source, index, customizer);\n }\n }\n return object;\n });\n }\n\n /**\n * Creates a `baseEach` or `baseEachRight` function.\n *\n * @private\n * @param {Function} eachFunc The function to iterate over a collection.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseEach(eachFunc, fromRight) {\n return function(collection, iteratee) {\n if (collection == null) {\n return collection;\n }\n if (!isArrayLike(collection)) {\n return eachFunc(collection, iteratee);\n }\n var length = collection.length,\n index = fromRight ? length : -1,\n iterable = Object(collection);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (iteratee(iterable[index], index, iterable) === false) {\n break;\n }\n }\n return collection;\n };\n }\n\n /**\n * Creates a base function for methods like `_.forIn` and `_.forOwn`.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseFor(fromRight) {\n return function(object, iteratee, keysFunc) {\n var index = -1,\n iterable = Object(object),\n props = keysFunc(object),\n length = props.length;\n\n while (length--) {\n var key = props[fromRight ? length : ++index];\n if (iteratee(iterable[key], key, iterable) === false) {\n break;\n }\n }\n return object;\n };\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the optional `this`\n * binding of `thisArg`.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createBind(func, bitmask, thisArg) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return fn.apply(isBind ? thisArg : this, arguments);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.lowerFirst`.\n *\n * @private\n * @param {string} methodName The name of the `String` case method to use.\n * @returns {Function} Returns the new case function.\n */\n function createCaseFirst(methodName) {\n return function(string) {\n string = toString(string);\n\n var strSymbols = hasUnicode(string)\n ? stringToArray(string)\n : undefined;\n\n var chr = strSymbols\n ? strSymbols[0]\n : string.charAt(0);\n\n var trailing = strSymbols\n ? castSlice(strSymbols, 1).join('')\n : string.slice(1);\n\n return chr[methodName]() + trailing;\n };\n }\n\n /**\n * Creates a function like `_.camelCase`.\n *\n * @private\n * @param {Function} callback The function to combine each word.\n * @returns {Function} Returns the new compounder function.\n */\n function createCompounder(callback) {\n return function(string) {\n return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');\n };\n }\n\n /**\n * Creates a function that produces an instance of `Ctor` regardless of\n * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n *\n * @private\n * @param {Function} Ctor The constructor to wrap.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCtor(Ctor) {\n return function() {\n // Use a `switch` statement to work with class constructors. See\n // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n // for more details.\n var args = arguments;\n switch (args.length) {\n case 0: return new Ctor;\n case 1: return new Ctor(args[0]);\n case 2: return new Ctor(args[0], args[1]);\n case 3: return new Ctor(args[0], args[1], args[2]);\n case 4: return new Ctor(args[0], args[1], args[2], args[3]);\n case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n }\n var thisBinding = baseCreate(Ctor.prototype),\n result = Ctor.apply(thisBinding, args);\n\n // Mimic the constructor's `return` behavior.\n // See https://es5.github.io/#x13.2.2 for more details.\n return isObject(result) ? result : thisBinding;\n };\n }\n\n /**\n * Creates a function that wraps `func` to enable currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {number} arity The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCurry(func, bitmask, arity) {\n var Ctor = createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length,\n placeholder = getHolder(wrapper);\n\n while (index--) {\n args[index] = arguments[index];\n }\n var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)\n ? []\n : replaceHolders(args, placeholder);\n\n length -= holders.length;\n if (length < arity) {\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, undefined,\n args, holders, undefined, undefined, arity - length);\n }\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return apply(fn, this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.find` or `_.findLast` function.\n *\n * @private\n * @param {Function} findIndexFunc The function to find the collection index.\n * @returns {Function} Returns the new find function.\n */\n function createFind(findIndexFunc) {\n return function(collection, predicate, fromIndex) {\n var iterable = Object(collection);\n if (!isArrayLike(collection)) {\n var iteratee = getIteratee(predicate, 3);\n collection = keys(collection);\n predicate = function(key) { return iteratee(iterable[key], key, iterable); };\n }\n var index = findIndexFunc(collection, predicate, fromIndex);\n return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;\n };\n }\n\n /**\n * Creates a `_.flow` or `_.flowRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new flow function.\n */\n function createFlow(fromRight) {\n return flatRest(function(funcs) {\n var length = funcs.length,\n index = length,\n prereq = LodashWrapper.prototype.thru;\n\n if (fromRight) {\n funcs.reverse();\n }\n while (index--) {\n var func = funcs[index];\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (prereq && !wrapper && getFuncName(func) == 'wrapper') {\n var wrapper = new LodashWrapper([], true);\n }\n }\n index = wrapper ? index : length;\n while (++index < length) {\n func = funcs[index];\n\n var funcName = getFuncName(func),\n data = funcName == 'wrapper' ? getData(func) : undefined;\n\n if (data && isLaziable(data[0]) &&\n data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) &&\n !data[4].length && data[9] == 1\n ) {\n wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);\n } else {\n wrapper = (func.length == 1 && isLaziable(func))\n ? wrapper[funcName]()\n : wrapper.thru(func);\n }\n }\n return function() {\n var args = arguments,\n value = args[0];\n\n if (wrapper && args.length == 1 && isArray(value)) {\n return wrapper.plant(value).value();\n }\n var index = 0,\n result = length ? funcs[index].apply(this, args) : value;\n\n while (++index < length) {\n result = funcs[index].call(this, result);\n }\n return result;\n };\n });\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with optional `this`\n * binding of `thisArg`, partial application, and currying.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [partialsRight] The arguments to append to those provided\n * to the new function.\n * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n var isAry = bitmask & WRAP_ARY_FLAG,\n isBind = bitmask & WRAP_BIND_FLAG,\n isBindKey = bitmask & WRAP_BIND_KEY_FLAG,\n isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),\n isFlip = bitmask & WRAP_FLIP_FLAG,\n Ctor = isBindKey ? undefined : createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length;\n\n while (index--) {\n args[index] = arguments[index];\n }\n if (isCurried) {\n var placeholder = getHolder(wrapper),\n holdersCount = countHolders(args, placeholder);\n }\n if (partials) {\n args = composeArgs(args, partials, holders, isCurried);\n }\n if (partialsRight) {\n args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n }\n length -= holdersCount;\n if (isCurried && length < arity) {\n var newHolders = replaceHolders(args, placeholder);\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, thisArg,\n args, newHolders, argPos, ary, arity - length\n );\n }\n var thisBinding = isBind ? thisArg : this,\n fn = isBindKey ? thisBinding[func] : func;\n\n length = args.length;\n if (argPos) {\n args = reorder(args, argPos);\n } else if (isFlip && length > 1) {\n args.reverse();\n }\n if (isAry && ary < length) {\n args.length = ary;\n }\n if (this && this !== root && this instanceof wrapper) {\n fn = Ctor || createCtor(fn);\n }\n return fn.apply(thisBinding, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.invertBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} toIteratee The function to resolve iteratees.\n * @returns {Function} Returns the new inverter function.\n */\n function createInverter(setter, toIteratee) {\n return function(object, iteratee) {\n return baseInverter(object, setter, toIteratee(iteratee), {});\n };\n }\n\n /**\n * Creates a function that performs a mathematical operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @param {number} [defaultValue] The value used for `undefined` arguments.\n * @returns {Function} Returns the new mathematical operation function.\n */\n function createMathOperation(operator, defaultValue) {\n return function(value, other) {\n var result;\n if (value === undefined && other === undefined) {\n return defaultValue;\n }\n if (value !== undefined) {\n result = value;\n }\n if (other !== undefined) {\n if (result === undefined) {\n return other;\n }\n if (typeof value == 'string' || typeof other == 'string') {\n value = baseToString(value);\n other = baseToString(other);\n } else {\n value = baseToNumber(value);\n other = baseToNumber(other);\n }\n result = operator(value, other);\n }\n return result;\n };\n }\n\n /**\n * Creates a function like `_.over`.\n *\n * @private\n * @param {Function} arrayFunc The function to iterate over iteratees.\n * @returns {Function} Returns the new over function.\n */\n function createOver(arrayFunc) {\n return flatRest(function(iteratees) {\n iteratees = arrayMap(iteratees, baseUnary(getIteratee()));\n return baseRest(function(args) {\n var thisArg = this;\n return arrayFunc(iteratees, function(iteratee) {\n return apply(iteratee, thisArg, args);\n });\n });\n });\n }\n\n /**\n * Creates the padding for `string` based on `length`. The `chars` string\n * is truncated if the number of characters exceeds `length`.\n *\n * @private\n * @param {number} length The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padding for `string`.\n */\n function createPadding(length, chars) {\n chars = chars === undefined ? ' ' : baseToString(chars);\n\n var charsLength = chars.length;\n if (charsLength < 2) {\n return charsLength ? baseRepeat(chars, length) : chars;\n }\n var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));\n return hasUnicode(chars)\n ? castSlice(stringToArray(result), 0, length).join('')\n : result.slice(0, length);\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the `this` binding\n * of `thisArg` and `partials` prepended to the arguments it receives.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} partials The arguments to prepend to those provided to\n * the new function.\n * @returns {Function} Returns the new wrapped function.\n */\n function createPartial(func, bitmask, thisArg, partials) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var argsIndex = -1,\n argsLength = arguments.length,\n leftIndex = -1,\n leftLength = partials.length,\n args = Array(leftLength + argsLength),\n fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n\n while (++leftIndex < leftLength) {\n args[leftIndex] = partials[leftIndex];\n }\n while (argsLength--) {\n args[leftIndex++] = arguments[++argsIndex];\n }\n return apply(fn, isBind ? thisArg : this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.range` or `_.rangeRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new range function.\n */\n function createRange(fromRight) {\n return function(start, end, step) {\n if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {\n end = step = undefined;\n }\n // Ensure the sign of `-0` is preserved.\n start = toFinite(start);\n if (end === undefined) {\n end = start;\n start = 0;\n } else {\n end = toFinite(end);\n }\n step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);\n return baseRange(start, end, step, fromRight);\n };\n }\n\n /**\n * Creates a function that performs a relational operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @returns {Function} Returns the new relational operation function.\n */\n function createRelationalOperation(operator) {\n return function(value, other) {\n if (!(typeof value == 'string' && typeof other == 'string')) {\n value = toNumber(value);\n other = toNumber(other);\n }\n return operator(value, other);\n };\n }\n\n /**\n * Creates a function that wraps `func` to continue currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {Function} wrapFunc The function to create the `func` wrapper.\n * @param {*} placeholder The placeholder value.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n var isCurry = bitmask & WRAP_CURRY_FLAG,\n newHolders = isCurry ? holders : undefined,\n newHoldersRight = isCurry ? undefined : holders,\n newPartials = isCurry ? partials : undefined,\n newPartialsRight = isCurry ? undefined : partials;\n\n bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);\n bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);\n\n if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {\n bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);\n }\n var newData = [\n func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,\n newHoldersRight, argPos, ary, arity\n ];\n\n var result = wrapFunc.apply(undefined, newData);\n if (isLaziable(func)) {\n setData(result, newData);\n }\n result.placeholder = placeholder;\n return setWrapToString(result, func, bitmask);\n }\n\n /**\n * Creates a function like `_.round`.\n *\n * @private\n * @param {string} methodName The name of the `Math` method to use when rounding.\n * @returns {Function} Returns the new round function.\n */\n function createRound(methodName) {\n var func = Math[methodName];\n return function(number, precision) {\n number = toNumber(number);\n precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);\n if (precision && nativeIsFinite(number)) {\n // Shift with exponential notation to avoid floating-point issues.\n // See [MDN](https://mdn.io/round#Examples) for more details.\n var pair = (toString(number) + 'e').split('e'),\n value = func(pair[0] + 'e' + (+pair[1] + precision));\n\n pair = (toString(value) + 'e').split('e');\n return +(pair[0] + 'e' + (+pair[1] - precision));\n }\n return func(number);\n };\n }\n\n /**\n * Creates a set object of `values`.\n *\n * @private\n * @param {Array} values The values to add to the set.\n * @returns {Object} Returns the new set.\n */\n var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {\n return new Set(values);\n };\n\n /**\n * Creates a `_.toPairs` or `_.toPairsIn` function.\n *\n * @private\n * @param {Function} keysFunc The function to get the keys of a given object.\n * @returns {Function} Returns the new pairs function.\n */\n function createToPairs(keysFunc) {\n return function(object) {\n var tag = getTag(object);\n if (tag == mapTag) {\n return mapToArray(object);\n }\n if (tag == setTag) {\n return setToPairs(object);\n }\n return baseToPairs(object, keysFunc(object));\n };\n }\n\n /**\n * Creates a function that either curries or invokes `func` with optional\n * `this` binding and partially applied arguments.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags.\n * 1 - `_.bind`\n * 2 - `_.bindKey`\n * 4 - `_.curry` or `_.curryRight` of a bound function\n * 8 - `_.curry`\n * 16 - `_.curryRight`\n * 32 - `_.partial`\n * 64 - `_.partialRight`\n * 128 - `_.rearg`\n * 256 - `_.ary`\n * 512 - `_.flip`\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to be partially applied.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;\n if (!isBindKey && typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var length = partials ? partials.length : 0;\n if (!length) {\n bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);\n partials = holders = undefined;\n }\n ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n arity = arity === undefined ? arity : toInteger(arity);\n length -= holders ? holders.length : 0;\n\n if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {\n var partialsRight = partials,\n holdersRight = holders;\n\n partials = holders = undefined;\n }\n var data = isBindKey ? undefined : getData(func);\n\n var newData = [\n func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,\n argPos, ary, arity\n ];\n\n if (data) {\n mergeData(newData, data);\n }\n func = newData[0];\n bitmask = newData[1];\n thisArg = newData[2];\n partials = newData[3];\n holders = newData[4];\n arity = newData[9] = newData[9] === undefined\n ? (isBindKey ? 0 : func.length)\n : nativeMax(newData[9] - length, 0);\n\n if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {\n bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);\n }\n if (!bitmask || bitmask == WRAP_BIND_FLAG) {\n var result = createBind(func, bitmask, thisArg);\n } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {\n result = createCurry(func, bitmask, arity);\n } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {\n result = createPartial(func, bitmask, thisArg, partials);\n } else {\n result = createHybrid.apply(undefined, newData);\n }\n var setter = data ? baseSetData : setData;\n return setWrapToString(setter(result, newData), func, bitmask);\n }\n\n /**\n * Used by `_.defaults` to customize its `_.assignIn` use to assign properties\n * of source objects to the destination object for all destination properties\n * that resolve to `undefined`.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to assign.\n * @param {Object} object The parent object of `objValue`.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsAssignIn(objValue, srcValue, key, object) {\n if (objValue === undefined ||\n (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {\n return srcValue;\n }\n return objValue;\n }\n\n /**\n * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source\n * objects into destination objects that are passed thru.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to merge.\n * @param {Object} object The parent object of `objValue`.\n * @param {Object} source The parent object of `srcValue`.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {\n if (isObject(objValue) && isObject(srcValue)) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, objValue);\n baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);\n stack['delete'](srcValue);\n }\n return objValue;\n }\n\n /**\n * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain\n * objects.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {string} key The key of the property to inspect.\n * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.\n */\n function customOmitClone(value) {\n return isPlainObject(value) ? undefined : value;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\n function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n arrLength = array.length,\n othLength = other.length;\n\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Check that cyclic values are equal.\n var arrStacked = stack.get(array);\n var othStacked = stack.get(other);\n if (arrStacked && othStacked) {\n return arrStacked == other && othStacked == array;\n }\n var index = -1,\n result = true,\n seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;\n\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, arrValue, index, other, array, stack)\n : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function(othValue, othIndex) {\n if (!cacheHas(seen, othIndex) &&\n (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n return seen.push(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(\n arrValue === othValue ||\n equalFunc(arrValue, othValue, bitmask, customizer, stack)\n )) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {\n switch (tag) {\n case dataViewTag:\n if ((object.byteLength != other.byteLength) ||\n (object.byteOffset != other.byteOffset)) {\n return false;\n }\n object = object.buffer;\n other = other.buffer;\n\n case arrayBufferTag:\n if ((object.byteLength != other.byteLength) ||\n !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n return false;\n }\n return true;\n\n case boolTag:\n case dateTag:\n case numberTag:\n // Coerce booleans to `1` or `0` and dates to milliseconds.\n // Invalid dates are coerced to `NaN`.\n return eq(+object, +other);\n\n case errorTag:\n return object.name == other.name && object.message == other.message;\n\n case regexpTag:\n case stringTag:\n // Coerce regexes to strings and treat strings, primitives and objects,\n // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n // for more details.\n return object == (other + '');\n\n case mapTag:\n var convert = mapToArray;\n\n case setTag:\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG;\n convert || (convert = setToArray);\n\n if (object.size != other.size && !isPartial) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked) {\n return stacked == other;\n }\n bitmask |= COMPARE_UNORDERED_FLAG;\n\n // Recursively compare objects (susceptible to call stack limits).\n stack.set(object, other);\n var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);\n stack['delete'](object);\n return result;\n\n case symbolTag:\n if (symbolValueOf) {\n return symbolValueOf.call(object) == symbolValueOf.call(other);\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n objProps = getAllKeys(object),\n objLength = objProps.length,\n othProps = getAllKeys(other),\n othLength = othProps.length;\n\n if (objLength != othLength && !isPartial) {\n return false;\n }\n var index = objLength;\n while (index--) {\n var key = objProps[index];\n if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n return false;\n }\n }\n // Check that cyclic values are equal.\n var objStacked = stack.get(object);\n var othStacked = stack.get(other);\n if (objStacked && othStacked) {\n return objStacked == other && othStacked == object;\n }\n var result = true;\n stack.set(object, other);\n stack.set(other, object);\n\n var skipCtor = isPartial;\n while (++index < objLength) {\n key = objProps[index];\n var objValue = object[key],\n othValue = other[key];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, objValue, key, other, object, stack)\n : customizer(objValue, othValue, key, object, other, stack);\n }\n // Recursively compare objects (susceptible to call stack limits).\n if (!(compared === undefined\n ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))\n : compared\n )) {\n result = false;\n break;\n }\n skipCtor || (skipCtor = key == 'constructor');\n }\n if (result && !skipCtor) {\n var objCtor = object.constructor,\n othCtor = other.constructor;\n\n // Non `Object` object instances with different constructors are not equal.\n if (objCtor != othCtor &&\n ('constructor' in object && 'constructor' in other) &&\n !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n result = false;\n }\n }\n stack['delete'](object);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseRest` which flattens the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n function flatRest(func) {\n return setToString(overRest(func, undefined, flatten), func + '');\n }\n\n /**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n }\n\n /**\n * Creates an array of own and inherited enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeysIn(object) {\n return baseGetAllKeys(object, keysIn, getSymbolsIn);\n }\n\n /**\n * Gets metadata for `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {*} Returns the metadata for `func`.\n */\n var getData = !metaMap ? noop : function(func) {\n return metaMap.get(func);\n };\n\n /**\n * Gets the name of `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {string} Returns the function name.\n */\n function getFuncName(func) {\n var result = (func.name + ''),\n array = realNames[result],\n length = hasOwnProperty.call(realNames, result) ? array.length : 0;\n\n while (length--) {\n var data = array[length],\n otherFunc = data.func;\n if (otherFunc == null || otherFunc == func) {\n return data.name;\n }\n }\n return result;\n }\n\n /**\n * Gets the argument placeholder value for `func`.\n *\n * @private\n * @param {Function} func The function to inspect.\n * @returns {*} Returns the placeholder value.\n */\n function getHolder(func) {\n var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;\n return object.placeholder;\n }\n\n /**\n * Gets the appropriate \"iteratee\" function. If `_.iteratee` is customized,\n * this function returns the custom method, otherwise it returns `baseIteratee`.\n * If arguments are provided, the chosen function is invoked with them and\n * its result is returned.\n *\n * @private\n * @param {*} [value] The value to convert to an iteratee.\n * @param {number} [arity] The arity of the created iteratee.\n * @returns {Function} Returns the chosen function or its result.\n */\n function getIteratee() {\n var result = lodash.iteratee || iteratee;\n result = result === iteratee ? baseIteratee : result;\n return arguments.length ? result(arguments[0], arguments[1]) : result;\n }\n\n /**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\n function getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n }\n\n /**\n * Gets the property names, values, and compare flags of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the match data of `object`.\n */\n function getMatchData(object) {\n var result = keys(object),\n length = result.length;\n\n while (length--) {\n var key = result[length],\n value = object[key];\n\n result[length] = [key, value, isStrictComparable(value)];\n }\n return result;\n }\n\n /**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\n function getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n }\n\n /**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\n function getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n }\n\n /**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbols = !nativeGetSymbols ? stubArray : function(object) {\n if (object == null) {\n return [];\n }\n object = Object(object);\n return arrayFilter(nativeGetSymbols(object), function(symbol) {\n return propertyIsEnumerable.call(object, symbol);\n });\n };\n\n /**\n * Creates an array of the own and inherited enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {\n var result = [];\n while (object) {\n arrayPush(result, getSymbols(object));\n object = getPrototype(object);\n }\n return result;\n };\n\n /**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n var getTag = baseGetTag;\n\n // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\n if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = baseGetTag(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : '';\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n }\n\n /**\n * Gets the view, applying any `transforms` to the `start` and `end` positions.\n *\n * @private\n * @param {number} start The start of the view.\n * @param {number} end The end of the view.\n * @param {Array} transforms The transformations to apply to the view.\n * @returns {Object} Returns an object containing the `start` and `end`\n * positions of the view.\n */\n function getView(start, end, transforms) {\n var index = -1,\n length = transforms.length;\n\n while (++index < length) {\n var data = transforms[index],\n size = data.size;\n\n switch (data.type) {\n case 'drop': start += size; break;\n case 'dropRight': end -= size; break;\n case 'take': end = nativeMin(end, start + size); break;\n case 'takeRight': start = nativeMax(start, end - size); break;\n }\n }\n return { 'start': start, 'end': end };\n }\n\n /**\n * Extracts wrapper details from the `source` body comment.\n *\n * @private\n * @param {string} source The source to inspect.\n * @returns {Array} Returns the wrapper details.\n */\n function getWrapDetails(source) {\n var match = source.match(reWrapDetails);\n return match ? match[1].split(reSplitDetails) : [];\n }\n\n /**\n * Checks if `path` exists on `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @param {Function} hasFunc The function to check properties.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n */\n function hasPath(object, path, hasFunc) {\n path = castPath(path, object);\n\n var index = -1,\n length = path.length,\n result = false;\n\n while (++index < length) {\n var key = toKey(path[index]);\n if (!(result = object != null && hasFunc(object, key))) {\n break;\n }\n object = object[key];\n }\n if (result || ++index != length) {\n return result;\n }\n length = object == null ? 0 : object.length;\n return !!length && isLength(length) && isIndex(key, length) &&\n (isArray(object) || isArguments(object));\n }\n\n /**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\n function initCloneArray(array) {\n var length = array.length,\n result = new array.constructor(length);\n\n // Add properties assigned by `RegExp#exec`.\n if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n result.index = array.index;\n result.input = array.input;\n }\n return result;\n }\n\n /**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneObject(object) {\n return (typeof object.constructor == 'function' && !isPrototype(object))\n ? baseCreate(getPrototype(object))\n : {};\n }\n\n /**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneByTag(object, tag, isDeep) {\n var Ctor = object.constructor;\n switch (tag) {\n case arrayBufferTag:\n return cloneArrayBuffer(object);\n\n case boolTag:\n case dateTag:\n return new Ctor(+object);\n\n case dataViewTag:\n return cloneDataView(object, isDeep);\n\n case float32Tag: case float64Tag:\n case int8Tag: case int16Tag: case int32Tag:\n case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n return cloneTypedArray(object, isDeep);\n\n case mapTag:\n return new Ctor;\n\n case numberTag:\n case stringTag:\n return new Ctor(object);\n\n case regexpTag:\n return cloneRegExp(object);\n\n case setTag:\n return new Ctor;\n\n case symbolTag:\n return cloneSymbol(object);\n }\n }\n\n /**\n * Inserts wrapper `details` in a comment at the top of the `source` body.\n *\n * @private\n * @param {string} source The source to modify.\n * @returns {Array} details The details to insert.\n * @returns {string} Returns the modified source.\n */\n function insertWrapDetails(source, details) {\n var length = details.length;\n if (!length) {\n return source;\n }\n var lastIndex = length - 1;\n details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];\n details = details.join(length > 2 ? ', ' : ' ');\n return source.replace(reWrapComment, '{\\n/* [wrapped with ' + details + '] */\\n');\n }\n\n /**\n * Checks if `value` is a flattenable `arguments` object or array.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n */\n function isFlattenable(value) {\n return isArray(value) || isArguments(value) ||\n !!(spreadableSymbol && value && value[spreadableSymbol]);\n }\n\n /**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\n function isIndex(value, length) {\n var type = typeof value;\n length = length == null ? MAX_SAFE_INTEGER : length;\n\n return !!length &&\n (type == 'number' ||\n (type != 'symbol' && reIsUint.test(value))) &&\n (value > -1 && value % 1 == 0 && value < length);\n }\n\n /**\n * Checks if the given arguments are from an iteratee call.\n *\n * @private\n * @param {*} value The potential iteratee value argument.\n * @param {*} index The potential iteratee index or key argument.\n * @param {*} object The potential iteratee object argument.\n * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n * else `false`.\n */\n function isIterateeCall(value, index, object) {\n if (!isObject(object)) {\n return false;\n }\n var type = typeof index;\n if (type == 'number'\n ? (isArrayLike(object) && isIndex(index, object.length))\n : (type == 'string' && index in object)\n ) {\n return eq(object[index], value);\n }\n return false;\n }\n\n /**\n * Checks if `value` is a property name and not a property path.\n *\n * @private\n * @param {*} value The value to check.\n * @param {Object} [object] The object to query keys on.\n * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n */\n function isKey(value, object) {\n if (isArray(value)) {\n return false;\n }\n var type = typeof value;\n if (type == 'number' || type == 'symbol' || type == 'boolean' ||\n value == null || isSymbol(value)) {\n return true;\n }\n return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||\n (object != null && value in Object(object));\n }\n\n /**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\n function isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n }\n\n /**\n * Checks if `func` has a lazy counterpart.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` has a lazy counterpart,\n * else `false`.\n */\n function isLaziable(func) {\n var funcName = getFuncName(func),\n other = lodash[funcName];\n\n if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {\n return false;\n }\n if (func === other) {\n return true;\n }\n var data = getData(other);\n return !!data && func === data[0];\n }\n\n /**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\n function isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n }\n\n /**\n * Checks if `func` is capable of being masked.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `func` is maskable, else `false`.\n */\n var isMaskable = coreJsData ? isFunction : stubFalse;\n\n /**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\n function isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n }\n\n /**\n * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` if suitable for strict\n * equality comparisons, else `false`.\n */\n function isStrictComparable(value) {\n return value === value && !isObject(value);\n }\n\n /**\n * A specialized version of `matchesProperty` for source values suitable\n * for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function matchesStrictComparable(key, srcValue) {\n return function(object) {\n if (object == null) {\n return false;\n }\n return object[key] === srcValue &&\n (srcValue !== undefined || (key in Object(object)));\n };\n }\n\n /**\n * A specialized version of `_.memoize` which clears the memoized function's\n * cache when it exceeds `MAX_MEMOIZE_SIZE`.\n *\n * @private\n * @param {Function} func The function to have its output memoized.\n * @returns {Function} Returns the new memoized function.\n */\n function memoizeCapped(func) {\n var result = memoize(func, function(key) {\n if (cache.size === MAX_MEMOIZE_SIZE) {\n cache.clear();\n }\n return key;\n });\n\n var cache = result.cache;\n return result;\n }\n\n /**\n * Merges the function metadata of `source` into `data`.\n *\n * Merging metadata reduces the number of wrappers used to invoke a function.\n * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`\n * may be applied regardless of execution order. Methods like `_.ary` and\n * `_.rearg` modify function arguments, making the order in which they are\n * executed important, preventing the merging of metadata. However, we make\n * an exception for a safe combined case where curried functions have `_.ary`\n * and or `_.rearg` applied.\n *\n * @private\n * @param {Array} data The destination metadata.\n * @param {Array} source The source metadata.\n * @returns {Array} Returns `data`.\n */\n function mergeData(data, source) {\n var bitmask = data[1],\n srcBitmask = source[1],\n newBitmask = bitmask | srcBitmask,\n isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);\n\n var isCombo =\n ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||\n ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||\n ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));\n\n // Exit early if metadata can't be merged.\n if (!(isCommon || isCombo)) {\n return data;\n }\n // Use source `thisArg` if available.\n if (srcBitmask & WRAP_BIND_FLAG) {\n data[2] = source[2];\n // Set when currying a bound function.\n newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;\n }\n // Compose partial arguments.\n var value = source[3];\n if (value) {\n var partials = data[3];\n data[3] = partials ? composeArgs(partials, value, source[4]) : value;\n data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];\n }\n // Compose partial right arguments.\n value = source[5];\n if (value) {\n partials = data[5];\n data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;\n data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];\n }\n // Use source `argPos` if available.\n value = source[7];\n if (value) {\n data[7] = value;\n }\n // Use source `ary` if it's smaller.\n if (srcBitmask & WRAP_ARY_FLAG) {\n data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);\n }\n // Use source `arity` if one is not provided.\n if (data[9] == null) {\n data[9] = source[9];\n }\n // Use source `func` and merge bitmasks.\n data[0] = source[0];\n data[1] = newBitmask;\n\n return data;\n }\n\n /**\n * This function is like\n * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * except that it includes inherited enumerable properties.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function nativeKeysIn(object) {\n var result = [];\n if (object != null) {\n for (var key in Object(object)) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\n function objectToString(value) {\n return nativeObjectToString.call(value);\n }\n\n /**\n * A specialized version of `baseRest` which transforms the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @param {Function} transform The rest array transform.\n * @returns {Function} Returns the new function.\n */\n function overRest(func, start, transform) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = transform(array);\n return apply(func, this, otherArgs);\n };\n }\n\n /**\n * Gets the parent value at `path` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} path The path to get the parent value of.\n * @returns {*} Returns the parent value.\n */\n function parent(object, path) {\n return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));\n }\n\n /**\n * Reorder `array` according to the specified indexes where the element at\n * the first index is assigned as the first element, the element at\n * the second index is assigned as the second element, and so on.\n *\n * @private\n * @param {Array} array The array to reorder.\n * @param {Array} indexes The arranged array indexes.\n * @returns {Array} Returns `array`.\n */\n function reorder(array, indexes) {\n var arrLength = array.length,\n length = nativeMin(indexes.length, arrLength),\n oldArray = copyArray(array);\n\n while (length--) {\n var index = indexes[length];\n array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;\n }\n return array;\n }\n\n /**\n * Gets the value at `key`, unless `key` is \"__proto__\" or \"constructor\".\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function safeGet(object, key) {\n if (key === 'constructor' && typeof object[key] === 'function') {\n return;\n }\n\n if (key == '__proto__') {\n return;\n }\n\n return object[key];\n }\n\n /**\n * Sets metadata for `func`.\n *\n * **Note:** If this function becomes hot, i.e. is invoked a lot in a short\n * period of time, it will trip its breaker and transition to an identity\n * function to avoid garbage collection pauses in V8. See\n * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)\n * for more details.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var setData = shortOut(baseSetData);\n\n /**\n * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n var setTimeout = ctxSetTimeout || function(func, wait) {\n return root.setTimeout(func, wait);\n };\n\n /**\n * Sets the `toString` method of `func` to return `string`.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var setToString = shortOut(baseSetToString);\n\n /**\n * Sets the `toString` method of `wrapper` to mimic the source of `reference`\n * with wrapper details in a comment at the top of the source body.\n *\n * @private\n * @param {Function} wrapper The function to modify.\n * @param {Function} reference The reference function.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Function} Returns `wrapper`.\n */\n function setWrapToString(wrapper, reference, bitmask) {\n var source = (reference + '');\n return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));\n }\n\n /**\n * Creates a function that'll short out and invoke `identity` instead\n * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`\n * milliseconds.\n *\n * @private\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new shortable function.\n */\n function shortOut(func) {\n var count = 0,\n lastCalled = 0;\n\n return function() {\n var stamp = nativeNow(),\n remaining = HOT_SPAN - (stamp - lastCalled);\n\n lastCalled = stamp;\n if (remaining > 0) {\n if (++count >= HOT_COUNT) {\n return arguments[0];\n }\n } else {\n count = 0;\n }\n return func.apply(undefined, arguments);\n };\n }\n\n /**\n * A specialized version of `_.shuffle` which mutates and sets the size of `array`.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @param {number} [size=array.length] The size of `array`.\n * @returns {Array} Returns `array`.\n */\n function shuffleSelf(array, size) {\n var index = -1,\n length = array.length,\n lastIndex = length - 1;\n\n size = size === undefined ? length : size;\n while (++index < size) {\n var rand = baseRandom(index, lastIndex),\n value = array[rand];\n\n array[rand] = array[index];\n array[index] = value;\n }\n array.length = size;\n return array;\n }\n\n /**\n * Converts `string` to a property path array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the property path array.\n */\n var stringToPath = memoizeCapped(function(string) {\n var result = [];\n if (string.charCodeAt(0) === 46 /* . */) {\n result.push('');\n }\n string.replace(rePropName, function(match, number, quote, subString) {\n result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));\n });\n return result;\n });\n\n /**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\n function toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n }\n\n /**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\n function toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n }\n\n /**\n * Updates wrapper `details` based on `bitmask` flags.\n *\n * @private\n * @returns {Array} details The details to modify.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Array} Returns `details`.\n */\n function updateWrapDetails(details, bitmask) {\n arrayEach(wrapFlags, function(pair) {\n var value = '_.' + pair[0];\n if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {\n details.push(value);\n }\n });\n return details.sort();\n }\n\n /**\n * Creates a clone of `wrapper`.\n *\n * @private\n * @param {Object} wrapper The wrapper to clone.\n * @returns {Object} Returns the cloned wrapper.\n */\n function wrapperClone(wrapper) {\n if (wrapper instanceof LazyWrapper) {\n return wrapper.clone();\n }\n var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);\n result.__actions__ = copyArray(wrapper.__actions__);\n result.__index__ = wrapper.__index__;\n result.__values__ = wrapper.__values__;\n return result;\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of elements split into groups the length of `size`.\n * If `array` can't be split evenly, the final chunk will be the remaining\n * elements.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to process.\n * @param {number} [size=1] The length of each chunk\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the new array of chunks.\n * @example\n *\n * _.chunk(['a', 'b', 'c', 'd'], 2);\n * // => [['a', 'b'], ['c', 'd']]\n *\n * _.chunk(['a', 'b', 'c', 'd'], 3);\n * // => [['a', 'b', 'c'], ['d']]\n */\n function chunk(array, size, guard) {\n if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {\n size = 1;\n } else {\n size = nativeMax(toInteger(size), 0);\n }\n var length = array == null ? 0 : array.length;\n if (!length || size < 1) {\n return [];\n }\n var index = 0,\n resIndex = 0,\n result = Array(nativeCeil(length / size));\n\n while (index < length) {\n result[resIndex++] = baseSlice(array, index, (index += size));\n }\n return result;\n }\n\n /**\n * Creates an array with all falsey values removed. The values `false`, `null`,\n * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to compact.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.compact([0, 1, false, 2, '', 3]);\n * // => [1, 2, 3]\n */\n function compact(array) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * Creates a new array concatenating `array` with any additional arrays\n * and/or values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to concatenate.\n * @param {...*} [values] The values to concatenate.\n * @returns {Array} Returns the new concatenated array.\n * @example\n *\n * var array = [1];\n * var other = _.concat(array, 2, [3], [[4]]);\n *\n * console.log(other);\n * // => [1, 2, 3, [4]]\n *\n * console.log(array);\n * // => [1]\n */\n function concat() {\n var length = arguments.length;\n if (!length) {\n return [];\n }\n var args = Array(length - 1),\n array = arguments[0],\n index = length;\n\n while (index--) {\n args[index - 1] = arguments[index];\n }\n return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));\n }\n\n /**\n * Creates an array of `array` values not included in the other given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * **Note:** Unlike `_.pullAll`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.without, _.xor\n * @example\n *\n * _.difference([2, 1], [2, 3]);\n * // => [1]\n */\n var difference = baseRest(function(array, values) {\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))\n : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `iteratee` which\n * is invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * **Note:** Unlike `_.pullAllBy`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var differenceBy = baseRest(function(array, values) {\n var iteratee = last(values);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))\n : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `comparator`\n * which is invoked to compare elements of `array` to `values`. The order and\n * references of result values are determined by the first array. The comparator\n * is invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.pullAllWith`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n *\n * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }]\n */\n var differenceWith = baseRest(function(array, values) {\n var comparator = last(values);\n if (isArrayLikeObject(comparator)) {\n comparator = undefined;\n }\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)\n : [];\n });\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.drop([1, 2, 3]);\n * // => [2, 3]\n *\n * _.drop([1, 2, 3], 2);\n * // => [3]\n *\n * _.drop([1, 2, 3], 5);\n * // => []\n *\n * _.drop([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function drop(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.dropRight([1, 2, 3]);\n * // => [1, 2]\n *\n * _.dropRight([1, 2, 3], 2);\n * // => [1]\n *\n * _.dropRight([1, 2, 3], 5);\n * // => []\n *\n * _.dropRight([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function dropRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the end.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.dropRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropRightWhile(users, ['active', false]);\n * // => objects for ['barney']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropRightWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropRightWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), true, true)\n : [];\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the beginning.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.dropWhile(users, function(o) { return !o.active; });\n * // => objects for ['pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropWhile(users, ['active', false]);\n * // => objects for ['pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), true)\n : [];\n }\n\n /**\n * Fills elements of `array` with `value` from `start` up to, but not\n * including, `end`.\n *\n * **Note:** This method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Array\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.fill(array, 'a');\n * console.log(array);\n * // => ['a', 'a', 'a']\n *\n * _.fill(Array(3), 2);\n * // => [2, 2, 2]\n *\n * _.fill([4, 6, 8, 10], '*', 1, 3);\n * // => [4, '*', '*', 10]\n */\n function fill(array, value, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {\n start = 0;\n end = length;\n }\n return baseFill(array, value, start, end);\n }\n\n /**\n * This method is like `_.find` except that it returns the index of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.findIndex(users, function(o) { return o.user == 'barney'; });\n * // => 0\n *\n * // The `_.matches` iteratee shorthand.\n * _.findIndex(users, { 'user': 'fred', 'active': false });\n * // => 1\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findIndex(users, ['active', false]);\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.findIndex(users, 'active');\n * // => 2\n */\n function findIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index);\n }\n\n /**\n * This method is like `_.findIndex` except that it iterates over elements\n * of `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });\n * // => 2\n *\n * // The `_.matches` iteratee shorthand.\n * _.findLastIndex(users, { 'user': 'barney', 'active': true });\n * // => 0\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findLastIndex(users, ['active', false]);\n * // => 2\n *\n * // The `_.property` iteratee shorthand.\n * _.findLastIndex(users, 'active');\n * // => 0\n */\n function findLastIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length - 1;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = fromIndex < 0\n ? nativeMax(length + index, 0)\n : nativeMin(index, length - 1);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index, true);\n }\n\n /**\n * Flattens `array` a single level deep.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flatten([1, [2, [3, [4]], 5]]);\n * // => [1, 2, [3, [4]], 5]\n */\n function flatten(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, 1) : [];\n }\n\n /**\n * Recursively flattens `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flattenDeep([1, [2, [3, [4]], 5]]);\n * // => [1, 2, 3, 4, 5]\n */\n function flattenDeep(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, INFINITY) : [];\n }\n\n /**\n * Recursively flatten `array` up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.4.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * var array = [1, [2, [3, [4]], 5]];\n *\n * _.flattenDepth(array, 1);\n * // => [1, 2, [3, [4]], 5]\n *\n * _.flattenDepth(array, 2);\n * // => [1, 2, 3, [4], 5]\n */\n function flattenDepth(array, depth) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(array, depth);\n }\n\n /**\n * The inverse of `_.toPairs`; this method returns an object composed\n * from key-value `pairs`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} pairs The key-value pairs.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.fromPairs([['a', 1], ['b', 2]]);\n * // => { 'a': 1, 'b': 2 }\n */\n function fromPairs(pairs) {\n var index = -1,\n length = pairs == null ? 0 : pairs.length,\n result = {};\n\n while (++index < length) {\n var pair = pairs[index];\n result[pair[0]] = pair[1];\n }\n return result;\n }\n\n /**\n * Gets the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias first\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the first element of `array`.\n * @example\n *\n * _.head([1, 2, 3]);\n * // => 1\n *\n * _.head([]);\n * // => undefined\n */\n function head(array) {\n return (array && array.length) ? array[0] : undefined;\n }\n\n /**\n * Gets the index at which the first occurrence of `value` is found in `array`\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. If `fromIndex` is negative, it's used as the\n * offset from the end of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.indexOf([1, 2, 1, 2], 2);\n * // => 1\n *\n * // Search from the `fromIndex`.\n * _.indexOf([1, 2, 1, 2], 2, 2);\n * // => 3\n */\n function indexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseIndexOf(array, value, index);\n }\n\n /**\n * Gets all but the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.initial([1, 2, 3]);\n * // => [1, 2]\n */\n function initial(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 0, -1) : [];\n }\n\n /**\n * Creates an array of unique values that are included in all given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersection([2, 1], [2, 3]);\n * // => [2]\n */\n var intersection = baseRest(function(arrays) {\n var mapped = arrayMap(arrays, castArrayLikeObject);\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped)\n : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `iteratee`\n * which is invoked for each element of each `arrays` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [2.1]\n *\n * // The `_.property` iteratee shorthand.\n * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }]\n */\n var intersectionBy = baseRest(function(arrays) {\n var iteratee = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n\n if (iteratee === last(mapped)) {\n iteratee = undefined;\n } else {\n mapped.pop();\n }\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped, getIteratee(iteratee, 2))\n : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `comparator`\n * which is invoked to compare elements of `arrays`. The order and references\n * of result values are determined by the first array. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.intersectionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }]\n */\n var intersectionWith = baseRest(function(arrays) {\n var comparator = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n\n comparator = typeof comparator == 'function' ? comparator : undefined;\n if (comparator) {\n mapped.pop();\n }\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped, undefined, comparator)\n : [];\n });\n\n /**\n * Converts all elements in `array` into a string separated by `separator`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to convert.\n * @param {string} [separator=','] The element separator.\n * @returns {string} Returns the joined string.\n * @example\n *\n * _.join(['a', 'b', 'c'], '~');\n * // => 'a~b~c'\n */\n function join(array, separator) {\n return array == null ? '' : nativeJoin.call(array, separator);\n }\n\n /**\n * Gets the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the last element of `array`.\n * @example\n *\n * _.last([1, 2, 3]);\n * // => 3\n */\n function last(array) {\n var length = array == null ? 0 : array.length;\n return length ? array[length - 1] : undefined;\n }\n\n /**\n * This method is like `_.indexOf` except that it iterates over elements of\n * `array` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.lastIndexOf([1, 2, 1, 2], 2);\n * // => 3\n *\n * // Search from the `fromIndex`.\n * _.lastIndexOf([1, 2, 1, 2], 2, 2);\n * // => 1\n */\n function lastIndexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);\n }\n return value === value\n ? strictLastIndexOf(array, value, index)\n : baseFindIndex(array, baseIsNaN, index, true);\n }\n\n /**\n * Gets the element at index `n` of `array`. If `n` is negative, the nth\n * element from the end is returned.\n *\n * @static\n * @memberOf _\n * @since 4.11.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=0] The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n *\n * _.nth(array, 1);\n * // => 'b'\n *\n * _.nth(array, -2);\n * // => 'c';\n */\n function nth(array, n) {\n return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;\n }\n\n /**\n * Removes all given values from `array` using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`\n * to remove elements from an array by predicate.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...*} [values] The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pull(array, 'a', 'c');\n * console.log(array);\n * // => ['b', 'b']\n */\n var pull = baseRest(pullAll);\n\n /**\n * This method is like `_.pull` except that it accepts an array of values to remove.\n *\n * **Note:** Unlike `_.difference`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pullAll(array, ['a', 'c']);\n * console.log(array);\n * // => ['b', 'b']\n */\n function pullAll(array, values) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values)\n : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `iteratee` which is\n * invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The iteratee is invoked with one argument: (value).\n *\n * **Note:** Unlike `_.differenceBy`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];\n *\n * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');\n * console.log(array);\n * // => [{ 'x': 2 }]\n */\n function pullAllBy(array, values, iteratee) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values, getIteratee(iteratee, 2))\n : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `comparator` which\n * is invoked to compare elements of `array` to `values`. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.differenceWith`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];\n *\n * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);\n * console.log(array);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]\n */\n function pullAllWith(array, values, comparator) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values, undefined, comparator)\n : array;\n }\n\n /**\n * Removes elements from `array` corresponding to `indexes` and returns an\n * array of removed elements.\n *\n * **Note:** Unlike `_.at`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...(number|number[])} [indexes] The indexes of elements to remove.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n * var pulled = _.pullAt(array, [1, 3]);\n *\n * console.log(array);\n * // => ['a', 'c']\n *\n * console.log(pulled);\n * // => ['b', 'd']\n */\n var pullAt = flatRest(function(array, indexes) {\n var length = array == null ? 0 : array.length,\n result = baseAt(array, indexes);\n\n basePullAt(array, arrayMap(indexes, function(index) {\n return isIndex(index, length) ? +index : index;\n }).sort(compareAscending));\n\n return result;\n });\n\n /**\n * Removes all elements from `array` that `predicate` returns truthy for\n * and returns an array of the removed elements. The predicate is invoked\n * with three arguments: (value, index, array).\n *\n * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`\n * to pull elements from an array by value.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = [1, 2, 3, 4];\n * var evens = _.remove(array, function(n) {\n * return n % 2 == 0;\n * });\n *\n * console.log(array);\n * // => [1, 3]\n *\n * console.log(evens);\n * // => [2, 4]\n */\n function remove(array, predicate) {\n var result = [];\n if (!(array && array.length)) {\n return result;\n }\n var index = -1,\n indexes = [],\n length = array.length;\n\n predicate = getIteratee(predicate, 3);\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result.push(value);\n indexes.push(index);\n }\n }\n basePullAt(array, indexes);\n return result;\n }\n\n /**\n * Reverses `array` so that the first element becomes the last, the second\n * element becomes the second to last, and so on.\n *\n * **Note:** This method mutates `array` and is based on\n * [`Array#reverse`](https://mdn.io/Array/reverse).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.reverse(array);\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function reverse(array) {\n return array == null ? array : nativeReverse.call(array);\n }\n\n /**\n * Creates a slice of `array` from `start` up to, but not including, `end`.\n *\n * **Note:** This method is used instead of\n * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are\n * returned.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function slice(array, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {\n start = 0;\n end = length;\n }\n else {\n start = start == null ? 0 : toInteger(start);\n end = end === undefined ? length : toInteger(end);\n }\n return baseSlice(array, start, end);\n }\n\n /**\n * Uses a binary search to determine the lowest index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedIndex([30, 50], 40);\n * // => 1\n */\n function sortedIndex(array, value) {\n return baseSortedIndex(array, value);\n }\n\n /**\n * This method is like `_.sortedIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedIndexBy(objects, { 'x': 4 }, 'x');\n * // => 0\n */\n function sortedIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));\n }\n\n /**\n * This method is like `_.indexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedIndexOf([4, 5, 5, 5, 6], 5);\n * // => 1\n */\n function sortedIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value);\n if (index < length && eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.sortedIndex` except that it returns the highest\n * index at which `value` should be inserted into `array` in order to\n * maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedLastIndex([4, 5, 5, 5, 6], 5);\n * // => 4\n */\n function sortedLastIndex(array, value) {\n return baseSortedIndex(array, value, true);\n }\n\n /**\n * This method is like `_.sortedLastIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 1\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');\n * // => 1\n */\n function sortedLastIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);\n }\n\n /**\n * This method is like `_.lastIndexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);\n * // => 3\n */\n function sortedLastIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value, true) - 1;\n if (eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.uniq` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniq([1, 1, 2]);\n * // => [1, 2]\n */\n function sortedUniq(array) {\n return (array && array.length)\n ? baseSortedUniq(array)\n : [];\n }\n\n /**\n * This method is like `_.uniqBy` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);\n * // => [1.1, 2.3]\n */\n function sortedUniqBy(array, iteratee) {\n return (array && array.length)\n ? baseSortedUniq(array, getIteratee(iteratee, 2))\n : [];\n }\n\n /**\n * Gets all but the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.tail([1, 2, 3]);\n * // => [2, 3]\n */\n function tail(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 1, length) : [];\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.take([1, 2, 3]);\n * // => [1]\n *\n * _.take([1, 2, 3], 2);\n * // => [1, 2]\n *\n * _.take([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.take([1, 2, 3], 0);\n * // => []\n */\n function take(array, n, guard) {\n if (!(array && array.length)) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.takeRight([1, 2, 3]);\n * // => [3]\n *\n * _.takeRight([1, 2, 3], 2);\n * // => [2, 3]\n *\n * _.takeRight([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.takeRight([1, 2, 3], 0);\n * // => []\n */\n function takeRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with elements taken from the end. Elements are\n * taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.takeRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeRightWhile(users, ['active', false]);\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeRightWhile(users, 'active');\n * // => []\n */\n function takeRightWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), false, true)\n : [];\n }\n\n /**\n * Creates a slice of `array` with elements taken from the beginning. Elements\n * are taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.takeWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeWhile(users, ['active', false]);\n * // => objects for ['barney', 'fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeWhile(users, 'active');\n * // => []\n */\n function takeWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3))\n : [];\n }\n\n /**\n * Creates an array of unique values, in order, from all given arrays using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.union([2], [1, 2]);\n * // => [2, 1]\n */\n var union = baseRest(function(arrays) {\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));\n });\n\n /**\n * This method is like `_.union` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which uniqueness is computed. Result values are chosen from the first\n * array in which the value occurs. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.unionBy([2.1], [1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n var unionBy = baseRest(function(arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.union` except that it accepts `comparator` which\n * is invoked to compare elements of `arrays`. Result values are chosen from\n * the first array in which the value occurs. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.unionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var unionWith = baseRest(function(arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);\n });\n\n /**\n * Creates a duplicate-free version of an array, using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons, in which only the first occurrence of each element\n * is kept. The order of result values is determined by the order they occur\n * in the array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniq([2, 1, 2]);\n * // => [2, 1]\n */\n function uniq(array) {\n return (array && array.length) ? baseUniq(array) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The order of result values is determined by the\n * order they occur in the array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n function uniqBy(array, iteratee) {\n return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `comparator` which\n * is invoked to compare elements of `array`. The order of result values is\n * determined by the order they occur in the array.The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.uniqWith(objects, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]\n */\n function uniqWith(array, comparator) {\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return (array && array.length) ? baseUniq(array, undefined, comparator) : [];\n }\n\n /**\n * This method is like `_.zip` except that it accepts an array of grouped\n * elements and creates an array regrouping the elements to their pre-zip\n * configuration.\n *\n * @static\n * @memberOf _\n * @since 1.2.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n *\n * _.unzip(zipped);\n * // => [['a', 'b'], [1, 2], [true, false]]\n */\n function unzip(array) {\n if (!(array && array.length)) {\n return [];\n }\n var length = 0;\n array = arrayFilter(array, function(group) {\n if (isArrayLikeObject(group)) {\n length = nativeMax(group.length, length);\n return true;\n }\n });\n return baseTimes(length, function(index) {\n return arrayMap(array, baseProperty(index));\n });\n }\n\n /**\n * This method is like `_.unzip` except that it accepts `iteratee` to specify\n * how regrouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * regrouped values.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip([1, 2], [10, 20], [100, 200]);\n * // => [[1, 10, 100], [2, 20, 200]]\n *\n * _.unzipWith(zipped, _.add);\n * // => [3, 30, 300]\n */\n function unzipWith(array, iteratee) {\n if (!(array && array.length)) {\n return [];\n }\n var result = unzip(array);\n if (iteratee == null) {\n return result;\n }\n return arrayMap(result, function(group) {\n return apply(iteratee, undefined, group);\n });\n }\n\n /**\n * Creates an array excluding all given values using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.pull`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...*} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.xor\n * @example\n *\n * _.without([2, 1, 2, 3], 1, 2);\n * // => [3]\n */\n var without = baseRest(function(array, values) {\n return isArrayLikeObject(array)\n ? baseDifference(array, values)\n : [];\n });\n\n /**\n * Creates an array of unique values that is the\n * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)\n * of the given arrays. The order of result values is determined by the order\n * they occur in the arrays.\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.without\n * @example\n *\n * _.xor([2, 1], [2, 3]);\n * // => [1, 3]\n */\n var xor = baseRest(function(arrays) {\n return baseXor(arrayFilter(arrays, isArrayLikeObject));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which by which they're compared. The order of result values is determined\n * by the order they occur in the arrays. The iteratee is invoked with one\n * argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2, 3.4]\n *\n * // The `_.property` iteratee shorthand.\n * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var xorBy = baseRest(function(arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `comparator` which is\n * invoked to compare elements of `arrays`. The order of result values is\n * determined by the order they occur in the arrays. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.xorWith(objects, others, _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var xorWith = baseRest(function(arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);\n });\n\n /**\n * Creates an array of grouped elements, the first of which contains the\n * first elements of the given arrays, the second of which contains the\n * second elements of the given arrays, and so on.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n */\n var zip = baseRest(unzip);\n\n /**\n * This method is like `_.fromPairs` except that it accepts two arrays,\n * one of property identifiers and one of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 0.4.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObject(['a', 'b'], [1, 2]);\n * // => { 'a': 1, 'b': 2 }\n */\n function zipObject(props, values) {\n return baseZipObject(props || [], values || [], assignValue);\n }\n\n /**\n * This method is like `_.zipObject` except that it supports property paths.\n *\n * @static\n * @memberOf _\n * @since 4.1.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);\n * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }\n */\n function zipObjectDeep(props, values) {\n return baseZipObject(props || [], values || [], baseSet);\n }\n\n /**\n * This method is like `_.zip` except that it accepts `iteratee` to specify\n * how grouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * grouped values.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {\n * return a + b + c;\n * });\n * // => [111, 222]\n */\n var zipWith = baseRest(function(arrays) {\n var length = arrays.length,\n iteratee = length > 1 ? arrays[length - 1] : undefined;\n\n iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;\n return unzipWith(arrays, iteratee);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` wrapper instance that wraps `value` with explicit method\n * chain sequences enabled. The result of such sequences must be unwrapped\n * with `_#value`.\n *\n * @static\n * @memberOf _\n * @since 1.3.0\n * @category Seq\n * @param {*} value The value to wrap.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'pebbles', 'age': 1 }\n * ];\n *\n * var youngest = _\n * .chain(users)\n * .sortBy('age')\n * .map(function(o) {\n * return o.user + ' is ' + o.age;\n * })\n * .head()\n * .value();\n * // => 'pebbles is 1'\n */\n function chain(value) {\n var result = lodash(value);\n result.__chain__ = true;\n return result;\n }\n\n /**\n * This method invokes `interceptor` and returns `value`. The interceptor\n * is invoked with one argument; (value). The purpose of this method is to\n * \"tap into\" a method chain sequence in order to modify intermediate results.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns `value`.\n * @example\n *\n * _([1, 2, 3])\n * .tap(function(array) {\n * // Mutate input array.\n * array.pop();\n * })\n * .reverse()\n * .value();\n * // => [2, 1]\n */\n function tap(value, interceptor) {\n interceptor(value);\n return value;\n }\n\n /**\n * This method is like `_.tap` except that it returns the result of `interceptor`.\n * The purpose of this method is to \"pass thru\" values replacing intermediate\n * results in a method chain sequence.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns the result of `interceptor`.\n * @example\n *\n * _(' abc ')\n * .chain()\n * .trim()\n * .thru(function(value) {\n * return [value];\n * })\n * .value();\n * // => ['abc']\n */\n function thru(value, interceptor) {\n return interceptor(value);\n }\n\n /**\n * This method is the wrapper version of `_.at`.\n *\n * @name at\n * @memberOf _\n * @since 1.0.0\n * @category Seq\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n *\n * _(object).at(['a[0].b.c', 'a[1]']).value();\n * // => [3, 4]\n */\n var wrapperAt = flatRest(function(paths) {\n var length = paths.length,\n start = length ? paths[0] : 0,\n value = this.__wrapped__,\n interceptor = function(object) { return baseAt(object, paths); };\n\n if (length > 1 || this.__actions__.length ||\n !(value instanceof LazyWrapper) || !isIndex(start)) {\n return this.thru(interceptor);\n }\n value = value.slice(start, +start + (length ? 1 : 0));\n value.__actions__.push({\n 'func': thru,\n 'args': [interceptor],\n 'thisArg': undefined\n });\n return new LodashWrapper(value, this.__chain__).thru(function(array) {\n if (length && !array.length) {\n array.push(undefined);\n }\n return array;\n });\n });\n\n /**\n * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.\n *\n * @name chain\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 }\n * ];\n *\n * // A sequence without explicit chaining.\n * _(users).head();\n * // => { 'user': 'barney', 'age': 36 }\n *\n * // A sequence with explicit chaining.\n * _(users)\n * .chain()\n * .head()\n * .pick('user')\n * .value();\n * // => { 'user': 'barney' }\n */\n function wrapperChain() {\n return chain(this);\n }\n\n /**\n * Executes the chain sequence and returns the wrapped result.\n *\n * @name commit\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2];\n * var wrapped = _(array).push(3);\n *\n * console.log(array);\n * // => [1, 2]\n *\n * wrapped = wrapped.commit();\n * console.log(array);\n * // => [1, 2, 3]\n *\n * wrapped.last();\n * // => 3\n *\n * console.log(array);\n * // => [1, 2, 3]\n */\n function wrapperCommit() {\n return new LodashWrapper(this.value(), this.__chain__);\n }\n\n /**\n * Gets the next value on a wrapped object following the\n * [iterator protocol](https://mdn.io/iteration_protocols#iterator).\n *\n * @name next\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the next iterator value.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 1 }\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 2 }\n *\n * wrapped.next();\n * // => { 'done': true, 'value': undefined }\n */\n function wrapperNext() {\n if (this.__values__ === undefined) {\n this.__values__ = toArray(this.value());\n }\n var done = this.__index__ >= this.__values__.length,\n value = done ? undefined : this.__values__[this.__index__++];\n\n return { 'done': done, 'value': value };\n }\n\n /**\n * Enables the wrapper to be iterable.\n *\n * @name Symbol.iterator\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the wrapper object.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped[Symbol.iterator]() === wrapped;\n * // => true\n *\n * Array.from(wrapped);\n * // => [1, 2]\n */\n function wrapperToIterator() {\n return this;\n }\n\n /**\n * Creates a clone of the chain sequence planting `value` as the wrapped value.\n *\n * @name plant\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @param {*} value The value to plant.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2]).map(square);\n * var other = wrapped.plant([3, 4]);\n *\n * other.value();\n * // => [9, 16]\n *\n * wrapped.value();\n * // => [1, 4]\n */\n function wrapperPlant(value) {\n var result,\n parent = this;\n\n while (parent instanceof baseLodash) {\n var clone = wrapperClone(parent);\n clone.__index__ = 0;\n clone.__values__ = undefined;\n if (result) {\n previous.__wrapped__ = clone;\n } else {\n result = clone;\n }\n var previous = clone;\n parent = parent.__wrapped__;\n }\n previous.__wrapped__ = value;\n return result;\n }\n\n /**\n * This method is the wrapper version of `_.reverse`.\n *\n * **Note:** This method mutates the wrapped array.\n *\n * @name reverse\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _(array).reverse().value()\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function wrapperReverse() {\n var value = this.__wrapped__;\n if (value instanceof LazyWrapper) {\n var wrapped = value;\n if (this.__actions__.length) {\n wrapped = new LazyWrapper(this);\n }\n wrapped = wrapped.reverse();\n wrapped.__actions__.push({\n 'func': thru,\n 'args': [reverse],\n 'thisArg': undefined\n });\n return new LodashWrapper(wrapped, this.__chain__);\n }\n return this.thru(reverse);\n }\n\n /**\n * Executes the chain sequence to resolve the unwrapped value.\n *\n * @name value\n * @memberOf _\n * @since 0.1.0\n * @alias toJSON, valueOf\n * @category Seq\n * @returns {*} Returns the resolved unwrapped value.\n * @example\n *\n * _([1, 2, 3]).value();\n * // => [1, 2, 3]\n */\n function wrapperValue() {\n return baseWrapperValue(this.__wrapped__, this.__actions__);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the number of times the key was returned by `iteratee`. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.countBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': 1, '6': 2 }\n *\n * // The `_.property` iteratee shorthand.\n * _.countBy(['one', 'two', 'three'], 'length');\n * // => { '3': 2, '5': 1 }\n */\n var countBy = createAggregator(function(result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n ++result[key];\n } else {\n baseAssignValue(result, key, 1);\n }\n });\n\n /**\n * Checks if `predicate` returns truthy for **all** elements of `collection`.\n * Iteration is stopped once `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * **Note:** This method returns `true` for\n * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because\n * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of\n * elements of empty collections.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n * @example\n *\n * _.every([true, 1, null, 'yes'], Boolean);\n * // => false\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.every(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.every(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.every(users, 'active');\n * // => false\n */\n function every(collection, predicate, guard) {\n var func = isArray(collection) ? arrayEvery : baseEvery;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning an array of all elements\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * **Note:** Unlike `_.remove`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.reject\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * _.filter(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.filter(users, { 'age': 36, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.filter(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.filter(users, 'active');\n * // => objects for ['barney']\n *\n * // Combining several predicates using `_.overEvery` or `_.overSome`.\n * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));\n * // => objects for ['fred', 'barney']\n */\n function filter(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning the first element\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false },\n * { 'user': 'pebbles', 'age': 1, 'active': true }\n * ];\n *\n * _.find(users, function(o) { return o.age < 40; });\n * // => object for 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.find(users, { 'age': 1, 'active': true });\n * // => object for 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.find(users, ['active', false]);\n * // => object for 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.find(users, 'active');\n * // => object for 'barney'\n */\n var find = createFind(findIndex);\n\n /**\n * This method is like `_.find` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=collection.length-1] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * _.findLast([1, 2, 3, 4], function(n) {\n * return n % 2 == 1;\n * });\n * // => 3\n */\n var findLast = createFind(findLastIndex);\n\n /**\n * Creates a flattened array of values by running each element in `collection`\n * thru `iteratee` and flattening the mapped results. The iteratee is invoked\n * with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [n, n];\n * }\n *\n * _.flatMap([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMap(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), 1);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDeep([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMapDeep(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), INFINITY);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDepth([1, 2], duplicate, 2);\n * // => [[1, 1], [2, 2]]\n */\n function flatMapDepth(collection, iteratee, depth) {\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(map(collection, iteratee), depth);\n }\n\n /**\n * Iterates over elements of `collection` and invokes `iteratee` for each element.\n * The iteratee is invoked with three arguments: (value, index|key, collection).\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * **Note:** As with other \"Collections\" methods, objects with a \"length\"\n * property are iterated like arrays. To avoid this behavior use `_.forIn`\n * or `_.forOwn` for object iteration.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias each\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEachRight\n * @example\n *\n * _.forEach([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `1` then `2`.\n *\n * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n */\n function forEach(collection, iteratee) {\n var func = isArray(collection) ? arrayEach : baseEach;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.forEach` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @alias eachRight\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEach\n * @example\n *\n * _.forEachRight([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `2` then `1`.\n */\n function forEachRight(collection, iteratee) {\n var func = isArray(collection) ? arrayEachRight : baseEachRight;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The order of grouped values\n * is determined by the order they occur in `collection`. The corresponding\n * value of each key is an array of elements responsible for generating the\n * key. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.groupBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': [4.2], '6': [6.1, 6.3] }\n *\n * // The `_.property` iteratee shorthand.\n * _.groupBy(['one', 'two', 'three'], 'length');\n * // => { '3': ['one', 'two'], '5': ['three'] }\n */\n var groupBy = createAggregator(function(result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n result[key].push(value);\n } else {\n baseAssignValue(result, key, [value]);\n }\n });\n\n /**\n * Checks if `value` is in `collection`. If `collection` is a string, it's\n * checked for a substring of `value`, otherwise\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * is used for equality comparisons. If `fromIndex` is negative, it's used as\n * the offset from the end of `collection`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {boolean} Returns `true` if `value` is found, else `false`.\n * @example\n *\n * _.includes([1, 2, 3], 1);\n * // => true\n *\n * _.includes([1, 2, 3], 1, 2);\n * // => false\n *\n * _.includes({ 'a': 1, 'b': 2 }, 1);\n * // => true\n *\n * _.includes('abcd', 'bc');\n * // => true\n */\n function includes(collection, value, fromIndex, guard) {\n collection = isArrayLike(collection) ? collection : values(collection);\n fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;\n\n var length = collection.length;\n if (fromIndex < 0) {\n fromIndex = nativeMax(length + fromIndex, 0);\n }\n return isString(collection)\n ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)\n : (!!length && baseIndexOf(collection, value, fromIndex) > -1);\n }\n\n /**\n * Invokes the method at `path` of each element in `collection`, returning\n * an array of the results of each invoked method. Any additional arguments\n * are provided to each invoked method. If `path` is a function, it's invoked\n * for, and `this` bound to, each element in `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array|Function|string} path The path of the method to invoke or\n * the function invoked per iteration.\n * @param {...*} [args] The arguments to invoke each method with.\n * @returns {Array} Returns the array of results.\n * @example\n *\n * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');\n * // => [[1, 5, 7], [1, 2, 3]]\n *\n * _.invokeMap([123, 456], String.prototype.split, '');\n * // => [['1', '2', '3'], ['4', '5', '6']]\n */\n var invokeMap = baseRest(function(collection, path, args) {\n var index = -1,\n isFunc = typeof path == 'function',\n result = isArrayLike(collection) ? Array(collection.length) : [];\n\n baseEach(collection, function(value) {\n result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);\n });\n return result;\n });\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the last element responsible for generating the key. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * var array = [\n * { 'dir': 'left', 'code': 97 },\n * { 'dir': 'right', 'code': 100 }\n * ];\n *\n * _.keyBy(array, function(o) {\n * return String.fromCharCode(o.code);\n * });\n * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }\n *\n * _.keyBy(array, 'dir');\n * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }\n */\n var keyBy = createAggregator(function(result, value, key) {\n baseAssignValue(result, key, value);\n });\n\n /**\n * Creates an array of values by running each element in `collection` thru\n * `iteratee`. The iteratee is invoked with three arguments:\n * (value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.\n *\n * The guarded methods are:\n * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,\n * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,\n * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,\n * `template`, `trim`, `trimEnd`, `trimStart`, and `words`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * _.map([4, 8], square);\n * // => [16, 64]\n *\n * _.map({ 'a': 4, 'b': 8 }, square);\n * // => [16, 64] (iteration order is not guaranteed)\n *\n * var users = [\n * { 'user': 'barney' },\n * { 'user': 'fred' }\n * ];\n *\n * // The `_.property` iteratee shorthand.\n * _.map(users, 'user');\n * // => ['barney', 'fred']\n */\n function map(collection, iteratee) {\n var func = isArray(collection) ? arrayMap : baseMap;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.sortBy` except that it allows specifying the sort\n * orders of the iteratees to sort by. If `orders` is unspecified, all values\n * are sorted in ascending order. Otherwise, specify an order of \"desc\" for\n * descending or \"asc\" for ascending sort order of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @param {string[]} [orders] The sort orders of `iteratees`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 34 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'barney', 'age': 36 }\n * ];\n *\n * // Sort by `user` in ascending order and by `age` in descending order.\n * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n */\n function orderBy(collection, iteratees, orders, guard) {\n if (collection == null) {\n return [];\n }\n if (!isArray(iteratees)) {\n iteratees = iteratees == null ? [] : [iteratees];\n }\n orders = guard ? undefined : orders;\n if (!isArray(orders)) {\n orders = orders == null ? [] : [orders];\n }\n return baseOrderBy(collection, iteratees, orders);\n }\n\n /**\n * Creates an array of elements split into two groups, the first of which\n * contains elements `predicate` returns truthy for, the second of which\n * contains elements `predicate` returns falsey for. The predicate is\n * invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the array of grouped elements.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true },\n * { 'user': 'pebbles', 'age': 1, 'active': false }\n * ];\n *\n * _.partition(users, function(o) { return o.active; });\n * // => objects for [['fred'], ['barney', 'pebbles']]\n *\n * // The `_.matches` iteratee shorthand.\n * _.partition(users, { 'age': 1, 'active': false });\n * // => objects for [['pebbles'], ['barney', 'fred']]\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.partition(users, ['active', false]);\n * // => objects for [['barney', 'pebbles'], ['fred']]\n *\n * // The `_.property` iteratee shorthand.\n * _.partition(users, 'active');\n * // => objects for [['fred'], ['barney', 'pebbles']]\n */\n var partition = createAggregator(function(result, value, key) {\n result[key ? 0 : 1].push(value);\n }, function() { return [[], []]; });\n\n /**\n * Reduces `collection` to a value which is the accumulated result of running\n * each element in `collection` thru `iteratee`, where each successive\n * invocation is supplied the return value of the previous. If `accumulator`\n * is not given, the first element of `collection` is used as the initial\n * value. The iteratee is invoked with four arguments:\n * (accumulator, value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.reduce`, `_.reduceRight`, and `_.transform`.\n *\n * The guarded methods are:\n * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,\n * and `sortBy`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduceRight\n * @example\n *\n * _.reduce([1, 2], function(sum, n) {\n * return sum + n;\n * }, 0);\n * // => 3\n *\n * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n * (result[value] || (result[value] = [])).push(key);\n * return result;\n * }, {});\n * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)\n */\n function reduce(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduce : baseReduce,\n initAccum = arguments.length < 3;\n\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);\n }\n\n /**\n * This method is like `_.reduce` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduce\n * @example\n *\n * var array = [[0, 1], [2, 3], [4, 5]];\n *\n * _.reduceRight(array, function(flattened, other) {\n * return flattened.concat(other);\n * }, []);\n * // => [4, 5, 2, 3, 0, 1]\n */\n function reduceRight(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduceRight : baseReduce,\n initAccum = arguments.length < 3;\n\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);\n }\n\n /**\n * The opposite of `_.filter`; this method returns the elements of `collection`\n * that `predicate` does **not** return truthy for.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.filter\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true }\n * ];\n *\n * _.reject(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.reject(users, { 'age': 40, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.reject(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.reject(users, 'active');\n * // => objects for ['barney']\n */\n function reject(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, negate(getIteratee(predicate, 3)));\n }\n\n /**\n * Gets a random element from `collection`.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n * @example\n *\n * _.sample([1, 2, 3, 4]);\n * // => 2\n */\n function sample(collection) {\n var func = isArray(collection) ? arraySample : baseSample;\n return func(collection);\n }\n\n /**\n * Gets `n` random elements at unique keys from `collection` up to the\n * size of `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @param {number} [n=1] The number of elements to sample.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the random elements.\n * @example\n *\n * _.sampleSize([1, 2, 3], 2);\n * // => [3, 1]\n *\n * _.sampleSize([1, 2, 3], 4);\n * // => [2, 3, 1]\n */\n function sampleSize(collection, n, guard) {\n if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {\n n = 1;\n } else {\n n = toInteger(n);\n }\n var func = isArray(collection) ? arraySampleSize : baseSampleSize;\n return func(collection, n);\n }\n\n /**\n * Creates an array of shuffled values, using a version of the\n * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n * @example\n *\n * _.shuffle([1, 2, 3, 4]);\n * // => [4, 1, 3, 2]\n */\n function shuffle(collection) {\n var func = isArray(collection) ? arrayShuffle : baseShuffle;\n return func(collection);\n }\n\n /**\n * Gets the size of `collection` by returning its length for array-like\n * values or the number of own enumerable string keyed properties for objects.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @returns {number} Returns the collection size.\n * @example\n *\n * _.size([1, 2, 3]);\n * // => 3\n *\n * _.size({ 'a': 1, 'b': 2 });\n * // => 2\n *\n * _.size('pebbles');\n * // => 7\n */\n function size(collection) {\n if (collection == null) {\n return 0;\n }\n if (isArrayLike(collection)) {\n return isString(collection) ? stringSize(collection) : collection.length;\n }\n var tag = getTag(collection);\n if (tag == mapTag || tag == setTag) {\n return collection.size;\n }\n return baseKeys(collection).length;\n }\n\n /**\n * Checks if `predicate` returns truthy for **any** element of `collection`.\n * Iteration is stopped once `predicate` returns truthy. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n * @example\n *\n * _.some([null, 0, 'yes', false], Boolean);\n * // => true\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.some(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.some(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.some(users, 'active');\n * // => true\n */\n function some(collection, predicate, guard) {\n var func = isArray(collection) ? arraySome : baseSome;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Creates an array of elements, sorted in ascending order by the results of\n * running each element in a collection thru each iteratee. This method\n * performs a stable sort, that is, it preserves the original sort order of\n * equal elements. The iteratees are invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {...(Function|Function[])} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 30 },\n * { 'user': 'barney', 'age': 34 }\n * ];\n *\n * _.sortBy(users, [function(o) { return o.user; }]);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]\n *\n * _.sortBy(users, ['user', 'age']);\n * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]\n */\n var sortBy = baseRest(function(collection, iteratees) {\n if (collection == null) {\n return [];\n }\n var length = iteratees.length;\n if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {\n iteratees = [];\n } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {\n iteratees = [iteratees[0]];\n }\n return baseOrderBy(collection, baseFlatten(iteratees, 1), []);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\n var now = ctxNow || function() {\n return root.Date.now();\n };\n\n /*------------------------------------------------------------------------*/\n\n /**\n * The opposite of `_.before`; this method creates a function that invokes\n * `func` once it's called `n` or more times.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {number} n The number of calls before `func` is invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var saves = ['profile', 'settings'];\n *\n * var done = _.after(saves.length, function() {\n * console.log('done saving!');\n * });\n *\n * _.forEach(saves, function(type) {\n * asyncSave({ 'type': type, 'complete': done });\n * });\n * // => Logs 'done saving!' after the two async saves have completed.\n */\n function after(n, func) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function() {\n if (--n < 1) {\n return func.apply(this, arguments);\n }\n };\n }\n\n /**\n * Creates a function that invokes `func`, with up to `n` arguments,\n * ignoring any additional arguments.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @param {number} [n=func.length] The arity cap.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.ary(parseInt, 1));\n * // => [6, 8, 10]\n */\n function ary(func, n, guard) {\n n = guard ? undefined : n;\n n = (func && n == null) ? func.length : n;\n return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);\n }\n\n /**\n * Creates a function that invokes `func`, with the `this` binding and arguments\n * of the created function, while it's called less than `n` times. Subsequent\n * calls to the created function return the result of the last `func` invocation.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {number} n The number of calls at which `func` is no longer invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * jQuery(element).on('click', _.before(5, addContactToList));\n * // => Allows adding up to 4 contacts to the list.\n */\n function before(n, func) {\n var result;\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function() {\n if (--n > 0) {\n result = func.apply(this, arguments);\n }\n if (n <= 1) {\n func = undefined;\n }\n return result;\n };\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of `thisArg`\n * and `partials` prepended to the arguments it receives.\n *\n * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for partially applied arguments.\n *\n * **Note:** Unlike native `Function#bind`, this method doesn't set the \"length\"\n * property of bound functions.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to bind.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * function greet(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n *\n * var object = { 'user': 'fred' };\n *\n * var bound = _.bind(greet, object, 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bind(greet, object, _, '!');\n * bound('hi');\n * // => 'hi fred!'\n */\n var bind = baseRest(function(func, thisArg, partials) {\n var bitmask = WRAP_BIND_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bind));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(func, bitmask, thisArg, partials, holders);\n });\n\n /**\n * Creates a function that invokes the method at `object[key]` with `partials`\n * prepended to the arguments it receives.\n *\n * This method differs from `_.bind` by allowing bound functions to reference\n * methods that may be redefined or don't yet exist. See\n * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)\n * for more details.\n *\n * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Function\n * @param {Object} object The object to invoke the method on.\n * @param {string} key The key of the method.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * var object = {\n * 'user': 'fred',\n * 'greet': function(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n * };\n *\n * var bound = _.bindKey(object, 'greet', 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * object.greet = function(greeting, punctuation) {\n * return greeting + 'ya ' + this.user + punctuation;\n * };\n *\n * bound('!');\n * // => 'hiya fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bindKey(object, 'greet', _, '!');\n * bound('hi');\n * // => 'hiya fred!'\n */\n var bindKey = baseRest(function(object, key, partials) {\n var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bindKey));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(key, bitmask, object, partials, holders);\n });\n\n /**\n * Creates a function that accepts arguments of `func` and either invokes\n * `func` returning its result, if at least `arity` number of arguments have\n * been provided, or returns a function that accepts the remaining `func`\n * arguments, and so on. The arity of `func` may be specified if `func.length`\n * is not sufficient.\n *\n * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curry(abc);\n *\n * curried(1)(2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(1)(_, 3)(2);\n * // => [1, 2, 3]\n */\n function curry(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curry.placeholder;\n return result;\n }\n\n /**\n * This method is like `_.curry` except that arguments are applied to `func`\n * in the manner of `_.partialRight` instead of `_.partial`.\n *\n * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curryRight(abc);\n *\n * curried(3)(2)(1);\n * // => [1, 2, 3]\n *\n * curried(2, 3)(1);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(3)(1, _)(2);\n * // => [1, 2, 3]\n */\n function curryRight(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curryRight.placeholder;\n return result;\n }\n\n /**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\n function debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n timeWaiting = wait - timeSinceLastCall;\n\n return maxing\n ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)\n : timeWaiting;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n clearTimeout(timerId);\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n }\n\n /**\n * Defers invoking the `func` until the current call stack has cleared. Any\n * additional arguments are provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to defer.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.defer(function(text) {\n * console.log(text);\n * }, 'deferred');\n * // => Logs 'deferred' after one millisecond.\n */\n var defer = baseRest(function(func, args) {\n return baseDelay(func, 1, args);\n });\n\n /**\n * Invokes `func` after `wait` milliseconds. Any additional arguments are\n * provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.delay(function(text) {\n * console.log(text);\n * }, 1000, 'later');\n * // => Logs 'later' after one second.\n */\n var delay = baseRest(function(func, wait, args) {\n return baseDelay(func, toNumber(wait) || 0, args);\n });\n\n /**\n * Creates a function that invokes `func` with arguments reversed.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to flip arguments for.\n * @returns {Function} Returns the new flipped function.\n * @example\n *\n * var flipped = _.flip(function() {\n * return _.toArray(arguments);\n * });\n *\n * flipped('a', 'b', 'c', 'd');\n * // => ['d', 'c', 'b', 'a']\n */\n function flip(func) {\n return createWrap(func, WRAP_FLIP_FLAG);\n }\n\n /**\n * Creates a function that memoizes the result of `func`. If `resolver` is\n * provided, it determines the cache key for storing the result based on the\n * arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is used as the map cache key. The `func`\n * is invoked with the `this` binding of the memoized function.\n *\n * **Note:** The cache is exposed as the `cache` property on the memoized\n * function. Its creation may be customized by replacing the `_.memoize.Cache`\n * constructor with one whose instances implement the\n * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)\n * method interface of `clear`, `delete`, `get`, `has`, and `set`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to have its output memoized.\n * @param {Function} [resolver] The function to resolve the cache key.\n * @returns {Function} Returns the new memoized function.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n * var other = { 'c': 3, 'd': 4 };\n *\n * var values = _.memoize(_.values);\n * values(object);\n * // => [1, 2]\n *\n * values(other);\n * // => [3, 4]\n *\n * object.a = 2;\n * values(object);\n * // => [1, 2]\n *\n * // Modify the result cache.\n * values.cache.set(object, ['a', 'b']);\n * values(object);\n * // => ['a', 'b']\n *\n * // Replace `_.memoize.Cache`.\n * _.memoize.Cache = WeakMap;\n */\n function memoize(func, resolver) {\n if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var memoized = function() {\n var args = arguments,\n key = resolver ? resolver.apply(this, args) : args[0],\n cache = memoized.cache;\n\n if (cache.has(key)) {\n return cache.get(key);\n }\n var result = func.apply(this, args);\n memoized.cache = cache.set(key, result) || cache;\n return result;\n };\n memoized.cache = new (memoize.Cache || MapCache);\n return memoized;\n }\n\n // Expose `MapCache`.\n memoize.Cache = MapCache;\n\n /**\n * Creates a function that negates the result of the predicate `func`. The\n * `func` predicate is invoked with the `this` binding and arguments of the\n * created function.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} predicate The predicate to negate.\n * @returns {Function} Returns the new negated function.\n * @example\n *\n * function isEven(n) {\n * return n % 2 == 0;\n * }\n *\n * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));\n * // => [1, 3, 5]\n */\n function negate(predicate) {\n if (typeof predicate != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return function() {\n var args = arguments;\n switch (args.length) {\n case 0: return !predicate.call(this);\n case 1: return !predicate.call(this, args[0]);\n case 2: return !predicate.call(this, args[0], args[1]);\n case 3: return !predicate.call(this, args[0], args[1], args[2]);\n }\n return !predicate.apply(this, args);\n };\n }\n\n /**\n * Creates a function that is restricted to invoking `func` once. Repeat calls\n * to the function return the value of the first invocation. The `func` is\n * invoked with the `this` binding and arguments of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var initialize = _.once(createApplication);\n * initialize();\n * initialize();\n * // => `createApplication` is invoked once\n */\n function once(func) {\n return before(2, func);\n }\n\n /**\n * Creates a function that invokes `func` with its arguments transformed.\n *\n * @static\n * @since 4.0.0\n * @memberOf _\n * @category Function\n * @param {Function} func The function to wrap.\n * @param {...(Function|Function[])} [transforms=[_.identity]]\n * The argument transforms.\n * @returns {Function} Returns the new function.\n * @example\n *\n * function doubled(n) {\n * return n * 2;\n * }\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var func = _.overArgs(function(x, y) {\n * return [x, y];\n * }, [square, doubled]);\n *\n * func(9, 3);\n * // => [81, 6]\n *\n * func(10, 5);\n * // => [100, 10]\n */\n var overArgs = castRest(function(func, transforms) {\n transforms = (transforms.length == 1 && isArray(transforms[0]))\n ? arrayMap(transforms[0], baseUnary(getIteratee()))\n : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));\n\n var funcsLength = transforms.length;\n return baseRest(function(args) {\n var index = -1,\n length = nativeMin(args.length, funcsLength);\n\n while (++index < length) {\n args[index] = transforms[index].call(this, args[index]);\n }\n return apply(func, this, args);\n });\n });\n\n /**\n * Creates a function that invokes `func` with `partials` prepended to the\n * arguments it receives. This method is like `_.bind` except it does **not**\n * alter the `this` binding.\n *\n * The `_.partial.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 0.2.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var sayHelloTo = _.partial(greet, 'hello');\n * sayHelloTo('fred');\n * // => 'hello fred'\n *\n * // Partially applied with placeholders.\n * var greetFred = _.partial(greet, _, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n */\n var partial = baseRest(function(func, partials) {\n var holders = replaceHolders(partials, getHolder(partial));\n return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);\n });\n\n /**\n * This method is like `_.partial` except that partially applied arguments\n * are appended to the arguments it receives.\n *\n * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var greetFred = _.partialRight(greet, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n *\n * // Partially applied with placeholders.\n * var sayHelloTo = _.partialRight(greet, 'hello', _);\n * sayHelloTo('fred');\n * // => 'hello fred'\n */\n var partialRight = baseRest(function(func, partials) {\n var holders = replaceHolders(partials, getHolder(partialRight));\n return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);\n });\n\n /**\n * Creates a function that invokes `func` with arguments arranged according\n * to the specified `indexes` where the argument value at the first index is\n * provided as the first argument, the argument value at the second index is\n * provided as the second argument, and so on.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to rearrange arguments for.\n * @param {...(number|number[])} indexes The arranged argument indexes.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var rearged = _.rearg(function(a, b, c) {\n * return [a, b, c];\n * }, [2, 0, 1]);\n *\n * rearged('b', 'c', 'a')\n * // => ['a', 'b', 'c']\n */\n var rearg = flatRest(function(func, indexes) {\n return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);\n });\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * created function and arguments from `start` and beyond provided as\n * an array.\n *\n * **Note:** This method is based on the\n * [rest parameter](https://mdn.io/rest_parameters).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.rest(function(what, names) {\n * return what + ' ' + _.initial(names).join(', ') +\n * (_.size(names) > 1 ? ', & ' : '') + _.last(names);\n * });\n *\n * say('hello', 'fred', 'barney', 'pebbles');\n * // => 'hello fred, barney, & pebbles'\n */\n function rest(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start === undefined ? start : toInteger(start);\n return baseRest(func, start);\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * create function and an array of arguments much like\n * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).\n *\n * **Note:** This method is based on the\n * [spread operator](https://mdn.io/spread_operator).\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Function\n * @param {Function} func The function to spread arguments over.\n * @param {number} [start=0] The start position of the spread.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.spread(function(who, what) {\n * return who + ' says ' + what;\n * });\n *\n * say(['fred', 'hello']);\n * // => 'fred says hello'\n *\n * var numbers = Promise.all([\n * Promise.resolve(40),\n * Promise.resolve(36)\n * ]);\n *\n * numbers.then(_.spread(function(x, y) {\n * return x + y;\n * }));\n * // => a Promise of 76\n */\n function spread(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start == null ? 0 : nativeMax(toInteger(start), 0);\n return baseRest(function(args) {\n var array = args[start],\n otherArgs = castSlice(args, 0, start);\n\n if (array) {\n arrayPush(otherArgs, array);\n }\n return apply(func, this, otherArgs);\n });\n }\n\n /**\n * Creates a throttled function that only invokes `func` at most once per\n * every `wait` milliseconds. The throttled function comes with a `cancel`\n * method to cancel delayed `func` invocations and a `flush` method to\n * immediately invoke them. Provide `options` to indicate whether `func`\n * should be invoked on the leading and/or trailing edge of the `wait`\n * timeout. The `func` is invoked with the last arguments provided to the\n * throttled function. Subsequent calls to the throttled function return the\n * result of the last `func` invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the throttled function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.throttle` and `_.debounce`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to throttle.\n * @param {number} [wait=0] The number of milliseconds to throttle invocations to.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=true]\n * Specify invoking on the leading edge of the timeout.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new throttled function.\n * @example\n *\n * // Avoid excessively updating the position while scrolling.\n * jQuery(window).on('scroll', _.throttle(updatePosition, 100));\n *\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });\n * jQuery(element).on('click', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * jQuery(window).on('popstate', throttled.cancel);\n */\n function throttle(func, wait, options) {\n var leading = true,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (isObject(options)) {\n leading = 'leading' in options ? !!options.leading : leading;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n return debounce(func, wait, {\n 'leading': leading,\n 'maxWait': wait,\n 'trailing': trailing\n });\n }\n\n /**\n * Creates a function that accepts up to one argument, ignoring any\n * additional arguments.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.unary(parseInt));\n * // => [6, 8, 10]\n */\n function unary(func) {\n return ary(func, 1);\n }\n\n /**\n * Creates a function that provides `value` to `wrapper` as its first\n * argument. Any additional arguments provided to the function are appended\n * to those provided to the `wrapper`. The wrapper is invoked with the `this`\n * binding of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {*} value The value to wrap.\n * @param {Function} [wrapper=identity] The wrapper function.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var p = _.wrap(_.escape, function(func, text) {\n * return '