1// noinspection JSVoidFunctionReturnValueUsed 2 3/* 4 * Main GA Events Plugin function 5 * 6 * TO DO: all variables with gaeMapper. prefix can be declared at top of function 7 * and have prefix removed thereafter. Although for now it could be helpful to 8 * keep prefix to make it obvious which values need to eventually be accessed 9 * directly from localized values. 10 * 11 * Exposes track_event, click_event 12 * 13 * Global: TO DO 14 */ 15var gaeMapper = (function () { 16 17 // Make sure options are present globally 18 if (typeof ga_options !== "undefined") { 19 20 /* 21 * We temporarily need to be able to create a selector of type id or class 22 * from the existing DB options. 23 * 24 * Eventually we will probably update the backend to store the selector as an 25 * option within the associative "click" (or otherwise named) array 26 * 27 * @param {sequential array} 28 */ 29 function makeSelector(click_option) { 30 var selector = ""; 31 if (click_option.type === 'class') { 32 selector += '.'; 33 } else if (click_option.type === 'id') { 34 selector += '#'; 35 } 36 37 selector += click_option.name; 38 return selector; 39 } 40 41 // Click elements from DB section begins 42 // TO DO clean up this code a bit to make it more like the scroll elements 43 // from DB section 44 var clickElementsFromDB = []; 45 46 var click_elements_from_options = ga_options.click_elements; 47 for (var i = 0; i < click_elements_from_options.length; i++) { 48 49 var clicked = click_elements_from_options[i]; 50 newClickElement = {}; 51 52 var nonInteraction = (clicked.bounce === "true"); 53 newClickElement.data = { 54 "select": makeSelector(clicked), 55 "category": clicked.category, 56 "action": clicked.action, 57 "label": clicked.label, 58 "bounce": nonInteraction, 59 "evalue": clicked.value, // "value" in DB. Change in project? 60 "link_click_delay": parseInt(ga_options.link_clicks_delay), 61 "universal": parseInt(ga_options.universal) 62 }; 63 64 // NB was unescapeChars(select) (from original js file) 65 newClickElement.selector = newClickElement.data.select; 66 clickElementsFromDB.push(newClickElement); 67 } 68 69 // Click elements from DB section ends 70 71 // Scrollelements from DB section begins 72 73 var scroll_elements_from_options = ga_options.scroll_elements; 74 var scrollElementsFromDB = []; 75 76 // NB these will be assigned meaningful names in backend eventually 77 if (typeof scroll_elements_from_options !== "undefined") { 78 scroll_elements_from_options.forEach(function (el) { 79 var nonInteraction = (el.bounce === "true"); 80 81 scrollElementsFromDB.push({ 82 "select": makeSelector(el), 83 "category": el.category, 84 "action": el.action, 85 "label": el.label, 86 "bounce": nonInteraction, 87 "evalue": el.value // "value" in DB. Change in project? 88 }); 89 }); 90 } 91 92 // Scrollelements from DB section ends 93 94 // Expose modified properties to global scope 95 return { 96 97 // From ga_options 98 clickElementsFromDB: clickElementsFromDB, 99 scrollElementsFromDB: scrollElementsFromDB, 100 advancedMode: ga_options.advanced, 101 forceSnippet: ga_options.force_snippet, 102 emailLinksTracking: ga_options.email_link_tracking, 103 telLinksTracking: ga_options.tel_link_tracking, 104 downloadTracking: ga_options.download_tracking, 105 downloadTrackingFileTypes: ga_options.download_tracking_type, 106 link_clicks_delay: ga_options.link_clicks_delay, 107 snippet_type: ga_options.snippet_type, 108 isFrontPage: gaePlaceholders.is_front_page, 109 pageTitle: gaePlaceholders.page_title, 110 scriptDebugMode: ga_options.script_debug_mode, 111 } 112 113 114 } // end if 115 116})(); 117 118 119gaEventsMain = (function ($) { 120 121 "use strict"; 122 123 // This is the tracking element. 124 // It could already have been created by another plugin. 125 var ga_element; 126 127 $(document).ready(function () { 128 129 // TO DO: This needs to be conditional on non-excluded roles! 130 // (Another export from mapper is probably the best way to get these 131 // at the current stage.) 132 applyBindings(); 133 }); 134 135 /* 136 * Apply bindings from values stored in DB 137 */ 138 function applyBindings() { 139 140 // Bind click events stored in DB to the DOM body 141 // "clicked is the data which gets passed to the click_event function 142 // "selector" is the id or class of the element clicked
143 gaeMapper.clickElementsFromDB.forEach(function (el) { 144 145 $('body').on('click', el.selector, el.data, click_event); 146 }); 147 148 bindDownloadTrackingEvents(); 149 bindEmailLinksTracking(); 150 bindTelLinksTracking(); 151 // Bind scroll events stored in the DB to the window 152 $(window).on('scroll', bindScrollEventsFromDB); 153 } 154 155 /** 156 * Binds Scroll events from DB 157 * 158 * @returns {undefined} 159 */ 160 function bindScrollEventsFromDB() { 161 162 // TO DO this code can be simplified a lot. May be better to use 163 // $('element').scroll() 164 165 var ga_window = $(window).height(); 166 var ga_scroll_top = $(document).scrollTop(); 167 168 for (var i = 0; i < gaeMapper.scrollElementsFromDB.length; i++) { 169 170 if (!gaeMapper.scrollElementsFromDB[i].sent) { 171 172 // NB was unescapeChars( gaeMapper.scrollElementsFromDB[i].select) 173 var $select = $(gaeMapper.scrollElementsFromDB[i].select); 174 gaeMapper.scrollElementsFromDB[i].offset = $select.offset(); 175 176 if (gaeMapper.scrollElementsFromDB[i].offset && ga_scroll_top + ga_window >= gaeMapper.scrollElementsFromDB[i].offset.top + $select.height()) { 177 track_event(gaeMapper.scrollElementsFromDB[i].category, gaeMapper.scrollElementsFromDB[i].action, gaeMapper.scrollElementsFromDB[i].label, gaeMapper.scrollElementsFromDB[i].bounce, gaeMapper.scrollElementsFromDB[i].evalue); 178 179 gaeMapper.scrollElementsFromDB[i].sent = true; 180 } 181 } 182 } 183 } 184 185 /** 186 * Binds Download Tracking 187 * 188 * @returns {undefined} 189 */ 190 function bindDownloadTrackingEvents() { 191 if (gaeMapper.downloadTracking === "1") { 192 gaeMapper.downloadTrackingFileTypes.forEach(function (ftype) { 193 var lowercase = ftype.toLowerCase(); 194 var uppercase = ftype.toUpperCase(); 195 196 $('body').on('click', 'a[href$=".' + lowercase + '"]', trackFileDownloaded); 197 $('body').on('click', 'a[href$=".' + uppercase + '"]', trackFileDownloaded); 198 }); 199 } 200 } // End of bindScrollEvents 201 202 function bindEmailLinksTracking() { 203 if (gaeMapper.emailLinksTracking === "1") { 204 $('body').on('click', 'a[href^="mailto:"]', function (e) { 205 e.preventDefault(); 206 var label = this.href.split(':').pop(); 207 var action = "Email Link"; 208 var category = getPageName(); 209 track_event(category,action,label,false, ""); 210 handleLinks(this, e); 211 }); 212 } 213 } 214 215 function bindTelLinksTracking() { 216 if (gaeMapper.telLinksTracking === "1") { 217 $('body').on('click', 'a[href^="tel:"]', function (e) { 218 e.preventDefault(); 219 var label = this.href.split(':').pop(); 220 var action = "Telephone Number Link"; 221 var category = getPageName(); 222 track_event(category,action,label,false, ""); 223 handleLinks(this, e); 224 }); 225 } 226 } 227 228 var trackFileDownloaded = function (e) { 229 e.preventDefault(); 230 var label = this.href.split('/').pop(); 231 var action = "Download"; 232 var category = getPageName(); 233 track_event(category,action,label,false, ""); 234 handleLinks(this, e); 235 }; 236 237 /* 238 * The main function for tracking events 239 * 240 * Param data types currently under review 241 * 242 * @param {string} category 243 * @param {string} action 244 * @param {string} label 245 * @param {bool} bounce 246 * @param {string}? evalue 247 * @returns {undefined} 248 */ 249 var track_event = function (category, action, label, bounce, evalue) { 250 if (typeof ga_element === "undefined") { 251 if (typeof ga !== 'undefined') { 252 ga_element = ga; 253 } else if (typeof _gaq !== 'undefined') { 254 ga_element = _gaq; 255 } else if (typeof __gaTracker === "function") { 256 ga_element = __gaTracker; 257 } else if (typeof gaplusu === "function") { 258 ga_element = gaplusu; 259 } else if (gaeMapper.snippet_type !== 'gtm' && typeof dataLayer === "undefined") { 260 return; 261 } 262 } 263 264 var event_category = !category ? 'uncategorized' : category; 265 category = event_category; 266 267 var event_action = !action ? '' : action; 268 action = event_action; 269 270 var event_label = !label ? '' : label; 271 label = event_label; 272 273 var event_value = !evalue ? '' : evalue; 274 var event_bounce = !bounce ? false : bounce; 275 switch (gaeMapper.forceSnippet) { 276 case "none": 277 if (useGlobalSiteTagSnippet()) { 278 globalSiteTagEvent(category, action, label, event_value, event_bounce); 279 } else if (useGoogleTagManagerSnippet()) { 280 googleTagManagerEvent(category, action, label, event_value, event_bounce); 281 } else if (useUniversalTrackingSnippet()) { 282 universalTrackingEvent(category, action, label, event_value, event_bounce); 283 } else if (useLegacySnippet()) { 284 legacyTrackingEvent(category, action, label, event_value, event_bounce); 285 } 286 break; 287 case "gtm": 288 googleTagManagerEvent(category, action, label, event_value, event_bounce); 289 break; 290 case "gst": 291 globalSiteTagEvent(category, action, label, event_value, event_bounce); 292 break; 293 case "universal": 294 universalTrackingEvent(category, action, label, event_value, event_bounce); 295 break; 296 } 297 }; // End of track_event function 298 299 var useGoogleTagManagerSnippet = function () { 300 return ( 301 gaeMapper.snippet_type === "gtm" || 302 (typeof dataLayer !== "undefined" && typeof gtag === "undefined") 303 ); 304 }; 305 306 var useGlobalSiteTagSnippet = function () {
307 return gaeMapper.snippet_type === "gst" || typeof gtag !== "undefined"; 308 }; 309 310 var useUniversalTrackingSnippet = function () { 311 return ( 312 gaeMapper.snippet_type === "universal" || 313 typeof ga !== "undefined" || 314 typeof __gaTracker === "function" 315 ); 316 }; 317 318 var useLegacySnippet = function () { 319 return gaeMapper.snippet_type === "legacy" || typeof _gaq !== "undefined"; 320 }; 321 322 // Use the Google Tag Manager object to send an event 323 var googleTagManagerEvent = function ( 324 category, 325 action, 326 label, 327 value, 328 bounce 329 ) { 330 dataLayer.push({ 331 event: "WPGAE", 332 eventCategory: category, 333 eventAction: action, 334 eventLabel: label, 335 eventValue: value, 336 nonInteraction: bounce, 337 }); 338 }; 339 340 var globalSiteTagEvent = function (category, action, label, value, bounce) { 341 gtag("event", action, { 342 // Event parameters 343 event_category: category, 344 event_label: label, 345 value: value, 346 non_interaction: bounce, 347 }); 348 }; 349 350 var universalTrackingEvent = function ( 351 category, 352 action, 353 label, 354 value, 355 bounce 356 ) { 357 if (value) { 358 ga_element("send", "event", category, action, label, value, { 359 nonInteraction: bounce, 360 }); 361 } else { 362 ga_element("send", "event", category, action, label, { 363 nonInteraction: bounce, 364 }); 365 } 366 }; 367 368 var legacyTrackingEvent = function (category, action, label, value, bounce) { 369 ga_element.push(["_trackEvent", category, action, label, value, bounce]); 370 }; 371 372 373 /** 374 * Click event function 375 * 376 * @param {event} event 377 * @returns {undefined} 378 */ 379 var click_event = function (event) { 380 381 track_event(event.data.category, event.data.action, event.data.label, event.data.bounce, event.data.evalue, this); 382 if (typeof event.data.link_click_delay !== 'undefined' && event.data.link_click_delay > 0 && typeof event.target.href !== 'undefined' && event.target.nodeName == "A") { 383 handleLinks(this, event); 384 } 385 }; // End of click event function 386 387 388 var isNewTab = function (self) { 389 var target = $(self).attr("target") 390 if (typeof target !== "undefined" && target.trim() === "_blank") { 391 return true; 392 } 393 return false; 394 } 395 396 var handleLinks = function (self, event) { 397 event.preventDefault(); 398 var link = getUrl(event); 399 400 401 var w; 402 var openInNewTab = isNewTab(self) 403 if (openInNewTab) { 404 w = window.open('', '_blank'); 405 } 406 407 var hash = isJustHashLink(link); 408 if (typeof hash !== "undefined" && hash !== "") { 409 window.location.hash = hash; 410 } else if (window.location.href !== link){ 411 setTimeout(function () { 412 if (openInNewTab) { 413 w.location.href = link; 414 } else { 415 window.location = link; 416 } 417 }, parseInt(gaeMapper.link_clicks_delay), w); 418 } 419 } 420 421 var getUrl = function ( event ) { 422 var url = ""; 423 if ( event.target.tagName !== "A" ) { 424 url = $( event.target ).parents( "a" ).attr( "href" ); 425 } else { 426 url = event.target.href; 427 } 428 return url; 429 }; 430 431 var isJustHashLink = function ( url ) { 432 if ( typeof url !== "undefined" && url.indexOf( "#" ) === 0 ) { 433 return url; 434 } 435 return ""; 436 }; 437 438 function getPageName () { 439 if ("1" === gaeMapper.isFrontPage) { 440 return "Home"; 441 } else { 442 if (typeof gaeMapper.pageTitle !== "undefined") { 443 return gaeMapper.pageTitle; 444 } 445 } 446 return ""; 447 } 448 449})(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.