1(function(){ 2 var BS_CUSTOMER_ID = 282; 3 var BS_COOKIE_DOMAIN_OVERRIDE = null; 4 var BS_API_URL = "https://botseller.ru"; 5 6 function autoDetectCookieDomain() { 7 // ÐÐ»Ñ apex domain ('botseller.ai' напÑÑмÑÑ) â не ÑÑавим domain 8 // (browser авÑомаÑиÑеÑки ÑаÑÐ¸Ñ cookie на Ð¿Ð¾Ð´Ð´Ð¾Ð¼ÐµÐ½Ñ ÐµÑли Ð¸Ñ Ð½ÐµÑ). 9 var host = window.location.hostname; 10 if (!host || host === 'localhost' || /^\d{1,3}(\.\d{1,3}){3}$/.test(host)) { 11 return null; // localhost/IP â без Ñвного domain 12 } 13 // ÐÑоÑÑÐ°Ñ ÑвÑиÑÑика: поÑледние 2 ÑегменÑа (botseller.ai / example.com). 14 // ÐÐ»Ñ .co.uk и подобнÑÑ eTLD ÐºÐ»Ð¸ÐµÐ½Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ пеÑедаÑÑ cookie_domain Ñвно. 15 var parts = host.split('.'); 16 if (parts.length < 2) return null; 17 return '.' + parts.slice(-2).join('.'); 18 } 19 20 var BS_COOKIE_DOMAIN = BS_COOKIE_DOMAIN_OVERRIDE || autoDetectCookieDomain(); 21 22 function getCookie(name) { 23 var m = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/[-.+*]/g, '\\$&') + '=([^;]*)')); 24 return m ? decodeURIComponent(m[1]) : null; 25 } 26 function setCookie(name, value, days) { 27 var d = new Date(); 28 d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); 29 var domainPart = BS_COOKIE_DOMAIN ? (';domain=' + BS_COOKIE_DOMAIN) : ''; 30 document.cookie = name + '=' + encodeURIComponent(value) 31 + ';expires=' + d.toUTCString() 32 + ';path=/;SameSite=Lax' + domainPart; 33 } 34 function uuidv4() { 35 if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { 36 return crypto.randomUUID(); 37 } 38 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 39 var r = Math.random() * 16 | 0; 40 var v = c === 'x' ? r : (r & 0x3 | 0x8); 41 return v.toString(16); 42 }); 43 } 44 function getUrlParam(name) { 45 var m = window.location.search.match(new RegExp('[?&]' + name + '=([^&]+)')); 46 return m ? decodeURIComponent(m[1]) : null; 47 } 48 49 // Click id ÑекламнÑÑ ÑиÑÑем и поиÑковиков. ЧаÑÑо ÑÑо ÐÐÐÐСТÐÐÐÐЫРпÑизнак 50 // иÑÑоÑника: пеÑÐµÑ Ð¾Ð´ из мобилÑного пÑÐ¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¯Ð½Ð´ÐµÐºÑа или из меÑÑенджеÑа 51 // пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ð±ÐµÐ· referrer, но ysclid в адÑеÑе оÑÑаÑÑÑÑ. 52 function collectClickIds() { 53 return { 54 ysclid: getUrlParam('ysclid'), 55 yclid: getUrlParam('yclid'), 56 gclid: getUrlParam('gclid'), 57 gbraid: getUrlParam('gbraid'), 58 wbraid: getUrlParam('wbraid'), 59 fbclid: getUrlParam('fbclid'), 60 ttclid: getUrlParam('ttclid') 61 }; 62 } 63 64 // ÐоÑкÑÐ¿ÐµÐ¹Ñ â ÑÑо Ñже пÑодÑкÑ, а не маÑкеÑинговÑй ÑÑаÑик. ÐеÑвое каÑание 65 // должно ÑказÑваÑÑ Ð½Ð° пÑблиÑнÑÑ ÑÑÑаниÑÑ, инаÑе иÑÑоÑником ÑегиÑÑÑаÑии 66 // ÑÑановиÑÑÑ Ñам кабинеÑ. 67 function isWorkspaceHost() { 68 return /^workspace\./.test(window.location.hostname); 69 } 70 71 // 1. Anonymous visitor ID â sticky 365 дней, генеÑиÑÑÑ Ð¾Ð´Ð¸Ð½ Ñаз. 72 if (!getCookie('_bs_anon_id')) { 73 setCookie('_bs_anon_id', uuidv4(), 365); 74 } 75 76 // 2. First touch snapshot â sticky 90 дней. ÐапиÑÑваем ТÐÐЬÐРеÑли 77 // cookie еÑÑ Ð½ÐµÑ: пÑи вÑоÑом визиÑе Ñ Ð´ÑÑгим UTM ÑÐ¾Ñ ÑанÑем пеÑвое каÑание. 78 if (!getCookie('_bs_first_touch') && !isWorkspaceHost()) { 79 var firstTouch = { 80 utm: { 81 source: getUrlParam('utm_source'), 82 medium: getUrlParam('utm_medium'), 83 campaign: getUrlParam('utm_campaign'), 84 content: getUrlParam('utm_content'), 85 term: getUrlParam('utm_term') 86 }, 87 click_ids: collectClickIds(), 88 partner_ref: getUrlParam('ref'), 89 referrer: document.referrer || null, 90 landing_url: window.location.href, 91 captured_at: new Date().toISOString() 92 }; 93 try { 94 setCookie('_bs_first_touch', JSON.stringify(firstTouch), 90); 95 } catch (e) { /* JSON.stringify failed â silently skip */ } 96 } 97 98 // 3. Meta click id â _fbc (ÑоÑÐ¼Ð°Ñ fb.1.{ts_ms}.{fbclid}, 90 дней). 99 // Conversions API маÑÑÐ¸Ñ Ð¿Ð¾ _fbc ÑилÑнее вÑего; Safari ITP ÑÐµÐ¶ÐµÑ 100 // JS-cookie до 24Ñ, поÑÑÐ¾Ð¼Ñ Ð¿ÐµÑезапиÑÑваем пÑи ÐÐÐÐÐÐ Ð·Ð°Ñ Ð¾Ð´Ðµ Ñ fbclid 101 // (новÑй клик = Ð½Ð¾Ð²Ð°Ñ Ð°ÑÑибÑÑиÑ), а ÑнапÑÐ¾Ñ Ð°ÑÑибÑÑии забиÑÐ°ÐµÑ Ð·Ð½Ð°Ñение 102 // в Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð·Ð°Ñвки/ÑегиÑÑÑаÑии. 103 var fbclid = getUrlParam('fbclid'); 104 if (fbclid && fbclid.length <= 512) {
105 setCookie('_fbc', 'fb.1.' + Date.now() + '.' + fbclid, 90); 106 } 107 108 // Hook Ð´Ð»Ñ ÑаÑÑиÑений: window.__bsTracker.customerId доÑÑÑпен ÑнаÑÑжи. 109 window.__bsTracker = window.__bsTracker || {}; 110 window.__bsTracker.customerId = BS_CUSTOMER_ID; 111 window.__bsTracker.anonId = getCookie('_bs_anon_id'); 112 113 // Phase 1D-2 + fix/pixel-spa-routing: page_view tracking Ñ Ð¿Ð¾Ð´Ð´ÐµÑжкой 114 // SPA-ÑоÑÑинга (Next.js App Router, Nextra, лÑбой React/Vue SPA). 115 // 116 // СовÑеменнÑе ÑайÑÑ Ð¼ÐµÐ½ÑÑÑ URL ÑеÑез history.pushState ÐÐРполной 117 // пеÑезагÑÑзки. pixel.js в обÑÑном script-tag загÑÑжаеÑÑÑ ÐÐÐÐ Ñаз 118 // и не Ð·Ð½Ð°ÐµÑ Ð¿Ñо поÑледÑÑÑие client-side навигаÑии. Ðез ÑÑого fix'а 119 // на /blog/article-X (поÑле клика по Link на /blog) pageview ÐÐ 120 // оÑпÑавлÑлÑÑ â Ñаб «ÐаÑÑÑÑÑ» показÑвал ÑолÑко пеÑвÑÑ ÑÑÑаниÑÑ. 121 // 122 // РеÑение â паÑÑим history.pushState/replaceState и ÑлÑÑаем popstate. 123 // Ðа каждое изменение URL вÑзÑваем sendPageView() Ñ dedupe-пÑовеÑкой. 124 if (BS_API_URL) { 125 var sentPageViews = {}; // map url->timestamp â dedupe в RAM 126 127 function sendPageView() { 128 var anonId = getCookie('_bs_anon_id'); 129 if (!anonId) return; 130 try { 131 var currentUrl = window.location.href; 132 var nowMs = Date.now(); 133 134 // Dedupe (fix/touchpoint-hardening): не ÑлÑм page_view еÑли 135 // на ТÐТ ÐÐ URL Ñже бÑл fetch за поÑледние 30 Ñек. ÐаÑиÑа Ð¾Ñ 136 // F5-spam, hot-reload, browser back/forward на Ð¾Ð´Ð½Ñ ÑÑÑаниÑÑ. 137 // In-memory map бÑÑÑÑее sessionStorage и не Ð¿Ð°Ð´Ð°ÐµÑ Ð² private mode. 138 if ((nowMs - (sentPageViews[currentUrl] || 0)) < 30000) { 139 return; 140 } 141 sentPageViews[currentUrl] = nowMs; 142 143 var firstTouchRaw = getCookie('_bs_first_touch'); 144 var firstTouch = null; 145 if (firstTouchRaw) { 146 try { firstTouch = JSON.parse(firstTouchRaw); } catch (e) {} 147 } 148 var payload = { 149 customer_id: BS_CUSTOMER_ID, 150 contact_identifier: anonId, 151 contact_identifier_type: 'anon', 152 event_type: 'page_view', 153 attribution_data: { 154 utm: { 155 source: getUrlParam('utm_source'), 156 medium: getUrlParam('utm_medium'), 157 campaign: getUrlParam('utm_campaign'), 158 content: getUrlParam('utm_content'), 159 term: getUrlParam('utm_term') 160 }, 161 click_ids: collectClickIds(), 162 partner_ref: getUrlParam('ref'), 163 first_touch: firstTouch, 164 context: { 165 landing_url: currentUrl, 166 page_path: window.location.pathname, 167 page_title: document.title || null, 168 referrer: document.referrer || null 169 } 170 } 171 }; 172 fetch(BS_API_URL + '/api/v1/touchpoints/track', { 173 method: 'POST', 174 headers: { 'Content-Type': 'application/json' }, 175 body: JSON.stringify(payload), 176 keepalive: true, 177 credentials: 'omit', 178 mode: 'cors' 179 }).catch(function() { /* fire-and-forget */ }); 180 } catch (e) { /* skip on any error */ } 181 } 182 183 // SPA-навигаÑиÑ: даÑм React/Next.js 100ms на обновление document.title 184 // и DOM ÐÐСÐÐ pushState â инаÑе title в attribution бÑÐ´ÐµÑ Ð¾Ñ Ð¿ÑоÑлой 185 // ÑÑÑаниÑÑ (UI «ÐаÑÑÑÑÑ» Ð¿Ð¾ÐºÐ°Ð¶ÐµÑ Ð½ÐµÐ¿ÑавилÑнÑй заголовок). 186 function delayedPageView() { 187 setTimeout(sendPageView, 100); 188 } 189 190 // 1. Initial pageview â ÑÑÐ°Ð·Ñ Ð¿Ñи загÑÑзке script'а 191 sendPageView(); 192 193 // 2. Patch history.pushState â Next.js Router / React Router / Vue Router 194 // делаÑÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ ÑÑо пÑи <Link> ÐºÐ»Ð¸ÐºÐ°Ñ . Ð¡Ð¾Ñ ÑанÑем оÑигинал и 195 // вÑзÑваем его поÑле наÑего Ñ Ñка. 196 var originalPushState = history.pushState; 197 history.pushState = function() { 198 var result = originalPushState.apply(this, arguments); 199 delayedPageView(); 200 return result; 201 }; 202 203 // 3. Patch history.replaceState â некоÑоÑÑе ÑоÑÑеÑÑ Ð¸ÑполÑзÑÑÑ ÐµÐ³Ð¾ 204 // Ð´Ð»Ñ query-паÑамеÑÑов. Тоже оÑÑлеживаем. 205 var originalReplaceState = history.replaceState; 206 history.replaceState = function() { 207 var result = originalReplaceState.apply(this, arguments); 208 delayedPageView(); 209 return result; 210 }; 211 212 // 4. popstate event â browser back/forward кнопки. 213 // pushState/replaceState его ÐРгенеÑÑÑ, поÑÑÐ¾Ð¼Ñ ÑлÑÑаем оÑделÑно. 214 window.addEventListener('popstate', delayedPageView); 215 } 216})();
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.