PageSourceSearch

https://www.mvy.com/includes/public/managed/v_a2e6ae52_6c6e6022/co…/common/virtuals/js/shared_footer.js

js mvy.com collected 2026-10-02 03:29:47 UTC 9,348 bytes, 280 lines download raw bytes

1// This file will be run in the footer on every page.
2// It should be used for content that relates
3// to your site as a whole, it should not be used for
4// plugin-specific js
5
6require(["site_gtm", "plugins_common_custom_ui_watcher", "sv_load!plugins_dynamic_content", "sv_site", "domReady!"], function(gtm, UIWatcher, dynamic, site) {
7	gtm.update();
8
9	// SEM tracking
10	var uiEvents = new UIWatcher({}, null, document);
11	uiEvents.on("change-page", handlePageChange);
12	uiEvents.on("view-change", handleFilterChange);
13	uiEvents.on("sort-change", handleFilterChange);
14	uiEvents.on("value-change", handleFilterChange);
15	uiEvents.on("toggle-show-more", handleShowMoreToggle);
16	uiEvents.on("item-click", handleItemClick);
17	uiEvents.on("tripbuilder", handleTripbuilder);
18	uiEvents.on("qv-tripbuilder", handleTripbuilder);
19	uiEvents.on("quick-view", handleQuickview);
20
21	// tracking handlers
22	function handleTripbuilder(payload){
23		var event = payload.eventContext,
24			widget = payload.widgetContext;
25		let ga4Data = {
26			eventName: event?.value === "Add" ? "add_to_favorites" : undefined,
27			itemType: widget?.name === "Coupons" ? 'offers' : (widget?.name.toLowerCase() !== "quickview" && widget?.name.toLowerCase() !== "quick view") ? widget?.name.toLowerCase() : event?.itemType,
28			itemName: event?.refTitle,
29			linkObject: event?.linkObject,
30			itemCategory: event?.crmc,
31			interactionSource: event?.interactionSource,
32			interactionText: event?.interactionText,
33			interactionUrl: event?.interactionUrl,
34			itemCity: event?.itemCity,
35			itemRegion: event?.itemRegion,
36			itemId: event?.itemId,
37			acctId: event?.acctId,
38			itemVariant: event?.itemVariant,
39			price: event?.price,
40			viatorEvent: event?.viatorEvent,
41			propertyId: event?.propertyId,
42			propertyType: event?.propertyType,
43			propertyName: event?.propertyName,
44			isFeatured: event?.isFeatured,
45		}
46		if (event?.crmsc) 
47			ga4Data.itemSubcategory = event?.crmsc;
48		if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject)
49			gtm.sendEvents({ tClient_ga4: ga4Data });
50	}
51	
52	function handleQuickview(payload){
53		var event = payload.eventContext;
54		let ga4Data = {
55			eventName: event?.eventName,
56			itemType: event?.itemType,
57			itemName: event?.itemName ?? event?.refTitle,
58			linkObject: event?.linkObject,
59			itemCategory: event?.crmc,
60			interactionSource: event?.interactionSource,
61			interactionText: event?.interactionText,
62			interactionUrl: event?.interactionUrl,
63			itemCity: event?.itemCity,
64			itemRegion: event?.itemRegion,
65			itemId: event?.itemId,
66			acctId: event?.acctId,
67			itemVariant: event?.itemVariant,
68			price: event?.price,
69			referralType: event?.referralType,
70			viatorEvent: event?.viatorEvent,
71			propertyId: event?.propertyId,
72			propertyType: event?.propertyType,
73			propertyName: event?.propertyName,
74			isFeatured: event?.isFeatured,
75		}
76		if (event?.crmsc) 
77			ga4Data.itemSubcategory = event?.crmsc;
78		if (event?.eventName === 'partner_referral')
79			ga4Data.value = 5;
80		if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject)
81			gtm.sendEvents({ tClient_ga4: ga4Data });
82	}
83
84	function handleShowMoreToggle(payload){
85		var event = payload.eventContext;
86		let ga4Data = {
87			eventName: "filter",
88			filterType: `${event?.refTitle} | ${event?.displayText}`,
89			itemType: event?.itemType,
90			interactionSource: event?.interactionSource,
91			interactionText: event?.interactionText,
92		}
93		if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType)
94			gtm.sendEvents({ tClient_ga4: ga4Data });
95	}
96
97	function handlePageChange(payload){
98		var event = payload.eventContext;
99		let ga4Data = {
100			eventName: "filter",
101			filterType: `${event?.refTitle} | ${event?.displayText}`,
102			itemType: event?.itemType,
103			interactionSource: event?.interactionSource,
104			interactionText: event?.interactionText,
105		}
106		if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType)
107			gtm.sendEvents({ tClient_ga4: ga4Data });
108	}
109
110	function handleItemClick(payload){
111		var event = payload.eventContext;
112		if (event?.name === "Quickview" || event?.name === "Quick View") {
113			var ga4Data = {
114				eventName: "filter",
115				filterType: "Open Quickview",
116				itemType: event?.itemType,
117				itemName: event?.itemName ?? event?.refTitle,
118				interactionSource: event?.interactionSource,
119				interactionText: event?.interactionText,
120				itemCategory: event?.crmc,
121				itemCity: event?.itemCity,
122				itemRegion: event?.itemRegion,
123				itemId: event?.itemId,
124				acctId: event?.acctId,
125				itemVariant: event?.itemVariant,
126				price: event?.price,
127				viatorEvent: event?.viatorEvent,
128				propertyId: event?.propertyId,
129				propertyType: event?.propertyType,
130				propertyName: event?.propertyName,
131				isFeatured: event?.isFeatured,
132			}
133			if (event?.crmsc)
134				ga4Data.itemSubcategory = event?.crmsc;
135			if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType)
136				gtm.sendEvents({tClient_ga4: ga4Data});
137		} else {
138			var ga4Data = {
139				eventName: event?.eventName,
140				widgetName: event?.widgetName,
141				itemType: event?.itemType,
142				itemName: event?.itemName ?? event?.refTitle,
143				linkObject: event?.linkObject,
144				itemCategory: event?.crmc,
145				interactionSource: event?.interactionSource,
146				interactionText: event?.interactionText,
147				interactionUrl: event?.interactionUrl,
148				itemCity: event?.itemCity,
149				itemRegion: event?.itemRegion,
150				itemId: event?.itemId,
151				acctId: event?.acctId,
152				itemVariant: event?.itemVariant,
153				price: event?.price,
154				referralType: event?.referralType,
155				viatorEvent: event?.viatorEvent,
156				propertyId: event?.propertyId,
157				propertyType: event?.propertyType,
158				propertyName: event?.propertyName,
159				isFeatured: event?.isFeatured,
160			}
161			if (event?.crmsc)
162				ga4Data.itemSubcategory = event?.crmsc;
163			if (event?.eventName === 'partner_referral')
164				ga4Data.value = 5;
165			if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject)
166				gtm.sendEvents({tClient_ga4: ga4Data});
167		}
168	}
169
170	function handleFilterChange (payload) {
171		var event = payload.eventContext;
172		let ga4Data = {
173			eventName: "filter",
174			filterType: `${event?.refTitle} | ${event?.displayText}`,
175			itemType: event?.itemType,
176			interactionSource: event?.interactionSource,
177			interactionText: event?.interactionText,
178		}
179		if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType)
180			gtm.sendEvents({ tClient_ga4: ga4Data });
181	}
182	// end SEM tracking
183
184	// Init quickview textboxes
185	if (site.siteConfig.custom[site.site].quickview.active) {
186		const textBoxes = document.querySelectorAll('[data-qv-enabled-textbox]');
187	
188		if (textBoxes.length) {
189			require(['plugins_common_custom_quickview_textbox'], function (qv) {
190				qv.init();
191			});
192		}
193	}
194});
195
196// Lazy-load js for widgets that have the [data-js] attribute on their root node
197(function() {
198	function initDataJs(searchScope, selector = '[data-js]' ){
199		require(['plugins_common_custom_lazyload'], function(lazyload) {
200			var widgetsToLoad = [];
201			let scope = searchScope ? searchScope : document;
202			var els = scope.querySelectorAll(selector);
203
204			for (var i = 0; i < els.length; i++) {
205				widgetsToLoad.push(els[i]);
206			}
207			
208			lazyload.lazy(widgetsToLoad, function(entry, io) {
209				var target = entry.target;
210				var jsFile = target.dataset.js;
211				var guid = target.dataset.guid;
212
213				require([jsFile], function(widget) {
214					widget.init(guid);
215					io.unobserve(target);
216				});
217			});
218		});
219	};
220
221	initDataJs();
222
223	let eventToSelectorMap = {
224		// each event needs a slightly different selector
225		"dynamicContentLoaded": "[data-dynamic-chosen] [data-js]",
226		"abContentLoaded": "[data-ab-chosen] [data-js]"
227	}
228	
229	Object.entries(eventToSelectorMap).forEach( event => {
230		const [eventName, selector] = event;
231		window.addEventListener(eventName, e => {
232			const collections = document.querySelector(`[data-guid="${e}"]`);
233			initDataJs(collections, selector);
234		});
235	});
236})();
237
238// Wrap core_textbox iframes to prevent horizontal overflow
239(function() {
240	const textBoxes = document.querySelectorAll('[data-sv-core-textbox]');
241	if (textBoxes.length) {
242		require(['plugins_common_custom_utils'], function(utils) {
243			textBoxes.forEach(utils.wrapIframes);
244		});
245	}
246})();
247
248// Remove critical styles from head after all CSS files have loaded
249// This prevents a situation where the critical styles cause an override of the CSS in files
250const criticalStyles = document.querySelector('[data-critical-styles]');
251if (criticalStyles) {
252	window.addEventListener('load', e =>
252 criticalStyles.remove());
253}
254
255// update textbox table border styles if border styles were chosen in the CMS
256(function() {
257	const textboxTables = Array.from(document.querySelectorAll('[data-sv-core-textbox] table[border]'));
258	
259	if (!textboxTables.length) return;
260
261	textboxTables.forEach((table) => {
262		const rowsAndCells = Array.from(table.querySelectorAll('tr, td'));
263		rowsAndCells.forEach((rc) => {
264			if (table.getAttribute('border')) {
265				rc.style.borderWidth = `${table.getAttribute('border')}px`
266			}
267			if (rc.tagName) {
268				if (table.getAttribute('cellpadding')) {
269					rc.style.padding = `${table.getAttribute('cellpadding')}px`
270				}	
271
272				if (table.getAttribute('cellspacing')) {
273					table.style.borderCollapse = 'revert';
274					rc.style.margin = `${table.getAttribute('cellspacing')}px`
275				}
276
277			}
278		});
279	});
280})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.