/* Boxette — захват рекламной атрибуции в общую cookie bx_attr (.boxette.uz). * * Ставится как есть на boxette.uz и в личный кабинет (profile / mprofile). * Полный контракт cookie — ТЗ фронтам, §2.1; правило про рекламные * идентификаторы — §1.3. * * Скрипт не должен ронять страницу ни при каких условиях: всё тело обёрнуто * в try/catch, и любая ошибка захвата просто оставляет cookie как была. */ (function () { try { var NAME = "bx_attr"; // Параметры, которые кладутся в cookie под тем же именем, что в URL. var KEYS = ["fbclid", "gclid", "gbraid", "wbraid", "utm_source", "utm_campaign"]; // Рекламная иерархия Meta. Ключ — каноническое (длинное) имя в cookie, // значение — имена в URL по приоритету: сначала длинное, потом короткий // синоним. В cookie всегда попадает длинное имя. var AD_IDS = { campaign_id: ["campaign_id", "cid"], adset_id: ["adset_id", "asid"], ad_id: ["ad_id", "aid"] }; // Идентификатор объявления — только десятичные цифры. Всё остальное // отбрасывается, и главное — нераскрывшийся макрос вида "{{adset.id}}" // (Meta подставляет значения в момент клика, а краулеры выкачивают // ссылку из объявления как есть). Такое значение, попав в отчёт, // выглядело бы обычным ad set — поэтому его лучше потерять. var DIGITS = /^[0-9]+$/; function readCookie() { var m = document.cookie.match(new RegExp("(?:^|; )" + NAME + "=([^;]*)")); if (!m) return {}; try { return JSON.parse(decodeURIComponent(m[1])) || {}; } catch (e) { return {}; } } var params = new URLSearchParams(window.location.search); var data = readCookie(); var attrTouched = false; var idCreated = false; KEYS.forEach(function (k) { var v = params.get(k); if (v) { data[k] = v; attrTouched = true; } }); Object.keys(AD_IDS).forEach(function (canonical) { var aliases = AD_IDS[canonical]; for (var i = 0; i < aliases.length; i++) { var v = params.get(aliases[i]); if (!v) continue; v = String(v).trim(); if (!DIGITS.test(v)) continue; // макрос или мусор — не сохраняем data[canonical] = v; attrTouched = true; return; // длинное имя выиграло, короткое уже не смотрим } }); if (!data.external_id) { data.external_id = window.crypto && crypto.randomUUID ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { var r = (Math.random() * 16) | 0; return (c === "x" ? r : (r & 3) | 8).toString(16); }); idCreated = true; } if (attrTouched) { data.ts = Date.now(); } if (attrTouched || idCreated) { document.cookie = NAME + "=" + encodeURIComponent(JSON.stringify(data)) + "; domain=.boxette.uz; path=/; max-age=7776000; secure; samesite=lax"; } } catch (e) { /* захват атрибуции никогда не ломает страницу */ } })();