Source: helpers/settings.js

import Utils from './utils';

/**
 * Init settings by merging various settings:
 * boostPFSConfig - in app lib
 * boostPFSAppConfig - in snippets/boost-pfs.liquid
 * boostPFSThemeConfig - in sections/collection-template.liquid, templates/search.liquid
 * boostPFSFilterConfig - in boost-pfs-filter.js
 * boostPFSInstantSearchConfig - in boost-pfs-search.js
 */
const init = () => {
	var mergedSettings = Settings;
	if (typeof boostPFSConfig != 'undefined' && boostPFSConfig.hasOwnProperty('settings') && boostPFSConfig.settings !== null) {
		mergedSettings = Utils.mergeObject(mergedSettings, boostPFSConfig.settings);
	}
	if (typeof boostPFSAppConfig !== 'undefined' && Object.keys(boostPFSAppConfig).length > 0) {
		mergedSettings = Utils.mergeObject(mergedSettings, boostPFSAppConfig);
	}
	if (typeof boostPFSThemeConfig !== 'undefined' && Object.keys(boostPFSThemeConfig).length > 0) {
		mergedSettings = Utils.mergeObject(mergedSettings, boostPFSThemeConfig);
	}
	if (typeof boostPFSFilterConfig !== 'undefined' && Object.keys(boostPFSFilterConfig).length > 0) {
		mergedSettings = Utils.mergeObject(mergedSettings, boostPFSFilterConfig);
	}
	if (typeof boostPFSInstantSearchConfig !== 'undefined' && Object.keys(boostPFSInstantSearchConfig).length > 0) {
		mergedSettings = Utils.mergeObject(mergedSettings, boostPFSInstantSearchConfig);
	}
	// Merge label from translations
	if (typeof Shopify !== 'undefined' 
				&& Shopify.hasOwnProperty('locale') 
				&& mergedSettings.hasOwnProperty('label')
				&& mergedSettings.hasOwnProperty('labelTranslations') 
				&& mergedSettings.labelTranslations !== undefined
				&& mergedSettings.labelTranslations.hasOwnProperty(Shopify.locale)
			) {
		var labelTranslations = mergedSettings.labelTranslations[Shopify.locale];
		mergedSettings.label = Utils.mergeObject(mergedSettings.label || {}, labelTranslations);
	}
	Settings = mergedSettings;
};

/**
 * Get value from setting based on id
 * @param {string} id - The setting id, for example 'general.showLoading'
 * @returns {string}
 */
const getSettingValue = (id) => {
	// If settings are overrided in a specific theme, merge settings's theme with default settings
	var result = '';
	if (Settings.hasOwnProperty(id)) {
		return Settings[id];
	} else {
		if (id.indexOf('.') > -1) {
			var indexArr = id.split('.');
			for (var k = 0; k < indexArr.length; k++) {
				if (result == '') {
					if (Settings.hasOwnProperty(indexArr[k])) {
						result = Settings[indexArr[k]];
					} else {
						return '';
					}
				} else {
					if (result.hasOwnProperty(indexArr[k])) {
						result = result[indexArr[k]];
					} else {
						return '';
					}
				}
			}
		}
	}
	return result;
};

/**
 * List of all settings in the app
 */
var Settings = {
	general: {
		/* Filter Tree Settings */
		// Filter tree
		enableFilter: true,
		filterTreeMobileStyle: 'style2', // 'style2', 'style3'
		filterTreeMobileStyleFullWidth: false, // true, false
		filterHorizontalColumn: '1', // 1, 2, 3, 'full'

		// Refine by
		showRefineBy: true, // Show Filter Selections on PC
		separateRefineByFromFilter: false, // Place <div class='boost-pfs-filter-refine-by-wrapper-v/h'><div> in the DOM to render refine-by.
		refineByHorizontalPosition: 'bottom', // 'bottom', 'top'

		// Mobile button
		changeMobileButtonLabel: false, // Change label when clicking Refine By button on mobile
		breakpointMobile: '767', // Start showing mobile

		// Loading icon
		showLoading: false, // Show Loading icon when requesting API
		showMobileLoading: false, // Show Loading icon on mobile
		showLoadMoreLoading: true, // Show Loading icon when infinite loading
		positionShowInfiniteLoading: 700,

		// Scroll to top
		activeScrollToTop: false, // Display Scroll to top button
		styleScrollToTop: 'style1', // Color of Scroll to top button: style1(black), style2(gray)

		/* Filter Options Settings */
		// Filter option general
		showSingleOption: true, // True = hide filter option with only one item
		showOutOfStockOption: false, // Show Out of Stock filter option
		showFilterOptionCount: true, // Display number of product of a filter option
		requestInstantly: false, // On Horizontal, make API request when clicking on filter item (no need to click apply)

		// Filter option label
		capitalizeFilterOptionValues: true, // Only cap 1st leter, change nothing else: hello wORLD => Hello WORLD
		forceCapitalizeFilterOptionValues: false, // Lower case then caps 1st letter each word: HELLO World => Hello World
		capitalizeFirstLetterFilterOptionValues: false, // Lower case then caps 1 letter only: HELLO World => Hello world

		// Filter option collapse
		collapseOnPCByDefault: false, // Collapse on PC by default
		collapseOnMobileByDefault: false, // Collapse on Mobile by default
		keepScrollState: true, // Keep the scroll position of each filter options after filtering
		keepToggleState: true, // On vertical, keep the collapsed state after filtering
		keepTabOpenState: false, // On horizontal, keep the tab open after applying

		// Filter option scroll bar
		activeFilterScrollbarPC: true, // Enable scrollbar (on PC)
		activeFilterScrollbarMobile: true, // Enable scrollbar (on Mobile)
		scrollFirstLoadLength: 24, // Only show x filter item initially, and load more on scroll

		// Filter option view more
		startViewMore: { list: 5, box: 3, swatch: 10 },
		startViewMoreH: { list: 10, box: 20, swatch: 10 }, // This option is used when filterHorizontalColumn = 'full'. Otherwise its (veritical setting x column)

		// Filter option - Range slider
		removePriceDecimal: true, // Remove '.00'
		rangeSliderMoneyFormat: '', // Take shop's moneyFormat if left empty.
		oneValueRangeSlider: false, // Show range slider when there's one value
		rangeSlidersStyle3: [], // List of filter option id to turn into range slider style3 (text box on bottom)
		rangeSlidersSingleHandle: [], // List of filter option id to turn into range slider style4: single handle
		advancedRangeSliders: [], // List of filter option id to turn into range slider. Ex: ['pf_t_size', 'pf_t_weight']
		shortenPipsRange: false, // Shorten range slider label. Example: 1000 -> 1k
		formatPipsRange: [
			{ node: 1000, symbol: 'K', fix: 0, suffix: false },
			{ node: 1000000, symbol: 'M', fix: 2, suffix: false }
		], // Fix: number of decimal: 1k, 1.2k, 1.23k. Suffix: 1.2k (suffix = true) or 1k2 (suffix = false).
		enable3rdCurrencySupport: false,

		// Filter option - Swatch
		imageExtension: ['jpg', 'JPG', 'png', 'PNG', 'jpeg', 'JPEG', 'gif', 'GIF'], // Use to build image URL
		swatchStyle: '', // 'circle-grid', 'circle-list', 'square-grid', 'square-list'. Default vertical is 'circle-grid', default horizontal is 'circle-list'
		swatchImageVersion: '1111111', // the '?v=' in swatch image URL
		removePrefixFromSwatchFile: true, // Remove filter option prefix from swatch file name.

		// Filter option - Box
		enableFilterOptionBoxStyle: true, // Auto adjust box item width
		filterOptionBoxCharWidth: 14, // Calculate box item width based on (character width x label length)

		// Filter option - Multi-level
		openMultiLevelByDefault: [], // [1] or [1,2] or [2] or []: level to open by default
		multiLevelCollectionSelectType: 'single', // Use 'single' or 'multiple' for the tags inside multi level collection

		/* Filter API Settings */
		// API options
		filterPrefixParam: 'pf_', // Default filter prefix param
		limit: 16, // Total products per page
		vendorParam: 'pf_v_vendor', // Used to build filter query param on vendor page
		typeParam: 'pf_pt_product_type', // Used to build filter query param on product type page
		priceMode: '', // max: use price_max to build price slider
		tagMode: '', // Used in Tag Page - if = 2: apply OR condition if there are multiple tags like this "/Outerwear+Coat+Outerwear-Bomber-Jacket"

		// Address bar URL
		urlScheme: 1, // 0: dont change url, 1: example 'color=red&color=blue', 2: example 'color=red,blue'
		isShortenUrlParam: false, // Use the 'shortenUrlParamList' to shorten param
		shortenUrlParamList: [], // List of ['filter_option_id:short_id']. Example: ['pf_otp_color:color', 'pf_t_size:size']. Make sure the values are unique, and don't contain special characters.

		/* Filter Product list Settings */
		// Product
		productAvailable: false, // Return products in stock or not from the first load
		variantAvailable: false, // Return variants in stock or not from the first load
		availableAfterFiltering: false, // Return products & variants in stock when filter option params exist on the url
		loadProductFirst: true, // True = Load product list from Shopify liquid on first load
		loadProductFirstBestSelling: false, // True = Load product list from Shopify liquid on first load with sort by 'best-selling'
		enableKeepScrollbackPosition: true, // Scroll to the previous position after going back from Product page
		addCollectionToProductUrl: true,
		showVariantImageBasedOnSelectedFilter: '', // Filter option id to change variant image based on that. Ex: 'pf_opt_color'

		// Pagination
		paginationType: 'default', // Pagination type: default, infinite, load_more
		paginationTypeAdvanced: true, // Append "page" param to url, used to Scroll to the previous position after returning from Product page
		activeLoadPreviousPage: false, // active load the previous page feature
		sessionStorageCurrentPreviousPage: 'boostPFSCurrentPreviousPage', // integer - the load previous page data: current previous page
		sessionStorageCurrentPage: 'boostPFSCurrentPage', // integer - the load previous page data: current page
		sessionStorageCurrentNextPage: 'boostPFSCurrentNextPage', // interger - the load previous page data: current next page
		sessionStoragePreviousPageEvent: 'boostPFSPreviousPageEvent', // 0 or 1: the load previous page data: current previous page event

		// Sorting
		sortingList: ['relevance', 'best-selling', 'manual', 'price-ascending', 'price-descending', 'title-ascending', 'title-descending', 'created-descending', 'created-ascending'],
		customSortingList: '',
		extraSortingList: '',
		sortingAvailableFirst: false,

		// Limit
		showLimitList: '4,8,12,16', // Define number list of Show limit dropdown

		// Display
		defaultDisplay: 'grid', // Default display type of product list
		collageNumber: 3, // Number of product items that is contained in Collage

		// Product placeholder
		showPlaceholderProductList: false,
		placeholderImageRatio: 1.4, //It is used for ratio of image area  of Skeleton product item. (height / width)
		placeholderProductGridItemClass: '', //In case the theme have grid/list mode, we need to define the classes for Product Grid mode.
		placeholderProductPerRow: 3,

		// Load product html from liquid
		loadProductFromLiquid: false,
		loadProductFromLiquidType: 'ajax', // 'ajax': call ajax to the store to get the html, 'sync': sync and return the html from our server

		/* OTP Settings */
		otpProductItemClass: '', // In case product grid has extra elements that's not product item, make OTP buttons bind to the right element
		enableAjaxCart: false,
		ajaxCartStyle: 'slide', // 'slide' or 'popup'
		showAjaxCartOnAdd: true,
		autoCloseMiniCart: false,
		autoCloseMiniCartDuration: 2000,
		selectOptionInProductItem: false, // Append the quickview's option inside the product item, instead of opening pop up when clicking "select option"
		selectOptionContainer: '',

		/* Analytics Setting */
		enableTrackingOrderRevenue: true,

		/* Other settings */
		enableSeo: true,
		boostCollection: 'boost-all' // A collection handle that we use for full-sync
	},
	search: {
		enableSearch: true,
		enableSuggestion: true,
		suggestionBlocks: [
			{ type: 'suggestions', label: 'Suggestions', status: 'active', number: 3 },
			{ type: 'collections', label: 'Collections', status: 'active', number: 2 },
			{ type: 'pages', label: 'Pages', status: 'active', number: 2 },
			{ type: 'products', label: 'Products', status: 'active', number: 3 },
		],
		suggesionMaxItems: 10,
		suggestionDymLimit: 2,
		suggestionMinLength: 1, // The minimum number of characters a user must type before a search is performed
		suggestionPosition: '', // left or right
		suggestionDelay: 200,
		suggestionWidth: 'auto', // auto, fullwidth, number: 100, 200.....
		suggestionTypes: [],
		suggestionStyle: '', // '', style2
		suggestionStyle2MainContainerSelector: 'header:first',
		suggestionStyle2ReverseProductBlock: false, // for style2 - swap position of product block and other blocks
		suggestionStyle2ProductPerRow: 3, // for style2: 2, 3
		suggestionMobileStyle: 'style1', // style1, style2
		showSuggestionLoading: true,
		showSuggestionProductVendor: true,
		showSuggestionProductPrice: true,
		showSuggestionProductSalePrice: true,
		showSuggestionProductImage: true,
		showSuggestionProductSku: false,
		showSearchBtnMobile: false,
		enableDefaultResult: true,
		enableFuzzy: true,
		productAvailable: false,
		removePriceDecimal: false,
		highlightSuggestionResult: true, // Highlight Text in the Search result
		openProductNewTab: false, // Open product in the new tab
		suggestionMode: 'prod', // prod or test,
		termKey: 'q',
		skipFields: [],
		reduceMinMatch: false, // false (default): results to 85% match. true: results to 50% match. An Integer Number: requires min match exact this number
		fullMinMatch: false, // Search results match 100% if the search term has more than 2 words. reduce_min_match will override the full_min_match
		enablePlusCharacterSearch: false, // Enable search with Plus character
		// Style Suggestion Header
		fontSizeSuggestionHeader: '',
		bgSuggestionHeader: '',
		colorSuggestionHeader: '',
		// SEO head-title
		enableFixHeadTitle: true,
		searchPanelList: ['products', 'collections', 'pages'],
		searchPanelDefault: 'products',
		searchPanelBlocks: {
			'products': {'label': 'Products', 'pageSize': 25, 'active': true},
			'collections': {'label': 'Collections', 'pageSize': 25, 'active': false},
			'pages': {'label': 'Pages', 'pageSize': 25, 'active': false}
		},
		suggestionNoResult: {
			search_terms: {
				label: '"Popular suggestions',
				status: true,
				data: []
			},
			products: {
				label: 'Products',
				status: true,
				data: []
			}
		}
	},
	init: init,
	getSettingValue: getSettingValue,
}

export default Settings;