1(function (global) { 2 function b64DecodeUnicode(str) { 3 // Convert base64 to a string 4 const binaryString = atob(str); 5 6 // Convert binary string to a byte array 7 const byteArray = new Uint8Array(binaryString.length); 8 for (var i = 0; i < binaryString.length; i++) { 9 byteArray[i] = binaryString.charCodeAt(i); 10 } 11 // Convert byte array to a string 12 const decoder = new TextDecoder('utf-8'); 13 return decoder.decode(byteArray); 14 } 15 16 global.insiteScripts = global.insiteScripts || {}; 17 global.insiteScripts.popup = 18 global.insiteScripts.popup || 19 function message(scriptOptions) { 20 const dmSmartScriptDuration = scriptOptions.duration, 21 dmSmartScriptSettings = scriptOptions.settings, 22 dmSmartScriptRuleId = scriptOptions.ruleId, 23 dmSmartScriptRuleType = scriptOptions.ruleType, 24 isForced = scriptOptions.isForced; 25 26 //popupName and settings are coming from BE 27 const shouldShowPopup = function (popupName, settings) { 28 if (shouldCheckMultipleConditions) { 29 const { conditionsToCheck } = 30 shouldShowActionRule(settings); 31 32 if (!conditionsToCheck) { 33 const cookieName = getSmartRuleCookieName( 34 popupName, 35 ActionType.POPUP 36 ); 37 return isForced || getCookie(cookieName) === null; //meaning conditions are filtered in BE 38 } else { 39 return conditionsToCheck.every( 40 ({ condition, callback }) => { 41 return ( 42 isForced || 43 (callback && 44 callback( 45 popupName, 46 condition, 47 ActionType.POPUP 48 )) 49 ); 50 } 51 ); 52 } 53 } else { 54 const { 55 shouldCheckUserVisitsCondition, 56 shouldCheckDateTimeRangeCondition, 57 } = shouldShowActionRule(settings); 58 59 const cookieName = getSmartRuleCookieName( 60 popupName, 61 ActionType.POPUP 62 ); 63 64 if (shouldCheckUserVisitsCondition) { 65 return ( 66 isForced || 67 shouldShowRuleObjectForUserVisit( 68 popupName, 69 settings, 70 ActionType.POPUP 71 ) 72 ); 73 } 74 if (shouldCheckDateTimeRangeCondition) { 75 return ( 76 isForced || 77 shouldShowRuleObjectForDateTimeRange( 78 popupName, 79 settings, 80 ActionType.POPUP 81 ) 82 ); 83 } 84 return isForced || getCookie(cookieName) === null; 85 } 86 }; 87 88 // If insite configured to show a popup then we don't want to show it everytime, once every "popup.insite.cookie.ttl" hours. 89 const onOpenPopup = function (popupName, event) { 90 if (isForced || window.Parameters?.disableTracking) { 91 return; 92 } 93 setSmartRuleCookie(popupName, ActionType.POPUP); 94 }; 95 96 const applyPopUp = () => { 97 const settings = JSON.parse( 98 b64DecodeUnicode(dmSmartScriptSettings) 99 ); 100 const popupName = settings.popupName;
101 const delay = isNaN(parseInt(settings.delay)) 102 ? 0 103 : parseInt(settings.delay); 104 const timingMode = 105 settings.timingMode === 'delay' || 106 settings.timingMode === 'exit-intent' || 107 settings.timingMode === 'scroll-percentage' 108 ? settings.timingMode 109 : delay > 0 110 ? 'delay' 111 : 'immediate'; 112 const durationDelay = Math.max( 113 parseInt(dmSmartScriptDuration, 10) || 0, 114 0 115 ); 116 const DESKTOP_EXIT_THRESHOLD = 12; 117 const MOBILE_SCROLL_EXIT_THRESHOLD = 160; 118 const MOBILE_SCROLL_MAX_DURATION_MS = 600; 119 const MOBILE_SCROLL_MIN_OFFSET = 80; 120 const DEFAULT_SCROLL_THRESHOLD = 50; 121 const MIN_SCROLL_THRESHOLD = 1; 122 const MAX_SCROLL_THRESHOLD = 100; 123 const parsedScrollThreshold = parseInt( 124 settings.scrollThreshold, 125 10 126 ); 127 const scrollThreshold = isNaN(parsedScrollThreshold) 128 ? DEFAULT_SCROLL_THRESHOLD 129 : Math.min( 130 Math.max(parsedScrollThreshold, MIN_SCROLL_THRESHOLD), 131 MAX_SCROLL_THRESHOLD 132 ); 133 const VALID_POPUP_POSITIONS = [ 134 'center_center', 135 'top_left', 136 'top_center', 137 'top_right', 138 'center_left', 139 'center_right', 140 'bottom_left', 141 'bottom_center', 142 'bottom_right', 143 ]; 144 const popupPosition = 145 VALID_POPUP_POSITIONS.indexOf(settings.position) !== -1 146 ? settings.position 147 : 'center_center'; 148 149 const options = { 150 onOpen: onOpenPopup.bind(null, popupName), 151 position: popupPosition, 152 additionalAttributes: [ 153 { 154 name: 'data-rule', 155 value: dmSmartScriptRuleId, 156 }, 157 { 158 name: 'data-rule-type', 159 value: dmSmartScriptRuleType, 160 }, 161 ], 162 }; 163 164 const openPopup = function () { 165 window?.waitForDeferred?.('dmAjax', () => { 166 $.dmrt.onLoad( 167 $.dmrt.components.popupService.displayPopup.bind( 168 null, 169 popupName, 170 options 171 ) 172 ); 173 if (window.dmsnowplow) { 174 dmsnowplow( 175 'trackStructEvent', 176 'insite', 177 'impression', 178 scriptOptions.ruleType, 179 scriptOptions.ruleId 180 ); 181 } 182 }); 183 }; 184 185 const showPopupIfAllowed = function () { 186 if (!popupName || !shouldShowPopup(popupName, settings)) { 187 return; 188 } 189 190 openPopup(); 191 }; 192 193 const isMobileExitIntentContext = function () { 194 const hasCoarsePointer = 195 typeof window.matchMedia === 'function' && 196 window.matchMedia('(pointer: coarse)').matches; 197 const hasTouchPoints = 198 typeof navigator !== 'undefined' && 199 navigator.maxTouchPoints > 0; 200 const isMobileUserAgent = 201 /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 202 navigator.userAgent || '' 203 ); 204 205 return hasCoarsePointer || hasTouchPoints || isMobileUserAgent; 206 }; 207 208 const registerDesktopExitIntent = function () { 209 let isTriggered = false;
210 211 const cleanup = function () { 212 document.removeEventListener('mouseout', onMouseOut); 213 }; 214 215 const onMouseOut = function (event) { 216 if ( 217 isTriggered || 218 event.relatedTarget || 219 event.toElement 220 ) { 221 return; 222 } 223 224 if ( 225 typeof event.clientY === 'number' && 226 event.clientY > DESKTOP_EXIT_THRESHOLD 227 ) { 228 return; 229 } 230 231 isTriggered = true; 232 cleanup(); 233 showPopupIfAllowed(); 234 }; 235 236 document.addEventListener('mouseout', onMouseOut); 237 }; 238 239 const registerMobileExitIntent = function () { 240 let isTriggered = false; 241 let hasInteracted = false; 242 let historyStateAdded = false; 243 let lastTouchY = null; 244 let lastTouchTime = 0; 245 246 const cleanup = function () { 247 document.removeEventListener( 248 'touchstart', 249 onTouchStart, 250 true 251 ); 252 document.removeEventListener('touchend', onTouchEnd, true); 253 document.removeEventListener('click', onInteraction, true); 254 document.removeEventListener( 255 'keydown', 256 onInteraction, 257 true 258 ); 259 window.removeEventListener('popstate', onPopState); 260 }; 261 262 const triggerExitIntent = function () { 263 if (isTriggered) { 264 return; 265 } 266 267 isTriggered = true; 268 cleanup(); 269 showPopupIfAllowed(); 270 }; 271 272 const onInteraction = function () { 273 hasInteracted = true; 274 275 if ( 276 historyStateAdded || 277 !window.history || 278 typeof window.history.pushState !== 'function' 279 ) { 280 return; 281 } 282 283 try { 284 window.history.pushState( 285 { dmPopupExitIntentArmed: true }, 286 document.title, 287 window.location.href 288 ); 289 historyStateAdded = true; 290 } catch (e) {} 291 }; 292 293 const onTouchStart = function (event) { 294 onInteraction(); 295 296 const touch = event.changedTouches?.[0]; 297 if (!touch) { 298 return; 299 } 300 301 lastTouchY = touch.clientY; 302 lastTouchTime = Date.now(); 303 }; 304 305 const onTouchEnd = function (event) { 306 if (isTriggered || !hasInteracted || lastTouchY === null) { 307 return; 308 } 309 310 const touch = event.changedTouches?.[0]; 311 if (!touch) { 312 return; 313 } 314 315 const scrollTop = 316 window.pageYOffset || 317 document.documentElement.scrollTop || 318 document.body.scrollTop || 319 0; 320 const touchDelta = touch.clientY - lastTouchY;
321 const elapsed = Date.now() - lastTouchTime; 322 323 lastTouchY = null; 324 lastTouchTime = 0; 325 326 if ( 327 scrollTop < MOBILE_SCROLL_MIN_OFFSET || 328 elapsed > MOBILE_SCROLL_MAX_DURATION_MS || 329 touchDelta < MOBILE_SCROLL_EXIT_THRESHOLD 330 ) { 331 return; 332 } 333 334 triggerExitIntent(); 335 }; 336 337 const onPopState = function () { 338 if (!hasInteracted) { 339 return; 340 } 341 342 triggerExitIntent(); 343 }; 344 345 document.addEventListener('touchstart', onTouchStart, true); 346 document.addEventListener('touchend', onTouchEnd, true); 347 document.addEventListener('click', onInteraction, true); 348 document.addEventListener('keydown', onInteraction, true); 349 window.addEventListener('popstate', onPopState); 350 }; 351 352 const registerExitIntent = function () { 353 if (isMobileExitIntentContext()) { 354 registerMobileExitIntent(); 355 return; 356 } 357 358 registerDesktopExitIntent(); 359 }; 360 361 const registerScrollPercentageTrigger = function (threshold) { 362 let isTriggered = false; 363 let isRafScheduled = false; 364 365 const cleanup = function () { 366 window.removeEventListener('scroll', onScroll, true); 367 }; 368 369 const getScrollPercent = function () { 370 const doc = document.documentElement; 371 const body = document.body || {}; 372 const scrollTop = 373 window.pageYOffset || 374 doc.scrollTop || 375 body.scrollTop || 376 0; 377 const viewportHeight = 378 window.innerHeight || doc.clientHeight || 0; 379 const documentHeight = Math.max( 380 doc.scrollHeight || 0, 381 body.scrollHeight || 0, 382 doc.offsetHeight || 0, 383 body.offsetHeight || 0, 384 doc.clientHeight || 0 385 ); 386 387 if (documentHeight <= 0) { 388 return 0; 389 } 390 391 return ( 392 ((scrollTop + viewportHeight) / documentHeight) * 393 100 394 ); 395 }; 396 397 const check = function () { 398 if (isTriggered) { 399 return; 400 } 401 402 if (getScrollPercent() >= threshold) { 403 isTriggered = true; 404 cleanup(); 405 showPopupIfAllowed(); 406 } 407 }; 408 409 const onScroll = function () { 410 if (isTriggered || isRafScheduled) { 411 return; 412 } 413 414 isRafScheduled = true; 415 const scheduleCheck = 416 typeof window.requestAnimationFrame === 'function' 417 ? window.requestAnimationFrame 418 : function (cb) { 419 return setTimeout(cb, 16); 420 }; 421 422 scheduleCheck(function () { 423 isRafScheduled = false; 424 check(); 425 }); 426 }; 427 428 window.addEventListener('scroll', onScroll, true); 429 430 // Initial check in case the page already exceeds the 431 // threshold (e.g. short page, or user scrolled before 432 // the script attached). 433 check(); 434 }; 435 436 if (!popupName || !shouldShowPopup(popupName, settings)) { 437 return; 438 } 439 440 if (timingMode === 'delay') { 441 setTimeout(function () { 442 showPopupIfAllowed(); 443 }, (delay + durationDelay) * 1000); 444 return; 445 } 446 447 if (timingMode === 'exit-intent') { 448 setTimeout(function () {
449 registerExitIntent(); 450 }, durationDelay * 1000); 451 return; 452 } 453 454 if (timingMode === 'scroll-percentage') { 455 setTimeout(function () { 456 registerScrollPercentageTrigger(scrollThreshold); 457 }, durationDelay * 1000); 458 return; 459 } 460 461 setTimeout(function () { 462 showPopupIfAllowed(); 463 }, durationDelay * 1000); 464 }; 465 466 (function () { 467 applyPopUp(); 468 })(); 469 }; 470})(this);
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.