PageSourceSearch

https://www.bestdirect.co.uk/skin/frontend/base/default/js/ec.js

js bestdirect.co.uk collected 2026-10-09 01:10:46 UTC 31,286 bytes, 1,536 lines download raw bytes

1/**
2 * Anowave Enhanced Ecommerce Tracking
3 *
4 * NOTICE OF LICENSE
5 *
6 * This source file is subject to the Anowave license that is
7 * available through the world-wide-web at this URL:
8 * http://www.anowave.com/license-agreement/
9 *
10 * DISCLAIMER
11 *
12 * Do not edit or add to this file if you wish to upgrade this extension to newer
13 * version in the future.
14 *
15 * @category 	Anowave
16 * @package 	Anowave_Ec
17 * @copyright 	Copyright (c) 2018 Anowave (http://www.anowave.com/)
18 * @license  	http://www.anowave.com/license-agreement/
19 */
20
21if ('undefined' === typeof log)
22{
23	var log = function (message) 
24	{
25	   	window.console && console.log ? console.log(message) : null;
26	};
27}
28
29var AEC = 
30{
31	textarea:null,
32	gtm: function()
33	{
34		if ("undefined" === typeof google_tag_manager)
35		{
36			/**
37			 * Log error to console
38			 */
39			log('Unable to detect Google Tag Manager. Please verify if GTM install snippet is available.');
40			
41			return false;
42		}
43
44		return true;
45	},
46	ajax: function(context,dataLayer)
47	{
48		var element = jQuery(context), qty = jQuery(':radio[name=qty]:checked, :text[name=qty], select[name=qty]').eq(0).val(), variant = [];
49
50		/**
51		 * Default quantity
52		 */
53		if ('undefined' === typeof qty)
54		{
55			qty = 1;
56		}
57		
58		/**
59		 * Collection of products added to cart
60		 */
61		var products = [];
62		
63		if (AEC.forceSelectors)
64		{
65			jQuery.each(['id','name','category','brand','price','quantity', 'attributes'], function(index, attribute)
66			{
67				element.data(attribute, element.attr('data-' + attribute));
68			});
69		}
70		
71		var attributes = {};
72		
73		if (!AEC.gtm())
74		{
75			/**
76			 * Invoke original click event(s)
77			 */
78			if (element.data('click'))
79			{
80				/**
81				 * Track time 
82				 */
83				AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
84				
85				eval(element.data('click'));
86			}
87			
88			return true;
89		}
90		
91		if(element.data('configurable'))
92		{
93			var attributes = jQuery('[name^="super_attribute"]'), variants = [];
94
95			/**
96			 * Load super attsibutes
97			 */
98
99			jQuery.each(attributes, function(index, attribute)
100			{
101				if (jQuery(attribute).is('select'))
102				{
103					var name = jQuery(attribute).attr('name'), id = name.substring(name.indexOf('[') + 1, name.lastIndexOf(']'));
104
105					var option = jQuery(attribute).find('option:selected');
106
107					if (0 < parseInt(option.val()))
108					{
109						variants.push(
110						{
111							id: 	id,
112							text: 	option.text()
113						});
114					}
115				}
116			});
117
118			if (attributes.length == variants.length)
119			{
120				for (i = 0, l = variants.length; i < l; i++)
121				{
122					for (a = 0, b = AEC.SUPER.length; a < b; a++)
123					{
124						if (AEC.SUPER[a].id == variants[i].id)
125						{
126							variant.push([AEC.SUPER[a].label,variants[i].text].join(':'));
127						}
128					}
129				}
130			}
131
132			if (!variant.length)
133			{
134				/**
135				 * Invoke original click event(s)
136				 */
137				if (element.data('click'))
138				{
139					/**
140					 * Track time 
141					 */
142					AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
143					
144					eval(element.data('click'));
145				}
146				
147				return false;
148			}
149		}
150
151		/**
152		 * Handle products with custom options
153		 */
154		if (element.data('options'))
155		{
156			variant = variant || [], options = element.data('options');
157			
158			var variant = (function($, variant)
159			{
160				var options = element.data('options'), current = [];
161
162				$('[name^="options"]').each(function()
163				{
164					$(this).find('option:selected').each(function()
165					{
166						current.push(parseInt($(this).attr('value')));
167					});
168				});
169
170				var currentOptions = [];
171
172				for (i = 0, l = current.length; i < l; i++)
173				{
174					$.each(options, function(index, option)
175					{
176						if (parseInt(option.id) == parseInt(current[i]))
177						{
178							currentOptions.push([option.label.toString().trim(),option.value.toString().trim()].join(':'));
179						}
180					});
181				}
182
183				variant.push(currentOptions.join('-'));
184
185				/**
186				 * Return variant
187				 */
188				return variant;
189				
190			})(jQuery, variant);
191		}
192
193		/**
194		 * Handle grouped elements
195		 */
196		if (element.data('grouped'))
197		{
198			for (u = 0, y = window.G.length; u < y; u++)
199			{
200				var qty = Math.abs(jQuery('[name="super_group[' + window.G[u].id + ']"]').val());
201
202				if (qty)
203				{
204					products.push(
205					{
206						'name': 		window.G[u].name,
207						'id': 		    window.G[u].sku,
208						'price': 		window.G[u].price,
209						'category': 	window.G[u].category,
210						'brand':		window.G[u].brand,
211						'quantity': 	qty
212					});
213				}
214			}
215		}
216		else
217		{
218			products.push(
219			{
220				'name': 		AEC.convert(element.data('name')),
221				'id': 		    AEC.convert(element.data('id')),
222				'price': 		AEC.convert(element.data('price')),
223				'category': 	AEC.convert(element.data('category')),
224				'brand':		AEC.convert(element.data('brand')),
225				'variant':		variant.join('-'),
226				'quantity': 	qty
227			});
228		}
229		
230		/**
231		 * Affiliation attributes
232		 */
233		for (i = 0, l = products.length; i < l; i++)
234		{
235			(function(product)
236			{
237				jQuery.each(AEC.parseJSON(element.data('attributes')), function(key, value)
238				{
239					product[key] = value;
240				});
241			})(products[i]);
242		}
243
244		var data = 
245		{
246			'event': 'addToCart',
247			'eventLabel': element.data('name'),
248			'ecommerce': 
249			{
250				'currencyCode': AEC.currencyCode,
251				'add': 
252				{
253					'products': products
254				}
255			},
256			'eventCallback': function() 
257			{
258				if (AEC.eventCallback)
259				{
260					if (element.data('click'))
261					{
262						eval(element.data('click'));
263					}
264					else if (element.is('a'))
265					{
266						document.location = element.attr('href');
267					}
268					else if (element.is('img') && element.parent().is('a'))
269					{
270						document.location = element.parent().attr('href');
271					}
272					else 
273					{
274						return true;
275					}
276				}
277	     	},
278	     	'eventTimeout': AEC.eventTimeout
279		};
280		
281		/**
282		 * Cookie Consent
283		 */
284		(function(callback, element)
285		{
286			if (AEC.CookieConsent.support)
287			{
288				if (AEC.CookieConsent.granted)
289				{
290					AEC.CookieConsent.queue(callback).process();
291				}
292				else 
293				{
294					if (AEC.eventCallback)
295					{
296						if (element.data('click'))
297						{
298							eval(element.data('click'));
299						}
300						else if (element.is('a'))
301						{
302							document.location = element.attr('href');
303						}
304						else if (element.is('img') && element.parent().is('a'))
305						{
306							document.location = element.parent().attr('href');
307						}
308						else 
309						{
310							return true;
311						}
312					}
313				}
314			}
315			else 
316			{
317				return callback.apply(window,[]);
318			}
319			
320		})((function(dataLayer,data, element)
321		{
322			return function()
323			{
324				dataLayer.push(data);
325				
326				/**
327				 * Track time 
328				 */
329				AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
330			}
331		})(dataLayer,data, element), element);
332
333		/**
334		 * Facebook Pixel Tracking
335		 */
336		if (AEC.facebook)
337		{
338			if ("undefined" !== typeof fbq)
339			{
340				var fb = [], price = 0;
341	
342				for (i = 0, l = products.length; i < l; i++)
343				{
344					fb.push(products[i].id);
345
346					/**
347					 * Accumulative price
348					 */
349					price += parseFloat(products[i].price);
350				}
351
352				(function(callback)
353				{
354					if (AEC.CookieConsent.support)
355					{
356						AEC.CookieConsent.queue(callback).process();
357					}
358					else 
359					{
360						callback.apply(window,[]);
361					}
362					
363				})((function(price, element)
364				{
365					return function()
366					{
367						fbq('track', 'AddToCart', 
368						{
369							content_name: 	element.data('name'),
370							content_ids: 	fb,
371							content_type: 	'product',
372							value: 			price,
373							currency: 		AEC.currencyCode
374						});
375					}
376				})(price, element));
377			}
378		}
379		
380		if (AEC.eventCallback)
381		{
382			return false;
383		}
384
385		return true;
386	},
387	click: function(context,dataLayer)
388	{
389		var element = jQuery(context);
390
391		if (AEC.forceSelectors)
392		{
393			jQuery.each(['id','name','category','brand','price','quantity','position','attributes'], function(index, attribute)
394			{
395				element.data(attribute, element.attr('data-' + attribute));
396			});
397		}
398
399		if (!AEC.gtm())
400		{
401			/**
402			 * Track time 
403			 */
404			AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_CLICK, element.data('name'), element.data('category'));
405			
406			return true;
407		}
408		
409		var item = 
410		{
411			'name': 		AEC.convert(element.data('name')),
412			'id': 			AEC.convert(element.data('id')),
413			'price': 		AEC.convert(element.data('price')),
414			'category': 	AEC.convert(element.data('category')),
415			'brand':		AEC.convert(element.data('brand')),
416			'quantity': 	AEC.convert(element.data('quantity')),
417			'position':		AEC.convert(element.data('position'))
418		};
419		
420		/**
421		 * Affiliation attributes
422		 */
423		jQuery.each(AEC.parseJSON(element.data('attributes')), function(key, value)
424		{
425			item[key] = value;
426		});
427
428		var data = 
429		{
430			'event': 'productClick',
431			'eventLabel': AEC.convert(element.data('name')),
432			'ecommerce': 
433			{
434				'click': 
435				{
436					'actionField': 
437					{
438						'list': AEC.convert(element.data('list'))
439					},
440					'products': 
441					[
442						item
443					]
444				}
445			},
446			'eventCallback': function() 
447			{
448				if (AEC.eventCallback)
449				{
450					if (element.data('click'))
451					{
452						eval(element.data('click'));
453					}
454					else if (element.is('a'))
455					{
456						document.location = element.attr('href');
457					}
458					else if (element.is('img') && element.parent().is('a'))
459					{
460						document.location = element.parent().attr('href');
461					}
462					else 
463					{
464						return true;
465					}
466				}
467	     	},
468	     	'eventTimeout': AEC.eventTimeout, 
469	     	'eventTarget': (function(element)
470	     	{
471	     		/**
472	     		 * Default target
473	     		 */
474	     		var target = 'Default';
475	     		
476	     		/**
477	     		 * Check if element is anchor
478	     		 */
479	     		if (element.is('a'))
480	     		{
481	     			target = 'Link';
482	     			
483	     			if (element.children().first().is('img'))
484	     			{
485	     				target = 'Image';
486	     			}
487	     		}
488	     		
489	     		if (element.is('button'))
490	     		{
491	     			target = 'Button';
492	     		}
493	     		
494	     		return target;
495	     		
496	     	})(element)
497		};
498
499		/**
500		 * Cookie Consent
501		 */
502		(function(callback, element)
503		{
504			if (AEC.CookieConsent.support)
505			{
506				if (AEC.CookieConsent.granted)
507				{
508					AEC.CookieConsent.queue(callback).process();
509				}
510				else 
511				{
512					if (AEC.eventCallback)
513					{
514						if (element.data('click'))
515						{
516							eval(element.data('click'));
517						}
518						else if (element.is('a'))
519						{
520							document.location = element.attr('href');
521						}
522						else if (element.is('img') && element.parent().is('a'))
523						{
524							document.location = element.parent().attr('href');
525						}
526						else 
527						{
528							return true;
529						}
530					}
531				}
532			}
533			else 
534			{
535				return callback.apply(window,[]);
536			}
537			
538		})((function(dataLayer,data, element)
539		{
540			return function()
541			{
542				dataLayer.push(data);
543				
544				/**
545				 * Track time 
546				 */
547				AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_CLICK, element.data('name'), element.data('category'));
548			}
549		})(dataLayer,data, element), element);	
550
551		if (AEC.eventCallback)
552		{
553			return false;
554		}
555		
556		return true;
557	},
558	ajaxList:function(context,dataLayer)
559	{
560		var element = jQuery(context);
561		
562		/**
563		 * Collection of products added to cart
564		 */
565		var products = [];
566
567		if (AEC.forceSelectors)
568		{
569			jQuery.each(['id','name','category','brand','price','quantity'], function(index, attribute)
570			{
571				element.data(attribute, element.attr('data-' + attribute));
572			});
573		}
574
575		if (!AEC.gtm())
576		{
577			if (AEC.eventCallback)
578			{
579				/**
580				 * Invoke original click event(s)
581				 */
582				if (element.data('click'))
583				{
584					/**
585					 * Track time 
586					 */
587					AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
588					
589					eval(element.data('click'));
590				}
591			}
592			
593			return true;
594		}
595
596		products.push(
597		{
598			'name': 		AEC.convert(element.data('name')),
599			'id': 		    AEC.convert(element.data('id')),
600			'price': 		AEC.convert(element.data('price')),
601			'category': 	AEC.convert(element.data('category')),
602			'brand':		AEC.convert(element.data('brand')),
603			'quantity': 	1
604		});
605		
606		/**
607		 * Affiliation attributes
608		 */
609		for (i = 0, l = products.length; i < l; i++)
610		{
611			(function(product)
612			{
613				jQuery.each(AEC.parseJSON(element.data('attributes')), function(key, value)
614				{
615					product[key] = value;
616				});
617			})(products[i]);
618		}
619
620		var data = 
621		{
622			'event': 'addToCart',
623			'eventLabel': element.data('name'),
624			'ecommerce': 
625			{
626				'currencyCode': AEC.currencyCode,
627				'add': 
628				{
629					'actionField':
630					{
631						'list':AEC.convert(element.data('list'))
632					},
633					'products': products
634				}
635			}
636		};
637
638		/**
639		 * Cookie Consent
640		 */
641		(function(callback, element)
642		{
643			if (AEC.CookieConsent.support)
644			{
645				if (AEC.CookieConsent.granted)
646				{
647					AEC.CookieConsent.queue(callback).process();
648				}
649				else 
650				{
651					if (element.data('click'))
652					{
653						/**
654						 * Track time 
655						 */
656						AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
657						
658						/**
659						 * Evaluate original click
660						 */
661						eval(element.data('click'));
662					}
663				}
664			}
665			else 
666			{
667				return callback.apply(window,[]);
668			}
669			
670		})((function(dataLayer,data, element)
671		{
672			return function()
673			{
674				dataLayer.push(data);
675				
676				/**
677				 * Track time 
678				 */
679				AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_ADD_TO_CART, element.data('name'), element.data('category'));
680			}
681		})(dataLayer,data, element), element);	
682		
683		/**
684		 * Invoke original click event(s)
685		 */
686		if (AEC.eventCallback)
687		{
688			if (element.data('click'))
689			{
690				eval(element.data('click'));
691			}
692		}
693
694		if (AEC.facebook)
695		{
696			if ("undefined" !== typeof fbq)
697			{
698				var fb = [], price = 0;
699	
700				for (i = 0, l = products.length; i < l; i++)
701				{
702					fb.push(products[i].id);
703
704					/**
705					 * Accumulative price
706					 */
707					price += parseFloat(products[i].price);
708				}
709
710				(function(callback)
711				{
712					if (AEC.CookieConsent.support)
713					{
714						AEC.CookieConsent.queue(callback).process();
715					}
716					else 
717					{
718						callback.apply(window,[]);
719					}
720					
721				})((function(price, element)
722				{
723					return function()
724					{
725						fbq('track', 'AddToCart', 
726						{
727							content_name: 	element.data('name'),
728							content_ids: 	fb,
729							content_type: 	'product',
730							value: 			price,
731							currency: 		AEC.currencyCode
732						});
733					}
734				})(price, element));
735			}
736		}
737		
738		return true;
739	},
740	remove: function(context, dataLayer)
741	{
742		var element = jQuery(context);
743
744		if (AEC.forceSelectors)
745		{
746			jQuery.each(['id','name','category','brand','price','quantity'], function(index, attribute)
747			{
748				element.data(attribute, element.attr('data-' + attribute));
749			});
750		}
751		
752		if (!AEC.gtm())
753		{
754			/**
755			 * Track time 
756			 */
757			AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_REMOVE_FROM_CART, element.data('name'), element.data('category'));
758			
759			return true;
760		}
761		
762		var item = 
763		{
764			'name': 		AEC.convert(element.data('name')),
765			'id': 			AEC.convert(element.data('id')),
766			'price': 		AEC.convert(element.data('price')),
767			'category': 	AEC.convert(element.data('category')),
768			'brand':		AEC.convert(element.data('brand')),
769			'quantity': 	AEC.convert(element.data('quantity')),
770			'variant':		AEC.convert(element.data('variant'))
771		};
772		
773		/**
774		 * Affiliation attributes
775		 */
776		jQuery.each(AEC.parseJSON(element.data('attributes')), function(key, value)
777		{
778			item[key] = value;
779		});
780
781		var data = 
782		{
783			'event': 'removeFromCart',
784			'eventLabel': AEC.convert(element.data('name')),
785			'ecommerce': 
786			{
787				'remove': 
788				{   
789					'products': 
790					[
791						item
792					]
793				}
794			},
795			'eventCallback': function() 
796			{
797				if (AEC.eventCallback)
798				{
799					if (element.data('click'))
800					{
801						eval(element.data('click'));
802					}
803					else if (element.is('a'))
804					{
805						document.location = element.attr('href');
806					}
807					else if (element.is('img') && element.parent().is('a'))
808					{
809						document.location = element.parent().attr('href');
810					}
811					else 
812					{
813						return true;
814					}
815				}
816	     	},
817	     	'eventTimeout': AEC.eventTimeout
818		};
819
820		if (element.data('mini-cart'))
821		{
822			if ('undefined' !== typeof Minicart)
823			{
824				(function(dataLayer, data, Minicart, element, stop)
825				{
826					Minicart.prototype.updateContentOnRemove = Minicart.prototype.updateContentOnRemove.wrap(function(parentMethod, result, el)
827					{
828						parentMethod(result, el);
829
830						if (!stop)
831						{
832							stop = true;
833
834							if ('undefined' != typeof result.success)
835							{
836								data['eventCallback'] 	= function(){};
837
838								/**
839								 * Fallback for private mode
840								 */
841								data['eventTimeout'] = AEC.eventTimeout;
842
843								/**
844								 * Push data
845								 */
846								dataLayer.push(data);
847	
848								/**
849								 * Track time 
850								 */
851								AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_REMOVE_FROM_CART, element.data('name'));
852							}
853						}
854					});
855				})(dataLayer, data, Minicart, element, false);
856			}
857		}
858		else 
859		{
860			/**
861			 * Track event
862			 */
863			dataLayer.push(data);
864
865			/**
866			 * Track time 
867			 */
868			AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_REMOVE_FROM_CART, element.data('name'));
869		}
870		
871		if (AEC.eventCallback)
872		{
873			return false;
874		}
875		
876		return true;
877	},
878	wishlist: function(context,dataLayer)
879	{
880		var element = jQuery(context);
881		
882		if (AEC.forceSelectors)
883		{
884			jQuery.each(['id','name','category','brand','price','quantity'], function(index, attribute)
885			{
886				element.data(attribute, element.attr('data-' + attribute));
887			});
888		}
889		
890		if (!AEC.gtm())
891		{
892			/**
893			 * Track time 
894			 */
895			AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_WISHLIST, element.data('name'), element.data('category'));
896			
897			return true;
898		}
899
900		var data = 
901		{
902			'event': 'addToWishlist',
903			'eventLabel': AEC.convert(element.data('name')),
904			'eventCallback': function() 
905			{
906				if (AEC.eventCallback)
907				{
908					if (element.data('click'))
909					{
910						eval(element.data('click'));
911					}
912					else if (element.is('a'))
913					{
914						document.location = element.attr('href');
915					}
916					else if (element.is('img') && element.parent().is('a'))
917					{
918						document.location = element.parent().attr('href');
919					}
920					else 
921					{
922						return true;
923					}
924				}
925	     	},
926	     	'eventTimeout': AEC.eventTimeout
927		};
928
929		/**
930		 * Push data
931		 */
932		dataLayer.push(data);
933
934		/**
935		 * Track time 
936		 */
937		AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_WISHLIST, element.data('name'), element.data('category'));
938
939		if (AEC.eventCallback)
940		{
941			return false;
942		}
943		
944		return true;
945	},
946	compare: function(context,dataLayer)
947	{
948		var element = jQuery(context);
949		
950		if (AEC.forceSelectors)
951		{
952			jQuery.each(['id','name','category','brand','price','quantity'], function(index, attribute)
953			{
954				element.data(attribute, element.attr('data-' + attribute));
955			});
956		}
957		
958		if (!AEC.gtm())
959		{
960			/**
961			 * Track time 
962			 */
963			AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_COMPARE, element.data('name'), element.data('category'));
964			
965			return true;
966		}
967
968		var data = 
969		{
970			'event': 'addToCompare',
971			'eventLabel': AEC.convert(element.data('name')),
972			'eventCallback': function() 
973			{
974				if (AEC.eventCallback)
975				{
976					if (element.data('click'))
977					{
978						eval(element.data('click'));
979					}
980					else if (element.is('a'))
981					{
982						document.location = element.attr('href');
983					}
984					else if (element.is('img') && element.parent().is('a'))
985					{
986						document.location = element.parent().attr('href');
987					}
988					else 
989					{
990						return true;
991					}
992				}
993	     	},
994	     	'eventTimeout': AEC.eventTimeout
995		};
996
997		/**
998		 * Push data
999		 */
1000		dataLayer.push(data);
1001
1002		/**
1003		 * Track time 
1004		 */
1005		AEC.Time.track(dataLayer, AEC.Const.TIMING_CATEGORY_PRODUCT_COMPARE, element.data('name'), element.data('category'));
1006
1007		if (AEC.eventCallback)
1008		{
1009			return false;
1010		}
1011		
1012		return true;
1013	},
1014	Callbacks:(function()
1015	{
1016		return {
1017			callbacks:[],
1018			queue: function(callback)
1019			{
1020				this.callbacks.push(callback);
1021
1022				return this;
1023			},
1024			apply: function()
1025			{
1026				while (this.callbacks.length)
1027				{
1028					this.callbacks.shift().apply(AEC,arguments);
1029				}
1030
1031				return this;
1032			}
1033		}
1034	})(),
1035	Time:(function()
1036	{
1037		var T = 
1038		{
1039			event: 			'trackTime',
1040			timingCategory:	'',
1041			timingVar:		'',
1042			timingValue:	-1,
1043			timingLabel:	''
1044		};
1045
1046		var time = new Date().getTime();
1047		
1048		return {
1049			track: function(dataLayer, category, variable, label)
1050			{
1051				T.timingValue = (new Date().getTime()) - time;
1052				
1053				if (category)
1054				{
1055					T.timingCategory = category;
1056				}
1057
1058				if (variable)
1059				{
1060					T.timingVar = variable;
1061				}
1062
1063				if (label)
1064				{
1065					T.timingLabel = label;
1066				}
1067				
1068				/**
1069				 * Track time
1070				 */
1071				dataLayer.push(T);
1072			},
1073			trackContinue: function(dataLayer, category, variable, label)
1074			{
1075				this.track(dataLayer, category, variable, label);
1076
1077				/**
1078				 * Reset time
1079				 */
1080				time = new Date().getTime();
1081			}
1082		}
1083	})(),
1084	Queue: (function()
1085	{
1086		return {
1087			queue: [],
1088			impressions: function(data)
1089			{
1090				if (AEC.Const.Viewport)
1091				{
1092					jQuery(window).on(
1093					{
1094						load: function()
1095						{
1096							AEC.Queue.bind(data);
1097						}
1098					});
1099				}
1100				else
1101				{
1102					dataLayer.push(data);
1103				}
1104				
1105				return this;
1106			},
1107			bind: function(data)
1108			{
1109				(function(queue, impressions)
1110				{
1111					jQuery.each(impressions, function(index, impression)
1112					{
1113						queue.push(impression);
1114					});
1115
1116				})(this.queue, data.ecommerce.impressions);
1117	
1118				if (this.queue.length)
1119				{
1120					(function(Queue, $)
1121					{
1122						var scroll = function()
1123						{
1124							var items = 
1125							{
1126								noticeable: $.grep(Queue.queue, function(element, index)
1127								{ 
1128									return Queue.isOnScreen($('[data-id="' + element.id + '"][data-event=productClick]'));
1129								}),
1130								concealed: $.grep(Queue.queue, function(element, index)
1131								{ 
1132									return !Queue.isOnScreen($('[data-id="' + element.id + '"][data-event=productClick]'));
1133								})
1134							};
1135							
1136							if (items.noticeable.length)
1137							{
1138								/**
1139								 * Set event
1140								 */
1141								data.event = 'impression';
1142								
1143								/**
1144								 * Update impressions event
1145								 */
1146								data.ecommerce.impressions 	= items.noticeable;
1147								
1148								/**
1149								 * Push item to dataLayer
1150								 */
1151								dataLayer.push(data);
1152							}
1153
1154							if (items.concealed.length)
1155							{
1156								Queue.queue = items.concealed;
1157							}
1158							else 
1159							{
1160								/**
1161								 * Empty queue
1162								 */
1163								Queue.queue = [];
1164								
1165								/**
1166								 * Remove scroll listener
1167								 */
1168								$(window).off('scroll.ec');
1169							}
1170						};
1171
1172						$(window).on('scroll.ec',scroll).trigger('scroll');
1173						
1174					})(this, jQuery);
1175				}
1176			},
1177			isOnScreen: function(element)
1178			{
1179				/**
1180				 * Default viewport
1181				 */
1182				var viewport = 
1183				{
1184					top: 0, bottom: 0
1185				};
1186				
1187				/**
1188				 * Check if element exists
1189				 */
1190				if ('undefined' === typeof element)
1191				{
1192					return false;
1193				}
1194				
1195				/**
1196				 * Get top
1197				 */
1198			    viewport.top = jQuery(window).scrollTop();
1199			    
1200			    /**
1201			     * Get bottom
1202			     */
1203			    viewport.bottom = viewport.top + jQuery(window).height();
1204			    
1205			    /**
1206			     * Get bounds
1207			     */
1208			    var bounds = {}, offset = element.offset();
1209			    
1210			    if ('undefined' === typeof offset)
1211			    {
1212			    	return false;
1213			    }
1214			    
1215			    if (offset == null)
1216			    {
1217			    	return false;
1218			    }
1219
1220			    if (!offset.hasOwnProperty('top'))
1221			    {
1222			    	return false;
1223			    }
1224
1225			    /**
1226			     * Get bounds top
1227			     */
1228			    bounds.top = element.offset().top;
1229 
1230			    /**
1231			     * Get bounds bottom
1232			     */
1233			    bounds.bottom = bounds.top + element.outerHeight();
1234			    
1235			    return ((bounds.top <= viewport.bottom) && (bounds.bottom >= viewport.top));
1236			}
1237		}
1238	})(),
1239	Cookie: (function() //This is an experimental feature to overcome FPC (Full Page Cache) related issues (beta)
1240	{
1241		return {
1242			data: null,
1243			privateData: null,
1244			push: function(dataLayer)
1245			{
1246				if (this.data)
1247				{
1248					/**
1249					 * Push data
1250					 */
1251					dataLayer.push(this.data);
1252
1253					/**
1254					 * Reset data to prevent further push
1255					 */
1256					this.data = null;
1257				}
1258				
1259				return this;
1260			},
1261			pushPrivate: function()
1262			{
1263				var data = this.getPrivateData();
1264				
1265				if (data)
1266				{
1267					dataLayer.push(
1268					{
1269						privateData: data
1270					});
1271				}
1272				
1273				return this;
1274			},
1275			visitor: function(data)
1276			{
1277				/**
1278				 * Set data
1279				 */
1280				this.data = data;
1281				
1282				return this;
1283			},
1284			detail: function(data)
1285			{
1286				this.data = data;
1287				
1288				return this;
1289			},
1290			purchase: function(data)
1291			{
1292				this.data = data;
1293				
1294				return this;
1295			},
1296			impressions: function(data)
1297			{
1298				this.data = data;
1299				
1300				return this;
1301			},
1302			checkout: function(data)
1303			{
1304				this.data = data;
1305				
1306				return this;
1307			},
1308			promotion: function(data)
1309			{
1310				this.data = data;
1311				
1312				return this;
1313			},
1314			promotionClick: function(data)
1315			{
1316				this.data = data;
1317				
1318				return this;
1319			},
1320			getPrivateData: function()
1321			{
1322				if (!this.privateData)
1323				{
1324					var cookie = this.get('privateData');
1325					
1326					if (cookie)
1327					{
1328						this.privateData = this.parse(cookie);
1329					}
1330				}
1331				
1332				return this.privateData;
1333			},
1334			get: function(name)
1335			{
1336				var start = document.cookie.indexOf(name + "="), len = start + name.length + 1;
1337				
1338				if ((!start) && (name != document.cookie.substring(0, name.length))) 
1339				{
1340				    return null;
1341				}
1342				
1343				if (start == -1) 
1344				{
1345					return null;
1346				}
1347									
1348				var end = document.cookie.indexOf(String.fromCharCode(59), len);
1349									
1350				if (end == -1) 
1351				{
1352					end = document.cookie.length;
1353				}
1354				
1355				return decodeURIComponent(document.cookie.substring(len, end));
1356			},
1357			remove: function(name) 
1358			{   
1359                document.cookie = name + "=" + "; path=/; expires=" + (new Date(0)).toUTCString();
1360                
1361                return this;
1362            },
1363			parse: function(json)
1364			{
1365				var json = decodeURIComponent(json.replace(/\+/g, ' '));
1366				
1367                return JSON.parse(json);
1368			},
1369			Storage: (function(cookie) //@todo: Replace Cookie with localStorage
1370			{
1371				return {
1372					set: function(property, value)
1373					{
1374						if ('undefined' !== typeof(Storage))
1375						{
1376							localStorage.setItem(property, JSON.stringify(value));
1377						}
1378						
1379						return this;
1380						
1381					},
1382					get: function(property)
1383					{
1384						if ('undefined' !== typeof(Storage))
1385						{
1386							return JSON.parse(localStorage.getItem(property));
1387						}
1388						
1389						return null;
1390					}
1391				}
1392			})(this)
1393		}
1394	})(),
1395	CookieConsent: (function()
1396	{
1397		return {
1398			support: false,
1399			granted: false,
1400			chain: [],
1401			queue: function(callback)
1402			{
1403				this.chain.push(callback);
1404				
1405				return this;
1406			},
1407			dispatch: function(consent)
1408			{
1409				this.granted = true;
1410				
1411				dataLayer.push(consent);
1412				
1413				return this.process();
1414			},
1415			process: function()
1416			{
1417				if (this.granted)
1418				{
1419					for (i = 0, l = this.chain.length; i < l; i++)
1420					{
1421						this.chain[i].apply(this,[]);
1422					}
1423					
1424					/**
1425					 * Reset chain
1426					 */
1427					this.chain = [];
1428				}
1429				
1430				return this;
1431			}
1432		}
1433	})(),
1434	convert: function(content)
1435	{
1436		if ('undefined' !== typeof content && '' !== content && -1 !== content.toString().indexOf('&'))
1437		{
1438			if (null == this.textarea)
1439			{
1440				this.textarea = jQuery('<textarea/>');
1441			}
1442			
1443			return this.textarea.html(content).text();
1444		}
1445
1446		return content;
1447	},
1448	getClientId: function()
1449	{
1450		if ('undefined' !== typeof ga)
1451		{
1452			return ga.getAll()[0].get('clientId');
1453		}
1454	},
1455	parseJSON: function(content)
1456	{
1457		if ('object' === typeof content)
1458		{
1459			return content;
1460		}
1461		
1462		if ('string' === typeof content)
1463		{
1464			try 
1465			{
1466				return JSON.parse(content);
1467			}
1468			catch (e){}
1469		}
1470		
1471		return {};
1472	}
1473};
1474
1475var GOOGLE_PAYLOAD_SIZE = 8192;
1476
1477/**
1478 * Calculate payload size (approx.)
1479 *  
1480 * @return int bytes
1481 */
1482var getPayloadSize = function(object) 
1483{
1484    var objects = [object];
1485    var size = 0;
1486
1487    for (var index = 0; index < objects.length; index++) 
1488    {
1489        switch (typeof objects[index]) 
1490        {
1491            case 'boolean':
1492                size += 4;
1493                break;
1494            case 'number':
1495                size += 8;
1496                break;
1497            case 'string':
1498                size += 2 * objects[index].length;
1499                break;
1500            case 'object':
1501                if (Object.prototype.toString.call(objects[index]) != '[object Array]') 
1502                {
1503                    for (var key in objects[index]) size += 2 * key.length;
1504                }
1505                for (var key in objects[index]) 
1506                {
1507                    var processed = false;
1508                    
1509                    for (var search = 0; search < objects.length; search++) 
1510                    {
1511                        if (objects[search] === objects[index][key]) {
1512                            processed = true;
1513                            break;
1514                        }
1515                    }
1516                    if (!processed) objects.push(objects[index][key]);
1517                }
1518        }
1519    }
1520    return size;
1521};
1522
1523/**
1524 * Chunk payload
1525 */
1526var getPayloadChunks = function(arr, len) 
1527{
1528	var chunks = [],i = 0, n = arr.length;
1529
1530	while (i < n) 
1531	{
1532	    chunks.push(arr.slice(i, i += len));
1533	}
1534
1535	return chunks;
1536};

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.