PageSourceSearch

https://www.privateequityinternational.com/wp-content/plugins/prag…/js/visitor-interactions.js?ver=1699264577

js privateequityinternational.com collected 2026-09-24 17:38:40 UTC 27,881 bytes, 1,036 lines download raw bytes

1/*global jQuery, Cookies, pwlGtmDataLayer:true*/
2
3/**
4 * Set the array for the Visitor Interactions JS
5 *
6 * PEI Media Data Layer Instructions v2.0
7 * 4. Visitor Interactions Tracking
8 *
9 * @type {object}
10 */
11var visitorInteractions = {};
12
13( function () {
14	'use strict';
15
16	visitorInteractions = {
17
18		/**
19		 * Detect when a user clicks to start a login or closes the dropdown.
20		 */
21		loginStartButton: function () {
22
23			var pageType = pwlGtmDataLayer.findDlValue( 'pageType' ),
24				pageName = pwlGtmDataLayer.findDlValue( 'pageName' );
25
26			var loginEvent = {
27				'event': 'virtual page view',
28				'pageType': 'identity',
29				'pageName': '/ga-virtual/identity/login',
30			};
31
32			var registerEvent = {
33				'event': 'virtual page view',
34				'pageType': 'identity',
35				'pageName': '/ga-virtual/identity/register',
36			};
37
38			var dropdownClosedEvent = {
39				'event': 'virtual page view',
40				'pageType': pageType,
41				'pageName': pageName,
42			};
43
44			// Top header dropdown.
45			jQuery( document ).on( 'click', '.top-header-user-account.td_ul_login > li > a', function () {
46				var topHeaderLogin = jQuery( '.top-header-user-account.td_ul_login' ),
47					loginForm = topHeaderLogin.find( '#blaize-login-top' ),
48					subMenu = topHeaderLogin.find( '.sub-menu' );
49
50				// Trigger the homepage event when the dropdown is closed,
51				// else trigger the login event.
52				if ( subMenu.length && subMenu.css( 'display' ) === 'none' ) {
53					pwlGtmDataLayer.push( dropdownClosedEvent );
54				} else if ( loginForm.length && loginForm.css( 'display' ) === 'block' ) {
55					pwlGtmDataLayer.push( loginEvent );
56				}
57			} );
58
59			// Trigger the homepage event when the dropdown is closed by clicking
60			// outside of it. Also check that the login/register form is not
61			// visible to avoid triggering the same event twice.
62			// See "pwl_modal_closed".
63			jQuery( document ).on( 'pwl_login_menu_closed', function () {
64				if ( jQuery( '.mfp-wrap.mfp-ready' ).length === 0 ) {
65					pwlGtmDataLayer.push( dropdownClosedEvent );
66				}
67			});
68
69			// "Click here to login" link in the modal.
70			jQuery( document ).on( 'click', '.blaize-to-login > a', function() {
71				pwlGtmDataLayer.push( loginEvent );
72			} );
73
74			// "Click here to register" link in the modal.
75			jQuery( document ).on( 'click', '.blaize-to-register > a', function() {
76				pwlGtmDataLayer.push( registerEvent );
77			});
78
79			jQuery(document).on('mouseup', '.btn-register', function () {
80				pwlGtmDataLayer.push( registerEvent );
81			} );
82
83		},
84
85		/**
86		 * Detect when a user clicks the my pei save article button
87		 *
88		 * Web Analytics - Event Register
89		 * PMP-4910 Web Analytics - PEI Subscriptions - How many times users save content
90		 */
91		myPeiSaveButton: function () {
92
93			var pushArray = {
94				'event': 'visitor interaction',
95				'interaction_name': 'button click',
96				'buttonName': 'myPEISaveArticle',
97			};
98
99			jQuery( document ).on( 'click', '.js-myPei-button--save[data-mypei_button_checked="not-saved"]', function () {
100				pwlGtmDataLayer.push( pushArray );
101			} );
102
103		},
104
105		/**
106		 * Detect when a user clicks the my pei share list button
107		 *
108		 * Web Analytics - Event Register
109		 * PMP-4912 Web Analytics - PEI Subscriptions - How many times users share lists
110		 */
111		myPeiShareListButton: function () {
112
113			var pushArray = {
114				'event': 'visitor interaction',
115				'interaction_name': 'button click',
116				'buttonName': 'myPEIShareList',
117			};
118
119			jQuery( document ).on( 'click', '.js-myPei-share-list', function () {
120				pwlGtmDataLayer.push( pushArray );
121			} );
122
123		},
124
125		/**
126		 * Detect when a user create a new my PEI list
127		 *
128		 * Web Analytics - Event Register
129		 * PMP-4911 Web Analytics - PEI Subscriptions - How many times users create lists
130		 */
131		myPeiCreateNewListButton: function () {
132
133			var pushArray = {
134				'event': 'visitor interaction',
135				'interaction_name': 'button click',
136				'buttonName': 'myPEINewListCreation',
137			};
138
139			var modalElement = jQuery( '#modal-create-list, #modal-add-article' );
140
141			if ( modalElement.length > 0 ) {
142
143				var createButton = modalElement.find( 'form' ).find(
144					'input[data-modal_action="add-article-to-new-list"], input[data-modal_action="create-list"]'
145				);
146
147				createButton.on( 'click', function () {
148					setTimeout( function () {
149						if ( modalElement.find( '.myPei-modal__notifications--error' ).length < 1 ) {
150							pwlGtmDataLayer.push( pushArray );
151						}
152					}, 1000 );
153				} );
154			}
155		},
156
157		/**
158		 * Detects when a user has clicked link in more stories pop up
159		 *
160		 * Data layer triggers on click of links contained in the “more stories” pop-out:
161		 *
162		 * PEI Media Data Layer Instructions v2.0
163		 * 4.3 More Stories Pop-Out
164		 **/
165		moreStoriesPopOut: function () {
166
167			var pushArray = {
168				'event': 'visitor interaction',
169				'interaction_name': 'more stories pop-out click',
170			};
171
172			jQuery( '.td-content-more-articles-box a' ).click( function ( event ) {
173
174				pushArray[ 'moreStoriesArticle' ] = event.currentTarget.text;
175
176				pwlGtmDataLayer.push( pushArray );
177
178			} );
179		},
180
181		/**
182		 * Detects when a user has clicked to visit the cart page
183		 *
184		 * Data layer triggers on click click of cart button
185		 *
186		 * PEI Media Data Layer Instructions v2.0
187		 * 4.9 Click to Cart
188		 **/
189		clickToCart: function () {
190
191			var pushArray = {
192				'event': 'visitor interaction',
193				'interaction_name': 'click to cart',
194			};
195
196			var pageType = pwlGtmDataLayer.findDlValue( 'pageType' );
197
198			jQuery( '.wc_cart_wrapper' ).click( function () {
199
200				pushArray[ 'clickToCartLoc' ] = pageType;
201
202				// Check to see if the bottom has been reached so we don't fire more than once.
203				pwlGtmDataLayer.push( pushArray );
204
205			} );
206
207		}
208		,
209
210		/**
211		 * Detects when a user has reached the end of an article
212		 *
213		 * When a user gets to the bottom of the article of the post type "post" then a Data Layer push will be
214		 * triggered.
215		 *
216		 * PEI Media Data Layer Instructions v2.0
217		 * 4.8 Article Scroll
218		 **/
219		articleScroll: function () {
220
221			var hasArticleScrolled = false;
222			var pushArray = {
223				'event': 'visitor interaction',
224				'interaction_name': 'end of content scroll',
225			};
226
227			jQuery( window ).scroll( function () {
228				// Check to see if the bottom has been reached so we don't fire more than once.
229				if ( hasArticleScrolled === false ) {
230
231					// Define the element to trigger the push.
232					var articleContainer = jQuery( 'article.type-post' );
233
234					if ( articleContainer.length ) {
235
236						var windowHeight = document.documentElement.clientHeight;
237						var currentDistanceFromTop = jQuery( document ).scrollTop();
238						var articleBottom = articleContainer[ 0 ].offsetHeight;
239						var elementDistanceFromTop = articleContainer.offset().top;
240
241						var articleBottomDistanceFromTop = windowHeight - elementDistanceFromTop + currentDistanceFromTop;
242
243						if ( articleBottomDistanceFromTop >= articleBottom ) {
244							hasArticleScrolled = true;
245							pwlGtmDataLayer.push( pushArray );
246						}
247					}
248				}
249
250			} );
251		}
252		,
253
254		/**
255		 * Detects when a user adds a product to a cart
256		 *
257		 * PEI Media Data Layer Instructions v2.0
258		 * 5.2 Add Product to Cart
259		 */
260		addProduct: function () {
261
262			var self = this,
263				pageType = pwlGtmDataLayer.findDlValue( 'pageType' ),
264				ecommVariables = this.findEcommerceVariables(),
265				pushArray = {
266					'event': 'visitor interaction',
267					'interaction_name': 'add to cart',
268				};
269
270			// category archive, subscription page
271			jQuery( '.add_to_cart_button' ).on( 'click', function () {
272
273				var $this = jQuery( this ),
274					quantity = self.getProductQty( $this );
275
276				if ( 0 === quantity ) {
277					// single product page
278					if ( jQuery( 'form.cart:first input[name=quantity]' ).length > 0 ) {
279						quantity = parseInt( jQuery( 'form.cart:first input[name=quantity]' ).val() );
280					} else { // category archive, subscription page
281						quantity = 1;
282					}
283				}
284
285				// get product data
286				var productData = self.getProductData( $this.data(), quantity );
287
288				pushArray[ 'ecommerce' ] = {
289					'currencyCode': self.getCurrencyCode(),
290					'add': {
291						'products': [ productData ],
292					},
293				};
294
295				pushArray[ 'productAddLoc' ] = pageType;
296
297				pwlGtmDataLayer.push( pushArray );
298
299			} );
300
301			// undo button
302			jQuery( '.td-page-content' ).on( 'click', '.restore-item', function () {
303				var undoURL = jQuery( this ).attr( 'href' );
304
305				// Remove scheme and domain
306				var finalURL = undoURL.substring( undoURL.indexOf( '?' ) + 1 );
307
308				// Get querystring values
309				var queryString = finalURL.split( '&' );
310
311				// Set up an empty array in which we store product data
312				var productDataArray = {};
313
314				// Push all the values to the productDataArray
315				for ( var i = 0, l = queryString.length; i < l; i ++ ) {
316					var values = queryString[ i ].split( '=' );
317
318					// If the value of the query string isn't set return it as empty
319					if ( values.length > 1 ) {
320						productDataArray[ values[ 0 ] ] = decodeURIComponent( values[ 1 ] );
321					} else {
322						productDataArray[ values[ 0 ] ] = '';
323					}
324				}
325
326				// Get product data
327				var productData = self.getProductData( productDataArray, productDataArray.quantity );
328
329				pushArray[ 'ecommerce' ] = {
330					'currencyCode': self.getCurrencyCode(),
331					'add': {
332						'products': [ productData ],
333					},
334				};
335
336				pushArray[ 'productAddLoc' ] = pageType;
337
338				pwlGtmDataLayer.push( pushArray );
339			} );
340		}
341		,
342
343		/**
344		 * Detects when a user removes from cart via "x" button
345		 *
346		 * PEI Media Data Layer Instructions v2.0
347		 * 5.3 Remove a Product from a Cart
348		 */
349		removeProduct: function () {
350
351			var self = this,
352				pageType = pwlGtmDataLayer.findDlValue( 'pageType' ),
353				pushArray = {
354					'event': 'visitor interaction',
355					'interaction_name': 'remove from cart',
356				};
357
358			jQuery( '.widget_shopping_cart, .product-remove' ).on( 'click', 'a.remove', function ( e ) {
359
360				var $this = jQuery( this ),
361					productData = $this;
362
363				// get product quantity
364				var quantity = self.getProductQty( $this );
365
366				// If there's no quantity return
367				if ( 0 == quantity ) {
368					return true;
369				}
370
371				// get product data
372				productData = self.getProductData( $this.data(), quantity );
373
374				pushArray[ 'ecommerce' ] = {
375					'currencyCode': self.getCurrencyCode(),
376					'remove': {
377						'products': [ productData ],
378					},
379				};
380
381				pushArray[ 'productRemoveLoc' ] = pageType;
382
383				pwlGtmDataLayer.push( pushArray );
384
385			} );
386
387		}
388		,
389
390		/**
391		 * Detects when user adds or removes item from cart
392		 *
393		 * PEI Media Data Layer Instructions v2.0
394		 * Covers 5.2 & 5.3 Add a Product to a Cart Remove a Product from a Cart
395		 */
396		updateProductCart: function () {
397
398			var self = this,
399				pageType = pwlGtmDataLayer.findDlValue( 'pageType' );
400
401			// When a user clicks the update cart button on cart page
402			jQuery( '[name=update_cart]' ).on( 'click', function () {
403
404				// Go through each product line
405				jQuery( '.product-quantity input.qty' ).each( function () {
406					var $this = jQuery( this ),
407						default_value = $this.prop( 'defaultValue' ),
408						new_value = parseInt( $this.val() );
409
410					// If new_value is not a number set it to default one
411					if ( isNaN( new_value ) ) {
412						new_value = default_value;
413					}
414
415					// If value has changed push event to Data Layer
416					if ( default_value != new_value ) {
417
418						var new_quantity_value,
419							interactionType,
420							interactionName,
421							interactionLocation,
422							pushArray = {
423								'event': 'visitor interaction',
424								'ecommerce': {},
425							};
426
427						// If default value is lessa than new value
428						// it means a product has been added
429						if ( default_value < new_value ) {
430
431							interactionType = 'add';
432							interactionName = 'add to cart';
433							interactionLocation = 'productAddLoc';
434							new_quantity_value = new_value - default_value;
435
436						}
437						// Otherwise a product has been removed
438						else {
439
440							interactionType = 'remove';
441							interactionName = 'remove from cart';
442							interactionLocation = 'productRemoveLoc';
443							new_quantity_value = default_value - new_value;
444
445						}
446
447						pushArray[ 'interaction_name' ] = interactionName;
448
449						pushArray[ 'ecommerce' ][ 'currencyCode' ] = self.getCurrencyCode();
450						pushArray[ 'ecommerce' ][ interactionType ] = {
451							'products': [
452								self.getProductData(
453									$this.closest( '.cart_item' ).find( '.remove' ).data(),
454									new_quantity_value,
455								),
456							],
457						};
458
459						pushArray[ interactionLocation ] = pageType;
460
461						pwlGtmDataLayer.push( pushArray );
462					}
463				} );
464			} );
465		}
466		,
467
468		/**
469		 * When a user proceeds through checkout
470		 *
471		 * PEI Media Data Layer Instructions v2.0
472		 * Covers 5.4 Commence Checkout Process
473		 */
474		commenceCheckout: function () {
475
476			var pushArray = {
477				'event': 'visitor interaction',
478				'interaction_name': 'commence checkout',
479			};
480
481			var self = this;
482
483			var pageType = pwlGtmDataLayer.findDlValue( 'pageType' );
484
485			var products = this.findCheckOutProducts();
486
487			if ( products ) {
488
489				jQuery( '.checkout-button' ).click( function () {
490
491					pushArray[ 'ecommerce' ] = {
492						'currencyCode': self.getCurrencyCode(),
493						'checkout': {
494							'actionField': { 'step': 1 },
495							'products': products,
496						},
497					};
498
499					pushArray[ 'checkoutLoc' ] = pageType;
500
501					pwlGtmDataLayer.push( pushArray );
502
503				} );
504			}
505		}
506		,
507
508		/**
509		 * When a user clicks a social follow, share button
510		 *
511		 * PEI Media Data Layer Instructions v2.4
512		 * Covers 4.4 Social Follow Buttons, 4.5 Social Sharing Buttons
513		 */
514		socialInteractions: function ( trigger, action ) {
515			var interactionType, interactionLabel, interactionValue,
516				pushArray = {
517					'event': 'visitor interaction',
518				};
519
520			jQuery( trigger ).on( 'click', function ( event ) {
521				var currentLink = jQuery( event.currentTarget );
522
523				// Set DL variables
524				if ( 'follow' === action ) {
525
526					interactionType = 'social follow click';
527					interactionLabel = 'socialFollowNetwork';
528					interactionValue = currentLink.attr( 'title' ).toLowerCase();
529
530				} else if ( 'share' === action ) {
531
532					interactionType = 'social share click';
533					interactionLabel = 'socialShareNetwork';
534					interactionValue = currentLink.find( 'span' ).text().toLowerCase();
535
536				}
537
538				// Return if the user clicked on email button
539				// (we use another function to register email interactions)
540				if ( 'email' === interactionValue )
541					return;
542
543				// Push array to Data Layer
544				if ( '' !== interactionValue ) {
545					pushArray[ 'interaction_name' ] = interactionType;
546					pushArray[ interactionLabel ] = interactionValue;
547
548					pwlGtmDataLayer.push( pushArray );
549				}
550			} );
551		}
552		,
553
554		/**
555		 * When a user clicks share by email button
556		 *
557		 * PEI Media Data Layer Instructions v2.4
558		 * Covers 4.6 Email Share Buttons
559		 */
560		emailInteractions: function () {
561			var pushArray = {
562				'event': 'visitor interaction',
563				'interaction_name': 'email share click',
564			};
565
566			checkSuccess();
567
568			function checkSuccess () {
569				if ( jQuery( '#sharing_email' ).find( 'form' ).css( 'display' ) === 'none' ) {
570					pwlGtmDataLayer.push( pushArray );
571				} else {
572					setTimeout( checkSuccess, 1000 ); //every 1 second. Not ideal but means no plugin needed.
573				}
574			}
575		}
576		,
577
578		/**
579		 * When a user clicks on a PDF attached to a Print Edition
580		 * or a link inside the post content.
581		 *
582		 * PEI Media Data Layer Instructions v2.4
583		 * Covers 4.7 Download file
584		 */
585		downloadAttachments: function () {
586			jQuery( 'a[href$=".pdf"]' ).on( 'click', function ( event ) {
587
588				// Get the url for the PDF
589				var url = event.currentTarget.href;
590
591				// Retrieve the file name from href add +1 since we don't want the / at the beginning
592				var filename = url.substring( url.lastIndexOf( '/' ) + 1 );
593
594				var pushArray = {
595					'event': 'visitor interaction',
596					'interaction_name': 'download file',
597					'downloadFileName': filename.replace( '.pdf', '' ), // Remove the file extension from the filename
598				};
599
600				pwlGtmDataLayer.push( pushArray );
601			} );
602		}
603		,
604
605		/**
606		 * Add current pageName to the search form so
607		 * that we can use this value for the search page Data Layer
608		 *
609		 * PEI Media Data Layer Instructions v2.4
610		 * Covers 2.4.3 Search Results Pages
611		 */
612		searchPage: function () {
613			if ( !window.dataLayer )
614				return;
615
616			var self = this;
617
618			jQuery( '.td-search-form, .td-search-form-widget' ).on( 'submit', function () {
619				self.setPageSearchCookie();
620			} );
621
622			jQuery( '#td-aj-search-mob, #td-aj-search' ).on( 'click', '.result-msg a', function () {
623				self.setPageSearchCookie();
624			} );
625		}
626		,
627
628		/**
629		 * Stores a cookie which value is current value for Article Access Method
630		 *
631		 * PEI Media Data Layer Instructions v2.4
632		 * Covers 2.4.2 Article Pages
633		 */
634		handleAccessMethod: function ( trigger, value, selector ) {
635			selector = selector || 'a';
636
637			if(value.indexOf('search results') !== -1) {
638				jQuery( '.algolia-hits-wrapper' ).on('click', trigger, function ( event ) {
639
640					value = 'search results: ' + jQuery('.ais-SearchBox-input').val();
641
642					Cookies.set( 'peiArticleAccessedFrom', value, {
643						// Set an expiry date of one minute.
644						// Using the end of session could lead
645						// to wrong value pushed to the Data Layer.
646						expires: new Date( new Date().getTime() + 60 * 1000 ),
647					} );
648				});
649			} else {
650				jQuery( trigger ).on( 'click', selector, function ( event ) {
651
652					// If href is not set return
653					if ( '#' === jQuery( event.currentTarget ).attr( 'href' ) )
654						return;
655					// We need to retrieve the value from the section title
656					// at the end of every article page
657					if ( 'article bottom' === value )
658						value = jQuery( '.td_block_related_posts' ).find( '.td-related-title a.td-cur-simple-item' ).text();
659					// We need to retrieve the value from the widget title
660					// on the right sidebar of every page that has one
661					if ( 'widgets' === value )
662						value = jQuery( this ).parents( trigger ).find( '.block-title' ).text();
663					Cookies.set( 'peiArticleAccessedFrom', value, {
664						// Set an expiry date of one minute.
665						// Using the end of session could lead
666						// to wrong value pushed to the Data Layer.
667						expires: new Date( new Date().getTime() + 60 * 1000 ),
668					} );
669				} );
670			}
671		}
672		,
673
674		/**
675		 * Set a cookie to store current pageName value
676		 * so we can use it later in Search Page
677		 */
678		setPageSearchCookie: function () {
679			Cookies.set( 'peiPageSearchMadeOn', pwlGtmDataLayer.findDlValue( 'pageName' ).replace( '/ga-virtual', '' ) );
680		}
681		,
682
683		/**
684		 * If we're on an article page set articleAccessedFrom property to the correct value
685		 */
686		setArticleAccessMethodValue: function () {
687			if ( 'article' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
688				var peiAccessCookie = Cookies.get( 'peiArticleAccessedFrom' ),
689					objectKey = this.findDlObject(),
690					articleAccessMethodValue = 'entry page';
691
692				if ( peiAccessCookie ) {
693					articleAccessMethodValue = peiAccessCookie;
694				}
695
696				if ( null !== objectKey ) {
697					window.dataLayer[ objectKey ][ 'articleAccessMethod' ] = articleAccessMethodValue;
698				}
699			}
700		}
701		,
702
703		/**
704		 * If we're on a search page set searchPage property to the correct value
705		 */
706		setSearchPageValue: function () {
707			if ( 'search results' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
708				var peiSearchCookie = Cookies.get( 'peiPageSearchMadeOn' ),
709					objectKey = this.findDlObject(),
710					peiPageSearchMadeOnValue = 'no value';
711
712				if ( peiSearchCookie ) {
713					peiPageSearchMadeOnValue = peiSearchCookie;
714				}
715
716				if ( null !== objectKey ) {
717					window.dataLayer[ objectKey ][ 'searchPage' ] = peiPageSearchMadeOnValue;
718				}
719			}
720		}
721		,
722
723		/**
724		 * Returns current page name formatted
725		 */
726		extractPageName: function () {
727			var pageName = pwlGtmDataLayer.findDlValue( 'pageName' );
728
729			var taxonomyName = pageName.substring( pageName.lastIndexOf( '/' ) + 1 );
730
731			return taxonomyName.replace( /-/g, ' ' );
732		}
733		,
734
735		/**
736		 * Return dataLayer object with values pushed via PHP on pageLoad
737		 */
738		findDlObject: function () {
739			for ( var i = 0, l = window.dataLayer.length; i < l; i ++ ) {
740				if ( window.dataLayer[ i ].hasOwnProperty( 'pageType' ) ) {
741					return i;
742				}
743			}
744
745			return null;
746		}
747		,
748
749		/**
750		 * Returns current pageType when multiple dataLayer elements on a page
751		 **/
752		findEcommerceVariables: function () {
753
754			var ecommVariables = '';
755
756			var i = '';
757
758			for ( i = 0; i < window.dataLayer.length; i ++ ) {
759				if ( window.dataLayer[ i ].hasOwnProperty( 'ecommerce' ) ) {
760					ecommVariables = window.dataLayer[ i ].ecommerce;
761					break;
762				}
763			}
764
765			return ecommVariables.detail;
766		}
767		,
768
769		findCheckOutProducts: function () {
770
771			if ( jQuery( 'a.remove' ).length > 0 ) {
772
773				var products = [];
774
775				jQuery( 'a.remove' ).each( function () {
776
777					var productData = jQuery( this );
778
779					var quantity = jQuery( this ).closest( '.cart_item' ).find( '.product-quantity input.qty' ).val();
780
781					if ( isNaN( quantity ) ) {
782						quantity = 1;
783					}
784
785					var product = {
786						'category': productData.data( 'product_category' ),
787						'name': productData.data( 'product_name' ).toLowerCase(),
788						'price': String( productData.data( 'product_price' ) ),
789						'sku': productData.data( 'product_sku' ),
790						'quantity': parseInt( quantity ),
791					};
792
793					products.push( product );
794
795				} );
796				return products;
797			}
798
799			//no items in checkout
800			return false;
801
802		}
803		,
804
805		/**
806		 * Retrieve product quantity for a product
807		 */
808		getProductQty: function ( el ) {
809			var quantity = 0,
810				quantity_element = el.closest( '.cart_item' ).find( '.product-quantity input.qty' );
811
812			if ( 0 == quantity_element.length ) {
813
814				var subscriptionQuantity = parseInt( el.closest( '.cart_item' ).find( '.product-quantity input' ).val() );
815
816				if ( subscriptionQuantity === 1 ) {
817
818					quantity = 1;
819
820				}
821
822				quantity_element = el.closest( '.mini_cart_item' ).find( '.quantity' );
823
824				if ( quantity_element.length > 0 ) {
825					quantity = parseInt( quantity_element.text() );
826
827					if ( isNaN( quantity ) ) {
828						quantity = 0;
829					}
830				}
831			} else {
832				quantity = quantity_element.val();
833			}
834
835			return quantity;
836		}
837		,
838
839		/**
840		 * Retrieve product data
841		 */
842		getProductData: function ( productData, quantity ) {
843			return {
844				'category': productData[ 'product_category' ],
845				'name': productData[ 'product_name' ].toLowerCase(),
846				'price': String( productData[ 'product_price' ] ),
847				'sku': productData[ 'product_sku' ],
848				'quantity': quantity,
849			};
850		}
851		,
852
853		/**
854		 * Retrieve current currency code from Aelia Currency Switcher configuration object
855		 */
856		getCurrencyCode: function () {
857			const params = window.wc_aelia_currency_switcher_params;
858
859			if ( ( 'object' === typeof params ) && params.selected_currency ) {
860				return params.selected_currency;
861			}
862
863			return 'GBP';
864		}
865		,
866
867		/**
868		 * Check if a modal has closed which means that any contained functionality has been successful
869		 * @param modalElement
870		 * @param pushArray
871		 */
872		checkModalSuccess: function ( modalElement, pushArray ) {
873
874			if ( modalElement.css( 'display' ) === 'none' ) {
875				setTimeout( checkSuccess, 1000 ); //every 1 second. Not ideal but means no plugin needed.
876			} else {
877				pwlGtmDataLayer.push( pushArray );
878			}
879
880		}
881		,
882
883	};
884
885} )();
886
887jQuery( document ).ready( function () {
888	visitorInteractions.moreStoriesPopOut();
889	visitorInteractions.clickToCart();
890	visitorInteractions.addProduct();
891	visitorInteractions.removeProduct();
892	visitorInteractions.updateProductCart();
893	visitorInteractions.commenceCheckout();
894	visitorInteractions.articleScroll();
895	visitorInteractions.socialInteractions( '.footer-social-wrap a', 'follow' );
896	visitorInteractions.socialInteractions( '.js-sharing-wrapper a', 'share' );
897	visitorInteractions.emailInteractions();
898	visitorInteractions.downloadAttachments();
899	visitorInteractions.searchPage();
900	visitorInteractions.setArticleAccessMethodValue();
901	visitorInteractions.setSearchPageValue();
902	visitorInteractions.myPeiShareListButton();
903	visitorInteractions.myPeiCreateNewListButton();
904	visitorInteractions.loginStartButton();
905
906	// Handle all the cases for accessMethod property for articles
907	// More Stories Pop Out
908	visitorInteractions.handleAccessMethod(
909		'.td-more-articles-box .td-module-thumb, ' +
910		'.td-more-articles-box .td-module-title',
911		'more stories pop out',
912	);
913
914	// Widgets
915	visitorInteractions.handleAccessMethod(
916		'.widget_dpe_fp_widget',
917		'widgets',
918	);
919
920	// Main nav items
921	visitorInteractions.handleAccessMethod(
922		'.sub-menu',
923		'main navigation',
924		'.td_block_inner .td-module-thumb a, .td_block_inner .entry-title a',
925	);
926
927	// Categories
928	if ( 'topic article list' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
929		visitorInteractions.handleAccessMethod(
930			// triggers
931			'body.category .td_module_16 .td-module-thumb, ' +
932			'body.category .td_module_16 .entry-title, ' +
933			'body.category .td_module_mx5 .td-module-thumb, ' +
934			'body.category .td_module_mx5 .td-meta-info-container .entry-title, ' +
935			'body.category .td_module_mx6 .td-module-thumb, ' +
936			'body.category .td_module_mx6 .td-meta-info-container .entry-title',
937			// value
938			'Category: ' + visitorInteractions.extractPageName(),
939		);
940	}
941
942	// Tags
943	if ( 'topic article list' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
944		visitorInteractions.handleAccessMethod(
945			// triggers
946			'body.tag .td_module_16 .td-module-thumb, ' +
947			'body.tag .td_module_16 .entry-title, ' +
948			'body.tag .td_module_mx5 .td-module-thumb, ' +
949			'body.tag .td_module_mx5 .td-meta-info-container .entry-title, ' +
950			'body.tag .td_module_mx6 .td-module-thumb, ' +
951			'body.tag .td_module_mx6 .td-meta-info-container .entry-title',
952			// value
953			'Tag: ' + visitorInteractions.extractPageName(),
954		);
955	}
956
957	// Authors
958	if ( 'author article list' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
959		visitorInteractions.handleAccessMethod(
960			// triggers
961			'.td_module_16 .td-module-thumb, ' +
962			'.td_module_16 .entry-title',
963			// values
964			'author article list',
965		);
966	}
967
968	// Related articles
969	if ( 'article' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
970		visitorInteractions.handleAccessMethod(
971			'.td_block_related_posts',
972			'article bottom',
973			'.td_module_related_posts .entry-title a, .td_module_related_posts .td-module-thumb a',
974		);
975	}
976
977	// Our Latest Posts
978	if ( 'error' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
979		visitorInteractions.handleAccessMethod(
980			// triggers
981			'.td_module_16 .td-module-thumb, ' +
982			'.td_module_16 .entry-title',
983			// values
984			'our latest posts',
985		);
986	}
987
988	// Homepage
989	if ( 'homepage' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
990		visitorInteractions.handleAccessMethod(
991			'.td-main-content-wrap',
992			'homepage',
993		);
994	}
995
996	// Magazine
997	if ( 'magazine archive' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
998		visitorInteractions.handleAccessMethod(
999			'.td-main-content .td_module_1',
1000			'magazine: ' + visitorInteractions.extractPageName(),
1001		);
1002	}
1003
1004	// Search results
1005	if ( 'search results' === pwlGtmDataLayer.findDlValue( 'pageType' ) ) {
1006		visitorInteractions.handleAccessMethod(
1007			// triggers
1008			'.ais-hits--thumbnail a, ' +
1009			'.entry-title a',
1010			// values
1011			'search results: ' + jQuery('.ais-SearchBox-input').val(),
1012		);
1013	}
1014
1015	// Site search
1016
1017	visitorInteractions.handleAccessMethod(
1018		//triggers
1019		'.td-head-form-search-wrap',
1020		//values
1021		'site search: '
1022	);
1023
1024} );
1025
1026//some of these interactions are coming from ajax so we need to wait until the page has completely loaded before loaded the function
1027jQuery( document ).ajaxStop( function () {
1028
1029	visitorInteractions.updateProductCart();
1030	visitorInteractions.commenceCheckout();
1031	visitorInteractions.myPeiSaveButton();
1032
1033	if ( jQuery( 'body' ).is( '.woocommerce-cart' ) ) {
1034		visitorInteractions.removeProduct();
1035	}
1036} );

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.