1'use strict'; 2 3/** 4 * Handles tracking for all events. 5 * 6 */ 7 8// Wrapping the code in an anonymous function to avoid conflicts. 9( function () { 10 11 // Holds previous hash. 12 const prevHash = window.location.hash; 13 14 const eventsTrackingMode = analytify_events_tracking.tracking_mode; 15 const GAMode = analytify_events_tracking.ga_mode; 16 const trackingCode = analytify_events_tracking.tracking_code; 17 18 function trim_link_string(x) { 19 return x.replace(/^\s+|\s+$/gm, ''); 20 } 21 22 /** 23 * Returns the type of link tracking. 24 * 25 * @param {object} el The element to track. 26 * @returns 27 */ 28 function get_tracking_type( el ) { 29 30 // Default tracking type. 31 let type = 'unknown'; 32 33 const link = el.href; 34 const {hostname} = el; 35 const {protocol} = el; 36 const {pathname} = el; 37 38 const {download_extension} = analytify_events_tracking; 39 const current_domain = analytify_events_tracking.root_domain; 40 41 const affiliate_links = 'undefined' !== typeof analytify_events_tracking.affiliate_link ? analytify_events_tracking.affiliate_link : {}; 42 43 if ( link.match(/^javascript\:/i) ) { 44 45 // if its a JS link, it's internal 46 type = 'internal'; 47 } else if ( 'tel' == trim_link_string(protocol) || 'tel:' == trim_link_string(protocol) ) { 48 49 // Track telephone event. 50 type = 'tel'; 51 } else if ( 'mailto' == trim_link_string(protocol) || 'mailto:' == trim_link_string(protocol) ) { 52 53 // Track mail event. 54 type = 'mailto'; 55 } else if ( 0 < hostname.length && 0 < current_domain.length && ! hostname.endsWith( current_domain ) ) { 56 57 // Track external links. 58 type = 'external'; 59 } else if ( 0 < Object.keys(affiliate_links).length && 0 < pathname.length ) { 60 for ( const affiliate_link_index in affiliate_links ) { 61 const affiliate_link = affiliate_links[affiliate_link_index]; 62 if ( affiliate_link.path && pathname.endsWith( affiliate_link.path ) ) { 63 type = 'outbound'; 64 break; 65 } 66 } 67 } 68 69 // Track download files. 70 if ( 'unknown' === type && 0 < download_extension.length ) { 71 let regExp = ''; 72 try { 73 regExp = new RegExp(`.*\\.(${ download_extension })(\\?.*)?$`); 74 } catch (e) { 75 76 // Silent fail - Invalid RegExp for download tracking 77 } 78 79 if ( 'undefined' != typeof regExp && el.href.match( regExp ) ) { 80 type = 'download'; 81 } 82 } 83 84 if ( 'unknown' === type ) { 85 type = 'internal'; 86 } 87 88 return type; 89 } 90 91 /** 92 * Sends the event call. 93 * 94 * @param {string} category Event category. 95 * @param {string} action Event action. 96 * @param {string} label Event label. 97 */ 98 function send_event_call(category, action, label) { 99 if ('gtag' === eventsTrackingMode) { 100 if ('function' !== typeof gtag) { 101 return; 102 } 103 104 if ( 'ga4' === GAMode ) { 105 const eventData = { 106 'wpa_link_action': action, 107 'wpa_category': category, 108 'wpa_link_label': label, 109 'wpa_device_category': get_device_category(), 110 'wpa_click_hour': new Date().getHours().toString(), 111 'wpa_click_day': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][new Date().getDay()], 112 'wpa_traffic_source': get_traffic_source(), 113 'wpa_country_code': extract_country_code(action), 114 }; 115 116 gtag('event', 'analytify_event_tracking', eventData); 117 } else { 118 gtag('event', action, { 119 'event_category': category, 120 'event_label': label, 121 }); 122 } 123 } else { 124 if ('function' === typeof ga) { 125 ga('send', 'event', category, action, label); 126 } 127 } 128 } 129 130 /** 131 * Determine device category based on screen size 132 */ 133 function get_device_category() { 134 const width = window.innerWidth; 135 if (768 >= width) {return 'mobile';} 136 if (1024 >= width) {return 'tablet';} 137 return 'desktop'; 138 } 139 140 /** 141 * True when document.referrer's hostname is host or a subdomain of host (e.g. www.google.com vs google.com). 142 * 143 * @param {string} referrer Full referrer URL 144 * @param {string} host Bare host, e.g. google.com 145 * @returns {boolean} 146 */ 147 function referrer_hostname_is_or_subdomain_of(referrer, host) { 148 try { 149 const h = new URL(referrer).hostname.toLowerCase(); 150 const b = String(host).toLowerCase(); 151 return h === b || h.endsWith(`.${ b}`); 152 } catch (e) { 153 return false;
154 } 155 } 156 157 /** 158 * Determine traffic source 159 */ 160 function get_traffic_source() { 161 const {referrer} = document; 162 const utmSource = get_url_parameter('utm_source'); 163 164 if (utmSource) {return utmSource;} 165 if (!referrer) {return 'direct';} 166 if (referrer_hostname_is_or_subdomain_of(referrer, 'google.com')) {return 'google';} 167 if (referrer_hostname_is_or_subdomain_of(referrer, 'facebook.com')) {return 'facebook';} 168 if (referrer_hostname_is_or_subdomain_of(referrer, 'twitter.com') || referrer_hostname_is_or_subdomain_of(referrer, 'x.com')) {return 'twitter';} 169 if (referrer_hostname_is_or_subdomain_of(referrer, 'linkedin.com')) {return 'linkedin';} 170 if (referrer_hostname_is_or_subdomain_of(referrer, 'instagram.com')) {return 'instagram';} 171 if (referrer_hostname_is_or_subdomain_of(referrer, 'youtube.com')) {return 'youtube';} 172 return 'referral'; 173 } 174 175 /** 176 * Get URL parameter value 177 */ 178 function get_url_parameter(name) { 179 const urlParams = new URLSearchParams(window.location.search); 180 return urlParams.get(name) || ''; 181 } 182 183 /** 184 * Extract country code from tel number 185 */ 186 function extract_country_code(telHref) { 187 if (!telHref || 'string' !== typeof telHref) { 188 return 'unknown'; 189 } 190 191 // Remove 'tel:' and any non-digit characters except '+' 192 const cleanNumber = telHref.replace('tel:', '').replace(/[^\d+]/g, ''); 193 194 if (cleanNumber.startsWith('+')) { 195 196 // Pakistani numbers (+92) 197 if (cleanNumber.startsWith('+92')) { 198 return '+92'; 199 } 200 201 // North American numbers (+1) 202 if (cleanNumber.startsWith('+1')) { 203 return '+1'; 204 } 205 206 // For others, extract up to 3 digits, but check common 2-digit codes first 207 // This is a heuristic approach since country codes vary in length 208 const threeDigitMatch = cleanNumber.match(/^\+(\d{1,3})/); 209 if (threeDigitMatch) { 210 const [ , code] = threeDigitMatch; 211 212 // Common 2-digit codes start with these prefixes 213 if (3 === code.length && ['20', '27', '30', '31', '32', '33', '34', '36', '39', '40', '41', '43', '44', '45', '46', '47', '48', '49', '51', '52', '53', '54', '55', '56', '57', '58', '60', '61', '62', '63', '64', '65', '66', '81', '82', '84', '86', '90', '91', '92', '93', '94', '95', '98'].some(prefix => code.startsWith(prefix))) { 214 return `+${code.substring(0, 2)}`; 215 } 216 return `+${code}`; 217 } 218 } 219 220 return 'unknown'; 221 } 222 223 /** 224 * Enhanced tel link tracking with comprehensive data collection 225 * 226 * @param {object} target The tel link element 227 */ 228 function track_enhanced_tel_event(target) { 229 230 // console.log('Enhanced tel tracking triggered for:', target.href); 231 232 const action = target.getAttribute('data-vars-ga-action') || target.href; 233 const label = target.getAttribute('data-vars-ga-label') || target.title || target.innerText || target.href; 234 235 send_event_call('tel', action, label); 236 } 237 238 /** 239 * Tracks events. 240 * 241 * @param {object} event Event object. 242 * @returns 243 */ 244 function track_event( event ) { 245 246 if ( '1' != analytify_events_tracking.is_track_user ) { 247 return; 248 } 249 250 event = event || window.event; 251 252 let target = event.target || event.srcElement; 253 254 // If link is not define. Get the parent link. 255 while (target && ('undefined' == typeof target.tagName || 'a' != target.tagName.toLowerCase() || !target.href)) { 256 target = target.parentNode; 257 } 258 259 // if its links 260 if ( target && target.href ) { 261 262 let action = ''; 263 let label = ''; 264 265 const tracking_type = get_tracking_type( target ); 266 267 switch ( tracking_type ) { 268 case 'outbound': 269 action = target.getAttribute('data-vars-ga-action') || target.href; 270 label = target.getAttribute('data-vars-ga-label') || target.title || target.innerText || target.href; 271 send_event_call( 'outbound-link', action, label ); 272 break; 273 274 case 'download': 275 action = target.getAttribute('data-vars-ga-action') || target.href; 276 label = target.getAttribute('data-vars-ga-label') || target.title || target.innerText || target.href; 277 send_event_call( 'download', action, label ); 278 break; 279 280 case 'tel': 281 track_enhanced_tel_event(target); 282 break; 283
284 case 'external': 285 action = target.getAttribute('data-vars-ga-action') || target.href; 286 label = target.getAttribute('data-vars-ga-label') || target.title || target.innerText || target.href; 287 send_event_call( 'external', action, label ); 288 break; 289 290 case 'mailto': 291 action = target.getAttribute('data-vars-ga-action') || target.href; 292 label = target.getAttribute('data-vars-ga-label') || target.title || target.innerText || target.href; 293 send_event_call( 'mail', action, label ); 294 break; 295 296 default: 297 break; 298 } 299 } 300 } 301 302 /** 303 * Tracks hash change. 304 */ 305 function track_hash() { 306 307 // If hash tracking is enabled. 308 if ( 'on' == analytify_events_tracking.anchor_tracking && prevHash != window.location.hash ) { 309 if ('gtag' === eventsTrackingMode) { 310 if ('function' === typeof gtag) { 311 gtag('config', trackingCode, { 'page_path': location.pathname + location.search + location.hash }); 312 } 313 } else if ('function' === typeof ga) { 314 ga('set', 'page', location.pathname + location.search + location.hash); 315 ga('send', 'pageview'); 316 } 317 } 318 } 319 320 // When hash changes. 321 window.addEventListener( 'hashchange', track_hash, false ); 322 323 // On Click. 324 window.addEventListener( "load", () => { 325 document.body.addEventListener( "click", track_event, false ); 326 }, false); 327 328}());
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.