PageSourceSearch

https://www.cyclable.com/modules/cdc_googletagmanager/views/js/ajaxdatalayer.js

js cyclable.com collected 2026-09-24 18:52:05 UTC 14,550 bytes, 439 lines download raw bytes

1/**
2 * NOTICE OF LICENSE
3 *
4 * This source file is subject to a commercial license from SAS Comptoir du Code
5 * Use, copy, modification or distribution of this source file without written
6 * license agreement from the SAS Comptoir du Code is strictly forbidden.
7 * In order to obtain a license, please contact us: [email protected]
8 *
9 * @package   cdc_googletagmanager
10 * @author    Vincent - Comptoir du Code
11 * @copyright Copyright(c) 2015-2025 SAS Comptoir du Code
12 * @license   https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
13 *
14 * Project Name : Google Tag Manager Enhanced Ecommerce (UA) Tracking
15 * Created By  : Comptoir du Code
16 * Created On  : 2016-06-02
17 * Support : https://addons.prestashop.com/contact-community.php?id_product=23806
18 */
19
20// CDC GTM Datalayer actions
21var cdcGtm = {
22	clearDatalayer : function() {
23		if (typeof dataLayer === 'undefined') {
24			dataLayer = [];
25		}
26		dataLayer.push({ ecommerce: null });
27	},
28
29	cloneDatalayer : function(dataLayer) {
30		let cloneDatalayer = JSON.parse(JSON.stringify(dataLayer));
31		// delete property gtm.uniqueEventId if exist to avoid override event
32		if ('gtm.uniqueEventId' in cloneDatalayer) {
33			delete cloneDatalayer['gtm.uniqueEventId'];
34		}
35
36		return cloneDatalayer;
37	},
38
39	addToCart : function(product_id, attribute_id, qtity, addedFromProductPage, callerElement) {
40		if(product_id) {
41			cdcGtm.pushProductToDatalayer('cart-add', product_id, attribute_id, qtity);
42		}
43	},
44
45	removeFromCart : function(product_id, attribute_id, qtity) {
46		if(product_id) {
47			cdcGtm.pushProductToDatalayer('cart-remove', product_id, attribute_id, qtity);
48		}
49	},
50
51	productClick : function(product_id, attribute_id) {
52		if(product_id) {
53			cdcGtm.pushProductToDatalayer('product-click', product_id, attribute_id);
54		}
55	},
56
57	displayCategoryAsync : function(product_ids) {
58		if(product_ids) {
59			let params = '';
60			if (typeof cdcDatalayer !== 'undefined' && cdcDatalayer && cdcDatalayer.ecommerce && cdcDatalayer.ecommerce.items
61				&& cdcDatalayer.ecommerce.items.length > 0 && cdcDatalayer.ecommerce.items[0].item_list_id
62				&& /^cat_\d+$/.test(cdcDatalayer.ecommerce.items[0].item_list_id)) {
63
64				// Extract the number after 'cat_'
65				const item_list_id = cdcDatalayer.ecommerce.items[0].item_list_id;
66				categoryId = parseInt(item_list_id.split('_')[1], 10);
67				if(categoryId) {
68					params = 'id_category=' + categoryId;
69				}
70			}
71
72			cdcGtm.pushProductListToDatalayer('category-display', product_ids, params);
73		}
74	},
75
76	pushDalayerFromAjaxResponse : function(cdcgtmreq) {
77		if (cdcgtmreq.readyState == 4) { /* use 4 instead of XMLHttpRequest.DONE for browser compatibility */
78			if (cdcgtmreq.status == 200) {
79				var datalayerJs = cdcgtmreq.responseText;
80				try {
81					let datalayerJson = JSON.parse(datalayerJs);
82					// debug log - data pushed to datalayer
83					// console.log(JSON.stringify(datalayerJson, undefined, 4));
84					cdcGtm.clearDatalayer();
85					dataLayer.push(datalayerJson);
86				} catch(e) {
87					console.log("[CDCGTM] error while parsing json");
88				}
89			}
90		}
91	},
92
93	/**
94	 * Get product from ajax and push it to datalayer
95	 * @param action
96	 * @param product_id
97	 * @param attribute_id
98	 * @param qtity
99	 */
100	pushProductToDatalayer : function(action, product_id, attribute_id, qtity) {
101
102		// convert from NaN to default value
103		attribute_id = attribute_id || 0;
104		qtity = qtity || 1;
105
106		// get product async
107		var cdcgtmreq = new XMLHttpRequest();
108		cdcgtmreq.onreadystatechange = function () { cdcGtm.pushDalayerFromAjaxResponse(cdcgtmreq); }
109
110		if(typeof cdcGtmApi !== 'undefined' && cdcGtmApi) {
111			cdcgtmreq.open("GET", cdcGtmApi + "?action=" + action + "&id=" + product_id + "&id_attribute=" + attribute_id + "&qtity=" + qtity, true);
112			cdcgtmreq.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
113			cdcgtmreq.send();
114		} else {
115			console.error("[CDCGTM] cdcGtmApi not defined, cannot send action " + action);
116		}
117	},
118
119	/**
120	 * Get a datalayer from a list of product ids
121	 * then push product list to datalayer
122	 * @param action
123	 * @param product_ids
124	 */
125	pushProductListToDatalayer : function(action, product_ids, params) {
126
127		// get product async
128		var cdcgtmreq = new XMLHttpRequest();
129		cdcgtmreq.onreadystatechange = function () { cdcGtm.pushDalayerFromAjaxResponse(cdcgtmreq); }
130
131		if(typeof cdcGtmApi !== 'undefined' && cdcGtmApi) {
132			if(params) {
133				params = '&' + params;
134			} else {
135				params = '';
136			}
137			cdcgtmreq.open("POST", cdcGtmApi + "?action=" + action + params, true);
138			cdcgtmreq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
139			cdcgtmreq.send("id_products=" + JSON.stringify(product_ids));
140		} else {
141			console.error("[CDCGTM] cdcGtmApi not defined, cannot send action " + action);
142		}
143	},
144
145	/**
146	 * Use the current datalayer, add data to it
147	 * then push it to GTM datalayer
148	 * @param data
149	 */
150	/*pushDataToCurrentDatalayer : function(data) {
151		try {
152			dataLayer = dataLayer || [];
153			let baseDatalayer = cdcDatalayer || {}
154			let mergedDatalayer = {...baseDatalayer, ...data}
155			dataLayer.push(mergedDatalayer);
156		} catch(e) {
157			console.log("[CDCGTM][pushDataToCurrentDatalayer] ERROR ");
158		}
159	},*/
160
161	handleValidateShippingStep : function($submitBtn, event, shippingName, doNotPreventClick) {
162		if(!$submitBtn.hasClass('clicked')) {
163			if(typeof doNotPreventClick === 'undefined' || !doNotPreventClick) {
164				event.preventDefault();
165			}
166
167			// push infos to datalayer
168			try {
169				dataLayer = dataLayer || [];
170				let cloneDatalayer = cdcGtm.cloneDatalayer(cdcDatalayer);
171				cloneDatalayer.event = 'add_shipping_info';
172				cloneDatalayer.ecommerce.shipping_tier = shippingName;
173				cdcGtm.clearDatalayer();
174				dataLayer.push(cloneDatalayer);
175			} catch (e) {
176				console.error(e);
177			}
178
179			// exec button action after waiting to send the datalayer
180			if(typeof doNotPreventClick === 'undefined' || !doNotPreventClick) {
181				setTimeout(function () {
182					$submitBtn.addClass('clicked');
183					$submitBtn.click();
184				}, 500);
185			}
186		}
187	},
188
189	handleValidatePayementStep : function(paymentName) {
190		// push infos to datalayer
191		try {
192			dataLayer = dataLayer || [];
193			let cloneDatalayer = cdcGtm.cloneDatalayer(cdcDatalayer);
194			cloneDatalayer.event = 'add_payment_info';
195			cloneDatalayer.ecommerce.payment_type = paymentName;
196			cdcGtm.clearDatalayer();
197			dataLayer.push(cloneDatalayer);
198		} catch (e) {
199			console.error(e);
200		}
201	}
202
203
204}
205
206/* CART : ADD / REMOVE PRODUCT ****************************************************************************************/
207
208// Prestashop 1.5 || 1.6
209if(typeof(ajaxCart) != 'undefined') {
210	// override ajaxCart.add function
211	var ajaxCartAddFunc = ajaxCart.add;
212	ajaxCart.add = function(idProduct, idCombination, addedFromProductPage, callerElement, quantity, wishlist) {
213		ajaxCartAddFunc(idProduct, idCombination, addedFromProductPage, callerElement, quantity, wishlist);
214		cdcGtm.addToCart(idProduct, idCombination, quantity, addedFromProductPage, callerElement);
215	}
216
217	// override ajax.remove function
218	var ajaxCartRemoveFunc = ajaxCart.remove;
219	ajaxCart.remove = function(idProduct, idCombination, customizationId, idAddressDelivery) {
220		ajaxCartRemoveFunc(idProduct, idCombination, customizationId, idAddressDelivery);
221		cdcGtm.removeFromCart(idProduct, idCombination);
222	}
223}
224
225// Prestashop >= 1.7
226else if(typeof(prestashop) != 'undefined') {
227	$(document).ready(function () {
228
229		// Prestashop ajax cart
230		prestashop.on(
231			'updateCart',
232			function (event) {
233				let requestData = {};
234
235				if (event && event.reason) {
236					requestData = {
237						id_product_attribute: event.reason.idProductAttribute,
238						id_product: event.reason.idProduct,
239						action: event.reason.linkAction
240					};
241
242					let quantity = 1;
243					if(event.resp && event.resp.quantity) {
244						quantity = event.resp.quantity;
245					}
246
247					if(requestData.action == 'add-to-cart') {
248						cdcGtm.addToCart(requestData.id_product, requestData.id_product_attribute, quantity, null, null);
249					} else if(requestData.action == 'delete-from-cart') {
250						cdcGtm.removeFromCart(requestData.id_product, requestData.id_product_attribute, quantity);
251					}
252				}
253			}
254		);
255
256		// cart creativeelements
257		document.addEventListener(
258			'awp-add-to-cart',
259			function (event) {
260				cdcGtm.addToCart(event.detail.productId, event.detail.id_product_attribute, event.detail.quantity, true, 'awp-add-to-cart');
261			}
262		);
263	});
264}
265
266// override deleteProductFromSummary (checkout page)
267var deleteProductFromSummary = (function(id) {
268	var original_deleteProductFromSummary = deleteProductFromSummary;
269	return function(id) {
270		var productId = 0;
271		var productAttributeId = 0;
272		var ids = 0;
273		ids = id.split('_');
274		productId = parseInt(ids[0]);
275		if (typeof(ids[1]) !== 'undefined') {
276			productAttributeId = parseInt(ids[1]);
277		}
278
279		var cart_qtity = parseInt($('input[name=quantity_' + id + ']').val());
280
281		cdcGtm.removeFromCart(productId, productAttributeId, cart_qtity);
282		original_deleteProductFromSummary(id);
283	}
284})();
285
286// override downQuantity (checkout page)
287var downQuantity = (function(id, qty) {
288	var original_downQuantity = downQuantity;
289	return function(id, qty) {
290		var productId = 0;
291		var productAttributeId = 0;
292		var ids = 0;
293		ids = id.split('_');
294		productId = parseInt(ids[0]);
295		if (typeof(ids[1]) !== 'undefined') {
296			productAttributeId = parseInt(ids[1]);
297		}
298
299		// qty
300		var val = $('input[name=quantity_' + id + ']').val();
301		var newVal = val;
302		if(typeof(qty) == 'undefined' || !qty)
303		{
304			new_qty = 1;
305			newVal = val - 1;
306		}
307		else if (qty < 0)
308			new_qty = -qty;
309
310		// if qtity is > 0, decrease qtity, if qtity = 0, it will be handled by "deleteProductFromSummary"
311		if(newVal > 0) {
312			cdcGtm.removeFromCart(productId, productAttributeId, new_qty);
313		}
314
315		original_downQuantity(id, qty);
316	}
317})();
318
319/* CART : CHOSE SHIPPING / PAYMENT ************************************************************************************/
320$(document).ready(function () {
321
322	if(typeof ajaxShippingEvent !== 'undefined' && ajaxShippingEvent) {
323		// chose shipping - prestashop 1.7
324		$('#checkout').on('click', '#checkout-delivery-step button[type=submit]', function (e) {
325			let $submitBtn = $(this);
326
327			// get shipping name
328			let $selectedShipping = $submitBtn.closest('#checkout-delivery-step').find('.delivery-options input[type=radio]:checked').closest('.delivery-option');
329			let shippingName = $selectedShipping.find('.carrier-name').text().trim();
330
331			cdcGtm.handleValidateShippingStep($submitBtn, e, shippingName);
332		});
333
334
335		// chose shipping - prestashop 1.6
336		$('#order').on('click', 'button[name=processCarrier]', function(e) {
337			let $submitBtn = $(this);
338
339			// get shipping name
340			let $selectedShipping = $submitBtn.closest('form[name=carrier_area]').find('.delivery_options input[type=radio]:checked').closest('.delivery_option');
341			let shippingName = $selectedShipping.find('td:not(.delivery_option_radio):not(.delivery_option_logo):not(.delivery_option_price)').find('strong').text().trim();
342			cdcGtm.handleValidateShippingStep($submitBtn, e, shippingName);
343		});
344
345
346		// chose shipping - module OPC onepagecheckoutps
347		$('#checkout').on('click', '#opc_content button[data-step=payment]', function (e) {
348			let $submitBtn = $(this);
349
350			// get shipping name
351			let $selectedShipping = $('#opc_step_shipping #form-shipping_container input[type=radio]:checked').closest('.carrier-item-content');
352			let shippingName = $selectedShipping.find('.delivery-detail .delivery_option_title').text().trim();
353			cdcGtm.handleValidateShippingStep($submitBtn, e, shippingName);
354		});
355	}
356
357
358	if(typeof ajaxPaymentEvent !== 'undefined' && ajaxPaymentEvent) {
359		// chose payment - prestashop 1.7
360		$('#checkout').on('change', '#checkout-payment-step input[type=radio][name=payment-option]', function (e) {
361			// push infos to datalayer
362			let paymentName = $(this).closest('.payment-option').find('label').text().trim();
363			cdcGtm.handleValidatePayementStep(paymentName);
364		});
365
366		// chose payment - prestashop 1.6
367		$('#order').on('click', '#HOOK_PAYMENT .payment_module a', function (e, cdcGtmTriggered) {
368			let $submitBtn = $(this);
369			e.preventDefault();
370
371			// push infos to datalayer
372			cdcGtm.handleValidatePayementStep($submitBtn.attr('title'));
373
374			// redirect to payment after waiting to send the datalayer
375			setTimeout(function () {
376				window.location.href = $submitBtn.attr("href");
377			}, 500);
378		});
379
380		// chose payment - module OPC onepagecheckoutps
381		$('#checkout').on('change', '#opc_step_payment_body input[type=radio][name=payment-option]', function (e) {
382			// push infos to datalayer
383			let paymentName = $(this).closest('.payment-option').find('.payment_content').text().trim();
384			cdcGtm.handleValidatePayementStep(paymentName);
385		});
386	}
387
388});
389
390
391
392/* PRODUCT CLICK ******************************************************************************************************/
393$(document).ready(function () {
394
395	// prestashop 1.7
396	$('article[data-id-product]').find('a').on('click', function() {
397		let $product = $(this).closest('article[data-id-product]');
398		let idProduct = $product.data('id-product');
399		let idProductAttribute = $product.data('id-product-attribute') | 0;
400		cdcGtm.productClick(idProduct, idProductAttribute);
401	});
402
403	// prestashop 1.6
404	$('.ajax_block_product').find('a.product-name,a.product_img_link,a.lnk_view,a.quick-view-mobile').on('click', function(e) {
405		let $productInfos = $(this).closest('.ajax_block_product').find('.cdcgtm_product');
406		let idProduct = $productInfos.data('id-product');
407		let idProductAttribute = $productInfos.data('id-product-attribute') | 0;
408		cdcGtm.productClick(idProduct, idProductAttribute);
409	});
410});
411
412
413/* PRODUCT LIST - LAZY LOADING ************************************************************************************/
414if(typeof(prestashop) != 'undefined') {
415	$(document).ready(function () {
416
417		// update product list
418		prestashop.on(
419			'updateProductList',
420			function (event) {
421
422				// use DOMParser to parse the HTML product list
423				const parser = new DOMParser();
424				const doc = parser.parseFromString(event.rendered_products, 'text/html');
425
426				// use a querySelectorAll to get all the products (article)
427				const products = doc.querySelectorAll('article[data-id-product]');
428
429				// use Array.from to convert the NodeList into an array of products
430				const productsList = Array.from(products).map(product => (
431					product.getAttribute('data-id-product') + '-' + product.getAttribute('data-id-product-attribute')
432				));
433
434				cdcGtm.displayCategoryAsync(productsList);
435			}
436		);
437
438	});
439}

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.