Source: helpers/utils.js

/**
 * Utils module
 * @module Utils
 */

import jQ from 'jquery';
import Settings from './settings';
import Globals from './globals';
import InstantSearchUtils from "./utils/instant-search-utils";
import FilterTreeUtils from "./utils/filter-tree-utils";
import FilterResultUtils from "./utils/filter-result-utils";

// Get the suitable size of image to optimize the speed of page load
const optimizeImage = (imageUrl, size) => {
	// If url is null, get default image
	if (imageUrl === null) imageUrl = boostPFSConfig.general.no_image_url;
	// Get the suitable size
	var size = typeof size !== 'undefined' ? size : 'large';
	var ext = Settings.getSettingValue('general.imageExtension');
	for (var k = 0; k < ext.length; k++) {
		imageUrl = imageUrl.replace('.' + ext[k] + '?', '_' + size + '.' + ext[k] + '?');
	}
	return imageUrl;
};

// Get the first image of a product
// If null, return default image
const getFeaturedImage = (images, size) => {
	var size = typeof size !== 'undefined' ? size : 'large';
	var featuredImage = optimizeImage(boostPFSConfig.general.no_image_url, size);
	if (images.length > 0) {
		featuredImage = typeof images[0] === 'object' ? optimizeImage(images[0]['src'], size) : optimizeImage(images[0], size);
	}
	return featuredImage;
};

/* -- Start Money Utils -- */

/**
 * Format money based on setting in Shopify
 * @param {String} money The money number
 * @param {String} [format] The format string of Money: <br />	
 * 	- {{ amount }}: $1,999.99<br />
 * 	- {{ amount_no_decimals }}: $1,999<br />
 * 	- {{ amount_with_comma_separator }}: 1.999,99&#8363;<br />
 * 	- {{ amount_no_decimals_with_comma_separator }}: 1.999&#8363;<br />
 * 	- {{ amount_with_space_separator_no_comma }}: $1,999.99
 * @param {Boolean} [withoutTrailingZeros] - Disable decimals number if it is zero
 */
const formatMoney = (money, format, withoutTrailingZeros) => {
	if (typeof format == 'undefined') var format = Globals.moneyFormat;
	if (typeof withoutTrailingZeros == 'undefined') var withoutTrailingZeros = false;
	// if (typeof Shopify.formatMoney === 'function') { return Shopify.formatMoney(money, format); }
	if (typeof money == 'string') { money = money.replace('.', ''); }
	var value = '';
	var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
	var formatString = format || '${{amount}}';
	function defaultOption(opt, def) {
		return (typeof opt == 'undefined' ? def : opt);
	}
	function formatWithDelimiters(number, precision, thousands, decimal) {
		precision = defaultOption(precision, 2);
		thousands = defaultOption(thousands, ',');
		decimal   = defaultOption(decimal, '.');
		if (isNaN(number) || number == null) { return 0; }
		number = parseFloat(number).toFixed(precision);
		var parts   = number.split('.');
		var dollars = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousands);
		var money   = parts[1] ? (decimal + parts[1]) : '';
		return withoutTrailingZeros == true ? (dollars + money).replace(/((\,00)|(\.00))$/g, '') : dollars + money;
	}
	switch(formatString.match(placeholderRegex)[1]) {
		case 'amount':
			value = formatWithDelimiters(money, 2);
			break;
		case 'amount_no_decimals':
			value = formatWithDelimiters(money, 0); 
			break;
		case 'amount_with_comma_separator':
			value = formatWithDelimiters(money, 2, '.', ',');
			break;
		case 'amount_no_decimals_with_comma_separator':
			value = formatWithDelimiters(money, 0, '.', ',');
			break;
		case 'amount_with_space_separator_no_comma':
			value = formatWithDelimiters(money, 2);
			break;
		default:
			value = formatWithDelimiters(money, 2);
			break;
	}
	formatString = formatString.replace(placeholderRegex, value);

	return moneyWrapper(formatString);
};

const moneyWrapper = (money) => {
	var htmlTemplate = '<span class="money">{{money}}</span>';
	return htmlTemplate.replace(/{{money}}/g, stripHtml(money));
}

/**
 * Format a number separator
 * @param {Number} number - Number to Format
 * @param {Number} precision - Integer.
 * @param {string} thousand - Thousand separator
 * @param {string} decimal - Decimal separator
 * @param {boolean} withoutTrailingZeros - Removes .00 or .0
 * @return {string}
 */
const formatNumberWithSeparator = (number, precision, thousand, decimal, withoutTrailingZeros) => {
	if (isNaN(number)) number = 0;
	if (isNaN(precision)) precision = 0;
	if (!decimal) {
		if (thousand == '.') {
			decimal = ',';
		} else {
			decimal = '.';
		}
	}

	number = parseFloat(number).toFixed(precision);

	var parts   = number.toString().split('.');
	var beforeDecimalString = parts[0];
	var afterDecimalString = parts[1] ? parts[1] : '';

	if (thousand) {
		beforeDecimalString = beforeDecimalString.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousand);
	}

	if (decimal && afterDecimalString) {
		if (withoutTrailingZeros && /0+/.test(afterDecimalString)) {
			afterDecimalString = '';
		} else {
			afterDecimalString = decimal + afterDecimalString;
		}
	}

	return beforeDecimalString + afterDecimalString;
}

/**
 * Get currency symbol of store
 * @example
 * Utils.getCurrency('<span class="money">${{amount}}</span>');
 * // return '$'
 */
const getCurrency = () => {
    var moneyFormat = jQ('<p>' + boostPFSConfig.shop.money_format + '</p>').text();
    return moneyFormat.replace(/{{[^}]*}}/g, '');
};

/**
 * Remove currency symbol from price
 * @param {String} price The price string
 * @returns {String} The price string without currency
 */
const removeCurrencySymbol = (price) => {
    // Remove html from price
    price = jQ('<p>' + price + '</p>').text();
    // Remove currency from price
    // return price.replace(/[^\d\.\,]/g, '');
    var currencyElements = getCurrency().split(' ');
    for (var k = 0; k < currencyElements.length; k++) {
        price = price.replace(currencyElements[k].trim(), '');
    }
    return price.trim();
};

/**
 * Get Range of rounding
 * @param {Boolean} isMax TRUE if want to return the max value or range
 */
const getRoundingRange = (isMax) => {
	if (typeof isMax == 'undefined') isMax = false;
	// Get the rounding rule
	var roundRuleList = [0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 1, 25, 50, 75, 90, 95, 99, 100, 250, 500, 750, 900, 950, 999, 1000];
	var currentCurrency = boostPFSAppConfig.general.current_currency.toLowerCase().trim();
	var roundRules = Settings.getSettingValue('currencyRoundingRules');
	var roundRule = roundRules && currentCurrency && roundRules.hasOwnProperty(currentCurrency) ? parseFloat(roundRules[currentCurrency]) : 0;
	var mulNum = false;

	if (roundRule > 0 && jQ.inArray(roundRule, roundRuleList) !== -1) {
		var mulNum = 0.99;
		if (roundRule > 100) {
			mulNum = 999;
		} else if (roundRule > 10) {
			mulNum = 99;
		} else if (roundRule > 1) {
			mulNum = 9;
		}
		if (isMax) {
			mulNum = roundRule > 1 ? mulNum + 1: mulNum + 0.01;
		}
	}
	return mulNum;
}

/**
 * Apply shopify rounding rule to product pice
 * @param {Number} price the product price
 */
const roundedPrice = (price) => {
	price = parseFloat(price).toFixed(2);
	// Get round rule
	var currentCurrency = boostPFSAppConfig.general.current_currency.toLowerCase().trim();
	var roundRules = Settings.getSettingValue('currencyRoundingRules');
	var roundRule = roundRules && currentCurrency && roundRules.hasOwnProperty(currentCurrency) ? roundRules[currentCurrency] : 0;
	// Get the max rounding rule (1, 100, 1000)
	var maxRounding = getRoundingRange(true);
	// Rounded the price
	if (maxRounding) {
		// Convert the price to useing decimal rounding: [xxx JPY] / 100, [xx.xxx VND] / 1000
		var roundRuleTmp = parseFloat(roundRule);
		roundRuleTmp = roundRuleTmp / maxRounding;
		price = price / maxRounding;
		// Rounded the price
		if (roundRuleTmp == 1) roundRuleTmp = 0;
		var priceInt = Math.floor(price);
		var priceDeci = (price - priceInt).toFixed(2);
		price = (priceDeci > roundRuleTmp) ? priceInt + 1 : priceInt
		price = price * maxRounding;
		// Fixed round rule
		if (roundRuleTmp == 0) roundRule = 0;
		price = price + parseFloat(roundRule);
	}
	return price;
}

/**
 * Convert price to the active currency
 * @param {Number} price - The product price
 * @param {Boolean} isRounded - TRUE if want to apply rounding rule
 */
const convertPriceBasedOnActiveCurrency = (price, isRounded) => {
	if (typeof isRounded == 'undefined') isRounded = true;
	if (price === null) return price;
	if (isEnableShopifyMultipleCurrencies() === true) {
		var convertPrice = price * Shopify.currency.rate;
		if (isRounded) {
			price = roundedPrice(convertPrice);
		} else {
			price = convertPrice;
		}
	}
	return parseFloat(price);
}

/**
 * Revert price to the default currency
 * @param {Number} price - The product price
 * @param {Boolean} isMin - TRUE if revert price based on the min-range of rounding
 */
const revertPriceToDefaultCurrency = (price, isMin) => {
	if(isEnableShopifyMultipleCurrencies() === true) {
		price = roundedPrice(price);
		if (isMin) {
			var minRound = getRoundingRange();
			if (minRound) {
				price = price - minRound;
			}
		}
		price = price / Shopify.currency.rate;
		return price.toFixed(8);
	}
	return price;
}

/* -- End Money Utils -- */

var windowWidth = null;
/**
 * Return true if the screen-width lesser then mobile break-point
 */
const isMobile = () => {
	if (!windowWidth) {
		// We don't want to re-calculate the width every time because it causes a style recalculation everytime.
		// So we store it
		windowWidth = jQ(window).width();

		// Calculate the width on resize
		jQ(window).on('resize', () => {
			windowWidth = jQ(window).width();
		})
	}
	return windowWidth <= Settings.getSettingValue('general.breakpointMobile');
};

/**
 * Check if the devivce is Mobile Device or not
 */
const isMobileDevice = () => {
	return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
};

/**
 * Check if a device is iOS or not
 */
const isiOS = () => {
	return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
};

/**
 * Check if a browser is Safari or not
 */
const isSafari = () => {
	return /Safari/.test(navigator.userAgent);
};

/**
 * Check if page is loaded by back button
 */
const isBackButton = () => {
	// NOTE: Doesn't work on iOS
	return window.performance && window.performance.navigation && window.performance.navigation.type == 2;
};

/**
 * Check whether the Boost params exist
 */
const checkExistBCParam = () => {
	return Globals.queryParams.hasOwnProperty('_') && Globals.queryParams['_'] == Globals.prefix;
};

/**
 * Chech whether collection param exist
 * @param {String} type Type of filter option
 */
const isCollectionParam = (type) => {
	if (type != 'collection' || (type == 'collection' && isSearchPage())) {
		return false;
	}
	return true;
};

/**
 * Check if the current page is Vendor page
 */
const isVendorPage = () => {
	return window.location.pathname.indexOf('/collections/vendors') > -1 ? true : false;
};

/**
 * Check if the current page is Type page
 */
const isTypePage = () => {
	return window.location.pathname.indexOf('/collections/types') > -1 ? true : false;
};

/**
 * Check if the current page is Tag page
 */
const isTagPage = () => {
	if (typeof Globals.currentTags !== 'undefined' && Globals.currentTags !== null && Globals.currentTags.length > 0) return true;
	return false;
};

/**
 * Check if the current page is Search page
 */
const isSearchPage = () => {
	// An URL of Google cache will look like this: webcache.googleusercontent.com/search?q=cache:xxx:https://xxx.xxx/search?xxx
	// Google cache URL
	if (Utils.getWindowLocation().href.includes('webcache.googleusercontent.com')) {
		return Utils.getWindowLocation().search.indexOf('search?') > -1;

	// Normal URL
	} else {
		return window.location.pathname.indexOf('/search') > -1;
	}
};

/**
 * Check if the current page is Cart page
 */
const isCartPage = () => {
	return window.location.pathname.indexOf('/cart') > -1;
};

/**
 * Check if the current page is Product page
 */
const isProductPage = () => {
	return window.location.pathname.indexOf('/products') > -1;
};

/**
 * Get the current search term
 */
const getSearchTerm = () => {
    return getParam(Globals.searchTermKey);
};

/**
 * Remove decimals from a number
 * @param {(String|Number)} value The number value
 * @param {String} delimiter Delimiter symbol
 */
const removeDecimal = (value, delimiter) => {
	var delimiter = typeof delimiter !== 'undefined' ? delimiter : Settings.getSettingValue('general.decimalDelimiter');
	var reg = new RegExp("(\\" + delimiter + "\\d+)+", "gi");
	return value.replace(reg, '');
};

/**
 * Slugify a string (convert a string to slug)
 * @param {String} text The string that need to slugify
 */
const slugify = (text) => {
	if (text == null || typeof text == 'object') return '';
	if (typeof text != 'string') {
		if (typeof text.toString != 'function'){
			return '';
		}
		else {
			text = text.toString();
		}
	}
	// return text.toString().toLowerCase()
	//     .replace(/[\s\/]+/g, '-')    // Replace spaces with -
	//     .replace(/[^\w\-]+/g, '')   // Remove all non-word chars
	//     .replace(/\-\-+/g, '-')     // Replace multiple - with single -
	//     .replace(/^-+/, '')         // Trim - from start of text
	//     .replace(/-+$/, '')         // Trim - from end of text
	text = text.toLowerCase();

	// Remove Latin
	var from = "àáäâãèéëêẽìíïîĩòóöôõùúüûũñç·/_,:;";
	var to = "aaaaaeeeeeiiiiiooooouuuuunc--_---";
	for (var i = 0, l = from.length; i < l; i++)
		text = text.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));

	// Remove Czech, Finnish special characters
	var specialCzechChars = 'ÁáÄäČčĎďÉéěÍíŇňÓóÖöŘřŠšŤťÚúůÝýŽž';
	var asciiCzechChars = 'AaAaCcDdEeeIiNnOoOoRrSsTtUuuYyZz';
	var specialCzechCharsLength = specialCzechChars.length;
	for (var j = 0; j < specialCzechCharsLength; j++) {
		text = text.replace(new RegExp(specialCzechChars.charAt(j), "g"), asciiCzechChars.charAt(j));
	}

	// Remove Norway special characters
	var specialNorwayChars = 'ÆæØøÅå';
	var asciiNorwayChars = ['AE', 'ae', 'O', 'o', 'A', 'a'];
	var specialNorwayCharsLength = specialNorwayChars.length;
	for (var k = 0; k < specialNorwayCharsLength; k++) {
		text = text.replace(new RegExp(specialNorwayChars.charAt(k), "g"), asciiNorwayChars[k]);
	}

	// Remove quotes
	text = text.replace(/'/g, '').replace(/"/g, '');

	return text.replace(/[\s\/]+/g, '-')    // Replace spaces with -
		.replace(/[`~!@#$%^&*()|+\-=?;:'",.<>\{\}\[\]\\\/]/g, '-')   // Replace all special characters with -
		.replace(/\-\-+/g, '-')     // Replace multiple - with single -
		.replace(/^-+/, '')         // Trim - from start of text
		.replace(/-+$/, '');         // Trim - from end of text
};

/**
 * Convert a slug to string
 * @param {String} slug The slug string
 * @param {String} divide The divide character
 */
const textify = (slug, divide) => {
	var divide = typeof divide !== 'undefined' ? divide : '-';
	var arr = slug.split(divide);
	var text = '';
	for (var k = 0; k < arr.length; k++) {
		text += arr[k].charAt(0).toUpperCase() + arr[k].slice(1);
		if (k < arr.length - 1) {
			text += ' ';
		}
	}
	return text;
};


/**
 * Escape a HTML string to displayed it
 * @param {String} string A string that contains HTML element.
 * @param {Boolean} preserveCR Set true if the end of line is carriage returns
 */
const escape = (string, preserveCR) => {
	preserveCR = preserveCR ? '&#13;' : '\n';
	return ('' + string) /* Forces the conversion to string. */
		.replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */
		.replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */
		.replace(/"/g, '&quot;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
        /*
        You may add other replacements here for HTML only
        (but it's not necessary).
        Or for XML, only if the named entities are defined in its DTD.
        */
		.replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
		.replace(/[\r\n]/g, preserveCR);
};

/**
 * Convert a object to array
 * @param {Object} obj The Object that needs to convert
 */
const convertObjectToArray = (obj) => {
	return Object.keys(obj).map(function (key) { return obj[key]; });
}

/**
 * Sort array of object by field
 * @param {Array} arr The array that needs to sort
 * @param {*} field The field of array which will be sorted by
 */
const sortArrayObject = (arr, field) => {
	if (typeof field !== 'undefined') {
		arr.sort(function (a, b) {
			var string1 = a[field], string2 = b[field];
			if (typeof string1 == 'string') { string1 = string1.toLowerCase(); }
			if (typeof string2 == 'string') { string2 = string2.toLowerCase(); }
			if (string1 < string2) return -1;
			if (string1 > string2) return 1;
			return 0;
		});
	}
}

/**
 * Get the URL param by name from URL
 * @param {String} name The name of URL param
 * @param {String} [url] The URL that needs to get param
 */
const getParam = (name, url) => {
	if (!url) {
		url = Utils.getWindowLocation().href;
	}
	name = name.replace(/[\[\]]/g, "\\$&");
	var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)");
	var results = regex.exec(url);
	if (!results) return null;
	if (!results[2]) return '';
	return decodeURIComponent(results[2].replace(/\+/g, " "));
}

// Merge two objects
const mergeObject = (obj1, obj2) => {
	for (var p in obj2) {
		try {
			// Property in destination object set; update its value.
			obj1[p] = obj2[p].constructor == Object ? mergeObject(obj1[p], obj2[p]) : obj2[p];
		} catch (e) {
			// Property in destination object not set; create it and set its value.
			obj1[p] = obj2[p];
		}
	}
	return obj1;
}

/**
 * Capitalize a string
 * @param {String} string The string that needs to Capitalize
 * @param {Boolean} [isLower] Set true if needs to Lowercase all before Capitalize
 * @param {Boolean} [onlyFirstLetter] Set true if just needs to Capitalize the first character
 */
const capitalize = (string, isLower, onlyFirstLetter) => {
	var isLower = typeof isLower !== 'undefined' ? isLower : false;
	var onlyFirstLetter = typeof onlyFirstLetter !== 'undefined' ? onlyFirstLetter : false;
	if (isLower) string = string.toLowerCase();
	if (onlyFirstLetter) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	} else {
		return string.replace(/(?:^|\s)\S/g, function (a) { return a.toUpperCase(); });
	}
}

/**
 * Find index of a value in array
 * @param {String} findValue The searched value
 * @param {Array} arr The Array
 * @param {String} key The searched key
 * @param {Boolean} sensitive Set true for case sensitive
 */
const findIndexArray = (findValue, arr, key, sensitive) => {
	if (typeof key !== 'undefined' && key !== null) {
		for (var i = 0; i < arr.length; i++) {
			if (typeof sensitive !== 'undefined' && sensitive == false) {
				arr[i][key] = arr[i][key].toLowerCase();
				findValue = findValue.toLowerCase();
			}
			if (arr[i][key] == findValue) return i;
		}
	} else {
		for (var i = 0; i < arr.length; i++) {
			if (typeof sensitive !== 'undefined' && sensitive == false) {
				arr[i] = arr[i].toLowerCase();
				findValue = findValue.toLowerCase();
			}
			if (arr[i] == findValue) return i;
		}
	}
	return -1;
};

/**
 * Get a value of an attribute in an element of an array of object
 * @param {String} findValue The searched value
 * @param {Array} arr The Array
 * @param {String} findKey The searched key
 * @param {String} destKey 
 */
const getValueInObjectArray = (findValue, arr, findKey, destKey) => {
	if (typeof findKey === 'undefined') findKey = 'key';
	if (typeof destKey === 'undefined') destKey = 'values';
	var index = findIndexArray(findValue, arr, findKey);
	if (index > -1 && arr[index].hasOwnProperty(destKey)) return arr[index][destKey];
	return '';
}

/**
 * Get file path
 * @param {String} fileName The file mane
 * @param {*} extension The file extension 
 * @param {*} version The version of file
 */
const getFilePath = (fileName, extension, version) => {
	var extension = typeof extension !== 'undefined' ? extension : 'png';
    var version = typeof version !== 'undefined' ? version : '';
    var filePath = Globals.fileUrl.split('?')[0];
    filePath += fileName + '.' + extension + (version ? ('?v=' + version) : '');
    return filePath;
}

/**
 * Check if the number is Interger
 * @param {Number} n The number value
 */
const isInt = (n) => {
	return Number(n) === n && n % 1 === 0;
}

/**
 * Check if the number is Float
 * @param {Number} n 
 */
const isFloat = (n) => {
	return Number(n) === n && n % 1 !== 0;
}

/**
 * Retrieve number of decimals
 * @param {Number} n The number that needs to get decimals
 */
const getNumberDecimals = (n) => {
	var splittedNumber = n.toString().split('.');
	if (splittedNumber.length > 1) {
		return splittedNumber[1].length;
	} else {
		return 0;
	}
}

/**
 * Removes duplicate values from an array
 * @param {Array} arr The array value
 */
const uniq = (arr) => {
	return arr.filter(function (value, index, self) {
		return self.indexOf(value) === index;
	});
}

/**
 * Strip all HTML tags from a string
 * @param {String} string The string that contains HTML tags
 */
const stripHtml = (string) => {
	return jQ('<p>' + string + '</p>').text();
}

/**
 * Remove all Script in a string
 * @param {String} string The string that contains Script tags
 */
const stripScriptTag = (string) => {
	if (string) return string.replace(/<script[^>]*>.*?<\/script>/gi, '');
}

/**
 * Add ellipsis to too long text (keep whole word)
 * @param str
 * @param limit
 * @param delimiter
 * @return {*}
 */
const truncateByWord = (str, limit, delimiter) => {
    if (typeof delimiter === 'undefined') delimiter = '...';
    str = (str.split(' ')).length > limit ? str.split(' ').splice(0, limit).join(' ') + delimiter : str.split(' ').splice(0, limit).join(' ');
    return str;
};

/**
 * Return true if the active currency is not default
 */
const isShopifyActiveCurrency = () => {
	return typeof Shopify !== 'undefined'
		&& Shopify.hasOwnProperty('currency')
		&& Shopify.currency.hasOwnProperty('rate')
		&& Shopify.currency.rate != 1.0;
}

/**
 * Return whether or not Shopify Multi-currencies is enabled
 */
const isEnableShopifyMultipleCurrencies = () => {
	return Settings.hasOwnProperty('general')
		&& Settings.general.hasOwnProperty('currencies')
		&& Settings.general.currencies.length > 1
		&& isShopifyActiveCurrency();
}

/**
 * Re-Build URL with locale
 * @param {String} url - The URL 
 */
const reBuildUrlBaseOnLocale = (url) => {
	// remove http or https
	url = url.replace('https://', '').replace('http://', '');
	var currentLocale = Settings.getSettingValue('general.current_locale');
	var publishedLocales = Settings.getSettingValue('general.published_locales');
	var publishedLocaleKeys = Object.keys(publishedLocales);
	var currentLocaleIndex = publishedLocaleKeys.indexOf(currentLocale);

	// If the locale isnt in the published list, or is default locale, return the normal url
	if (currentLocaleIndex < 0 || publishedLocales[currentLocale] == true) return url;

	var urlArr = url.split('/');
	if (urlArr.length > 1 && publishedLocaleKeys.length && currentLocale.length) {
		var isUrlHasLocaleString = publishedLocaleKeys.indexOf(urlArr[1]) > -1;
		if (isUrlHasLocaleString) {
			urlArr[1] = currentLocale;
		} else {
			urlArr.splice(1, 0, currentLocale);
		}
	}
	return urlArr.join('/');
}

/**
 * Check if app is loaded in google lighthouse mobile
 */
const isGLHMobile = () => {
	return navigator && navigator.userAgent && navigator.userAgent.includes(atob('TGlnaHRob3VzZQ==')) && Utils.isMobile() && !Utils.isSearchPage();
}

const getWindowLocation = () => {
	// Escape window.location.href
	var href = window.location.href;
	var escapedHref = href.replace(/%3C/g, '&lt;').replace(/%3E/g, '&gt;');

	// Rebuild window.location.href
	var rebuildHrefArr = [];
	for (var i = 0; i < escapedHref.length; i++) {
		rebuildHrefArr.push(escapedHref.charAt(i));
	}
	var rebuildHref = rebuildHrefArr.join('').split('&lt;').join('%3C').split('&gt;').join('%3E');

	// Rebuild window.location.search
	var rebuildSearch = "";
	var hrefWithoutHash = rebuildHref.replace(/#.*$/, '');
	if (hrefWithoutHash.split('?').length > 1) {
		rebuildSearch = hrefWithoutHash.split('?')[1];
		if (rebuildSearch.length > 0) {
			rebuildSearch = '?' + rebuildSearch;
		}
	}

	return {
		pathname: window.location.pathname,
		href: rebuildHref,
		search: rebuildSearch
	}
}

const setWindowLocation = (url) => {
	window.location.href = url;
}

const Utils = {
	// General Utils
	escape: escape,
	findIndexArray: findIndexArray,
	getParam: getParam,
	getSearchTerm: getSearchTerm,
	getValueInObjectArray: getValueInObjectArray,
	getFilePath: getFilePath,
	getNumberDecimals: getNumberDecimals,
	isMobile: isMobile,
	isMobileDevice: isMobileDevice,
	isiOS: isiOS,
	isSafari: isSafari,
	isBackButton: isBackButton,
	isCartPage: isCartPage,
	isProductPage: isProductPage,
	isSearchPage: isSearchPage,
	isVendorPage: isVendorPage,
	isTagPage: isTagPage,
	isTypePage: isTypePage,
	isGLHMobile: isGLHMobile,
	mergeObject: mergeObject,
	optimizeImage: optimizeImage,
	getFeaturedImage: getFeaturedImage,
	slugify: slugify,
	capitalize: capitalize,
	textify: textify,
	stripHtml: stripHtml,
	stripScriptTag: stripScriptTag,
	truncateByWord: truncateByWord,
	removeDecimal: removeDecimal,
	formatMoney: formatMoney,
	moneyWrapper: moneyWrapper,
	formatNumberWithSeparator: formatNumberWithSeparator,
	getCurrency: getCurrency,
	removeCurrencySymbol: removeCurrencySymbol,
	isShopifyActiveCurrency: isShopifyActiveCurrency,
	isEnableShopifyMultipleCurrencies: isEnableShopifyMultipleCurrencies,
	roundedPrice: roundedPrice,
	convertPriceBasedOnActiveCurrency: convertPriceBasedOnActiveCurrency,
	revertPriceToDefaultCurrency: revertPriceToDefaultCurrency,
	reBuildUrlBaseOnLocale: reBuildUrlBaseOnLocale,
	getWindowLocation: getWindowLocation,
	setWindowLocation: setWindowLocation,
	/**
	 * Utils of Instant search <br />
	 * @example
	 * var isFullWidth = Utils.InstantSearch.isFullWidthMobile();
	 * @alias module:Utils.InstantSearch
	 * @see module:Utils/InstantSearchUtils
	 */
	InstantSearch: InstantSearchUtils,
	isFullWidthMobile: InstantSearchUtils.isFullWidthMobile,
	isStyle2: InstantSearchUtils.isStyle2,
	/**
	 * Utils of Filter tree
	 * @alias module:Utils.FilterTree
	 * @see module:Utils/FilterTreeUtils
	 */
	FilterTree: FilterTreeUtils,
	checkExistFilterOptionParam: FilterTreeUtils.checkExistFilterOptionParam,
	encodeURIParamValue: FilterTreeUtils.encodeURIParamValue,
	/**
	 * Utils of Filter tree
	 * @alias module:Utils.FilterResult
	 * @see module:Utils/FilterResultUtils
	 */
	FilterResult: FilterResultUtils,
	buildProductItemUrl: FilterResultUtils.buildProductItemUrl,
	removePageParamFromUrl: FilterResultUtils.removePageParamFromUrl,
	removeCollectionScopeParamFromUrl: FilterResultUtils.removeCollectionScopeParamFromUrl,
	buildToolbarLink: FilterResultUtils.buildToolbarLink,
	isDefaultPaginationType: FilterResultUtils.isDefaultPaginationType,
	isLoadMorePaginationType: FilterResultUtils.isLoadMorePaginationType,
	isInfiniteLoadingPaginationType: FilterResultUtils.isInfiniteLoadingPaginationType,
	isLoadPreviousPagePaginationType: FilterResultUtils.isLoadPreviousPagePaginationType,
	getSortingList: FilterResultUtils.getSortingList,
	getProductMetafield: FilterResultUtils.getProductMetafield,
	isNoFilterResult: FilterResultUtils.isNoFilterResult
}

export default Utils;