1/* global document: true */ 2/* global window: true */ 3/* global console: true */ 4/* exported smileTracker */ 5 6/** 7 * DISCLAIMER 8 * 9 * Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer 10 * versions in the future. 11 * 12 * @category Smile 13 * @package Smile\ElasticsuiteTracker 14 * @author Aurelien FOUCRET <[email protected]> 15 * @copyright 2020 Smile 16 * @license Open Software License ("OSL") v. 3.0 17 */ 18const smileTracker = (function () { 19 20 "use strict"; 21 22 const guid = (function() { 23 function s4() { 24 return Math.floor((1 + Math.random()) * 0x10000) 25 .toString(16) 26 .substring(1); 27 } 28 return function() { 29 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 30 s4() + '-' + s4() + s4() + s4(); 31 }; 32 }()); 33 34 function getCookie(cookieName) { 35 const name = cookieName + "="; 36 const ca = document.cookie.split(';'); 37 for (let i = 0; i < ca.length; i++) { 38 let c = ca[i]; 39 while (c.charAt(0) === ' ') { 40 c = c.substring(1); 41 } 42 if (c.indexOf(name) !== -1) { 43 return c.substring(name.length, c.length); 44 } 45 } 46 return null; 47 } 48 49 function setCookie(cookieName, cookieValue, expiresAt, path) { 50 const expires = "expires=" + expiresAt.toUTCString(); 51 document.cookie = cookieName + "=" + cookieValue + "; " + expires + "; path=" + path; 52 } 53 54 // Retrieve values for a param into URL 55 function getQueryStringParameterByName(name) { 56 let results = null; 57 58 if (name && name.replace) { 59 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 60 const regex = new RegExp("[\\?&]" + name + "=([^&#]*)"); 61 results = regex.exec(window.location.search); 62 } 63 64 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 65 } 66 67 // Add page title and page URL to the tracked variables 68 function addStandardPageVars() { 69 // Website base url tracking (eg. mydomain.com) 70 this.addPageVar("site", window.location.hostname); 71 72 // Page URL tracking (eg. home.html) 73 this.addPageVar("url", window.location.pathname); 74 75 // Page title tracking 76 this.addPageVar("title", document.title); 77 } 78 79 // Append GA campaign variable to the tracked variables 80 // Following URL variables are used : 81 // - utm_source 82 // - utm_campaign 83 // - utm_medium 84 function addCampaignVars() { 85 // GA variables to be fetched from URI 86 const urlParams = ['utm_source', 'utm_campaign', 'utm_medium', 'utm_term']; 87 88 urlParams.forEach(function (element) { 89 const paramName = element; 90 const paramValue = getQueryStringParameterByName(paramName); 91 if (paramValue) { 92 // Append the GA param to the tracker 93 this.addPageVar(paramName, paramValue); 94 } 95 }.bind(this)); 96 } 97 98 function addReferrerVars() { 99 if (document.referrer) { 100 let parser = document.createElement('a'); 101 parser.href = document.referrer; 102 this.addPageVar('referrer.domain', parser.hostname); 103 this.addPageVar('referrer.page', parser.pathname); 104 } 105 } 106 107 function addResolutionVars() { 108 this.addPageVar('resolution.x', window.screen.availWidth); 109 this.addPageVar('resolution.y', window.screen.availHeight); 110 return this; 111 } 112 113 function addCustomerVars(customerData) { 114 getCustomerDataCodeToTrack().forEach(function (customerDataCode) { 115 if (customerData.hasOwnProperty('tracking') && customerData.tracking.hasOwnProperty(customerDataCode)) { 116 this.addCustomerVar(customerDataCode, customerData.tracking[customerDataCode]); 117 } 118 }.bind(this)); 119 } 120 121 function addMetaPageVars() { 122 const metaTags = document.getElementsByTagName('meta'); 123 for (let tagIndex = 0; tagIndex < metaTags.length; tagIndex++) { 124 if (metaTags[tagIndex].getAttribute('name')) { 125 let components = metaTags[tagIndex].getAttribute('name').split(':'); 126 if (components.length === 2 && components[0] === 'sct') { 127 let varName = components[1]; 128 this.addPageVar(varName, metaTags[tagIndex].getAttribute('content')); 129 } 130 } 131 } 132 } 133 134 function getTrackerVars() { 135 if (this.trackerVarsAdded === false) { 136 addStandardPageVars.bind(this)(); 137 addReferrerVars.bind(this)(); 138 addCampaignVars.bind(this)(); 139 addMetaPageVars.bind(this)(); 140 addResolutionVars.bind(this)(); 141 this.trackerVarsAdded = true; 142 } 143
144 let urlParams = []; 145 146 for (let currentVar in this.vars) { 147 if ({}.hasOwnProperty.call(this.vars, currentVar)) { 148 urlParams.push(currentVar + "=" + this.vars[currentVar]); 149 } 150 } 151 152 return urlParams; 153 } 154 155 function getTrackerPostVars() { 156 if (this.trackerVarsAdded === false) { 157 addStandardPageVars.bind(this)(); 158 addReferrerVars.bind(this)(); 159 addCampaignVars.bind(this)(); 160 addMetaPageVars.bind(this)(); 161 addResolutionVars.bind(this)(); 162 this.trackerVarsAdded = true; 163 } 164 165 Object.keys(this.vars).forEach(key => { 166 this.vars[key] = decodeURIComponent(this.vars[key]); 167 }); 168 169 return bracketVarsToJson(this.vars); 170 } 171 172 function getTrackerUrl() { 173 let urlParams = getTrackerVars.bind(this)(); 174 return this.baseUrl + "?" + urlParams.join('&'); 175 } 176 177 function getCustomerDataCodeToTrack() { 178 return ['id', 'group_id', 'company_id']; 179 } 180 181 function setTrackerStyle(imgNode) { 182 imgNode.setAttribute('style', 'position: absolute; top: 0; left: 0; visibility: hidden;'); 183 } 184 185 // Send the tag to the remote server 186 // Append a transparent pixel to the body 187 function sendTag(forceCollect = false) { 188 initSession.bind(this)(); 189 initCustomerData.bind(this)(); 190 191 if (this.config && this.config.hasOwnProperty('storeId')) { 192 addPageVar.bind(this)('store_id', this.config.storeId); 193 } 194 195 if (this.trackerSent === false || forceCollect === true) { 196 if (this.endpointUrl) { 197 const eventData = {'eventData': getTrackerPostVars.bind(this)()}; 198 let request = new XMLHttpRequest(); 199 request.open('POST', this.endpointUrl, true); 200 request.setRequestHeader('Content-Type', 'application/json'); 201 request.send(JSON.stringify(eventData)); 202 } else { 203 const bodyNode = document.getElementsByTagName('body')[0]; 204 buildTrackingImg.bind(this)(bodyNode, getTrackerUrl.bind(this)()); 205 } 206 this.trackerSent = true; 207 this.vars = {}; 208 } 209 } 210 211 function sendTelemetry() { 212 initSession.bind(this)(); 213 initCustomerData.bind(this)(); 214 getTrackerVars.bind(this); 215 216 const vars = bracketVarsToJson(this.vars); 217 218 if (this.telemetryEnabled && this.telemetryTrackerSent === false) { 219 let request = new XMLHttpRequest(); 220 request.open('POST', this.telemetryUrl, true); 221 request.setRequestHeader('Content-Type', 'application/json'); 222 request.send(JSON.stringify(vars)); 223 224 this.telemetryTrackerSent = true; 225 } 226 } 227 228 function bracketVarsToJson(vars) { 229 let result = {}; 230 231 for (const i in vars) { 232 let a = i.match(/([^\[\]]+)(\[[^\[\]]+[^\]])*?/g), 233 p = vars[i]; 234 let j = a.length; 235 while (j--) { 236 let q = {}; 237 q[a[j]] = p; 238 p = q; 239 } 240 241 let k = Object.keys(p)[0], 242 o = result; 243 244 while (k in o) { 245 p = p[k]; 246 o = o[k]; 247 k = Object.keys(p)[0]; 248 } 249 250 o[k] = p[k]; 251 } 252 253 return result; 254 } 255 256 function buildTrackingImg(bodyNode, trackingUrl) { 257 let imgNode = document.createElement('img'); 258 imgNode.setAttribute('src', trackingUrl); 259 imgNode.setAttribute('alt', ''); 260 setTrackerStyle(imgNode); 261 bodyNode.appendChild(imgNode); 262 } 263 264 // Append a variable to the page 265 function addVariable(varName, value) { 266 this.vars[varName] = encodeURIComponent(value); 267 return this; 268 } 269 270 function addSessionVar(varName, value) { 271 addVariable.bind(this)(transformVarName.bind(this)(varName, 'session'), value); 272 } 273 274 function addPageVar(varName, value) { 275 addVariable.bind(this)(transformVarName.bind(this)(varName , 'page'), value); 276 } 277 278 function addCustomerVar(varName, value) { 279 addVariable.bind(this)(transformVarName.bind(this)(varName , 'customer'), value); 280 } 281 282 function transformVarName(varName, prefix) { 283 return prefix + "[" + varName.replace(/[.]/g, "][") + "]"; 284 } 285 286 function initSession() { 287 if (this.config && this.config.hasOwnProperty('sessionConfig')) { 288 const config = this.config.sessionConfig; 289 const expireAt = new Date(); 290 const path = config['path'] || '/'; 291 292 if (!this.sessionInitialized) { 293 if (getCookie(config['visit_cookie_name']) === null) { 294 expireAt.setSeconds(expireAt.getSeconds() + parseInt(config['visit_cookie_lifetime'], 10));
295 setCookie(config['visit_cookie_name'], guid(), expireAt, path); 296 } else { 297 expireAt.setSeconds(expireAt.getSeconds() + parseInt(config['visit_cookie_lifetime'], 10)); 298 setCookie(config['visit_cookie_name'], getCookie(config['visit_cookie_name']), expireAt, path); 299 } 300 301 if (getCookie(config['visitor_cookie_name']) === null) { 302 expireAt.setDate(expireAt.getDate() + parseInt(config['visitor_cookie_lifetime'], 10)); 303 setCookie(config['visitor_cookie_name'], guid(), expireAt, path); 304 } 305 this.sessionInitialized = true; 306 } 307 308 addSessionVar.bind(this)('uid', getCookie(config['visit_cookie_name'])); 309 addSessionVar.bind(this)('vid', getCookie(config['visitor_cookie_name'])); 310 } 311 } 312 313 function initCustomerData() { 314 try { 315 let mageStorage = localStorage.getItem('mage-cache-storage'); 316 if (mageStorage !== null) { 317 mageStorage = JSON.parse(mageStorage); 318 if ((mageStorage.customer !== undefined)) { 319 addCustomerVars.bind(this)(mageStorage.customer); 320 } 321 } 322 } catch (e) { 323 // Nothing. 324 } 325 } 326 327 // Implementation of the tracker 328 const SmileTrackerImpl = function() { 329 this.vars = {}; 330 this.trackerSent = false; 331 this.telemetryTrackerSent = false; 332 this.trackerVarsAdded = false; 333 this.sessionInitialized = false; 334 this.customerData = {}; 335 }; 336 337 SmileTrackerImpl.prototype.sendTag = function (forceCollect = false) { 338 if (document.readyState !== 'loading') { 339 sendTag.bind(this)(forceCollect); 340 sendTelemetry.bind(this)(); 341 } else { 342 document.addEventListener('DOMContentLoaded', function () { 343 sendTag.bind(this)(forceCollect); 344 sendTelemetry.bind(this)(); 345 }.bind(this)); 346 } 347 }; 348 349 SmileTrackerImpl.prototype.setConfig = function (config) { 350 this.config = config; 351 this.baseUrl = config.beaconUrl; 352 this.endpointUrl = false; 353 if (config.hasOwnProperty('endpointUrl') && (config.endpointUrl.length !== 0)) { 354 this.endpointUrl = config.endpointUrl; 355 } 356 this.telemetryEnabled = config.telemetryEnabled; 357 this.telemetryUrl = config.telemetryUrl; 358 359 360 }; 361 362 SmileTrackerImpl.prototype.addPageVar = function (varName, value) { 363 addPageVar.bind(this)(varName, value); 364 }; 365 366 SmileTrackerImpl.prototype.addCustomerVar = function (varName, value) { 367 addCustomerVar.bind(this)(varName, value); 368 }; 369 370 SmileTrackerImpl.prototype.addSessionVar = function (varName, value) { 371 addSessionVar.bind(this)(varName, value); 372 }; 373 374 return new SmileTrackerImpl(); 375}());
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.