Source: helpers/navigation.js

import jQ from 'jquery';

import Utils from '../helpers/utils';
import Globals from '../helpers/globals';
import Settings from '../helpers/settings';
import FilterApi from '../api/filter-api';
import BoostPFS from '../boost-pfs';

/**
 * The current history state object
 * @type {Object}
 */
var historyState = {};
var defaultParamValues = {};
var newAddressBarPath = window.location.pathname;
var newWindowTitle = document.title;
var currentUrl = "";
var shortParamsMap = new Map();
var longParamMap = new Map();

/**
 * Init history state with default values.
 * Bind to 'popstate' event to reinit the history
 */
const init = () => {
	currentUrl = Utils.getWindowLocation().href;

	defaultParamValues = {
		page: 1,
		limit: Settings.getSettingValue('general.limit'),
		sort: Utils.isSearchPage() ? 'relevance' : Globals.defaultSorting,
		display: Settings.getSettingValue('general.defaultDisplay'),
		tab: Settings.getSettingValue('general.searchPanelDefault'),
	}

	// Bind back button event
	jQ(window).on('popstate', Navigation.onPopState);

	// Init shortParamsMap and longParamMap
	Navigation.initShortenUrl();
}

/**
 * On back button
 */
const onPopState = (event) => {
	historyState = event.originalEvent.state;
	var boostPFSFilterInstance = BoostPFS.instance.filter;

	// Hash change also fires popstate event, so we need to check for hashchange, and return without re-init filter
	var url = Utils.getWindowLocation().href;
	var isHashChange = (url.includes('#') || currentUrl.includes('#')) && url.split('#')[0] == currentUrl;
	if (!boostPFSFilterInstance || isHashChange) return;

	// Trigger click on tab Product in Search Page Display
	if (Utils.isSearchPage() && jQ('.' + Class.searchResultPanelItem).length > 0) { 
		jQ('.' + Class.searchResultPanelItem).first().trigger('click');
	}
	// Re-init filter
	boostPFSFilterInstance.filterLoadingIcon.setShow(true);
	FilterApi.updateParamsFromUrl();
	FilterApi.getFilterData('history', boostPFSFilterInstance.setData.bind(boostPFSFilterInstance));
}

/**
 * Init mapping for shorten URL params
 */
const initShortenUrl = () => {
	// Check settings
	var shortParamSettings = Settings.getSettingValue('general.shortenUrlParamList');
	if (Settings.getSettingValue('general.isShortenUrlParam') && Array.isArray(shortParamSettings)) {
		shortParamSettings.forEach(paramSetting => {
			if (typeof paramSetting == 'string') {
				// 'paramSetting' has the format 'filter_option_id:short_id', for example: 'pf_opt_color:color'.
				var splitParams = paramSetting.split(':');
				if (splitParams.length == 2) {
					var filterOptionParam = splitParams[0].trim();
					var shortFilterOptionParam = splitParams[1].trim();
					if (filterOptionParam && shortFilterOptionParam) {
						shortParamsMap.set(filterOptionParam, shortFilterOptionParam);
						longParamMap.set(shortFilterOptionParam, filterOptionParam);
					}
				}
			}
		})
	}
}

/**
 * Update the address bar using Globals.queryParams object
 * Push new state to history
 */
const updateAddressBar = () => {
	if (Settings.getSettingValue('general.urlScheme') == 0 || !window.history || typeof window.history.pushState != 'function') return;
	var newUrl = buildAddressBarUrl();

	// Remove functions from query params
	var cleanQueryParams = JSON.parse(JSON.stringify(Globals.queryParams));
	Globals.queryParams = cleanQueryParams;

	// Save to  history
	history.pushState({
		param: Globals.queryParams
	}, newWindowTitle, newUrl);
	currentUrl = newUrl;

	const event = new Event('boost-pfs-change-address-bar');
	window.dispatchEvent(event);
}

/**
 * Build address bar URL from Globals.queryParams object.
 * @returns {string} - New URL
 */
const buildAddressBarUrl = () => {
	var queryParams = Globals.queryParams;
	var urlPath = window.location.protocol + "//" + window.location.hostname + newAddressBarPath;
	var urlQueryString = Utils.getWindowLocation().search;
	var urlSearchParams = new URLSearchParams(urlQueryString);
	var hasFilterOptionParam = false;

	var urlScheme = Settings.getSettingValue('general.urlScheme');

	// Add & update param values 
	Object.keys(queryParams).forEach(key => {
		var value = queryParams[key];
		
		// Filter params
		if (key.startsWith(Globals.prefix)) {
			// Get the short url param key
			var shortKey = shortParamsMap.get(key);
			if (!shortKey) shortKey = key;

			urlSearchParams.delete(shortKey);
			if (Array.isArray(value)) {

				// Build urlSearchParams by urlScheme
				switch (urlScheme) {
					case 0:
						// Don't build filter params
						break;
					case 2:
						// Example: color=red,blue
						urlSearchParams.set(shortKey, value);
						break;
					case 1:
					default:
						// Example: color=red&color=blue
						value.forEach((singleValue) => {
							urlSearchParams.append(shortKey, singleValue);
						})
						break;
				}
				hasFilterOptionParam = true;
			} else if (typeof value !== 'undefined' && value !== null) {
				urlSearchParams.set(shortKey, value);
				hasFilterOptionParam = true;
			}
			
		// Search query param
		} else if (key == Globals.searchTermKey && typeof value == 'string') {
			urlSearchParams.set(key, value);
		
		// Other params: page, limit, display, sort: remove these params if they're at default value
		} else if (Globals.otherParams.includes(key)) {
			if (value == defaultParamValues[key]) {
				urlSearchParams.delete(key);
			} else {
				urlSearchParams.set(key, value);
			}
		}
	});

	// Remove non-existing params values
	var keysToBeRemoved = []
	for (var key of urlSearchParams.keys()) {
		var longKey = longParamMap.get(key);
		if (!longKey) longKey = key;
		if (longKey.startsWith(Globals.prefix) || Globals.imutableFilterTree.includes(longKey) || longKey == Globals.searchTermKey) {
			if (!queryParams.hasOwnProperty(longKey)) {
				keysToBeRemoved.push(key);
			}
		}
	}
	keysToBeRemoved.forEach(key => {
		urlSearchParams.delete(key);
	})

	Globals.hasFilterOptionParam = hasFilterOptionParam;

	// Uncomment to add "_=pf" param
	// Globals.hasFilterOptionParam = hasFilterOptionParam;
	// if (hasFilterOptionParam) {
	// 	urlSearchParams.set('_', Globals.prefix);
	// } else {
	// 	urlSearchParams.delete('_');
	// }

	var url = urlPath;
	var queryString = urlSearchParams.toString();
	if (queryString) {
		if (urlScheme == 2) {
			queryString = queryString.replace(/%2C/g,",");
		}
		url += ('?' + queryString);
	}

	return url;
}

/**
 * Set the new address bar path (when changing collections/tags)
 * after filtering
 * @param {string} path
 */
const setAddressBarPathAfterFilter = (path) => {
	if (typeof path == 'string' && path.startsWith('/')) {
		newAddressBarPath = path;
	} else {
		newAddressBarPath = window.location.pathname;
	}
}

/**
 * Set the new window title (when changing collections/tags/pages)
 * after filtering
 * @param {string} title
 */
const setWindowTitleAfterFilter = (title) => {
	if (typeof title == 'string' && title != '' && !title.includes('undefined') && !title.includes('null')) {
		newWindowTitle = title;
	} else {
		newWindowTitle = document.title;
	}
}

const getHistoryState = () => {
	return historyState;
}

const Navigation = {
	init: init,
	initShortenUrl: initShortenUrl,
	updateAddressBar: updateAddressBar,
	buildAddressBarUrl: buildAddressBarUrl,
	setAddressBarPathAfterFilter: setAddressBarPathAfterFilter,
	setWindowTitleAfterFilter: setWindowTitleAfterFilter,
	getHistoryState: getHistoryState,
	onPopState: onPopState,
	shortParamsMap: shortParamsMap,
	longParamMap: longParamMap
}

export default Navigation;