{"version":3,"file":"js/chunks/util.js","sources":["webpack:///./org_colony/cartridge/js/util.js"],"sourcesContent":["const forEach = require('lodash.foreach');\nconst isString = require('lodash.isstring');\n\nconst smallBreakpoint = 320;\nconst mediumBreakpoint = 480;\nconst largeBreakpoint = 768;\nconst desktopBreakpoint = 1025;\nconst maxBreakpoint = 1280;\nconst mobileMenuBreakpoint = desktopBreakpoint;\n\nconst util = {\n /**\n * @desc Media breakpoints that are used throughout the Javascript\n */\n breakpoints: {\n xs: smallBreakpoint,\n sm: mediumBreakpoint,\n md: largeBreakpoint,\n lg: desktopBreakpoint,\n xl: maxBreakpoint,\n 'mobile-menu': mobileMenuBreakpoint,\n },\n\n /**\n * @function\n * @description Returns either an object with all of the available breakpoints or a specific viewport based on the given size\n * @param {string} size The viewport to return\n * @param {string} breakpoints A custom breakpoints object\n */\n getViewports(size, breakpoints) {\n const bps = typeof breakpoints !== 'undefined' ? breakpoints : this.breakpoints;\n\n if (typeof size !== 'undefined') {\n if (bps[size]) {\n return bps[size];\n }\n window.console.error('Unexpected viewport size given in util.getViewports');\n throw new Error('Unexpected viewport size given in util.getViewports');\n } else {\n return breakpoints;\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() {\n const w = window.innerWidth;\n const viewports = util.getViewports();\n let viewportName = 'max';\n // traverse the object from small up to desktop, and return the first match\n forEach(viewports, (value, name) => {\n if (w <= value) {\n viewportName = name;\n return false;\n }\n return true;\n });\n return viewportName;\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(url, name, value) {\n // quit if the param already exists\n if (url.indexOf(`${name}=`) !== -1) {\n return url;\n }\n const 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(url, name) {\n if (url.indexOf('?') === -1 || url.indexOf(`${name}=`) === -1) {\n return url;\n }\n const [domain, query] = url.split('?');\n const newParams = [];\n let paramUrl = query;\n let hash = null;\n\n // if there is a hash at the end, store the hash\n if (query.indexOf('#') > -1) {\n [paramUrl, hash] = query.split('#');\n }\n const params = paramUrl.split('&');\n for (let i = 0; i < params.length; i += 1) {\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(url, params) {\n let newUrl = url;\n forEach(params, (value, name) => {\n newUrl = this.appendParamToURL(newUrl, name, value);\n });\n return newUrl;\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(url) {\n let qs;\n if (!isString(url)) { return null; }\n const 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(el, offsetToTop) {\n const width = el.offsetWidth;\n const height = el.offsetHeight;\n let top = el.offsetTop;\n let left = el.offsetLeft;\n let currentElement = el;\n\n while (currentElement.offsetParent) {\n currentElement = currentElement.offsetParent;\n top += currentElement.offsetTop;\n left += currentElement.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 return false;\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(path) {\n return this.appendParamToURL(path, 'format', 'ajax');\n },\n\n /**\n * @function\n * @description\n * @param {String} url\n */\n toAbsoluteUrl(url) {\n return url.indexOf('http') !== 0 && url.charAt(0) !== '/' ? `/${url}` : 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(urls) {\n const len = urls.length;\n for (let i = 0; i < len; i += 1) {\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(url) {\n return $('<link/>').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() {\n const len = this.loadedCssFiles.length;\n for (let i = 0; i < len; i += 1) {\n $(this.loadedCssFiles[i]).remove();\n }\n this.loadedCssFiles = [];\n },\n /**\n * @function\n * @description Loads js dynamically from given urls\n * @param {Array} urls Array of urls from which js will be dynamically loaded.\n */\n loadDynamicJs(urls) {\n const len = urls.length;\n for (let i = 0; i < len; i += 1) {\n this.loadedJsFiles.push(this.loadJsFile(urls[i]));\n }\n },\n\n /**\n * @function\n * @description Loads js file dynamically from given url\n * @param {String} url The url from which js file will be dynamically loaded.\n */\n loadJsFile(url) {\n return $('<script/>').appendTo($('body')).attr({\n type: 'text/javascript',\n }).attr('src', url);\n },\n // array to keep track of the dynamically loaded JS files\n loadedJsFiles: [],\n\n /**\n * @function\n * @description Removes all js files which were dynamically loaded\n */\n clearDynamicJs() {\n const len = this.loadedJsFiles.length;\n for (let i = 0; i < len; i += 1) {\n $(this.loadedJsFiles[i]).remove();\n }\n this.loadedJsFiles = [];\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(qs) {\n if (!qs || qs.length === 0) { return {}; }\n const url = qs.toString();\n const params = {};\n\n // Use the String::replace method to iterate over each\n // name-value pair in the string.\n url.replace(\n /([^?=&]+)(=([^&]*))?/g,\n ($0, $1, $2, $3) => {\n params[$1] = decodeURIComponent($3);\n },\n );\n return params;\n },\n\n fillAddressFields(address, $form) {\n forEach(address, (value, field) => {\n if (field !== 'ID' && field !== 'UUID' && field !== 'key') {\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\n fillAddressDisplay(address, $container) {\n forEach(address, (value, field) => {\n if (field !== 'ID' && field !== 'UUID' && field !== 'key') {\n $container.find(`.${field}`).text(address[field]);\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() {\n $('form').find('textarea[data-character-limit], input[data-character-limit]').each((index, element) => {\n const characterLimit = $(element).data('character-limit');\n const charCountHtml = String.format(\n Resources.CHAR_LIMIT_MSG,\n `<span class=\"char-remain-count\">${characterLimit}</span>`,\n );\n let charCountContainer = $(element).next('div.char-count');\n if (charCountContainer.length === 0) {\n charCountContainer = $('<div class=\"char-count\"/>').insertAfter($(element));\n }\n charCountContainer.html(charCountHtml);\n // trigger the keydown event so that any existing character data is calculated\n $(element).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(container, message) {\n $(container).on('click', '.delete', () => window.confirm(message));\n },\n /**\n * @function\n * @description Scrolls a browser window to a given x point\n * @param {String} The x coordinate\n */\n scrollBrowser(xLocation, callback) {\n $('html, body').animate({scrollTop: xLocation}, 500, callback);\n },\n\n /**\n * @function\n * @desc Determines if the device that is being used is mobile\n * @returns {Boolean}\n */\n isMobile() {\n const mobileAgentHash = ['mobile', 'tablet', 'phone', 'ipad', 'ipod', 'android', 'blackberry', 'windows ce', 'opera mini', 'palm'];\n let idx = 0;\n let isMobile = false;\n const userAgent = (navigator.userAgent).toLowerCase();\n\n while (mobileAgentHash[idx] && !isMobile) {\n isMobile = (userAgent.indexOf(mobileAgentHash[idx]) >= 0);\n idx += 1;\n }\n return isMobile;\n },\n\n /**\n * Executes a callback function when the user has stopped resizing the screen.\n *\n * @param {function} callback\n * @var obj timeout\n *\n * @return {function}\n */\n smartResize(callback) {\n let timeout;\n\n $(window).on('resize', () => {\n clearTimeout(timeout);\n timeout = setTimeout(callback, 100);\n }).resize();\n\n return callback;\n },\n\n /**\n * @function\n * @desc Generates a min-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {object} breakpoints - Override of the util breakpoints (optional)\n */\n mediaBreakpointUp(size, breakpoints) {\n const breakpoint = this.getViewports(size, breakpoints);\n const mediaQuery = window.matchMedia(`(min-width: ${breakpoint}px)`);\n return mediaQuery.matches;\n },\n\n /**\n * @function\n * @desc Generates a min-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {object} breakpoints - Override of the util breakpoints object (optional)\n */\n mediaBreakpointDown(size, breakpoints) {\n const bps = typeof breakpoints !== 'undefined' ? breakpoints : this.breakpoints;\n const nextSize = this.getNextObjectKey(bps, size);\n\n if (typeof nextSize === 'string') {\n const breakpoint = this.getViewports(nextSize, breakpoints) - 1;\n const mediaQuery = window.matchMedia(`(max-width: ${breakpoint}px)`);\n return mediaQuery.matches;\n }\n return true;\n },\n\n /**\n * @function\n * @desc Generates a min-width and max-width matchMedia media queries based on the given params\n * @param {string} sizeMin - Min breakpoint to use for the media query\n * @param {string} sizeMax - Max breakpoint to use for the media query\n * @param {object} breakpoints - Override of the util breakpoints object (optional)\n */\n mediaBreakpointBetween(sizeMin, sizeMax, breakpoints) {\n const min = this.mediaBreakpointUp(sizeMin, breakpoints);\n const max = this.mediaBreakpointDown(sizeMax, breakpoints);\n\n return min && max;\n },\n\n /**\n * @function\n * @desc Generates a min-width and max-width matchMedia media query based on the given params\n * @param {string} size - Breakpoint to use for the media query\n * @param {object} breakpoints - Override of the util breakpoints object (optional)\n */\n mediaBreakpointOnly(size, breakpoints) {\n return this.mediaBreakpointBetween(size, size, breakpoints);\n },\n\n /**\n * @function\n * @desc Retrieves the next key in the object or null if it doesn't exist\n * @returns {string}|{null}\n */\n getNextObjectKey(obj, key) {\n const keys = Object.keys(obj);\n const nextIndex = keys.indexOf(key) + 1;\n\n if (keys.length > nextIndex) {\n return keys[nextIndex];\n }\n return null;\n },\n\n /**\n * @function\n * @desc Retrieves the util breakpoints object\n * @returns {object}\n */\n getBreakpoints() {\n return this.breakpoints;\n },\n\n /**\n * @function\n * @description Generate svg icon\n * @param icon {String} svg icon name\n * @returns {String} SVG tag\n */\n svg(icon, extraClasses) {\n const classes = extraClasses || '';\n return `<svg class=\"icon ${icon} ${classes} svg-${icon}-dims\"><use xlink:href=\"#${icon}\"/></svg>`;\n },\n\n backToTop() {\n const bttScrollTriggerDesktop = window.SitePreferences.BTT_DESKTOP;\n const bttScrollTriggerMobile = window.SitePreferences.BTT_MOBILE;\n const scrollTop = $(window).scrollTop();\n const desktopQuery = matchMedia('(min-width: 768px)');\n let bttScrollTrigger;\n\n if (desktopQuery.matches) {\n bttScrollTrigger = bttScrollTriggerDesktop;\n } else {\n bttScrollTrigger = bttScrollTriggerMobile;\n }\n\n if (scrollTop > bttScrollTrigger) {\n $('.back-to-top').addClass('show');\n } else {\n $('.back-to-top').removeClass('show');\n }\n },\n\n /**\n * @function\n * @description Sets checkbox values and aria attributes\n * @param {String} input\n */\n setCheckboxValues(input) {\n const $hiddenInput = $(input).parents('.checkbox').find('.input-checkbox');\n if ($hiddenInput.is(':checked')) {\n $hiddenInput.val(false).attr('aria-checked', 'false');\n } else {\n $hiddenInput.val(true).attr('aria-checked', 'true');\n }\n },\n\n /**\n * @function\n * @description Set's a mutation observer on the einstein product carousel to know when it can init\n * @param {String} carouselContainerSelector\n * @param {String} tileContainerSelector\n * @param {Number} specificSlotSelector\n * @param {Number} numberOfSlides\n * @example util.initDynamicCarousel('Dom-Selector', 'Dom-Selector', jQeury Object Index, Number of slides);\n * */\n initDynamicCarousel(carouselContainerSelector, tileContainerSelector, specificSlotSelector, numberOfSlides) {\n // Pass in param that defines which slot on the page we trying to init if there is only one on the page pass in 0\n const specificSlot = specificSlotSelector || 0;\n // Grab the defined tile recommendation tile container\n const carouselContainer = $(carouselContainerSelector).get(specificSlot);\n // If we don't get a result for any reason return us out\n if (!carouselContainer) {\n return;\n }\n // Mutation observer config\n const carouselObserverConfig = {childList: true, subtree: true};\n // Define mutation observer action too take\n const dynamicCarouselObserver = new MutationObserver(() => {\n try {\n const $tileContainer = $(carouselContainer).find(tileContainerSelector);\n $tileContainer.not('.slick-initialized').slick({\n speed: SitePreferences.SLIDE_SHOW_SPEED,\n dots: false,\n slide: '.grid-tile',\n arrows: true,\n slidesToShow: numberOfSlides || 4,\n slidesToScroll: 1,\n infinite: false,\n responsive: [\n {\n breakpoint: util.getViewports('lg'),\n settings: {\n slidesToShow: 3,\n slidesToScroll: 1,\n },\n },\n {\n breakpoint: util.getViewports('md'),\n settings: {\n slidesToShow: 1,\n slidesToScroll: 1,\n },\n },\n ],\n });\n // This try catch will catch errors with slick as well as if there are not enough slides to fully init the carousel\n } catch (e) {\n console.log(e);\n }\n });\n // Start observing container\n dynamicCarouselObserver.observe(carouselContainer, carouselObserverConfig);\n },\n};\n\nmodule.exports = util;\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAMA;AAEA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;A","sourceRoot":""}