1;(function () { 2 // Client download screen for launcher download buttons. 3 // 4 // Two jobs, one per page: 5 // 1. On a page with a launcher CTA â send the click to the same URL with 6 // ?status=downloading, where the block renders the screen server-side 7 // instead of its CTA content (see include/download-screen.php). 8 // 2. On the screen itself â start the download, and report the retry click. 9 // 10 // Without JS the CTA stays a plain link to the launcher, so the download keeps 11 // working; only the screen is skipped. 12 13 // Only CTAs of blocks that actually render the screen opt in with 14 // data-download-screen (see include/download-screen.php) â elsewhere the link 15 // has to keep downloading directly. 16 var TRIGGER_SELECTOR = 'a[data-download-screen]' 17 var LAUNCHER_RE = /gdn\.gaijin\.net\/launcher/i 18 var START_SELECTOR = '[data-download-screen-start]' 19 20 var STATUS_PARAM = 'status' 21 var STATUS_VALUE = 'downloading' 22 23 var DISTR_PARAM = 'distr' 24 var GSID_COOKIE = 'gsid' 25 26 var CLICK_EVENT = 'download_launcher_button_click' 27 var RETRY_EVENT = 'download_launcher_try_again_click' 28 29 // Gamesight event + GTM/GA signal. Both are best-effort: analytics.js owns 30 // consent handling, GTM enforces its own. 31 function track(eventName) { 32 if (window.UFL_ANALYTICS && typeof window.UFL_ANALYTICS.send === 'function') { 33 window.UFL_ANALYTICS.send(eventName) 34 } 35 if (window.dataLayer && typeof window.dataLayer.push === 'function') { 36 window.dataLayer.push({ event: eventName }) 37 } 38 } 39 40 /* 41 * Gamesight attribution id, mirrored into a first-party cookie by 42 * assets/js/gsid.js. It is read here rather than rendered into the screen's 43 * HTML: the markup then holds nothing visitor-specific, so a cached copy can 44 * never hand one visitor's id to another. The cookie is already set by the time 45 * the screen is reached (it is the second page view), so the download does not 46 * have to wait for the SDK either. 47 */ 48 // Parsed with string operations on purpose: Autoptimize's JS minifier drops the 49 // line break after a regex literal that has no flags, and this file relies on 50 // ASI â /â¦/ followed by the next `var` then reads as invalid regex flags and 51 // takes down the whole script. 52 function gsidFromCookie() { 53 var parts = document.cookie ? document.cookie.split(';') : [] 54 55 for (var i = 0; i < parts.length; i++) { 56 var pair = parts[i].split('=') 57 if (pair.shift().trim() !== GSID_COOKIE) continue 58 59 var value = pair.join('=') 60 try { 61 return decodeURIComponent(value) 62 } catch (e) { 63 return value 64 } 65 } 66 67 return '' 68 } 69 70 // `set`, not append: gsid.js targets launcher links too, so this must stay 71 // idempotent whichever of the two runs first. 72 function withDistr(href) { 73 var gsid = gsidFromCookie() 74 if (!href || !gsid) return href 75 try { 76 var url = new URL(href, window.location.href) 77 url.searchParams.set(DISTR_PARAM, gsid) 78 return url.toString() 79 } catch (e) { 80 return href 81 } 82 } 83 84 function triggerDownload(href) { 85 if (!href) return 86 var link = document.createElement('a') 87 link.href = href 88 link.rel = 'noopener noreferrer' 89 document.body.appendChild(link) 90 link.click() 91 link.remove() 92 } 93 94 function isPlainClick(event) { 95 return !( 96 event.defaultPrevented || 97 event.button !== 0 || 98 event.metaKey || 99 event.ctrlKey || 100 event.shiftKey || 101 event.altKey 102 ) 103 } 104 105 /* ââ 1. CTA page ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 106 107 function screenUrl() { 108 var url = new URL(window.location.href) 109 url.searchParams.set(STATUS_PARAM, STATUS_VALUE) 110 return url.toString() 111 } 112 113 function onCtaClick(event) { 114 var link = event.target && event.target.closest ? event.target.closest(TRIGGER_SELECTOR) : null 115 if (!link || !isPlainClick(event)) return 116 117 var href = link.getAttribute('href') || '' 118 119 // AppsFlyer rewrote this button to a OneLink (store redirect, not a direct 120 // launcher download) â that flow leaves the page, so no screen. 121 if (link.hasAttribute('data-af-btn') && !LAUNCHER_RE.test(href)) return 122 123 var target = '' 124 try { 125 target = screenUrl() 126 } catch (e) { 127 return // malformed location: leave the plain download link alone 128 } 129 130 event.preventDefault() 131 track(CLICK_EVENT) 132 window.location.assign(target) 133 } 134 135 /* ââ 2. Download screen âââââââââââââââââââââââââââââââââââââââââââââââââ */ 136 137 /* 138 * The automatic start belongs to a fresh navigation onto the screen only. On a 139 * reload â or a back/forward step onto it â the installer must not be fetched 140 * again: refreshing after a finished download started a second one. The visible 141 * "Try again" link stays as the deliberate retry. 142 */ 143 function isFreshNavigation() { 144 try { 145 var entries = performance.getEntriesByType ? performance.getEntriesByType('navigation') : null
146 if (entries && entries.length && entries[0].type) return entries[0].type === 'navigate' 147 if (performance.navigation) return performance.navigation.type === 0 148 } catch (e) { 149 // Timing API unavailable: fall through and start as before. 150 } 151 152 return true 153 } 154 155 function initScreen(startLink) { 156 // Applied to the link itself, so the automatic start and a manual "Try again" 157 // both carry the attribution id. 158 var href = withDistr(startLink.getAttribute('href')) 159 if (href) startLink.setAttribute('href', href) 160 161 startLink.addEventListener('click', function () { 162 track(RETRY_EVENT) 163 }) 164 165 if (!isFreshNavigation()) return 166 167 // Start once the screen has been painted, so the browser's download UI never 168 // appears over the previous page. 169 var start = function () { 170 triggerDownload(startLink.getAttribute('href')) 171 } 172 173 if (window.requestAnimationFrame) { 174 window.requestAnimationFrame(function () { 175 window.requestAnimationFrame(start) 176 }) 177 } else { 178 start() 179 } 180 } 181 182 function init() { 183 var startLink = document.querySelector(START_SELECTOR) 184 if (startLink) { 185 initScreen(startLink) 186 return 187 } 188 189 // Bound unconditionally: gsid.js and appsflyer.js rewrite these hrefs after 190 // load, so what matches the selector is not settled at this point. 191 document.addEventListener('click', onCtaClick) 192 } 193 194 document.addEventListener('DOMContentLoaded', init) 195})()
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.