1/*! 2* dwAnalytics - Web Analytics Tracking 3* Based partially on Piwik 4* 5* @link http://piwik.org 6* @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later 7*/ 8 9// Create dw namespace if necessary 10if (typeof dw == 'undefined') { 11 var dw = {}; 12} 13 14if (typeof dw.__dwAnalyticsLoaded == 'undefined') 15{ 16 dw.__dwAnalyticsLoaded = true; 17 18 // DWAnalytics singleton and namespace 19 dw.__dwAnalytics = function () { 20 /************************************************************ 21 * Private data 22 ************************************************************/ 23 24 var MAX_URL_LENGTH = 2000; 25 26 var expireDateTime, 27 28 /* plugins */ 29 plugins = {}, 30 31 /* alias frequently used globals for added minification */ 32 documentAlias = document, 33 navigatorAlias = navigator, 34 screenAlias = screen, 35 windowAlias = window, 36 hostnameAlias = windowAlias.location.hostname; 37 38 /************************************************************ 39 * Private methods 40 ************************************************************/ 41 42 /* 43 * Is property (or variable) defined? 44 */ 45 function isDefined(property) { 46 return typeof property !== 'undefined'; 47 } 48 49 /* 50 * DWAnalytics Tracker class 51 * 52 * trackerUrl and trackerSiteId are optional arguments to the constructor 53 * 54 * See: Tracker.setTrackerUrl() and Tracker.setSiteId() 55 */ 56 function Tracker(trackerUrl, siteId) { 57 /************************************************************ 58 * Private members 59 ************************************************************/ 60 61 var // Tracker URL 62 configTrackerUrl = trackerUrl + '?' || '?', 63 64 // Document URL 65 configCustomUrl, 66 67 // Document title 68 configTitle = documentAlias.title, 69 70 // Client-side data collection 71 browserHasCookies = '0', 72 pageReferrer, 73 74 // Plugin, Parameter name, MIME type, detected 75 pluginMap = { 76 // document types 77 pdf: ['pdf', 'application/pdf', '0'], 78 // media players 79 quicktime: ['qt', 'video/quicktime', '0'], 80 realplayer: ['realp', 'audio/x-pn-realaudio-plugin', '0'], 81 wma: ['wma', 'application/x-mplayer2', '0'], 82 // interactive multimedia 83 director: ['dir', 'application/x-director', '0'], 84 flash: ['fla', 'application/x-shockwave-flash', '0'], 85 // RIA 86 java: ['java', 'application/x-java-vm', '0'], 87 gears: ['gears', 'application/x-googlegears', '0'], 88 silverlight: ['ag', 'application/x-silverlight', '0'] 89 }, 90 91 // Guard against installing the link tracker more than once per Tracker instance 92 linkTrackingInstalled = false, 93 94 /* 95 * encode or escape 96 * - encodeURIComponent added in IE5.5 97 */ 98 escapeWrapper = windowAlias.encodeURIComponent || escape, 99 100 /* 101 * decode or unescape 102 * - decodeURIComponent added in IE5.5 103 */ 104 unescapeWrapper = windowAlias.decodeURIComponent || unescape, 105 106 /* 107 * registered (user-defined) hooks 108 */ 109 registeredHooks = {}; 110 111 /* 112 * Set cookie value 113 */ 114 function setCookie(cookieName, value, daysToExpire, path, domain, secure) { 115 var expiryDate; 116 117 if (daysToExpire) { 118 // time is in milliseconds 119 expiryDate = new Date(); 120 // there are 1000 * 60 * 60 * 24 milliseconds in a day (i.e., 86400000 or 8.64e7) 121 expiryDate.setTime(expiryDate.getTime() + daysToExpire * 8.64e7); 122 } 123 124 documentAlias.cookie = cookieName + '=' + escapeWrapper(value) + 125 (daysToExpire ? ';expires=' + expiryDate.toGMTString() : '') + 126 ';path=' + (path ? path : '/') + 127 (domain ? ';domain=' + domain : '') + 128 (secure ? ';secure' : ''); 129 } 130 131 /* 132 * Get cookie value 133 */ 134 function getCookie(cookieName) { 135 var cookiePattern = new RegExp('(^|;)[ ]*' + cookieName + '=([^;]*)'), 136 137 cookieMatch = cookiePattern.exec(documentAlias.cookie); 138 139 return cookieMatch ? unescapeWrapper(cookieMatch[2]) : 0; 140 } 141 142 /* 143 * Send image request to DWAnalytics server using GET. 144 * The infamous web bug is a transparent, single pixel (1x1) image 145 * Async with a delay of 100ms. 146 */ 147 function getImage(urls) { 148 dw.__timeoutCallback = function() 149 { 150 for (var i = 0; i < urls.length; i++) 151 { 152 var image = new Image(1, 1); 153 image.onLoad = function () {}; 154 image.src = urls[i]; 155 } 156 }; 157 setTimeout(dw.__timeoutCallback, 100); 158 } 159 160 /* 161 * Browser plugin tests 162 */ 163 function detectBrowserPlugins() { 164 var i, mimeType; 165 166 // Safari and Opera 167 // IE6: typeof navigator.javaEnabled == 'unknown' 168 if (typeof navigatorAlias.javaEnabled !== 'undefined' && navigatorAlias.javaEnabled()) { 169 pluginMap.java[2] = '1'; 170 } 171 172 // Firefox 173 if (typeof windowAlias.GearsFactory === 'function') { 174 pluginMap.gears[2] = '1'; 175 } 176 177 if (navigatorAlias.mimeTypes && navigatorAlias.mimeTypes.length) { 178 for (i in pluginMap) { 179 mimeType = navigatorAlias.mimeTypes[pluginMap[i][1]]; 180 if (mimeType && mimeType.enabledPlugin) { 181 pluginMap[i][2] = '1'; 182 } 183 } 184 } 185 } 186 187 /* 188 * Get page referrer 189 */ 190 function getReferrer() { 191 var referrer = ''; 192 try { 193 referrer = top.document.referrer; 194 } catch (e) { 195 if (parent) { 196 try { 197 referrer = parent.document.referrer; 198 } catch (e2) { 199 referrer = ''; 200 } 201 } 202 } 203 if (referrer === '') { 204 referrer = documentAlias.referrer; 205 } 206 207 return referrer; 208 } 209 210 /* 211 * Does browser have cookies enabled (for this site)? 212 */ 213 function hasCookies() { 214 var testCookieName = '_pk_testcookie'; 215 if (!isDefined(navigatorAlias.cookieEnabled)) {
216 setCookie(testCookieName, '1'); 217 return getCookie(testCookieName) == '1' ? '1' : '0'; 218 } 219 220 return navigatorAlias.cookieEnabled ? '1' : '0'; 221 } 222 223 /* 224 * Log the page view / visit 225 */ 226 function logPageView(analyticsContext, collectedData, customTitle) { 227 var id = Math.random(); 228 229 var tuples = constructDataSet(analyticsContext, collectedData, customTitle, id); 230 231 if (collectedData != null && collectedData.debugEnabled) { 232 var text = ''; 233 for (var i = 0; i < tuples.length; i++) 234 { 235 text += tuples[i][0] + '"=' + tuples[i][1] + '"\n'; 236 } 237 alert(text); 238 } 239 240 241 var urls = createUrls(analyticsContext, configTrackerUrl, tuples, id); 242 getImage(urls); 243 } 244 245 /************************************************************ 246 * Constructor 247 ************************************************************/ 248 249 /* 250 * initialize tracker 251 */ 252 pageReferrer = getReferrer(); 253 browserHasCookies = hasCookies(); 254 detectBrowserPlugins(); 255 256 try { 257 process_anact_cookie(); 258 } catch (err) {}; 259 260 /************************************************************ 261 * Public data and methods 262 ************************************************************/ 263 264 return { 265 /* 266 * Log visit to this page (called from the bottom of the page). 267 */ 268 trackPageView: function (customTitle) { 269 logPageView(null, null, customTitle); 270 }, 271 /* 272 * Log visit to this page (called from the dwac script). 273 */ 274 trackPageViewWithProducts: function (analyticsContext, collectedData, customTitle) { 275 logPageView(analyticsContext, collectedData, customTitle); 276 } 277 }; 278 279 280 function appendToRequest(/*Array*/ tuple, /*string*/ request) { 281 282 var beginningChar = request.charAt(request.length - 1) == '?' ? '' : '&'; 283 return request + beginningChar + tuple[0] + "=" + tuple[1]; 284 } 285 286 function lengthOfTuple(/*Array*/ tuple) { 287 return tuple[0].length + tuple[1].length + 2; 288 } 289 290 function constructDataSet(/*object*/ analyticsContext, collectedData, /*string*/ customTitle, /*number*/ id) { 291 var tuples = [ 292 ["url", escapeWrapper(isDefined(configCustomUrl) ? configCustomUrl : documentAlias.location.href)], 293 ["res", screenAlias.width + 'x' + screenAlias.height], 294 ["cookie", browserHasCookies], 295 ["ref", escapeWrapper(pageReferrer)], 296 ["title", escapeWrapper(isDefined(customTitle) && customTitle != null ? customTitle : configTitle)] 297 ]; 298 299 // plugin data 300 for (var index in pluginMap) { 301 tuples.push([pluginMap[index][0], pluginMap[index][2]]) 302 } 303 304 if (analyticsContext != null && analyticsContext.dwEnabled) 305 { 306 tuples.push(["dwac", id]); 307 tuples.push(["cmpn", analyticsContext.sourceCode]); 308 tuples.push(["tz", analyticsContext.timeZone]); 309 310 analyticsContext.category = dw.ac._category; 311 if (dw.ac._searchData) { 312 analyticsContext.searchData = dw.ac._searchData; 313 } 314 315 addProducts(analyticsContext, collectedData, tuples); 316 } 317 318 return tuples; 319 } 320 321 function addProducts(/*object*/ analyticsContext, /*object*/ collectedData, /*Array*/ tuples) 322 { 323 tuples.push(["pcc", analyticsContext.siteCurrency]); 324 tuples.push(["pct", analyticsContext.customer]); 325 tuples.push(["pcat", analyticsContext.category]); 326 if (analyticsContext.searchData) { 327 if (analyticsContext.searchData.q) tuples.push(["pst-q", analyticsContext.searchData.q]); 328 if (analyticsContext.searchData.searchID) tuples.push(["pst-id", analyticsContext.searchData.searchID]); 329 if (analyticsContext.searchData.refs) tuples.push(["pst-refs", analyticsContext.searchData.refs]); 330 if (analyticsContext.searchData.sort) tuples.push(["pst-sort", analyticsContext.searchData.sort]); 331 if (undefined != analyticsContext.searchData.persd) tuples.push(["pst-pers", analyticsContext.searchData.persd]); 332 if (analyticsContext.searchData.imageUUID) tuples.push(["pst-img", analyticsContext.searchData.imageUUID]); 333 if (analyticsContext.searchData.suggestedSearchText) tuples.push(["pst-sug", analyticsContext.searchData.suggestedSearchText]); 334 if (analyticsContext.searchData.locale) tuples.push(["pst-loc", analyticsContext.searchData.locale]); 335 if (analyticsContext.searchData.queryLocale) tuples.push(["pst-qloc", analyticsContext.searchData.queryLocale]); 336 if (undefined != analyticsContext.searchData.showProducts) tuples.push(["pst-show", analyticsContext.searchData.showProducts]); 337 } 338 339 var pies = collectedData.productImpressions.getEntries(); 340 var pres = collectedData.productRecommendations.getEntries(); 341 var pves = collectedData.productViews.getEntries(); 342 343 var count = 0; 344 for (var i = 0; i < pies.length; i++) { 345 tuples.push([("pid-" + count), pies[i].value.id]); 346 tuples.push([("pev-" + count), "event3"]); 347 count++; 348 } 349 350 for (var j = 0; j < pres.length; j++) { 351 tuples.push([("pid-" + count), pres[j].value.id]); 352 tuples.push([("pev-" + count), "event3"]); 353 tuples.push([("evr4-" + count), 'Yes']); 354 count++; 355 } 356 357 for (var k = 0; k < pves.length; k++) { 358 tuples.push([("pid-" + count), pves[k].value.id]); 359 tuples.push([("pev-" + count), "event4"]); 360 count++; 361 } 362 } 363 364 function createUrls(analyticsContext, configTrackerUrl, tuples, id) { 365 var urls = []; 366 var request = configTrackerUrl; 367 for (var i = 0; i < tuples.length; i++) { 368 // we don't want to break up a product grouping, for example, 369 // ["pid-0","p1"],["pev-0","event3"],["evr4-0",'Yes'] should not be broken into two separate requests 370 var nextTupleIsProductAndWouldMakeRequestTooLong = (tuples[i][0].slice(0, "pid-".length) == "pid-") 371 && (lengthOfTuple(tuples[i]) 372 + ((i + 1) < tuples.length ? lengthOfTuple(tuples[i + 1]) : 0) 373 + ((i + 2) < tuples.length ? lengthOfTuple(tuples[i + 2]) : 0) 374 + request.length > MAX_URL_LENGTH); 375 376 var nextTupleIsNotProductAndWouldMakeRequestTooLong = (tuples[i][0].slice(0, "pid-".length) != "pid-") 377 && (lengthOfTuple(tuples[i]) + request.length > MAX_URL_LENGTH); 378 379 if (nextTupleIsProductAndWouldMakeRequestTooLong || nextTupleIsNotProductAndWouldMakeRequestTooLong) { 380 urls.push(request); 381 // close the current request and create a new one, 382 // the new request should have the basic dwac, cmpn,tz, pcc, pct, pcat values 383 request = appendToRequest(["dwac", id], configTrackerUrl); 384 if (analyticsContext != null && analyticsContext.dwEnabled) { 385 request = appendToRequest(["cmpn", analyticsContext.sourceCode], request); 386 request = appendToRequest(["tz", analyticsContext.timeZone], request); 387 request = appendToRequest(["pcc", analyticsContext.siteCurrency], request); 388 request = appendToRequest(["pct", analyticsContext.customer], request); 389 request = appendToRequest(["pcat", analyticsContext.category], request); 390 if (analyticsContext.searchData) { 391 if (analyticsContext.searchData.q) appendToRequest(["pst-q", analyticsContext.searchData.q], request); 392 if (analyticsContext.searchData.searchID) appendToRequest(["pst-id", analyticsContext.searchData.searchID], request); 393 if (analyticsContext.searchData.refs) appendToRequest(["pst-refs", JSON.stringify(analyticsContext.searchData.refs)], request); 394 if (analyticsContext.searchData.sort) appendToRequest(["pst-sort", JSON.stringify(analyticsContext.searchData.sort)], request); 395 if (undefined != analyticsContext.searchData.persd) appendToRequest(["pst-pers", analyticsContext.searchData.persd], request); 396 if (analyticsContext.searchData.imageUUID) appendToRequest(["pst-img", analyticsContext.searchData.imageUUID], request); 397 if (analyticsContext.searchData.suggestedSearchText) appendToRequest(["pst-sug", analyticsContext.searchData.suggestedSearchText], request); 398 if (analyticsContext.searchData.locale) appendToRequest(["pst-loc", analyticsContext.searchData.locale], request); 399 if (analyticsContext.searchData.queryLocale) appendToRequest(["pst-qloc", analyticsContext.searchData.queryLocale], request); 400 if (undefined != analyticsContext.searchData.showProducts) appendToRequest(["pst-show", analyticsContext.searchData.showProducts], request); 401 } 402 } 403 } 404 405 request = appendToRequest(tuples[i], request); 406 } 407 408 // Add the "do not follow" cookie in the URL as a query param 409 // The cookie is set per-site, and gets applied to all sub-sites, if present 410 var doNotFollowCookieValue = getCookie('dw_dnt'); 411 412 // If cookie not found 413 if (doNotFollowCookieValue === 0 414 || doNotFollowCookieValue === '' 415 || doNotFollowCookieValue === null 416 || doNotFollowCookieValue === false) { 417 // do nothing, meaning tracking is allowed 418 } 419 // Cookie found, i.e. value should be '0' or '1' (string values) 420 else { 421 // Set whatever the cookie value is 422 request = appendToRequest([ "dw_dnt", doNotFollowCookieValue ], request); 423 } 424 425 urls.push(request); 426 return urls; 427 } 428 } 429 430 function extractCookieValueAndRemoveCookie(cookieName) { 431 var cookieValue = extractEncodedCookieValue (cookieName); 432 if (cookieValue) { 433 document.cookie = cookieName + '=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;'; 434 } 435 return cookieValue; 436 }; 437 438 function extractEncodedCookieValue (cookieName) { 439 return decodeURIComponent ( 440 document.cookie.replace( 441 new RegExp('(?:(?:^|.*;)\\s*' 442 + encodeURIComponent(cookieName).replace(/[\-\.\+\*]/g, '\\$&') 443 + '\\s*\\=\\s*([^;]*).*$)|^.*$') 444 , '$1').replace(/\+/g, '%20') 445 ) || null; 446 } 447 448 function process_anact_cookie() { 449 if (dw.ac) { 450 dw.ac._anact=extractCookieValueAndRemoveCookie('__anact'); 451 if ( dw.ac._anact && !dw.ac.eventsIsEmpty() ) { 452 return; 453 } 454 if ( dw.ac._anact_nohit_tag || dw.ac._anact ) { 455 var unescaped = dw.ac._anact_nohit_tag ? dw.ac._anact_nohit_tag : dw.ac._anact; 456 var jsonPayload = JSON.parse(unescaped); 457 if (jsonPayload) { 458 var payload = Array.isArray(jsonPayload) ? jsonPayload[0] : jsonPayload; 459 if (payload 460 && 'viewSearch' == payload.activityType 461 && payload.parameters) { 462 var params = payload.parameters; 463 var search_params = {}; 464 search_params.q = params.searchText; 465 search_params.suggestedSearchText = params.suggestedSearchText; 466 search_params.persd = params.personalized; 467 search_params.refs = params.refinements; 468 search_params.sort = params.sorting_rule; 469 search_params.imageUUID = params.image_uuid; 470 search_params.showProducts = params.showProducts; 471 search_params.searchID = params.searchID; 472 search_params.locale = params.locale; 473 search_params.queryLocale = params.queryLocale; 474 dw.ac.applyContext({searchData: search_params}); 475 var products = params.products; 476 if (products && Array.isArray(products)) { 477 for (i = 0; i < products.length; i++) { 478 if (products[i]) { 479 dw.ac._capture({ id : products[i].id, type : dw.ac.EV_PRD_SEARCHHIT }); 480 } 481 } 482 } 483 } 484 } 485 } 486 } 487 }; 488 489 /************************************************************ 490 * Public data and methods 491 ************************************************************/ 492 493 return { 494 /* 495 * Get Tracker 496 */ 497 getTracker: function (analyticsUrl) { 498 return new Tracker(analyticsUrl); 499 } 500 }; 501 }(); 502}
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.