PageSourceSearch

https://www.teva.org.il/app/plugins/activetrail-for-woocommerce/assets/js/event-tracker.js?ver=1.2.4

js teva.org.il collected 2026-09-24 09:29:04 UTC 13,845 bytes, 573 lines download raw bytes

1/**
2 * ActiveTrail Event Tracker
3 * Frontend JavaScript for tracking ecommerce events
4 */
5
6(function ($) {
7	'use strict';
8
9	// Event tracker object
10	var ActiveTrailEventTracker = {
11
12		// Configuration
13		config: {
14			ajax_url: activetrail_events.ajax_url,
15			nonce: activetrail_events.nonce,
16			customer_email: activetrail_events.customer_email,
17			session_id: activetrail_events.session_id,
18			currency: activetrail_events.currency,
19			debug: activetrail_events.debug
20		},
21
22		// Event queue for batching
23		eventQueue: [],
24
25		// Initialize the tracker
26		init: function () {
27			this.bindEvents();
28			this.trackPageView();
29			this.setupPeriodicSync();
30
31			if (this.config.debug) {
32				console.log( 'ActiveTrail Event Tracker initialized' );
33			}
34		},
35
36		// Bind event listeners
37		bindEvents: function () {
38			var self = this;
39
40			// Track product view on product pages
41			if ($( 'body' ).hasClass( 'single-product' )) {
42				this.trackProductView();
43			}
44
45			// Track add to cart button clicks
46			$( document ).on(
47				'click',
48				'.single_add_to_cart_button',
49				function (e) {
50					var $form     = $( this ).closest( 'form.cart' );
51					var productId = $form.find( 'input[name="add-to-cart"]' ).val() ||
52								$form.find( 'button[name="add-to-cart"]' ).val();
53					var quantity  = $form.find( 'input[name="quantity"]' ).val() || 1;
54
55					if (productId) {
56						self.trackAddToCartIntent( productId, quantity );
57					}
58				}
59			);
60
61			// Track AJAX add to cart
62			$( document.body ).on(
63				'added_to_cart',
64				function (event, fragments, cart_hash, $button) {
65					var productId = $button.data( 'product_id' ) || $button.val();
66					var quantity  = $button.data( 'quantity' ) || 1;
67
68					if (productId) {
69						self.trackAddToCart( productId, quantity );
70					}
71				}
72			);
73
74			// Track cart page interactions
75			if ($( 'body' ).hasClass( 'woocommerce-cart' )) {
76				this.bindCartEvents();
77			}
78
79			// Track checkout page
80			if ($( 'body' ).hasClass( 'woocommerce-checkout' )) {
81				this.trackCheckoutStart();
82				this.bindCheckoutEvents();
83			}
84
85			// Track remove from cart
86			$( document ).on(
87				'click',
88				'.remove',
89				function (e) {
90					var $link     = $( this );
91					var productId = self.extractProductIdFromRemoveLink( $link.attr( 'href' ) );
92
93					if (productId) {
94						self.trackRemoveFromCart( productId );
95					}
96				}
97			);
98
99			// Track quantity updates in cart
100			$( document ).on(
101				'change',
102				'.cart .qty',
103				function () {
104					var $input      = $( this );
105					var productId   = self.extractProductIdFromCartRow( $input.closest( 'tr' ) );
106					var newQuantity = parseInt( $input.val() ) || 0;
107					var oldQuantity = parseInt( $input.data( 'old-quantity' ) ) || 0;
108
109					if (productId && newQuantity !== oldQuantity) {
110						self.trackCartQuantityUpdate( productId, oldQuantity, newQuantity );
111					}
112
113					// Store new quantity as old quantity for next change
114					$input.data( 'old-quantity', newQuantity );
115				}
116			);
117
118			// Store initial quantities
119			$( '.cart .qty' ).each(
120				function () {
121					$( this ).data( 'old-quantity', parseInt( $( this ).val() ) || 0 );
122				}
123			);
124		},
125
126		// Bind cart-specific events
127		bindCartEvents: function () {
128			var self = this;
129
130			// Track cart view
131			this.trackEvent(
132				'cart_view',
133				{
134					cart_total: this.getCartTotal(),
135					cart_items_count: this.getCartItemsCount()
136				}
137			);
138
139			// Track proceed to checkout clicks
140			$( document ).on(
141				'click',
142				'.checkout-button',
143				function () {
144					self.trackEvent(
145						'proceed_to_checkout',
146						{
147							cart_total: self.getCartTotal(),
148							cart_items_count: self.getCartItemsCount()
149						}
150					);
151				}
152			);
153		},
154
155		// Bind checkout-specific events
156		bindCheckoutEvents: function () {
157			var self = this;
158
159			// Track checkout steps
160			$( document ).on(
161				'change',
162				'#billing_email',
163				function () {
164					if ($( this ).val()) {
165						self.trackEvent(
166							'checkout_email_entered',
167							{
168								email: $( this ).val(),
169								cart_total: self.getCartTotal()
170							}
171						);
172					}
173				}
174			);
175
176			// Track payment method selection
177			$( document ).on(
178				'change',
179				'input[name="payment_method"]',
180				function () {
181					self.trackEvent(
182						'checkout_payment_method_selected',
183						{
184							payment_method: $( this ).val(),
185							cart_total: self.getCartTotal()
186						}
187					);
188				}
189			);
190
191			// Track place order attempts
192			$( document ).on(
193				'click',
194				'#place_order',
195				function () {
196					self.trackEvent(
197						'checkout_place_order_clicked',
198						{
199							cart_total: self.getCartTotal(),
200							payment_method: $( 'input[name="payment_method"]:checked' ).val()
201						}
202					);
203				}
204			);
205		},
206
207		// Track page view
208		trackPageView: function () {
209			var pageType = this.getPageType();
210
211			this.trackEvent(
212				'page_view',
213				{
214					page_type: pageType,
215					page_url: window.location.href,
216					page_title: document.title,
217					referrer: document.referrer
218				}
219			);
220		},
221
222		// Track product view
223		trackProductView: function () {
224			var productData = this.getProductData();
225
226			if (productData.product_id) {
227				this.trackEvent( 'product_view', productData, productData.product_price );
228			}
229		},
230
231		// Track add to cart intent (button click)
232		trackAddToCartIntent: function (productId, quantity) {
233			var productData      = this.getProductData( productId );
234			productData.quantity = quantity;
235			productData.intent   = true;
236
237			this.trackEvent( 'add_to_cart_intent', productData, productData.product_price * quantity );
238		},
239
240		// Track successful add to cart
241		trackAddToCart: function (productId, quantity) {
242			var productData      = this.getProductData( productId );
243			productData.quantity = quantity;
244
245			this.trackEvent( 'add_to_cart', productData, productData.product_price * quantity );
246		},
247
248		// Track remove from cart
249		trackRemoveFromCart: function (productId) {
250			this.trackEvent(
251				'remove_from_cart',
252				{
253					product_id: productId,
254					cart_total: this.getCartTotal()
255				}
256			);
257		},
258
259		// Track cart quantity update
260		trackCartQuantityUpdate: function (productId, oldQuantity, newQuantity) {
261			this.trackEvent(
262				'cart_quantity_update',
263				{
264					product_id: productId,
265					old_quantity: oldQuantity,
266					new_quantity: newQuantity,
267					quantity_change: newQuantity - oldQuantity,
268					cart_total: this.getCartTotal()
269				}
270			);
271		},
272
273		// Track checkout start
274		trackCheckoutStart: function () {
275			this.trackEvent(
276				'checkout_start',
277				{
278					cart_total: this.getCartTotal(),
279					cart_items_count: this.getCartItemsCount(),
280					currency: this.config.currency
281				},
282				this.getCartTotal()
283			);
284		},
285
286		// Generic event tracking method
287		trackEvent: function (eventType, eventData, eventValue) {
288			eventData  = eventData || {};
289			eventValue = eventValue || 0;
290
291			// Add common data
292			eventData.timestamp         = new Date().toISOString();
293			eventData.user_agent        = navigator.userAgent;
294			eventData.screen_resolution = screen.width + 'x' + screen.height;
295			eventData.viewport_size     = $( window ).width() + 'x' + $( window ).height();
296
297			var event = {
298				event_type: eventType,
299				event_data: eventData,
300				event_value: parseFloat( eventValue ) || 0,
301				customer_email: this.config.customer_email,
302				session_id: this.config.session_id,
303				currency: this.config.currency
304			};
305
306			// Add to queue
307			this.eventQueue.push( event );
308
309			// Send immediately for critical events
310			if (this.isCriticalEvent( eventType )) {
311				this.sendEvent( event );
312			}
313
314			if (this.config.debug) {
315				console.log( 'Event tracked:', eventType, eventData );
316			}
317		},
318
319		// Check if event is critical and should be sent immediately
320		isCriticalEvent: function (eventType) {
321			var criticalEvents = [
322				'add_to_cart',
323				'remove_from_cart',
324				'checkout_start',
325				'checkout_place_order_clicked'
326			];
327
328			return criticalEvents.indexOf( eventType ) !== -1;
329		},
330
331		// Send single event to server
332		sendEvent: function (event) {
333			var self = this;
334
335			$.ajax(
336				{
337					url: this.config.ajax_url,
338					type: 'POST',
339					data: {
340						action: 'activetrail_track_event',
341						nonce: this.config.nonce,
342						event_type: event.event_type,
343						event_data: event.event_data,
344						event_value: event.event_value
345					},
346					success: function (response) {
347						if (self.config.debug) {
348							console.log( 'Event sent successfully:', event.event_type );
349						}
350					},
351					error: function (xhr, status, error) {
352						if (self.config.debug) {
353							console.error( 'Failed to send event:', event.event_type, error );
354						}
355
356						// Store in localStorage for retry
357						self.storeEventForRetry( event );
358					}
359				}
360			);
361		},
362
363		// Send queued events in batch
364		sendQueuedEvents: function () {
365			if (this.eventQueue.length === 0) {
366				return;
367			}
368
369			var eventsToSend = this.eventQueue.splice( 0, 10 ); // Send max 10 at a time
370			var self         = this;
371
372			eventsToSend.forEach(
373				function (event) {
374					if ( ! self.isCriticalEvent( event.event_type )) {
375						self.sendEvent( event );
376					}
377				}
378			);
379		},
380
381		// Store event in localStorage for retry
382		storeEventForRetry: function (event) {
383			try {
384				var storedEvents = JSON.parse( localStorage.getItem( 'activetrail_failed_events' ) || '[]' );
385				storedEvents.push( event );
386
387				// Keep only last 50 failed events
388				if (storedEvents.length > 50) {
389					storedEvents = storedEvents.slice( -50 );
390				}
391
392				localStorage.setItem( 'activetrail_failed_events', JSON.stringify( storedEvents ) );
393			} catch (e) {
394				if (this.config.debug) {
395					console.error( 'Failed to store event for retry:', e );
396				}
397			}
398		},
399
400		// Retry failed events from localStorage
401		retryFailedEvents: function () {
402			try {
403				var storedEvents = JSON.parse( localStorage.getItem( 'activetrail_failed_events' ) || '[]' );
404
405				if (storedEvents.length > 0) {
406					var self = this;
407
408					storedEvents.forEach(
409						function (event) {
410							self.sendEvent( event );
411						}
412					);
413
414					// Clear stored events after retry
415					localStorage.removeItem( 'activetrail_failed_events' );
416				}
417			} catch (e) {
418				if (this.config.debug) {
419					console.error( 'Failed to retry events:', e );
420				}
421			}
422		},
423
424		// Setup periodic sync
425		setupPeriodicSync: function () {
426			var self = this;
427
428			// Send queued events every 30 seconds
429			setInterval(
430				function () {
431					self.sendQueuedEvents();
432				},
433				30000
434			);
435
436			// Retry failed events every 5 minutes
437			setInterval(
438				function () {
439					self.retryFailedEvents();
440				},
441				300000
442			);
443
444			// Send events before page unload
445			$( window ).on(
446				'beforeunload',
447				function () {
448					self.sendQueuedEvents();
449				}
450			);
451		},
452
453		// Helper methods
454		getPageType: function () {
455			var body = $( 'body' );
456
457			if (body.hasClass( 'single-product' )) {
458				return 'product';
459			}
460			if (body.hasClass( 'woocommerce-cart' )) {
461				return 'cart';
462			}
463			if (body.hasClass( 'woocommerce-checkout' )) {
464				return 'checkout';
465			}
466			if (body.hasClass( 'woocommerce-account' )) {
467				return 'account';
468			}
469			if (body.hasClass( 'woocommerce-shop' ) || body.hasClass( 'archive' )) {
470				return 'shop';
471			}
472
473			return 'other';
474		},
475
476		getProductData: function (productId) {
477			var data = {
478				product_id: productId || this.getProductIdFromPage(),
479				product_name: this.getProductName(),
480				product_price: this.getProductPrice(),
481				product_category: this.getProductCategory(),
482				product_sku: this.getProductSku(),
483				product_url: window.location.href
484			};
485
486			return data;
487		},
488
489		getProductIdFromPage: function () {
490			// Try multiple methods to get product ID
491			var productId = $( 'input[name="add-to-cart"]' ).val() ||
492							$( 'button[name="add-to-cart"]' ).val() ||
493							$( '.single_add_to_cart_button' ).data( 'product_id' ) ||
494							$( 'body' ).attr( 'class' ).match( /postid-(\d+)/ );
495
496			if (Array.isArray( productId )) {
497				productId = productId[1];
498			}
499
500			return parseInt( productId ) || 0;
501		},
502
503		getProductName: function () {
504			return $( '.product_title' ).text() ||
505					$( 'h1.entry-title' ).text() ||
506					document.title.split( ' - ' )[0] || '';
507		},
508
509		getProductPrice: function () {
510			var priceText = $( '.price .amount' ).first().text() ||
511							$( '.price' ).text() ||
512							$( '.woocommerce-Price-amount' ).first().text() || '';
513
514			// Extract numeric value
515			var price = priceText.replace( /[^\d.,]/g, '' ).replace( ',', '.' );
516			return parseFloat( price ) || 0;
517		},
518
519		getProductCategory: function () {
520			var categories = [];
521			$( '.posted_in a' ).each(
522				function () {
523					categories.push( $( this ).text() );
524				}
525			);
526			return categories.join( ', ' );
527		},
528
529		getProductSku: function () {
530			return $( '.sku' ).text() || '';
531		},
532
533		getCartTotal: function () {
534			var totalText = $( '.cart-subtotal .amount' ).text() ||
535							$( '.order-total .amount' ).text() ||
536							$( '.cart_totals .amount' ).last().text() || '';
537
538			var total = totalText.replace( /[^\d.,]/g, '' ).replace( ',', '.' );
539			return parseFloat( total ) || 0;
540		},
541
542		getCartItemsCount: function () {
543			return parseInt( $( '.cart_item' ).length ) ||
544					parseInt( $( '.cart-contents-count' ).text() ) || 0;
545		},
546
547		extractProductIdFromRemoveLink: function (href) {
548			if ( ! href) {
549				return null;
550			}
551
552			var match = href.match( /remove_item=([^&]+)/ );
553			return match ? match[1] : null;
554		},
555
556		extractProductIdFromCartRow: function ($row) {
557			var classes = $row.attr( 'class' ) || '';
558			var match   = classes.match( /cart_item_(\d+)/ );
559			return match ? parseInt( match[1] ) : null;
560		}
561	};
562
563	// Initialize when document is ready
564	$( document ).ready(
565		function () {
566			ActiveTrailEventTracker.init();
567		}
568	);
569
570	// Make it globally available for manual tracking
571	window.ActiveTrailEventTracker = ActiveTrailEventTracker;
572
573})( jQuery );

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.