PageSourceSearch

https://vppbazzar.com/wp-content/plugins/mw-analytics/assets/js/tracker.js

js vppbazzar.com collected 2026-10-02 01:48:18 UTC 9,553 bytes, 221 lines download raw bytes

1/**
2 * MW Analytics — first-party tracking beacon.
3 *
4 * Cookieless. Sends one pageview on load (and on SPA route changes), then a
5 * time-on-page value on unload. No personal data is collected client-side.
6 */
7( function () {
8	'use strict';
9	var C = window.MWATrack || {};
10	if ( ! C.collect ) { return; }
11
12	// Respect a global opt-out flag if a consent tool sets one.
13	if ( window.MWA_DO_NOT_TRACK === true ) { return; }
14
15	var enter = Date.now();
16	var hitId = 0;
17	var lastPath = '';
18
19	function qp( name ) {
20		try {
21			return new URLSearchParams( window.location.search ).get( name ) || '';
22		} catch ( e ) { return ''; }
23	}
24
25	function sendPageview() {
26		var path = window.location.pathname || '/';
27		if ( path === lastPath ) { return; } // Avoid duplicate on same path.
28		lastPath = path;
29		enter = Date.now();
30		hitId = 0;
31
32		var body = {
33			path: path,
34			hostname: window.location.hostname,
35			referrer: document.referrer || '',
36			utm_source: qp( 'utm_source' ),
37			utm_medium: qp( 'utm_medium' ),
38			utm_campaign: qp( 'utm_campaign' )
39		};
40
41		fetch( C.collect, {
42			method: 'POST',
43			headers: { 'Content-Type': 'application/json' },
44			body: JSON.stringify( body ),
45			credentials: 'omit',
46			keepalive: true
47		} ).then( function ( r ) { return r.json(); } )
48			.then( function ( d ) { if ( d && d.id ) { hitId = d.id; } } )
49			.catch( function () {} );
50	}
51
52	function sendDuration() {
53		if ( ! hitId || ! C.duration ) { return; }
54		var seconds = Math.round( ( Date.now() - enter ) / 1000 );
55		if ( seconds < 1 ) { return; }
56		var payload = JSON.stringify( { id: hitId, seconds: seconds } );
57		try {
58			if ( navigator.sendBeacon ) {
59				navigator.sendBeacon( C.duration, new Blob( [ payload ], { type: 'application/json' } ) );
60				return;
61			}
62		} catch ( e ) {}
63		fetch( C.duration, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, keepalive: true, credentials: 'omit' } ).catch( function () {} );
64	}
65
66	// Fire the duration beacon once when the page is being hidden/left.
67	var durationSent = false;
68	function flush() {
69		if ( durationSent ) { return; }
70		durationSent = true;
71		sendDuration();
72	}
73	document.addEventListener( 'visibilitychange', function () {
74		if ( document.visibilityState === 'hidden' ) { flush(); }
75	} );
76	window.addEventListener( 'pagehide', flush );
77
78	// SPA route changes: re-arm on history navigation.
79	[ 'pushState', 'replaceState' ].forEach( function ( m ) {
80		var orig = history[ m ];
81		if ( typeof orig === 'function' ) {
82			history[ m ] = function () {
83				var r = orig.apply( this, arguments );
84				flush(); durationSent = false; sendPageview();
85				return r;
86			};
87		}
88	} );
89	window.addEventListener( 'popstate', function () { flush(); durationSent = false; sendPageview(); } );
90
91	// Initial pageview.
92	if ( document.readyState === 'complete' || document.readyState === 'interactive' ) {
93		sendPageview();
94	} else {
95		document.addEventListener( 'DOMContentLoaded', sendPageview );
96	}
97
98	/* ---- Event / CTA tracking ---- */
99	var EV = C.ev || {};
100	function sendEvent( event, label, value ) {
101		if ( ! C.event ) { return; }
102		var payload = JSON.stringify( { event: event, label: ( label || '' ).slice( 0, 190 ), path: window.location.pathname || '/', value: value || 0 } );
103		try {
104			if ( navigator.sendBeacon ) { navigator.sendBeacon( C.event, new Blob( [ payload ], { type: 'application/json' } ) ); return; }
105		} catch ( e ) {}
106		fetch( C.event, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, keepalive: true, credentials: 'omit' } ).catch( function () {} );
107	}
108	// Expose a manual API: window.mwaTrack('event_name', 'label', value)
109	window.mwaTrack = function ( event, label, value ) { sendEvent( event, label, value ); };
110
111	if ( EV.on ) {
112		var selList = ( EV.selectors || '' ).split( /[\n,]+/ ).map( function ( s ) { return s.trim(); } ).filter( Boolean );
113		function text( el ) { return ( el.getAttribute( 'aria-label' ) || el.textContent || el.value || '' ).replace( /\s+/g, ' ' ).trim().slice( 0, 120 ); }
114		function matchesAny( el ) { for ( var i = 0; i < selList.length; i++ ) { try { if ( el.closest( selList[ i ] ) ) { return el.closest( selList[ i ] ); } } catch ( e ) {} } return null; }
115
116		document.addEventListener( 'click', function ( e ) {
117			var t = e.target;
118			if ( ! t || ! t.closest ) { return; }
119
120			// Explicit opt-in via data attribute.
121			var dataEl = t.closest( '[data-mwa-event]' );
122			if ( dataEl ) { sendEvent( dataEl.getAttribute( 'data-mwa-event' ) || 'cta', dataEl.getAttribute( 'data-mwa-label' ) || text( dataEl ) ); return; }
123
124			var a = t.closest( 'a[href]' );
125			if ( a ) {
126				var href = a.getAttribute( 'href' ) || '';
127				var low = href.toLowerCase();
128				if ( EV.whatsapp && ( /wa\.me|api\.whatsapp\.com|web\.whatsapp\.com/.test( low ) || low.indexOf( 'whatsapp:' ) === 0 ) ) { sendEvent( 'whatsapp', text( a ) || 'WhatsApp' ); return; }
129				if ( low.indexOf( 'tel:' ) === 0 ) { sendEvent( 'call', href.replace( /^tel:/i, '' ) ); return; }
130				if ( low.indexOf( 'mailto:' ) === 0 ) { sendEvent( 'email', href.replace( /^mailto:/i, '' ).split( '?' )[ 0 ] ); return; }
131				// Configured CTA selectors.
132				var m = matchesAny( a );
133				if ( m ) { sendEvent( 'cta', text( m ) ); return; }
134				// Outbound links.
135				if ( EV.outbound && /^https?:\/\//i.test( href ) ) {
136					try { if ( new URL( href, location.href ).host !== location.host ) { sendEvent( 'outbound', new URL( href, location.href ).host ); return; } } catch ( e ) {}
137				}
138				return;
139			}
140			// Non-anchor CTA (buttons matched by selector).
141			var cm = matchesAny( t );
142			if ( cm ) { sendEvent( 'cta', text( cm ) ); }
143		}, true );
144
145		if ( EV.forms ) {
146			document.addEventListener( 'submit', function ( e ) {
147				var f = e.target;
148				if ( ! f || f.tagName !== 'FORM' ) { return; }
149				if ( f.getAttribute( 'role' ) === 'search' || /\bsearch\b/i.test( f.className || '' ) ) { return; }
150				var label = f.getAttribute( 'name' ) || f.getAttribute( 'id' ) || ( f.className || '' ).split( ' ' )[ 0 ] || 'form';
151				sendEvent( 'form_submit', label );
152			}, true );
153		}
154	}
155
156	/* ---- Heatmaps (click points + scroll depth) ---- */
157	var HM = C.hm || {};
158	if ( C.heat && HM.on && ( ( Math.random() * 100 ) <= ( HM.sample || 100 ) ) ) {
159		var clicks = [];
160		var maxDepth = 0;
161		var MAXPTS = 60;
162
163		function docHeight() {
164			var b = document.body, e = document.documentElement;
165			return Math.max( b ? b.scrollHeight : 0, b ? b.offsetHeight : 0, e ? e.clientHeight : 0, e ? e.scrollHeight : 0, e ? e.offsetHeight : 0 );
166		}
167		function updateDepth() {
168			var dh = docHeight();
169			if ( dh <= 0 ) { return; }
170			var reached = ( window.pageYOffset || document.documentElement.scrollTop || 0 ) + window.innerHeight;
171			var pm = Math.max( 0, Math.min( 1000, Math.round( reached / dh * 1000 ) ) );
172			if ( pm > maxDepth ) { maxDepth = pm; }
173		}
174		window.addEventListener( 'scroll', updateDepth, { passive: true } );
175		window.addEventListener( 'resize', updateDepth, { passive: true } );
176		updateDepth();
177
178		function capture( x, y ) {
179			if ( clicks.length >= MAXPTS ) { return; }
180			var w = window.innerWidth || 1;
181			var xpm = Math.max( 0, Math.min( 1000, Math.round( ( x / w ) * 1000 ) ) );
182			clicks.push( [ xpm, Math.round( y ) ] );
183		}
184		// Capture on pointerdown (covers mouse + touch reliably); fall back to
185		// click for environments without pointer events.
186		if ( window.PointerEvent ) {
187			document.addEventListener( 'pointerdown', function ( e ) { capture( e.clientX, ( e.pageY != null ? e.pageY : e.clientY + ( window.pageYOffset || 0 ) ) ); }, true );
188		} else {
189			document.addEventListener( 'click', function ( e ) { capture( e.clientX, ( e.pageY != null ? e.pageY : e.clientY + ( window.pageYOffset || 0 ) ) ); }, true );
190			document.addEventListener( 'touchstart', function ( e ) { var t = e.touches && e.touches[ 0 ]; if ( t ) { capture( t.clientX, t.clientY + ( window.pageYOffset || 0 ) ); } }, true );
191		}
192
193		var sentClicks = 0;
194		var scrollSent = false;
195		// Send new clicks (and, on the final flush, the scroll depth). Sending
196		// clicks incrementally means data survives even if the unload beacon is
197		// dropped (common on mobile); scroll is sent once so pageview counts stay
198		// accurate.
199		function sendHeat( isFinal ) {
200			updateDepth();
201			var batch = clicks.slice( sentClicks );
202			var sendScroll = isFinal && ! scrollSent;
203			if ( ! batch.length && ! sendScroll ) { return; }
204			var body = { path: window.location.pathname || '/', vw: window.innerWidth, dh: docHeight() };
205			if ( batch.length ) { body.clicks = batch; }
206			if ( sendScroll ) { body.scroll = maxDepth; scrollSent = true; }
207			sentClicks = clicks.length;
208			var payload = JSON.stringify( body );
209			try {
210				if ( navigator.sendBeacon ) { navigator.sendBeacon( C.heat, new Blob( [ payload ], { type: 'application/json' } ) ); return; }
211			} catch ( e ) {}
212			fetch( C.heat, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, keepalive: true, credentials: 'omit' } ).catch( function () {} );
213		}
214		var heatSent = false;
215		function flushHeat() { if ( heatSent ) { return; } heatSent = true; sendHeat( true ); }
216		// Periodic partial flush so long sessions do not lose clicks.
217		var heatTimer = setInterval( function () { if ( clicks.length > sentClicks ) { sendHeat( false ); } }, 20000 );
218		document.addEventListener( 'visibilitychange', function () { if ( document.visibilityState === 'hidden' ) { flushHeat(); } } );
219		window.addEventListener( 'pagehide', function () { clearInterval( heatTimer ); flushHeat(); } );
220	}
221} )();

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.