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