{"id":9320,"date":"2026-08-09T15:37:31","date_gmt":"2026-08-09T13:37:31","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/narzedzia\/gtm-container-history-deep-search-engine\/"},"modified":"2026-10-23T15:00:33","modified_gmt":"2026-10-23T13:00:33","slug":"gtm-container-history-deep-search-engine","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/","title":{"rendered":"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione"},"content":{"rendered":"<div class=\"gtm-analyser-container lw-tool-surface\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<div class=\"lw-aussenhinweis\" style=\"background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left: 3px solid var(--lw-tool-warn); border-radius: 6px; padding: 14px 16px; margin-bottom: 20px;\">\n<div style=\"color: var(--lw-tool-warn); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 6px;\">Wymaga logowania do Google<\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin: 0 0 8px; font-size: 0.9rem; line-height: 1.55;\">W odr\u00f3\u017cnieniu od pozosta\u0142ych narz\u0119dzi to nie dzia\u0142a samodzielnie. Odczytuje dane z Google Tag Manager REST API v2 i dlatego wymaga logowania do Google. \u017b\u0105dane uprawnienie to tagmanager.readonly \u2014 wy\u0142\u0105cznie do odczytu, niczego nie da si\u0119 przez nie zmieni\u0107.<\/p>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin: 0; font-size: 0.9rem; line-height: 1.55;\">Pobrane dane pozostaj\u0105 w tej karcie: analiza dzia\u0142a w przegl\u0105darce, do lukaswojcik.com nic nie trafia. Wype\u0142niony poni\u017cej OAuth Client ID mo\u017cna zast\u0105pi\u0107 w\u0142asnym.<\/p>\n<\/div>\n<p style=\"color: var(--text-secondary); margin-bottom: 20px; line-height: 1.6;\">\n        Po\u0142\u0105czenie bezpo\u015brednio z <strong>Google Tag Manager REST API v2<\/strong> przy u\u017cyciu OAuth 2.0.<br \/>\n        Narz\u0119dzie pobiera nag\u0142\u00f3wki wersji i przeszukuje setki wersji kontenera pod k\u0105tem tag\u00f3w, skrypt\u00f3w, aktywator\u00f3w lub zmiennych, bez r\u0119cznego eksportu plik\u00f3w.\n    <\/p>\n<p id=\"gtm-element-history-intro\" style=\"color: var(--text-secondary); margin-bottom: 20px; line-height: 1.6;\"><strong>Historia zmian jednego elementu:<\/strong> po zalogowaniu mo\u017cna wpisa\u0107 pojedynczy tag, aktywator, zmienn\u0105, szablon, folder, klienta, transformacj\u0119 lub stref\u0119 \u2013 wed\u0142ug nazwy, identyfikatora albo adresu z GTM. Narz\u0119dzie wymienia ka\u017cd\u0105 wersj\u0119, w kt\u00f3rej element utworzono, zmieniono, przemianowano, usuni\u0119to lub przywr\u00f3cono, z numerem, nazw\u0105 i notatkami wersji, datami, r\u00f3\u017cnicami pole po polu, zmianami w u\u017cywanych aktywatorach i zmiennych oraz nieopublikowanymi zmianami w obszarach roboczych.<\/p>\n<p id=\"gtm-unification-background\" style=\"color: var(--text-secondary); margin-bottom: 20px; line-height: 1.6;\"><strong>Wcze\u015bniejsze om\u00f3wienie po\u0142\u0105czenia:<\/strong> <a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/digital-analytics-pl\/polaczenie-google-tag-i-tag-managera-destinations-wyzwalacz-gtm-init-i-o-czym-decyduje-id-ladowania\/\">Po\u0142\u0105czenie Google Tag i Tag Managera: destinations, wyzwalacz gtm init i o czym decyduje ID \u0142adowania<\/a>.<\/p>\n<p id=\"gtm-unification-article\" style=\"color: var(--text-secondary); margin-bottom: 20px; line-height: 1.6;\"><strong>Kontekst migracji:<\/strong> <a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/digital-analytics-pl\/polaczenie-google-tag-managera-i-gtag-js-co-zmienia-sie-w-2026-roku\/\">Po\u0142\u0105czenie Google Tag Managera i gtag.js: co zmienia si\u0119 w 2026 roku<\/a>. Pa\u017adziernikowe om\u00f3wienie wyja\u015bnia optymalizacj\u0119 kontenera, zdarzenia gtag.config i kontrolowan\u0105 migracj\u0119.<\/p>\n<div id=\"gtm-tool-update-20261019\" style=\"border-left:3px solid var(--accent);padding:12px 16px;margin:0 0 24px;background:var(--bg-body);border-radius:6px;color:var(--text-secondary);line-height:1.6;\">\n<p style=\"margin:0 0 8px;\"><strong>Aktualizacja z 19.10.2026:<\/strong> Wyszukiwanie obejmuje wybrane listy tag\u00f3w, regu\u0142, zmiennych, szablon\u00f3w i folder\u00f3w w pobranych wersjach. Nie obejmuje wszystkich ustawie\u0144 centralnych ani uprawnie\u0144. Kod wykonawczy Google mo\u017ce zmieni\u0107 si\u0119 bez nowej wersji; brak dopasowania <code>gtag.config<\/code> nie wyklucza tego zdarzenia podczas dzia\u0142ania strony. Opcjonalna optymalizacja wymaga osobnego sprawdzenia ustawie\u0144 centralnych i tag\u00f3w cel\u00f3w. Uprawnienia nie s\u0105 wersjonowane i nie wracaj\u0105 po przywr\u00f3ceniu wersji.<\/p>\n<p style=\"margin:0;font-size:0.9em;\"><a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/digital-analytics-pl\/polaczenie-google-tag-managera-i-gtag-js-co-zmienia-sie-w-2026-roku\/\">Kontekst pa\u017adziernikowych zmian<\/a> \u00b7 <a href=\"https:\/\/support.google.com\/tagmanager\/answer\/4620708?hl=en\">Informacje Google o wydaniach<\/a> \u00b7 <a href=\"https:\/\/support.google.com\/analytics\/answer\/16659369\">Optymalizacja i uprawnienia<\/a><\/p>\n<\/div>\n<div id=\"gtm-related-links-20261020\" class=\"lw-gtm-related\" style=\"margin:20px 0;color:var(--text-secondary);line-height:1.6;\">\n<p><strong>Powi\u0105zane instrukcje i narz\u0119dzia:<\/strong><\/p>\n<ul>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/digital-analytics-pl\/tutorials-pl\/tutorial-znalezienie-wersji-kontenera-w-ktorej-zmienil-sie-tag\/\">Znalezienie wersji ze zmian\u0105 tagu<\/a> \u2013 archiwum wersji, odciski i ograniczenia dowod\u00f3w historycznych.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/tworzenie-stron-internetowych\/porownywanie-dwoch-eksportow-kontenera-gtm-normalizacja-przed-diffem-i-czego-nie-pokazuje-porownanie-wierszami\/\">Normalizacja i por\u00f3wnanie eksport\u00f3w GTM<\/a> \u2013 r\u00f3\u017cnice konfiguracji i ograniczenia por\u00f3wnania tekstu.<\/li>\n<\/ul>\n<\/div>\n<p>    <!-- STEP 1: OAUTH & API AUTHENTICATION --><\/p>\n<div style=\"background: var(--bg-body, #101416); padding: 20px; border-radius: 8px; border: 1px solid var(--border); margin-bottom: 20px;\">\n<h3 style=\"color: var(--accent); font-size: 1rem; margin-top: 0; margin-bottom: 12px;\">1. Uwierzytelnianie w Google API<\/h3>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end;\">\n<div style=\"flex: 1; min-width: 280px;\">\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\" for=\"api-client-id\">Google Cloud OAuth Client ID:<\/label><br \/>\n                <input type=\"text\" id=\"api-client-id\" value=\"423029313598-2ko0jilc28eevp253rqncvodg2181e1l.apps.googleusercontent.com\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-panel); border: 1px solid var(--border); color: var(--text-primary); border-radius: 6px; font-family: monospace;\">\n            <\/div>\n<div>\n                <button id=\"auth-btn\" class=\"button\" style=\"background: var(--accent); color:var(--on-accent); border: none; padding: 11px 22px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Autoryzuj przez Google<\/button>\n            <\/div>\n<\/p><\/div>\n<div data-mp-mask id=\"auth-status\" style=\"margin-top: 10px; font-size: 0.85rem; color: var(--text-secondary);\">Status: <strong style=\"color: var(--fail);\">Brak po\u0142\u0105czenia<\/strong><\/div>\n<\/p><\/div>\n<p>    <!-- STEP 2: CONTAINER SELECTOR --><\/p>\n<div id=\"container-selector-panel\" style=\"background: var(--bg-body); padding: 20px; border-radius: 8px; border: 1px solid var(--border); margin-bottom: 20px; display: none;\">\n<h3 style=\"color: var(--accent); font-size: 1rem; margin-top: 0; margin-bottom: 12px;\">2. Wyb\u00f3r kontenera GTM i zakresu wersji<\/h3>\n<div style=\"display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 15px;\">\n<div>\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\">Konto GTM:<\/label><br \/>\n                <select data-mp-mask id=\"account-select\" style=\"width: 100%; padding: 10px; background: var(--bg-panel); color: var(--text-primary); border: 1px solid var(--border); border-radius: 6px;\"><option value=\"\">&#8212; Wybierz konto &#8212;<\/option><\/select>\n            <\/div>\n<div>\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\">Kontener:<\/label><br \/>\n                <select data-mp-mask id=\"container-select\" style=\"width: 100%; padding: 10px; background: var(--bg-panel); color: var(--text-primary); border: 1px solid var(--border); border-radius: 6px;\" disabled><option value=\"\">&#8212; Wybierz kontener &#8212;<\/option><\/select>\n            <\/div>\n<div>\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\">Zakres wersji do przeszukania:<\/label><br \/>\n                <select id=\"range-select\" style=\"width: 100%; padding: 10px; background: var(--bg-panel); color: var(--text-primary); border: 1px solid var(--border); border-radius: 6px;\" disabled><option value=\"30\">Ostatnie 30 wersji (szybko)<\/option><option value=\"100\">Ostatnie 100 wersji<\/option><option value=\"300\">Ostatnie 300 wersji<\/option><option value=\"all\">WSZYSTKIE wersje (pe\u0142ne przeszukanie)<\/option><\/select>\n            <\/div>\n<\/p><\/div>\n<div data-mp-mask id=\"header-status\" style=\"font-size: 0.85rem; color: var(--text-secondary);\"><\/div>\n<\/p><\/div>\n<p>    <!-- MODE TABS: DEEP SEARCH \/ ELEMENT HISTORY (23.10.2026) --><\/p>\n<div id=\"gtm-mode-tabs\" role=\"tablist\" style=\"display: none; gap: 8px; flex-wrap: wrap; margin-bottom: 15px;\">\n        <button type=\"button\" role=\"tab\" class=\"gtm-mode-tab button\" data-mode=\"search\" aria-selected=\"true\" style=\"border: 1px solid var(--accent); padding: 10px 18px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Wyszukiwanie pog\u0142\u0119bione<\/button><br \/>\n        <button type=\"button\" role=\"tab\" class=\"gtm-mode-tab button\" data-mode=\"history\" aria-selected=\"false\" style=\"border: 1px solid var(--accent); padding: 10px 18px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Historia zmian jednego elementu<\/button>\n    <\/div>\n<p>    <!-- STEP 3: DEEP SEARCH CONTROLS --><\/p>\n<div id=\"search-controls-panel\" style=\"background: var(--bg-body); padding: 20px; border-radius: 8px; border: 1px solid var(--border); margin-bottom: 25px; display: none;\">\n<h3 style=\"color: var(--accent); font-size: 1rem; margin-top: 0; margin-bottom: 12px;\">3. Pog\u0142\u0119bione przeszukiwanie historii<\/h3>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; margin-bottom: 15px;\">\n<div style=\"flex: 1; min-width: 260px;\">\n                <input type=\"text\" id=\"search-query\" placeholder=\"np. G-XXXXXXX, fbq(, analytics_storage lub ^gtm\\..*\" style=\"width: 100%; padding: 12px; background: var(--bg-panel); border: 1px solid var(--border); color: var(--text-primary); border-radius: 6px; font-family: monospace;\">\n            <\/div>\n<div>\n                <button id=\"run-api-search-btn\" class=\"button\" style=\"background: var(--accent); color:var(--on-accent); border: none; padding: 12px 28px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Uruchom pog\u0142\u0119bione wyszukiwanie przez API<\/button>\n            <\/div>\n<\/p><\/div>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--border); padding-top: 15px; font-size: 0.85rem;\">\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" id=\"opt-regex\"> Wyra\u017cenie regularne<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" id=\"opt-case\"> Rozr\u00f3\u017cniaj wielko\u015b\u0107 liter<\/label><br \/>\n            <span style=\"color: var(--border);\">|<\/span><br \/>\n            <span style=\"color: var(--text-primary); font-weight: bold;\">Zakres przeszukiwania:<\/span><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" class=\"scope-cb\" value=\"tag\" checked> Tagi<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" class=\"scope-cb\" value=\"trigger\" checked> Aktywatory<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" class=\"scope-cb\" value=\"variable\" checked> Zmienne<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" class=\"scope-cb\" value=\"customTemplate\" checked> Szablony<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" class=\"scope-cb\" value=\"folder\" checked> Foldery<\/label>\n        <\/div>\n<p>        <!-- PROGRESS BAR --><\/p>\n<div id=\"progress-container\" style=\"margin-top: 15px; display: none;\">\n<div style=\"display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 5px;\">\n                <span data-mp-mask id=\"progress-text\">Trwa przeszukiwanie wersji przez GTM API&#8230;<\/span><br \/>\n                <span data-mp-mask id=\"progress-percent\">0%<\/span>\n            <\/div>\n<div style=\"width: 100%; height: 8px; background: var(--bg-raised); border-radius: 4px; overflow: hidden;\">\n<div id=\"progress-bar\" style=\"width: 0%; height: 100%; background: var(--ok); transition: width 0.2s;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>    <!-- STEP 3 (ALTERNATIVE): CHANGE HISTORY OF ONE ELEMENT (23.10.2026) --><\/p>\n<div id=\"element-history-panel\" style=\"background: var(--bg-body); padding: 20px; border-radius: 8px; border: 1px solid var(--border); margin-bottom: 25px; display: none;\">\n<h3 style=\"color: var(--accent); font-size: 1rem; margin-top: 0; margin-bottom: 12px;\">3. Historia zmian jednego elementu<\/h3>\n<p style=\"color: var(--text-secondary); font-size: 0.9rem; line-height: 1.55; margin: 0 0 15px;\">Wpis: nazwa, identyfikator albo adres skopiowany z Mened\u017cera tag\u00f3w (\u2026\/tags\/12). Element jest \u015bledzony wed\u0142ug identyfikatora, wi\u0119c zmiany nazwy zostaj\u0105 w jednej historii. Obowi\u0105zuje zakres wersji z kroku 2; wersje ju\u017c odczytane zostaj\u0105 w pami\u0119ci dla nast\u0119pnego elementu.<\/p>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 15px;\">\n<div style=\"min-width: 170px;\">\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\" for=\"eh-type\">Rodzaj elementu:<\/label><br \/>\n                <select id=\"eh-type\" style=\"width: 100%; padding: 11px 10px; background: var(--bg-panel); color: var(--text-primary); border: 1px solid var(--border); border-radius: 6px;\"><option value=\"any\">Wszystkie rodzaje<\/option><option value=\"tag\">Tag<\/option><option value=\"trigger\">Aktywator<\/option><option value=\"variable\">Zmienna<\/option><option value=\"builtInVariable\">Zmienna wbudowana<\/option><option value=\"customTemplate\">Szablon<\/option><option value=\"folder\">Folder<\/option><option value=\"client\">Klient (serwer)<\/option><option value=\"transformation\">Transformacja (serwer)<\/option><option value=\"zone\">Strefa<\/option><\/select>\n            <\/div>\n<div style=\"flex: 1; min-width: 240px;\">\n                <label style=\"color: var(--text-secondary); display: block; font-size: 0.85rem; margin-bottom: 6px;\" for=\"eh-query\">Nazwa, identyfikator lub adres GTM:<\/label><br \/>\n                <input type=\"text\" id=\"eh-query\" list=\"eh-suggestions\" autocomplete=\"off\" data-mp-mask data-rc-private placeholder=\"np. GA4 - purchase, 42 lub https:\/\/tagmanager.google.com\/#\/container\/\u2026\/tags\/42\" style=\"width: 100%; padding: 11px 12px; background: var(--bg-panel); border: 1px solid var(--border); color: var(--text-primary); border-radius: 6px; font-family: monospace;\"><br \/>\n                <datalist id=\"eh-suggestions\"><\/datalist>\n            <\/div>\n<div>\n                <button id=\"eh-run-btn\" type=\"button\" class=\"button\" style=\"background: var(--accent); color: var(--on-accent); border: none; padding: 12px 28px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Poka\u017c histori\u0119 zmian<\/button>\n            <\/div>\n<\/p><\/div>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--border); padding-top: 15px; font-size: 0.85rem;\">\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" id=\"eh-deps\" checked> Uwzgl\u0119dnij u\u017cywane aktywatory i zmienne<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" id=\"eh-drafts\" checked> Uwzgl\u0119dnij nieopublikowane zmiany w obszarach roboczych<\/label><br \/>\n            <label style=\"color: var(--text-secondary); cursor: pointer;\"><input type=\"checkbox\" id=\"eh-touched\"> Poka\u017c zapisy bez r\u00f3\u017cnicy w konfiguracji<\/label>\n        <\/div>\n<div data-mp-mask id=\"eh-status\" style=\"margin-top: 12px; font-size: 0.85rem; color: var(--text-secondary);\"><\/div>\n<div id=\"eh-progress\" style=\"margin-top: 15px; display: none;\">\n<div style=\"display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 5px;\">\n                <span data-mp-mask id=\"eh-progress-text\"><\/span><br \/>\n                <span data-mp-mask id=\"eh-progress-percent\">0%<\/span>\n            <\/div>\n<div style=\"width: 100%; height: 8px; background: var(--bg-raised); border-radius: 4px; overflow: hidden;\">\n<div id=\"eh-progress-bar\" style=\"width: 0%; height: 100%; background: var(--ok); transition: width 0.2s;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>    <!-- RESULTS --><\/p>\n<div data-mp-mask id=\"api-search-results\"><\/div>\n<div data-mp-mask data-rc-private id=\"element-history-results\" style=\"display: none;\"><\/div>\n<\/div>\n<p><script>\n(() => {\n    let accessToken = null;\n    let tokenClient = null;\n    let versionHeaders = [];\n    const accountsMap = {};\n    const containersMap = {};<\/p>\n<p>    const authBtn = document.getElementById(\"auth-btn\");\n    const authStatus = document.getElementById(\"auth-status\");\n    const accSelect = document.getElementById(\"account-select\");\n    const contSelect = document.getElementById(\"container-select\");\n    const rangeSelect = document.getElementById(\"range-select\");\n    const headerStatus = document.getElementById(\"header-status\");\n    const resultsEl = document.getElementById(\"api-search-results\");\n    const progressCont = document.getElementById(\"progress-container\");\n    const progressBar = document.getElementById(\"progress-bar\");\n    const progressText = document.getElementById(\"progress-text\");\n    const progressPercent = document.getElementById(\"progress-percent\");<\/p>\n<p>    const esc = (str) => String(str).replace(\/&\/g, \"&amp;\").replace(\/<\/g, \"&lt;\").replace(\/>\/g, \"&gt;\");<\/p>\n<p>    \/\/ 1. OAUTH 2.0 INITIALIZATION\n    authBtn.addEventListener(\"click\", () => {\n        const clientId = document.getElementById(\"api-client-id\").value.trim();\n        if (!clientId) {\n            alert(\"Prosz\u0119 poda\u0107 prawid\u0142owy Google Cloud Client ID.\");\n            return;\n        }<\/p>\n<p>        if (!window.google || !google.accounts || !google.accounts.oauth2) {\n            \/\/ Google Identity Services nur nach Einwilligung (consented.eu), siehe js\/lw-google-login.js\n            if (window.lwGoogleSdk) { window.lwGoogleSdk().then(() => authBtn.click(), () => {}); }\n            return;\n        }<\/p>\n<p>        tokenClient = google.accounts.oauth2.initTokenClient({\n            client_id: clientId,\n            scope: \"https:\/\/www.googleapis.com\/auth\/tagmanager.readonly\",\n            callback: (resp) => {\n                if (resp.error) {\n                    authStatus.innerHTML = \"Status: <strong style='color:#ff6b6b;'>B\u0142\u0105d uwierzytelniania: \" + esc(resp.error) + \"<\/strong>\";\n                    return;\n                }\n                accessToken = resp.access_token;\n                authStatus.innerHTML = \"Status: <strong style='color:#51cf66;'>Po\u0142\u0105czono (tylko odczyt)<\/strong>\";\n                document.getElementById(\"container-selector-panel\").style.display = \"block\";\n                loadAccounts();\n            }\n        });\n        tokenClient.requestAccessToken();\n    });<\/p>\n<p>    \/\/ 2. API FETCHER WITH RATE-LIMIT THROTTLING & ADVANCED ERROR DECODING\n    const gtmApi = async (path) => {\n        const res = await fetch(\"https:\/\/tagmanager.googleapis.com\/tagmanager\/v2\/\" + path, {\n            headers: { \"Authorization\": \"Bearer \" + accessToken }\n        });\n        if (res.status === 429) {\n            await new Promise(r => setTimeout(r, 1500));\n            return gtmApi(path);\n        }\n        if (!res.ok) {\n            let errDetails = res.statusText || \"Nieznany b\u0142\u0105d API\";\n            try {\n                const errJson = await res.json();\n                if (errJson && errJson.error && errJson.error.message) {\n                    errDetails = errJson.error.message;\n                }\n            } catch (e) {}\n            throw new Error(\"B\u0142\u0105d API \" + res.status + \": \" + errDetails);\n        }\n        return res.json();\n    };<\/p>\n<p>    const loadAccounts = async () => {\n        try {\n            accSelect.innerHTML = \"<option>Wczytywanie kont...<\/option>\";\n            const data = await gtmApi(\"accounts\");\n            accSelect.innerHTML = \"<option value=''>-- Wybierz konto --<\/option>\";\n            (data.account || []).forEach(a => {\n                accountsMap[a.accountId] = a.name;\n                const opt = document.createElement(\"option\");\n                opt.value = a.accountId;\n                opt.textContent = a.name + \" (\" + a.accountId + \")\";\n                accSelect.appendChild(opt);\n            });\n        } catch (e) {\n            accSelect.innerHTML = \"<option>B\u0142\u0105d wczytywania kont<\/option>\";\n            alert(\"Nie uda\u0142o si\u0119 pobra\u0107 kont: \" + e.message);\n        }\n    };<\/p>\n<p>    accSelect.addEventListener(\"change\", async () => {\n        const accId = accSelect.value;\n        if (!accId) return;\n        try {\n            contSelect.innerHTML = \"<option>Wczytywanie kontener\u00f3w...<\/option>\";\n            contSelect.disabled = true;\n            const data = await gtmApi(\"accounts\/\" + accId + \"\/containers\");\n            contSelect.innerHTML = \"<option value=''>-- Wybierz kontener --<\/option>\";\n            (data.container || []).forEach(c => {\n                containersMap[c.containerId] = c;\n                const opt = document.createElement(\"option\");\n                opt.value = c.containerId;\n                opt.textContent = c.name + \" (\" + c.publicId + \")\";\n                contSelect.appendChild(opt);\n            });\n            contSelect.disabled = false;\n        } catch (e) {\n            alert(\"Nie uda\u0142o si\u0119 wczyta\u0107 kontener\u00f3w: \" + e.message);\n        }\n    });<\/p>\n<p>    contSelect.addEventListener(\"change\", async () => {\n        const accId = accSelect.value;\n        const contId = contSelect.value;\n        if (!contId) return;\n        try {\n            headerStatus.innerHTML = \"Pobieranie metadanych wszystkich wersji kontenera...\";\n            const data = await gtmApi(\"accounts\/\" + accId + \"\/containers\/\" + contId + \"\/version_headers\");\n            versionHeaders = (data.containerVersionHeader || []).sort((a, b) => Number(b.containerVersionId) - Number(a.containerVersionId));\n            headerStatus.innerHTML = \"<strong style='color:#51cf66;'>Gotowe:<\/strong> Found <strong>\" + versionHeaders.length + \" versions<\/strong>.\";\n            rangeSelect.disabled = false;\n            ehContainerReady();\n        } catch (e) {\n            headerStatus.innerHTML = \"<span style='color:#ff6b6b;'>B\u0142\u0105d wczytywania nag\u0142\u00f3wk\u00f3w wersji: \" + esc(e.message) + \"<\/span>\";\n        }\n    });<\/p>\n<p>    \/\/ 3. DEEP SEARCH TRAVERSAL LOGIC\n    const searchInObject = (obj, regex, path = \"\") => {\n        const matches = [];\n        if (typeof obj === \"string\") {\n            if (regex.test(obj)) matches.push({ path: path || \"value\", snippet: obj });\n        } else if (Array.isArray(obj)) {\n            obj.forEach((item, idx) => matches.push(...searchInObject(item, regex, path ? path + \"[\" + idx + \"]\" : \"[\" + idx + \"]\")));\n        } else if (obj && typeof obj === \"object\") {\n            for (const key in obj) {\n                if (Object.prototype.hasOwnProperty.call(obj, key)) {\n                    const val = obj[key];\n                    const nextPath = path ? path + \".\" + key : key;\n                    if (regex.test(key)) matches.push({ path: nextPath, snippet: typeof val === \"object\" ? JSON.stringify(val) : String(val) });\n                    matches.push(...searchInObject(val, regex, nextPath));\n                }\n            }\n        }\n        return matches;\n    };<\/p>\n<p>    const getBadge = (type) => {\n        const colors = {\n            tag: { bg: \"#1a3a4b\", color: \"#51cf66\", label: \"TAG\" },\n            trigger: { bg: \"#4b3a1a\", color: \"#ff922b\", label: \"AKTYWATOR\" },\n            variable: { bg: \"#2a1a4b\", color: \"#339af0\", label: \"ZMIENNA\" },\n            customTemplate: { bg: \"#4b1a47\", color: \"#cc5de8\", label: \"SZABLON\" },\n            folder: { bg: \"#242b30\", color: \"#a0a6b0\", label: \"FOLDER\" }\n        };\n        const c = colors[type] || { bg: \"#242b30\", color: \"#ffffff\", label: type.toUpperCase() };\n        return \"<span style='background:\" + c.bg + \"; color:\" + c.color + \"; padding:3px 8px; border-radius:4px; font-size:0.75rem; font-weight:700;'>\" + c.label + \"<\/span>\";\n    };<\/p>\n<p>    \/\/ 4. QUEUED SCANNER ENGINE\n    document.getElementById(\"run-api-search-btn\").addEventListener(\"click\", async () => {\n        const query = document.getElementById(\"search-query\").value.trim();\n        if (!query) { alert(\"Prosz\u0119 poda\u0107 fraz\u0119 do wyszukania.\"); return; }<\/p>\n<p>        const isRegex = document.getElementById(\"opt-regex\").checked;\n        const isCase = document.getElementById(\"opt-case\").checked;\n        const activeScopes = Array.from(document.querySelectorAll(\".scope-cb:checked\")).map(cb => cb.value);<\/p>\n<p>        let regex;\n        try {\n            const flags = isCase ? \"g\" : \"gi\";\n            regex = isRegex ? new RegExp(query, flags) : new RegExp(query.replace(\/[.*+?^${}()|[\\]\\\\]\/g, \"\\\\$&\"), flags);\n        } catch (e) {\n            alert(\"Nieprawid\u0142owe wyra\u017cenie regularne: \" + e.message); return;\n        }<\/p>\n<p>        const rangeVal = rangeSelect.value;\n        const toScan = rangeVal === \"all\" ? versionHeaders : versionHeaders.slice(0, Number(rangeVal));<\/p>\n<p>        resultsEl.innerHTML = \"\";\n        progressCont.style.display = \"block\";\n        const allResults = [];\n        let completed = 0;<\/p>\n<p>        \/\/ Run batch pool of 3 concurrent API calls to avoid 429 limit\n        const concurrency = 3;\n        for (let i = 0; i < toScan.length; i += concurrency) {\n            const batch = toScan.slice(i, i + concurrency);\n            await Promise.all(batch.map(async (vh) => {\n                try {\n                    const fullVer = await gtmApi(vh.path);\n                    activeScopes.forEach((scope) => {\n                        const items = fullVer[scope] || [];\n                        items.forEach((item) => {\n                            const itemMatches = searchInObject(item, regex);\n                            if (itemMatches.length > 0) {\n                                allResults.push({\n                                    versionId: vh.containerVersionId,\n                                    versionName: vh.name || \"(bez nazwy)\",\n                                    type: scope,\n                                    entityName: item.name || \"(element bez nazwy)\",\n                                    matches: itemMatches\n                                });\n                            }\n                        });\n                    });\n                } catch (err) {\n                    console.error(\"Error scanning version \" + vh.containerVersionId, err);\n                } finally {\n                    completed++;\n                    const pct = Math.round((completed \/ toScan.length) * 100);\n                    progressBar.style.width = pct + \"%\";\n                    progressPercent.textContent = pct + \"%\";\n                    progressText.textContent = \"Sprawdzono wersj\u0119 \" + completed + \" z \" + toScan.length + \"...\";\n                }\n            }));\n        }<\/p>\n<p>        progressText.textContent = \"Przeszukiwanie zako\u0144czone!\";<\/p>\n<p>        if (!allResults.length) {\n            resultsEl.innerHTML = \"<\/p>\n<div style='background:var(--bg-body); padding:25px; border-radius:6px; border:1px solid var(--border); text-align:center; color:var(--text-secondary);'>Nie znaleziono dopasowa\u0144 dla <strong>\" + esc(query) + \"<\/strong> across scanned versions.<\/div>\n<p>\";\n            return;\n        }<\/p>\n<p>        allResults.sort((a, b) => Number(b.versionId) - Number(a.versionId));<\/p>\n<p>        const cardsHtml = allResults.map((res) => {\n            const matchRows = res.matches.slice(0, 6).map((m) => [\n                \"<\/p>\n<div style='background:#0b1114; border:1px solid var(--border); padding:10px; border-radius:4px; margin-top:8px; font-family:monospace; font-size:0.85rem; overflow-x:auto;'>\",\n                \"  <span style='color:#ff922b; display:block; margin-bottom:4px;'>\" + esc(m.path) + \"<\/span>\",\n                \"  <span style='color:#e0e6ed; white-space:pre-wrap;'>\" + esc(m.snippet.slice(0, 400)) + (m.snippet.length > 400 ? \"...\" : \"\") + \"<\/span>\",\n                \"<\/div>\n<p>\"\n            ].join(\"\")).join(\"\");<\/p>\n<p>            return [\n                \"<\/p>\n<div style='background:var(--bg-body); border:1px solid var(--border); border-radius:8px; padding:18px; margin-bottom:15px;'>\",\n                \"  <\/p>\n<div style='display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px; border-bottom:1px solid var(--border); padding-bottom:12px; margin-bottom:12px;'>\",\n                \"    <\/p>\n<div style='display:flex; align-items:center; gap:10px;'>\",\n                \"      \" + getBadge(res.type),\n                \"      <strong style='color:var(--text-primary); font-size:1.05rem;'>\" + esc(res.entityName) + \"<\/strong>\",\n                \"    <\/div>\n<p>\",\n                \"    <\/p>\n<div style='color:var(--text-secondary); font-size:0.85rem;'>\",\n                \"      Znaleziono w wersji: <strong style='color:var(--accent);'>v\" + esc(res.versionId) + \"<\/strong> (\" + esc(res.versionName) + \")\",\n                \"    <\/div>\n<p>\",\n                \"  <\/p><\/div>\n<p>\",\n                \"  <\/p>\n<div style='color:var(--text-secondary); font-size:0.85rem;'>Dopasowane w\u0142a\u015bciwo\u015bci (\" + res.matches.length + \"):<\/div>\n<p>\",\n                \"  \" + matchRows,\n                \"<\/p><\/div>\n<p>\"\n            ].join(\"\");\n        }).join(\"\");<\/p>\n<p>        resultsEl.innerHTML = [\n            \"<\/p>\n<h3 style='color:var(--text-primary); margin-bottom:15px; font-size:1.15rem;'>Dopasowania w historii: \" + allResults.length + \"<\/h3>\n<p>\",\n            cardsHtml\n        ].join(\"\");\n    });<\/p>\n<p>    \/\/ 5. CHANGE HISTORY OF ONE ELEMENT (23.10.2026)\n    \/\/ Tracks a tag, trigger, variable etc. by its ID across the versions in the chosen range.\n    \/\/ Each version is reduced to normalized element strings; identical strings are shared, so a\n    \/\/ second element of the same container is answered from memory without new API calls.\n    const T = {\"types\": {\"tag\": \"Tag\", \"trigger\": \"Aktywator\", \"variable\": \"Zmienna\", \"builtInVariable\": \"Zmienna wbudowana\", \"customTemplate\": \"Szablon\", \"folder\": \"Folder\", \"client\": \"Klient\", \"transformation\": \"Transformacja\", \"zone\": \"Strefa\", \"gtagConfig\": \"Konfiguracja tagu Google\"}, \"loadingList\": \"Wczytywanie listy element\u00f3w najnowszej wersji ...\", \"listReady\": \"Lista element\u00f3w z wersji v{v} wczytana \u2013 podpowiedzi pojawiaj\u0105 si\u0119 podczas pisania.\", \"urlOtherContainer\": \"Uwaga: wklejony adres nale\u017cy do innego konta lub kontenera ni\u017c wybrany.\", \"moreLines\": \"\u2026 jeszcze {n} wierszy\", \"fromLine\": \"od wiersza {n}\", \"absent\": \"nieustawione\", \"before\": \"Przed\", \"after\": \"Po\", \"moreFields\": \"\u2026 jeszcze {n} p\u00f3l (pe\u0142na lista w eksporcie JSON)\", \"depsTitle\": \"U\u017cywane elementy\", \"depAdded\": \"nowo u\u017cywany\", \"depRemoved\": \"ju\u017c nieu\u017cywany\", \"depChanged\": \"zmieniony\", \"depMissing\": \"brak\", \"depNotInContainer\": \"nie wyst\u0119puje w tej wersji\", \"fieldsChanged\": \"Liczba r\u00f3\u017cni\u0105cych si\u0119 p\u00f3l: {n}\", \"openInGtm\": \"otw\u00f3rz wersj\u0119 w GTM\", \"live\": \"Opublikowana\", \"ev_baseline\": \"Obecny w najstarszej sprawdzonej wersji\", \"ev_created\": \"Utworzono\", \"ev_restored\": \"Ponownie obecny\", \"ev_removed\": \"Usuni\u0119to\", \"ev_changed\": \"Zmieniono konfiguracj\u0119\", \"ev_renamed\": \"Zmiana nazwy z \u201e{from}\u201d na \u201e{to}\u201d\", \"ev_depChanged\": \"Sam bez zmian \u2013 zmieni\u0142 si\u0119 u\u017cywany element\", \"ev_touched\": \"Zapisano ponownie bez r\u00f3\u017cnicy w konfiguracji\", \"ev_draft\": \"Obszar roboczy \u201e{ws}\u201d: {status}, jeszcze nieopublikowane\", \"k_baseline\": \"Pocz\u0105tek zakresu\", \"k_created\": \"Utworzono\", \"k_restored\": \"Przywr\u00f3cono\", \"k_removed\": \"Usuni\u0119to\", \"k_changed\": \"Zmieniono\", \"k_renamed\": \"Nowa nazwa\", \"k_depChanged\": \"Zale\u017cno\u015b\u0107\", \"k_touched\": \"Zapisano\", \"k_draft\": \"Wersja robocza\", \"ws_added\": \"dodano\", \"ws_deleted\": \"usuni\u0119to\", \"ws_updated\": \"zmieniono\", \"ws_none\": \"bez zmian\", \"draftCompared\": \"Pola r\u00f3\u017cni\u0105ce si\u0119 od wersji v{v}: {n}\", \"version\": \"Wersja\", \"versionDate\": \"Zapis wersji\", \"elementDate\": \"Ostatni zapis elementu\", \"versionNotes\": \"Notatki do wersji\", \"absentSince\": \"brak od wersji nast\u0119puj\u0105cej po v{v}\", \"gapNote\": \"Nie uda\u0142o si\u0119 odczyta\u0107 wcze\u015bniejszych wersji: {n}\", \"showConfig\": \"Poka\u017c konfiguracj\u0119 (pola: {n})\", \"account\": \"Konto GTM\", \"container\": \"Kontener\", \"scanned\": \"Sprawdzono\", \"scannedText\": \"wersje: {n} (od {from} do {to})\", \"unreadable\": \"nieodczytane wersje: {n}\", \"rangeHint\": \"Starsze wersje le\u017c\u0105 poza wybranym zakresem; opcja \u201eWSZYSTKIE wersje\u201d w kroku 2 obejmuje ca\u0142\u0105 histori\u0119.\", \"liveVersion\": \"Wersja opublikowana\", \"noLive\": \"brak publikacji\", \"draftError\": \"Nie uda\u0142o si\u0119 odczyta\u0107 zmian w obszarach roboczych:\", \"exportJson\": \"Eksport JSON\", \"exportCsv\": \"Eksport CSV\", \"elementId\": \"Identyfikator\", \"firstSeen\": \"Pierwsze wyst\u0105pienie\", \"beforeRange\": \"utworzony w tej wersji lub wcze\u015bniej\", \"lastChange\": \"Ostatnia zmiana\", \"noChange\": \"brak zmian w zakresie\", \"changeCount\": \"Zmiany w zakresie\", \"inLive\": \"Cz\u0119\u015b\u0107 opublikowanej wersji\", \"yes\": \"tak\", \"no\": \"nie\", \"liveOutOfRange\": \"opublikowana wersja poza zakresem\", \"names\": \"Nazwy w historii\", \"limits\": \"Ograniczenia: GTM API nie podaje autora ani dla elementu, ani dla wersji \u2013 osob\u0119 publikuj\u0105c\u0105 wersj\u0119 pokazuje lista wersji w interfejsie Mened\u017cera tag\u00f3w. Daty pochodz\u0105 z odcisk\u00f3w (fingerprint), kt\u00f3re API zwraca dla wersji i element\u00f3w; Google opisuje je jako znacznik zmiany, obecnie zawieraj\u0105 czas zapisu w milisekundach. Wersja utrwala konfiguracj\u0119 z chwili jej utworzenia, wi\u0119c zmiany wprowadzone i cofni\u0119te mi\u0119dzy dwiema wersjami nie zostawiaj\u0105 \u015bladu. Kod wykonawczy dostarczany przez Google, ustawienia ca\u0142ego kontenera i uprawnienia nie wchodz\u0105 w to por\u00f3wnanie. Wszystko dzia\u0142a w tej karcie przegl\u0105darki, nic nie trafia do lukaswojcik.com.\", \"multiMatch\": \"Wpis pasuje do element\u00f3w: {n}; ka\u017cdy ma poni\u017cej w\u0142asn\u0105 histori\u0119.\", \"needQuery\": \"Wymagana nazwa, identyfikator lub adres GTM.\", \"noVersions\": \"Ten kontener nie ma jeszcze wersji.\", \"progress\": \"Odczyt wersji {n} z {total} ...\", \"authExpired\": \"Logowanie Google wygas\u0142o (HTTP 401). Wymagana ponowna autoryzacja w kroku 1.\", \"nothingReadable\": \"Nie uda\u0142o si\u0119 odczyta\u0107 \u017cadnej wersji z zakresu.\", \"notFound\": \"W sprawdzonych wersjach ({n}) nie ma elementu o nazwie lub numerze \u201e{q}\u201d.\", \"similar\": \"Podobne nazwy w najnowszej wersji:\", \"loadingDrafts\": \"Sprawdzanie obszar\u00f3w roboczych pod k\u0105tem nieopublikowanych zmian ...\", \"tooMany\": \"Pasuj\u0105ce elementy: {n}; pokazano pierwsze 10. Dok\u0142adniejsza nazwa, identyfikator lub rodzaj elementu zaw\u0119\u017ca wynik.\"};\n    const EH_LOCALE = \"pl-PL\";\n    const EH_TYPES = {\n        tag: \"tagId\", trigger: \"triggerId\", variable: \"variableId\", builtInVariable: \"type\",\n        customTemplate: \"templateId\", folder: \"folderId\", client: \"clientId\",\n        transformation: \"transformationId\", zone: \"zoneId\", gtagConfig: \"gtagConfigId\"\n    };\n    const EH_URL_TYPES = { tags: \"tag\", triggers: \"trigger\", variables: \"variable\", templates: \"customTemplate\", folders: \"folder\", clients: \"client\", transformations: \"transformation\", zones: \"zone\" };\n    const EH_IGNORE = { fingerprint: 1, path: 1, tagManagerUrl: 1, accountId: 1, containerId: 1, workspaceId: 1 };\n    const EH_EVENT_COLORS = { created: \"var(--ok)\", restored: \"var(--ok)\", baseline: \"var(--text-secondary)\", changed: \"var(--accent)\", renamed: \"var(--accent)\", removed: \"var(--fail)\", depChanged: \"var(--lw-tool-warn)\", touched: \"var(--text-secondary)\", draft: \"var(--lw-tool-warn)\" };<\/p>\n<p>    const ehPanel = document.getElementById(\"element-history-panel\");\n    const ehResults = document.getElementById(\"element-history-results\");\n    const ehTabs = document.getElementById(\"gtm-mode-tabs\");\n    const ehType = document.getElementById(\"eh-type\");\n    const ehQuery = document.getElementById(\"eh-query\");\n    const ehList = document.getElementById(\"eh-suggestions\");\n    const ehRunBtn = document.getElementById(\"eh-run-btn\");\n    const ehStatus = document.getElementById(\"eh-status\");\n    const ehProg = document.getElementById(\"eh-progress\");\n    const ehProgBar = document.getElementById(\"eh-progress-bar\");\n    const ehProgText = document.getElementById(\"eh-progress-text\");\n    const ehProgPct = document.getElementById(\"eh-progress-percent\");<\/p>\n<p>    let ehMode = \"search\";\n    let ehCache = new Map();\n    let ehIntern = new Map();\n    let ehLiveId = null;\n    let ehNewest = null;\n    let ehGen = 0;\n    let ehLast = null;<\/p>\n<p>    const ehAttr = (s) => esc(s).replace(\/\"\/g, \"&quot;\").replace(\/'\/g, \"&#39;\");\n    const ehFmt = (s, o) => String(s).replace(\/\\{(\\w+)\\}\/g, (m, k) => (o && k in o ? o[k] : m));\n    const ehTypeName = (t) => T.types[t] || t;\n    const ehNumSort = (a, b) => Number(a.id) - Number(b.id);<\/p>\n<p>    const ehDate = (fp) => {\n        if (!\/^\\d{13}$\/.test(String(fp || \"\"))) return \"\";\n        const n = Number(fp);\n        if (n < 1325376000000 || n > Date.now() + 86400000) return \"\";\n        try { return new Date(n).toLocaleString(EH_LOCALE, { dateStyle: \"medium\", timeStyle: \"short\" }); }\n        catch (e) { return new Date(n).toISOString(); }\n    };<\/p>\n<p>    \/\/ Parameter lists [{type,key,value}] become objects keyed by \"key\", so a diff names\n    \/\/ \"parameter.html.value\" instead of a position that shifts when a parameter is added.\n    const ehNorm = (v, top) => {\n        if (Array.isArray(v)) {\n            const keyed = v.length > 0 && v.every(x => x && typeof x === \"object\" && !Array.isArray(x) && typeof x.key === \"string\");\n            if (keyed && new Set(v.map(x => x.key)).size === v.length) {\n                const o = {};\n                v.slice().sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0)).forEach(x => {\n                    const item = Object.assign({}, x);\n                    delete item.key;\n                    o[x.key] = ehNorm(item, false);\n                });\n                return o;\n            }\n            return v.map(x => ehNorm(x, false));\n        }\n        if (v && typeof v === \"object\") {\n            const o = {};\n            Object.keys(v).sort().forEach(k => { if (!(top && EH_IGNORE[k])) o[k] = ehNorm(v[k], false); });\n            return o;\n        }\n        return v;\n    };<\/p>\n<p>    const ehFlat = (v, p, out) => {\n        if (v && typeof v === \"object\") {\n            const isArr = Array.isArray(v);\n            const ks = isArr ? v.map((x, i) => i) : Object.keys(v);\n            if (!ks.length) { out.set(p || \"value\", isArr ? \"[]\" : \"{}\"); return out; }\n            ks.forEach(k => ehFlat(v[k], isArr ? p + \"[\" + k + \"]\" : (p ? p + \".\" + k : k), out));\n        } else {\n            out.set(p || \"value\", v === undefined || v === null ? \"\" : String(v));\n        }\n        return out;\n    };<\/p>\n<p>    const ehRecord = (vh, ver) => {\n        const rec = {\n            id: String(vh.containerVersionId), name: ver.name || vh.name || \"\", desc: ver.description || \"\",\n            fp: ver.fingerprint || \"\", url: ver.tagManagerUrl || \"\", ok: true, items: {}\n        };\n        Object.keys(EH_TYPES).forEach(type => {\n            const list = ver[type] || [];\n            const m = new Map();\n            list.forEach(it => {\n                const id = String(it[EH_TYPES[type]] == null ? \"\" : it[EH_TYPES[type]]);\n                if (!id) return;\n                const j = JSON.stringify(ehNorm(it, true));\n                const f = String(it.fingerprint || \"\");\n                const key = f + \"\\u0000\" + j;\n                let entry = ehIntern.get(key);\n                if (!entry) { entry = { n: String(it.name || \"\"), f: f, j: j }; ehIntern.set(key, entry); }\n                m.set(id, entry);\n            });\n            rec.items[type] = m;\n        });\n        return rec;\n    };<\/p>\n<p>    const ehFetch = async (vh, gen) => {\n        if (ehCache.has(vh.path)) return ehCache.get(vh.path);\n        let rec;\n        try {\n            const ver = await gtmApi(vh.path);\n            rec = ehRecord(vh, ver);\n            if (gen === ehGen) ehCache.set(vh.path, rec);\n        } catch (e) {\n            rec = { id: String(vh.containerVersionId), name: vh.name || \"\", ok: false, err: e.message, items: {} };\n        }\n        return rec;\n    };<\/p>\n<p>    const ehShowMode = (mode) => {\n        ehMode = mode;\n        const history = mode === \"history\";\n        document.getElementById(\"search-controls-panel\").style.display = history ? \"none\" : \"block\";\n        resultsEl.style.display = history ? \"none\" : \"block\";\n        ehPanel.style.display = history ? \"block\" : \"none\";\n        ehResults.style.display = history ? \"block\" : \"none\";\n        ehTabs.querySelectorAll(\".gtm-mode-tab\").forEach(btn => {\n            const active = btn.getAttribute(\"data-mode\") === mode;\n            btn.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n            btn.style.background = active ? \"var(--accent)\" : \"transparent\";\n            btn.style.color = active ? \"var(--on-accent)\" : \"var(--accent)\";\n        });\n    };\n    ehTabs.querySelectorAll(\".gtm-mode-tab\").forEach(btn => btn.addEventListener(\"click\", () => ehShowMode(btn.getAttribute(\"data-mode\"))));<\/p>\n<p>    const ehFillList = () => {\n        ehList.innerHTML = \"\";\n        if (!ehNewest) return;\n        const sel = ehType.value;\n        const types = sel === \"any\" ? Object.keys(EH_TYPES) : [sel];\n        const seen = new Set();\n        let count = 0;\n        types.forEach(t => {\n            const m = ehNewest.items[t];\n            if (!m) return;\n            m.forEach((it, id) => {\n                if (count >= 3000 || !it.n || seen.has(t + it.n)) return;\n                seen.add(t + it.n);\n                const opt = document.createElement(\"option\");\n                opt.value = it.n;\n                opt.label = ehTypeName(t) + (t === \"builtInVariable\" ? \"\" : \" \u00b7 ID \" + id);\n                ehList.appendChild(opt);\n                count++;\n            });\n        });\n    };\n    ehType.addEventListener(\"change\", ehFillList);<\/p>\n<p>    \/\/ Called by the container selector once the version headers are loaded.\n    const ehContainerReady = async () => {\n        const gen = ++ehGen;\n        ehCache = new Map();\n        ehIntern = new Map();\n        ehLiveId = null;\n        ehNewest = null;\n        ehLast = null;\n        ehResults.innerHTML = \"\";\n        ehList.innerHTML = \"\";\n        ehTabs.style.display = \"flex\";\n        ehShowMode(ehMode);\n        const base = \"accounts\/\" + accSelect.value + \"\/containers\/\" + contSelect.value;\n        try {\n            const live = await gtmApi(base + \"\/versions:live\");\n            if (gen === ehGen && live && live.containerVersionId != null) ehLiveId = String(live.containerVersionId);\n        } catch (e) { \/* container was never published *\/ }\n        if (gen !== ehGen || !versionHeaders.length) return;\n        ehStatus.textContent = T.loadingList;\n        let rec = null;\n        for (let i = 0; i < Math.min(3, versionHeaders.length) &#038;&#038; !(rec &#038;&#038; rec.ok); i++) {\n            rec = await ehFetch(versionHeaders[i], gen);\n            if (gen !== ehGen) return;\n        }\n        if (rec.ok) {\n            ehNewest = rec;\n            ehFillList();\n            ehStatus.textContent = ehFmt(T.listReady, { v: rec.id });\n        } else {\n            ehStatus.textContent = \"\";\n        }\n    };\n\n    const ehParse = (raw) => {\n        const q = raw.trim();\n        const r = { q: q, type: ehType.value, id: null, warn: \"\" };\n        const m = q.match(\/\\\/(tags|triggers|variables|templates|folders|clients|transformations|zones)\\\/(\\d+)\/);\n        if (m) {\n            r.type = EH_URL_TYPES[m[1]];\n            r.id = m[2];\n            const c = q.match(\/accounts\\\/(\\d+)\\\/containers\\\/(\\d+)\/);\n            if (c && (c[1] !== accSelect.value || c[2] !== contSelect.value)) r.warn = T.urlOtherContainer;\n        }\n        return r;\n    };<\/p>\n<p>    const ehByName = (rec, type) => {\n        rec._byName = rec._byName || {};\n        if (!rec._byName[type]) {\n            const m = new Map();\n            (rec.items[type] || new Map()).forEach((it, id) => { if (!m.has(it.n)) m.set(it.n, id); });\n            rec._byName[type] = m;\n        }\n        return rec._byName[type];\n    };<\/p>\n<p>    \/\/ Triggers of a tag, the template behind a cvt_ type and every {{variable}}, followed\n    \/\/ through variables and triggers until no new reference appears.\n    const ehDeps = (rec, type, id, entry) => {\n        const out = new Map();\n        const queue = [{ t: type, e: entry }];\n        const add = (dt, did, label) => {\n            const key = dt + \":\" + did;\n            if (out.has(key) || (dt === type && did === id)) return;\n            const d = rec.items[dt] && rec.items[dt].get(did);\n            out.set(key, d ? { t: dt, id: did, n: d.n, j: d.j } : { t: dt, id: did, n: label || did, j: null });\n            if (d && dt !== \"builtInVariable\") queue.push({ t: dt, e: d });\n        };\n        while (queue.length) {\n            const cur = queue.shift();\n            let obj;\n            try { obj = JSON.parse(cur.e.j); } catch (e) { continue; }\n            if (cur.t === \"tag\") {\n                [].concat(obj.firingTriggerId || [], obj.blockingTriggerId || []).forEach(x => add(\"trigger\", String(x)));\n            }\n            if (\/^cvt_\/.test(String(obj.type || \"\"))) {\n                (rec.items.customTemplate || new Map()).forEach((tpl, tid) => { if (String(obj.type).endsWith(\"_\" + tid)) add(\"customTemplate\", tid); });\n            }\n            const re = \/\\{\\{([^{}]+)\\}\\}\/g;\n            let m;\n            while ((m = re.exec(cur.e.j))) {\n                const name = m[1].trim();\n                const vid = ehByName(rec, \"variable\").get(name);\n                if (vid) { add(\"variable\", vid); continue; }\n                const bid = ehByName(rec, \"builtInVariable\").get(name);\n                if (bid) { add(\"builtInVariable\", bid); continue; }\n                add(\"variable\", \"?\" + name, name);\n            }\n        }\n        return out;\n    };<\/p>\n<p>    const ehDiff = (aj, bj) => {\n        const a = ehFlat(aj ? JSON.parse(aj) : {}, \"\", new Map());\n        const b = ehFlat(bj ? JSON.parse(bj) : {}, \"\", new Map());\n        const rows = [];\n        new Set([...a.keys(), ...b.keys()]).forEach(p => {\n            const x = a.has(p) ? a.get(p) : null;\n            const y = b.has(p) ? b.get(p) : null;\n            if (x !== y) rows.push({ path: p, before: x, after: y });\n        });\n        return rows.sort((r1, r2) => (r1.path < r2.path ? -1 : r1.path > r2.path ? 1 : 0));\n    };<\/p>\n<p>    const ehDepDiff = (a, b) => {\n        const out = [];\n        new Set([...a.keys(), ...b.keys()]).forEach(k => {\n            const x = a.get(k), y = b.get(k);\n            if (x && !y) out.push({ kind: \"depRemoved\", t: x.t, id: x.id, n: x.n, missing: !x.j });\n            else if (!x && y) out.push({ kind: \"depAdded\", t: y.t, id: y.id, n: y.n, missing: !y.j });\n            else if (x.j !== y.j) {\n                if (!y.j) out.push({ kind: \"depMissing\", t: y.t, id: y.id, n: y.n, missing: true });\n                else out.push({ kind: \"depChanged\", t: y.t, id: y.id, n: y.n, nBefore: x.n, rows: ehDiff(x.j, y.j) });\n            }\n        });\n        return out;\n    };<\/p>\n<p>    const ehTimeline = (recs, type, id, opts) => {\n        const events = [];\n        let prev = null;\n        let lastPresent = null;\n        let gap = 0;\n        recs.forEach((rec, idx) => {\n            if (!rec.ok) { gap++; return; }\n            const cur = (rec.items[type] && rec.items[type].get(id)) || null;\n            const deps = cur && opts.deps ? ehDeps(rec, type, id, cur) : null;\n            const base = { version: rec, entry: cur, gap: gap, rows: [], deps: [] };\n            if (!prev) {\n                if (cur) events.push(Object.assign(base, { kind: \"baseline\", rows: ehDiff(null, cur.j) }));\n            } else if (!prev.cur && cur) {\n                if (lastPresent) events.push(Object.assign(base, { kind: \"restored\", rows: ehDiff(lastPresent.cur.j, cur.j), since: lastPresent.rec, deps: deps && lastPresent.deps ? ehDepDiff(lastPresent.deps, deps) : [] }));\n                else events.push(Object.assign(base, { kind: \"created\", rows: ehDiff(null, cur.j) }));\n            } else if (prev.cur && !cur) {\n                events.push(Object.assign(base, { kind: \"removed\", entry: prev.cur }));\n            } else if (prev.cur && cur) {\n                const rows = prev.cur.j !== cur.j ? ehDiff(prev.cur.j, cur.j) : [];\n                const depRows = deps && prev.deps ? ehDepDiff(prev.deps, deps) : [];\n                const renamed = prev.cur.n !== cur.n;\n                if (rows.length || depRows.length) {\n                    events.push(Object.assign(base, {\n                        kind: renamed ? \"renamed\" : rows.length ? \"changed\" : \"depChanged\",\n                        rows: rows, deps: depRows, oldName: renamed ? prev.cur.n : \"\"\n                    }));\n                } else if (opts.touched && prev.cur.f !== cur.f) {\n                    events.push(Object.assign(base, { kind: \"touched\" }));\n                }\n            }\n            if (cur) lastPresent = { cur: cur, rec: rec, deps: deps };\n            prev = { cur: cur, deps: deps, rec: rec };\n            gap = 0;\n        });\n        return events;\n    };<\/p>\n<p>    const ehDrafts = async (keys, newestRec) => {\n        const out = [];\n        const base = \"accounts\/\" + accSelect.value + \"\/containers\/\" + contSelect.value;\n        const ws = await gtmApi(base + \"\/workspaces\");\n        for (const w of (ws.workspace || [])) {\n            const st = await gtmApi(w.path + \"\/status\");\n            (st.workspaceChange || []).forEach(ch => {\n                Object.keys(EH_TYPES).forEach(t => {\n                    if (!ch[t]) return;\n                    const id = String(ch[t][EH_TYPES[t]] == null ? \"\" : ch[t][EH_TYPES[t]]);\n                    if (!keys.has(t + \":\" + id)) return;\n                    const before = newestRec && newestRec.items[t] && newestRec.items[t].get(id);\n                    const j = JSON.stringify(ehNorm(ch[t], true));\n                    out.push({\n                        kind: \"draft\", t: t, id: id, workspace: w.name || w.workspaceId, status: ch.changeStatus || \"\",\n                        fpEntity: ch[t].fingerprint || \"\", name: ch[t].name || \"\",\n                        rows: ch.changeStatus === \"deleted\" ? [] : ehDiff(before ? before.j : null, j)\n                    });\n                });\n            });\n        }\n        return out;\n    };<\/p>\n<p>    \/\/ Line diff for code fields (Custom HTML, template code): common start and end are cut off,\n    \/\/ the middle is compared line by line.\n    const ehLineDiff = (a, b) => {\n        const A = a.split(\"\\n\"), B = b.split(\"\\n\");\n        let s = 0;\n        while (s < A.length &#038;&#038; s < B.length &#038;&#038; A[s] === B[s]) s++;\n        let e = 0;\n        while (e < A.length - s &#038;&#038; e < B.length - s &#038;&#038; A[A.length - 1 - e] === B[B.length - 1 - e]) e++;\n        const x = A.slice(s, A.length - e), y = B.slice(s, B.length - e);\n        let ops = [];\n        if (x.length * y.length <= 250000) {\n            const n = x.length, m = y.length, w = m + 1;\n            const L = new Uint32Array((n + 1) * w);\n            for (let i = n - 1; i >= 0; i--) for (let j = m - 1; j >= 0; j--) {\n                L[i * w + j] = x[i] === y[j] ? L[(i + 1) * w + j + 1] + 1 : Math.max(L[(i + 1) * w + j], L[i * w + j + 1]);\n            }\n            let i = 0, j = 0;\n            while (i < n &#038;&#038; j < m) {\n                if (x[i] === y[j]) { ops.push([\" \", x[i]]); i++; j++; }\n                else if (L[(i + 1) * w + j] >= L[i * w + j + 1]) { ops.push([\"-\", x[i]]); i++; }\n                else { ops.push([\"+\", y[j]]); j++; }\n            }\n            while (i < n) ops.push([\"-\", x[i++]]);\n            while (j < m) ops.push([\"+\", y[j++]]);\n        } else {\n            ops = x.map(l => [\"-\", l]).concat(y.map(l => [\"+\", l]));\n        }\n        const before = A.slice(Math.max(0, s - 2), s).map(l => [\" \", l]);\n        const after = A.slice(A.length - e, A.length - e + 2).map(l => [\" \", l]);\n        return { start: Math.max(0, s - 2) + 1, ops: before.concat(ops, after) };\n    };<\/p>\n<p>    const ehMark = (a, b) => {\n        let s = 0;\n        while (s < a.length &#038;&#038; s < b.length &#038;&#038; a[s] === b[s]) s++;\n        let e = 0;\n        while (e < a.length - s &#038;&#038; e < b.length - s &#038;&#038; a[a.length - 1 - e] === b[b.length - 1 - e]) e++;\n        const part = (str, color) => esc(str.slice(0, s)) + \"<mark style='background:\" + color + \"; color:inherit; border-radius:2px;'>\" + esc(str.slice(s, str.length - e)) + \"<\/mark>\" + esc(str.slice(str.length - e));\n        return [part(a, \"color-mix(in srgb, var(--fail) 30%, transparent)\"), part(b, \"color-mix(in srgb, var(--ok) 30%, transparent)\")];\n    };<\/p>\n<p>    const ehRefName = (path, value, rec) => {\n        if (value == null || value === \"\" || !rec) return \"\";\n        let t = null;\n        if (\/(^|\\.)(firingTriggerId|blockingTriggerId)\\[\\d+\\]$\/.test(path)) t = \"trigger\";\n        else if (\/(^|\\.)parentFolderId$\/.test(path)) t = \"folder\";\n        if (t) {\n            const it = rec.items[t] && rec.items[t].get(value);\n            return it ? it.n : \"\";\n        }\n        if (path === \"type\" && \/^cvt_\/.test(value)) {\n            let n = \"\";\n            (rec.items.customTemplate || new Map()).forEach((tpl, tid) => { if (value.endsWith(\"_\" + tid)) n = tpl.n; });\n            return n;\n        }\n        return \"\";\n    };<\/p>\n<p>    const ehRowsHtml = (rows, recBefore, recAfter) => {\n        if (!rows.length) return \"\";\n        const box = \"background:var(--bg-panel); border:1px solid var(--border); border-radius:4px; padding:6px 8px; font-family:monospace; font-size:0.82rem; white-space:pre-wrap; overflow-wrap:anywhere; margin:0;\";\n        return rows.slice(0, 200).map(r => {\n            const head = \"<\/p>\n<div style='color:var(--accent); font-family:monospace; font-size:0.8rem; margin:10px 0 4px; overflow-wrap:anywhere;'>\" + esc(r.path) + \"<\/div>\n<p>\";\n            const bRef = ehRefName(r.path, r.before, recBefore), aRef = ehRefName(r.path, r.after, recAfter);\n            const ref = (n) => (n ? \" <span style='color:var(--text-secondary);'>(\" + esc(n) + \")<\/span>\" : \"\");\n            const before = r.before == null ? null : r.before, after = r.after == null ? null : r.after;\n            if (before != null && after != null && (before.indexOf(\"\\n\") >= 0 || after.indexOf(\"\\n\") >= 0)) {\n                const d = ehLineDiff(before, after);\n                const lines = d.ops.slice(0, 400).map(op => {\n                    const bg = op[0] === \"-\" ? \"color-mix(in srgb, var(--fail) 18%, transparent)\" : op[0] === \"+\" ? \"color-mix(in srgb, var(--ok) 18%, transparent)\" : \"transparent\";\n                    return \"<\/p>\n<div style='background:\" + bg + \";'>\" + esc(op[0] + \" \" + op[1]) + \"<\/div>\n<p>\";\n                }).join(\"\");\n                const more = d.ops.length > 400 ? \"<\/p>\n<div style='color:var(--text-secondary);'>\" + esc(ehFmt(T.moreLines, { n: d.ops.length - 400 })) + \"<\/div>\n<p>\" : \"\";\n                return head + \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.78rem; margin-bottom:3px;'>\" + esc(ehFmt(T.fromLine, { n: d.start })) + \"<\/div>\n<div style='\" + box + \" white-space:pre; overflow-x:auto; overflow-wrap:normal;'>\" + lines + more + \"<\/div>\n<p>\";\n            }\n            let bHtml, aHtml;\n            if (before != null && after != null) {\n                const cut = (s) => (s.length > 2000 ? s.slice(0, 2000) + \" \u2026\" : s);\n                [bHtml, aHtml] = ehMark(cut(before), cut(after));\n            } else {\n                const cut = (s) => esc(s.length > 2000 ? s.slice(0, 2000) + \" \u2026\" : s);\n                bHtml = before == null ? \"<em style='color:var(--text-secondary);'>\" + esc(T.absent) + \"<\/em>\" : cut(before);\n                aHtml = after == null ? \"<em style='color:var(--text-secondary);'>\" + esc(T.absent) + \"<\/em>\" : cut(after);\n            }\n            return head + \"<\/p>\n<div style='display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:6px;'>\" +\n                \"<\/p>\n<div>\n<div style='color:var(--fail); font-size:0.75rem; font-weight:700;'>\" + esc(T.before) + \"<\/div>\n<pre style='\" + box + \"'>\" + bHtml + ref(bRef) + \"<\/pre>\n<\/div>\n<p>\" +\n                \"<\/p>\n<div>\n<div style='color:var(--ok); font-size:0.75rem; font-weight:700;'>\" + esc(T.after) + \"<\/div>\n<pre style='\" + box + \"'>\" + aHtml + ref(aRef) + \"<\/pre>\n<\/div>\n<\/div>\n<p>\";\n        }).join(\"\") + (rows.length > 200 ? \"<\/p>\n<div style='color:var(--text-secondary); margin-top:8px;'>\" + esc(ehFmt(T.moreFields, { n: rows.length - 200 })) + \"<\/div>\n<p>\" : \"\");\n    };<\/p>\n<p>    \/\/ Full configuration of a new element: one line per field instead of before\/after pairs.\n    const ehConfigHtml = (rows, rec) => \"<\/p>\n<div style='background:var(--bg-panel); border:1px solid var(--border); border-radius:4px; padding:8px 10px; margin-top:8px; font-family:monospace; font-size:0.82rem; overflow-x:auto;'>\" +\n        rows.map(r => {\n            const v = r.after == null ? \"\" : r.after;\n            const n = ehRefName(r.path, v, rec);\n            return \"<\/p>\n<div style='display:flex; gap:12px; flex-wrap:wrap; padding:3px 0; border-bottom:1px solid var(--border);'><span style='color:var(--accent); min-width:220px;'>\" + esc(r.path) + \"<\/span><span style='white-space:pre-wrap; overflow-wrap:anywhere; flex:1; min-width:180px; color:var(--text-primary);'>\" + esc(v.length > 4000 ? v.slice(0, 4000) + \" \u2026\" : v) + (n ? \" <span style='color:var(--text-secondary);'>(\" + esc(n) + \")<\/span>\" : \"\") + \"<\/span><\/div>\n<p>\";\n        }).join(\"\") + \"<\/p><\/div>\n<p>\";<\/p>\n<p>    const ehBadge = (text, color) => \"<span style='display:inline-block; border:1px solid \" + color + \"; color:\" + color + \"; padding:2px 8px; border-radius:4px; font-size:0.72rem; font-weight:700; letter-spacing:0.04em; text-transform:uppercase;'>\" + esc(text) + \"<\/span>\";<\/p>\n<p>    const ehDepHtml = (deps, recBefore, recAfter) => {\n        if (!deps.length) return \"\";\n        return \"<\/p>\n<div style='margin-top:14px; border-top:1px dashed var(--border); padding-top:10px;'>\n<div style='color:var(--text-primary); font-weight:700; font-size:0.88rem;'>\" + esc(T.depsTitle) + \"<\/div>\n<p>\" +\n            deps.map(d => {\n                const label = ehTypeName(d.t) + \" \u00b7 \" + (d.n || d.id) + (d.id.charAt(0) === \"?\" || d.t === \"builtInVariable\" ? \"\" : \" (ID \" + d.id + \")\");\n                const text = T[d.kind];\n                const inner = d.kind === \"depChanged\"\n                    ? \"<\/p>\n<details style='margin-top:4px;'>\n<summary style='cursor:pointer; color:var(--text-secondary); font-size:0.85rem;'>\" + esc(ehFmt(T.fieldsChanged, { n: d.rows.length })) + \"<\/summary>\n<p>\" + ehRowsHtml(d.rows, recBefore, recAfter) + \"<\/details>\n<p>\"\n                    : \"\";\n                return \"<\/p>\n<div style='margin-top:8px; font-size:0.88rem; color:var(--text-secondary);'>\" + ehBadge(text, d.kind === \"depAdded\" ? \"var(--ok)\" : d.kind === \"depChanged\" ? \"var(--lw-tool-warn)\" : \"var(--fail)\") + \" <strong style='color:var(--text-primary);'>\" + esc(label) + \"<\/strong>\" + (d.missing ? \" \u2013 \" + esc(T.depNotInContainer) : \"\") + inner + \"<\/div>\n<p>\";\n            }).join(\"\") + \"<\/p><\/div>\n<p>\";\n    };<\/p>\n<p>    const ehVersionLink = (rec) => (\/^https:\\\/\\\/tagmanager\\.google\\.com\\\/\/.test(rec.url || \"\") ? \" \u00b7 <a href='\" + ehAttr(rec.url) + \"' target='_blank' rel='noopener noreferrer'>\" + esc(T.openInGtm) + \"<\/a>\" : \"\");<\/p>\n<p>    const ehEventHtml = (ev, prevRecOf) => {\n        const rec = ev.version;\n        const color = EH_EVENT_COLORS[ev.kind];\n        const verDate = ehDate(rec.fp);\n        const entDate = ev.entry && ev.kind !== \"removed\" ? ehDate(ev.entry.f) : \"\";\n        const live = ehLiveId === rec.id ? \" \" + ehBadge(T.live, \"var(--ok)\") : \"\";\n        const title = ev.kind === \"renamed\" ? ehFmt(T.ev_renamed, { from: ev.oldName, to: ev.entry.n }) : T[\"ev_\" + ev.kind];\n        const meta = [];\n        if (verDate) meta.push(esc(T.versionDate) + \": <strong style='color:var(--text-primary);'>\" + esc(verDate) + \"<\/strong>\");\n        if (entDate) meta.push(esc(T.elementDate) + \": \" + esc(entDate));\n        if (ev.since) meta.push(esc(ehFmt(T.absentSince, { v: ev.since.id })));\n        if (ev.gap) meta.push(\"<span style='color:var(--lw-tool-warn);'>\" + esc(ehFmt(T.gapNote, { n: ev.gap })) + \"<\/span>\");\n        const rowsHtml = ev.rows.length\n            ? (ev.kind === \"baseline\" || ev.kind === \"created\"\n                ? \"<\/p>\n<details style='margin-top:10px;'>\n<summary style='cursor:pointer; color:var(--text-secondary); font-size:0.85rem;'>\" + esc(ehFmt(T.showConfig, { n: ev.rows.length })) + \"<\/summary>\n<p>\" + ehConfigHtml(ev.rows, rec) + \"<\/details>\n<p>\"\n                : \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.85rem; margin-top:10px;'>\" + esc(ehFmt(T.fieldsChanged, { n: ev.rows.length })) + \"<\/div>\n<p>\" + ehRowsHtml(ev.rows, prevRecOf(rec), rec))\n            : \"\";\n        return \"<\/p>\n<div style='background:var(--bg-body); border:1px solid var(--border); border-left:3px solid \" + color + \"; border-radius:8px; padding:16px 18px; margin-bottom:12px;'>\" +\n            \"<\/p>\n<div style='display:flex; justify-content:space-between; gap:10px; flex-wrap:wrap; align-items:center;'>\" +\n            \"<\/p>\n<div style='display:flex; gap:10px; align-items:center; flex-wrap:wrap;'>\" + ehBadge(T[\"k_\" + ev.kind], color) + \"<strong style='color:var(--text-primary);'>\" + esc(title) + \"<\/strong><\/div>\n<p>\" +\n            \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.85rem;'>\" + esc(T.version) + \" <strong style='color:var(--accent);'>v\" + esc(rec.id) + \"<\/strong>\" + (rec.name ? \" (\" + esc(rec.name) + \")\" : \"\") + live + ehVersionLink(rec) + \"<\/div>\n<\/div>\n<p>\" +\n            (meta.length ? \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.84rem; margin-top:8px; display:flex; gap:6px 16px; flex-wrap:wrap;'>\" + meta.map(x => \"<span>\" + x + \"<\/span>\").join(\"\") + \"<\/div>\n<p>\" : \"\") +\n            (rec.desc ? \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.84rem; margin-top:6px; white-space:pre-wrap; overflow-wrap:anywhere;'><strong>\" + esc(T.versionNotes) + \":<\/strong> \" + esc(rec.desc.slice(0, 1000)) + \"<\/div>\n<p>\" : \"\") +\n            rowsHtml + ehDepHtml(ev.deps || [], prevRecOf(rec), rec) + \"<\/p><\/div>\n<p>\";\n    };<\/p>\n<p>    const ehDraftHtml = (d) => {\n        const color = EH_EVENT_COLORS.draft;\n        const entDate = ehDate(d.fpEntity);\n        const st = T[\"ws_\" + d.status] || d.status;\n        return \"<\/p>\n<div style='background:var(--bg-body); border:1px dashed \" + color + \"; border-radius:8px; padding:16px 18px; margin-bottom:12px;'>\" +\n            \"<\/p>\n<div style='display:flex; gap:10px; align-items:center; flex-wrap:wrap;'>\" + ehBadge(T.k_draft, color) + \"<strong style='color:var(--text-primary);'>\" + esc(ehFmt(T.ev_draft, { ws: d.workspace, status: st })) + \"<\/strong><\/div>\n<p>\" +\n            (entDate ? \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.84rem; margin-top:8px;'>\" + esc(T.elementDate) + \": \" + esc(entDate) + \"<\/div>\n<p>\" : \"\") +\n            (d.rows.length ? \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.85rem; margin-top:10px;'>\" + esc(ehFmt(T.draftCompared, { n: d.rows.length, v: ehLast.newest.id })) + \"<\/div>\n<p>\" + ehRowsHtml(d.rows, ehLast.newest, ehLast.newest) : \"\") +\n            \"<\/p><\/div>\n<p>\";\n    };<\/p>\n<p>    const ehDownload = (name, mime, text) => {\n        const url = URL.createObjectURL(new Blob([text], { type: mime }));\n        const a = document.createElement(\"a\");\n        a.href = url;\n        a.download = name;\n        document.body.appendChild(a);\n        a.click();\n        a.remove();\n        setTimeout(() => URL.revokeObjectURL(url), 2000);\n    };<\/p>\n<p>    const ehExportData = () => {\n        const L = ehLast;\n        return {\n            tool: \"GTM element change history \u2013 lukaswojcik.com\",\n            created: new Date().toISOString(),\n            account: L.account, container: L.container, liveVersion: ehLiveId,\n            scannedVersions: L.scanned, unreadableVersions: L.unreadable,\n            elements: L.histories.map(h => ({\n                type: h.type, id: h.id, name: h.name, names: h.names,\n                events: h.events.map(ev => ({\n                    event: ev.kind, version: ev.version.id, versionName: ev.version.name, versionNotes: ev.version.desc,\n                    versionSaved: ehDate(ev.version.fp), elementSaved: ev.entry ? ehDate(ev.entry.f) : \"\",\n                    previousName: ev.oldName || undefined, unreadableVersionsBefore: ev.gap || undefined,\n                    fields: ev.rows, dependencies: (ev.deps || []).map(d => ({ change: d.kind, type: d.t, id: d.id, name: d.n, fields: d.rows || [] }))\n                })),\n                drafts: h.drafts.map(d => ({ workspace: d.workspace, status: d.status, elementSaved: ehDate(d.fpEntity), fields: d.rows }))\n            }))\n        };\n    };<\/p>\n<p>    const ehCsv = () => {\n        const q = (v) => '\"' + String(v == null ? \"\" : v).replace(\/\"\/g, '\"\"') + '\"';\n        const lines = [[\"element_type\", \"element_id\", \"element_name\", \"version\", \"version_name\", \"version_saved\", \"event\", \"field\", \"before\", \"after\"].map(q).join(\",\")];\n        ehLast.histories.forEach(h => {\n            h.events.forEach(ev => {\n                const base = [h.type, h.id, ev.entry ? ev.entry.n : h.name, ev.version.id, ev.version.name, ehDate(ev.version.fp), ev.kind];\n                const rows = ev.rows.length ? ev.rows : [{ path: \"\", before: \"\", after: \"\" }];\n                rows.forEach(r => lines.push(base.concat([r.path, r.before, r.after]).map(q).join(\",\")));\n                (ev.deps || []).forEach(d => {\n                    (d.rows && d.rows.length ? d.rows : [{ path: \"\", before: \"\", after: \"\" }]).forEach(r => {\n                        lines.push(base.concat([d.kind + \" \" + d.t + \":\" + d.id + \" \" + d.n + (r.path ? \" \u2192 \" + r.path : \"\"), r.before, r.after]).map(q).join(\",\"));\n                    });\n                });\n            });\n            h.drafts.forEach(d => {\n                (d.rows.length ? d.rows : [{ path: \"\", before: \"\", after: \"\" }]).forEach(r => {\n                    lines.push([h.type, h.id, d.name || h.name, \"workspace\", d.workspace, ehDate(d.fpEntity), \"draft_\" + d.status, r.path, r.before, r.after].map(q).join(\",\"));\n                });\n            });\n        });\n        return \"\ufeff\" + lines.join(\"\\r\\n\");\n    };<\/p>\n<p>    const ehRender = () => {\n        const L = ehLast;\n        const rangeNote = L.isAll ? \"\" : \" \" + esc(T.rangeHint);\n        const summaryHead = \"<\/p>\n<div style='background:var(--bg-body); border:1px solid var(--border); border-radius:8px; padding:16px 18px; margin-bottom:18px; color:var(--text-secondary); font-size:0.9rem; line-height:1.6;'>\" +\n            \"<\/p>\n<div><strong style='color:var(--text-primary);'>\" + esc(T.account) + \":<\/strong> \" + esc(L.account.name) + \" (\" + esc(L.account.id) + \") \u00b7 <strong style='color:var(--text-primary);'>\" + esc(T.container) + \":<\/strong> \" + esc(L.container.name) + \" (\" + esc(L.container.publicId) + \")<\/div>\n<p>\" +\n            \"<\/p>\n<div><strong style='color:var(--text-primary);'>\" + esc(T.scanned) + \":<\/strong> \" + esc(ehFmt(T.scannedText, { n: L.scanned, from: L.from, to: L.to })) + (L.unreadable ? \" \u00b7 <span style='color:var(--lw-tool-warn);'>\" + esc(ehFmt(T.unreadable, { n: L.unreadable })) + \"<\/span>\" : \"\") + rangeNote + \"<\/div>\n<p>\" +\n            \"<\/p>\n<div><strong style='color:var(--text-primary);'>\" + esc(T.liveVersion) + \":<\/strong> \" + (ehLiveId ? \"v\" + esc(ehLiveId) : esc(T.noLive)) + \"<\/div>\n<p>\" +\n            (L.warn ? \"<\/p>\n<div style='color:var(--lw-tool-warn);'>\" + esc(L.warn) + \"<\/div>\n<p>\" : \"\") +\n            (L.draftError ? \"<\/p>\n<div style='color:var(--lw-tool-warn);'>\" + esc(T.draftError) + \" \" + esc(L.draftError) + \"<\/div>\n<p>\" : \"\") +\n            \"<\/p>\n<div style='margin-top:10px; display:flex; gap:10px; flex-wrap:wrap;'>\" +\n            \"<button type='button' id='eh-export-json' class='button' style='background:transparent; color:var(--accent); border:1px solid var(--accent); padding:7px 14px; border-radius:6px; cursor:pointer; font-weight:700;'>\" + esc(T.exportJson) + \"<\/button>\" +\n            \"<button type='button' id='eh-export-csv' class='button' style='background:transparent; color:var(--accent); border:1px solid var(--accent); padding:7px 14px; border-radius:6px; cursor:pointer; font-weight:700;'>\" + esc(T.exportCsv) + \"<\/button><\/div>\n<\/div>\n<p>\";<\/p>\n<p>        const okRecs = L.recs.filter(r => r.ok);\n        const prevRecOf = (rec) => { const i = okRecs.indexOf(rec); return i > 0 ? okRecs[i - 1] : null; };<\/p>\n<p>        const blocks = L.histories.map(h => {\n            const changes = h.events.filter(ev => ev.kind !== \"baseline\" && ev.kind !== \"touched\");\n            const lastChange = changes.length ? changes[changes.length - 1] : null;\n            const first = h.events[0];\n            const inLive = ehLiveId && L.recs.some(r => r.ok && r.id === ehLiveId && r.items[h.type] && r.items[h.type].has(h.id));\n            const facts = [\n                [T.elementId, h.id],\n                [T.firstSeen, first ? \"v\" + first.version.id + (first.kind === \"baseline\" ? \" \u2013 \" + T.beforeRange : \"\") + (ehDate(first.version.fp) ? \" \u00b7 \" + ehDate(first.version.fp) : \"\") : \"\u2013\"],\n                [T.lastChange, lastChange ? \"v\" + lastChange.version.id + \" \u00b7 \" + T[\"k_\" + lastChange.kind] + (ehDate(lastChange.version.fp) ? \" \u00b7 \" + ehDate(lastChange.version.fp) : \"\") : T.noChange],\n                [T.changeCount, String(changes.length)],\n                [T.inLive, ehLiveId ? (L.liveScanned ? (inLive ? T.yes : T.no) : T.liveOutOfRange) : \"\u2013\"]\n            ];\n            if (h.names.length > 1) facts.push([T.names, h.names.join(\" \u2192 \")]);\n            const head = \"<\/p>\n<div style='margin:22px 0 12px;'>\n<div style='display:flex; gap:10px; align-items:center; flex-wrap:wrap;'>\" + ehBadge(ehTypeName(h.type), \"var(--accent)\") + \"<strong style='color:var(--text-primary); font-size:1.1rem;'>\" + esc(h.name) + \"<\/strong><\/div>\n<p>\" +\n                \"<\/p>\n<div style='display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:8px; margin-top:10px;'>\" +\n                facts.map(f => \"<\/p>\n<div style='background:var(--bg-body); border:1px solid var(--border); border-radius:6px; padding:8px 10px;'>\n<div style='color:var(--text-secondary); font-size:0.75rem;'>\" + esc(f[0]) + \"<\/div>\n<div style='color:var(--text-primary); font-size:0.9rem; overflow-wrap:anywhere;'>\" + esc(f[1]) + \"<\/div>\n<\/div>\n<p>\").join(\"\") +\n                \"<\/p><\/div>\n<\/div>\n<p>\";\n            const drafts = h.drafts.map(ehDraftHtml).join(\"\");\n            const evs = h.events.slice().reverse().map(ev => ehEventHtml(ev, prevRecOf)).join(\"\");\n            return head + drafts + evs;\n        }).join(\"\");<\/p>\n<p>        const note = \"<\/p>\n<div style='color:var(--text-secondary); font-size:0.82rem; line-height:1.55; margin-top:18px; border-top:1px solid var(--border); padding-top:12px;'>\" + esc(T.limits) + \"<\/div>\n<p>\";\n        ehResults.innerHTML = summaryHead + (L.histories.length > 1 ? \"<\/p>\n<div style='color:var(--lw-tool-warn); font-size:0.9rem;'>\" + esc(ehFmt(T.multiMatch, { n: L.histories.length })) + \"<\/div>\n<p>\" : \"\") + blocks + note;\n        const stamp = new Date().toISOString().slice(0, 10);\n        const fname = \"gtm-element-history-\" + (L.container.publicId || \"container\") + \"-\" + stamp;\n        document.getElementById(\"eh-export-json\").addEventListener(\"click\", () => ehDownload(fname + \".json\", \"application\/json\", JSON.stringify(ehExportData(), null, 2)));\n        document.getElementById(\"eh-export-csv\").addEventListener(\"click\", () => ehDownload(fname + \".csv\", \"text\/csv;charset=utf-8\", ehCsv()));\n    };<\/p>\n<p>    const ehRun = async () => {\n        const raw = ehQuery.value;\n        if (!raw.trim()) { ehStatus.textContent = T.needQuery; ehQuery.focus(); return; }\n        if (!versionHeaders.length) { ehStatus.textContent = T.noVersions; return; }\n        const parsed = ehParse(raw);\n        const gen = ehGen;\n        const rangeVal = rangeSelect.value;\n        const toScan = rangeVal === \"all\" ? versionHeaders : versionHeaders.slice(0, Number(rangeVal));\n        const opts = { deps: document.getElementById(\"eh-deps\").checked, drafts: document.getElementById(\"eh-drafts\").checked, touched: document.getElementById(\"eh-touched\").checked };<\/p>\n<p>        ehRunBtn.disabled = true;\n        ehResults.innerHTML = \"\";\n        ehStatus.textContent = \"\";\n        const missing = toScan.filter(vh => !ehCache.has(vh.path));\n        const recsByPath = new Map();\n        toScan.forEach(vh => { if (ehCache.has(vh.path)) recsByPath.set(vh.path, ehCache.get(vh.path)); });\n        let authFailed = false;\n        if (missing.length) {\n            ehProg.style.display = \"block\";\n            let done = 0;\n            const concurrency = 3;\n            for (let i = 0; i < missing.length &#038;&#038; !authFailed; i += concurrency) {\n                await Promise.all(missing.slice(i, i + concurrency).map(async vh => {\n                    const rec = await ehFetch(vh, gen);\n                    if (!rec.ok && \/\\b401\\b\/.test(rec.err || \"\")) authFailed = true;\n                    recsByPath.set(vh.path, rec);\n                    done++;\n                    const pct = Math.round(done \/ missing.length * 100);\n                    ehProgBar.style.width = pct + \"%\";\n                    ehProgPct.textContent = pct + \"%\";\n                    ehProgText.textContent = ehFmt(T.progress, { n: done, total: missing.length });\n                }));\n                if (gen !== ehGen) { ehRunBtn.disabled = false; ehProg.style.display = \"none\"; return; }\n            }\n            ehProg.style.display = \"none\";\n        }\n        ehRunBtn.disabled = false;\n        if (authFailed) { ehStatus.innerHTML = \"<span style='color:var(--fail);'>\" + esc(T.authExpired) + \"<\/span>\"; return; }<\/p>\n<p>        const recs = toScan.map(vh => recsByPath.get(vh.path) || { id: String(vh.containerVersionId), ok: false, items: {} }).sort(ehNumSort);\n        const okRecs = recs.filter(r => r.ok);\n        if (!okRecs.length) { ehStatus.innerHTML = \"<span style='color:var(--fail);'>\" + esc(T.nothingReadable) + \"<\/span>\"; return; }<\/p>\n<p>        const types = parsed.type === \"any\" ? Object.keys(EH_TYPES) : [parsed.type];\n        const lc = parsed.q.toLowerCase();\n        const keys = new Map();\n        okRecs.forEach(rec => types.forEach(t => {\n            const m = rec.items[t];\n            if (!m) return;\n            if (parsed.id) { if (m.has(parsed.id)) keys.set(t + \":\" + parsed.id, { t: t, id: parsed.id }); return; }\n            m.forEach((it, id) => {\n                if (it.n.toLowerCase() === lc || id === parsed.q || (t === \"builtInVariable\" && id.toLowerCase() === lc)) keys.set(t + \":\" + id, { t: t, id: id });\n            });\n        }));<\/p>\n<p>        if (!keys.size) {\n            const sugg = [];\n            const newest = okRecs[okRecs.length - 1];\n            types.forEach(t => (newest.items[t] || new Map()).forEach((it, id) => {\n                if (sugg.length < 12 &#038;&#038; lc &#038;&#038; it.n.toLowerCase().indexOf(lc) >= 0) sugg.push(ehTypeName(t) + \": \" + it.n);\n            }));\n            ehStatus.innerHTML = \"\";\n            ehResults.innerHTML = \"<\/p>\n<div style='background:var(--bg-body); padding:22px; border-radius:6px; border:1px solid var(--border); color:var(--text-secondary);'>\" +\n                esc(ehFmt(T.notFound, { q: parsed.q, n: okRecs.length })) + (parsed.warn ? \"<br \/>\" + esc(parsed.warn) : \"\") +\n                (sugg.length ? \"<\/p>\n<div style='margin-top:10px;'>\" + esc(T.similar) + \"<\/div>\n<ul style='margin:6px 0 0;'>\" + sugg.map(s => \"<\/p>\n<li>\" + esc(s) + \"<\/li>\n<p>\").join(\"\") + \"<\/ul>\n<p>\" : \"\") + \"<\/p><\/div>\n<p>\";\n            return;\n        }<\/p>\n<p>        const list = Array.from(keys.values()).slice(0, 10);\n        const histories = list.map(k => {\n            const events = ehTimeline(recs, k.t, k.id, opts);\n            const names = [];\n            okRecs.forEach(r => { const it = r.items[k.t] && r.items[k.t].get(k.id); if (it && names[names.length - 1] !== it.n) names.push(it.n); });\n            return { type: k.t, id: k.id, name: names[names.length - 1] || k.id, names: names, events: events, drafts: [] };\n        });<\/p>\n<p>        let draftError = \"\";\n        if (opts.drafts) {\n            ehStatus.textContent = T.loadingDrafts;\n            try {\n                const drafts = await ehDrafts(new Set(list.map(k => k.t + \":\" + k.id)), okRecs[okRecs.length - 1]);\n                drafts.forEach(d => { const h = histories.find(x => x.type === d.t && x.id === d.id); if (h) h.drafts.push(d); });\n            } catch (e) { draftError = e.message; }\n            if (gen !== ehGen) return;\n        }<\/p>\n<p>        const cont = containersMap[contSelect.value] || {};\n        ehLast = {\n            recs: recs, histories: histories, newest: okRecs[okRecs.length - 1], warn: parsed.warn, draftError: draftError,\n            account: { id: accSelect.value, name: accountsMap[accSelect.value] || \"\" },\n            container: { id: contSelect.value, name: cont.name || \"\", publicId: cont.publicId || \"\" },\n            scanned: okRecs.length, unreadable: recs.length - okRecs.length, from: \"v\" + okRecs[0].id, to: \"v\" + okRecs[okRecs.length - 1].id,\n            isAll: rangeVal === \"all\" || toScan.length === versionHeaders.length,\n            liveScanned: !!ehLiveId && okRecs.some(r => r.id === ehLiveId)\n        };\n        ehStatus.textContent = keys.size > 10 ? ehFmt(T.tooMany, { n: keys.size }) : \"\";\n        ehRender();\n    };<\/p>\n<p>    ehRunBtn.addEventListener(\"click\", ehRun);\n    ehQuery.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") { e.preventDefault(); ehRun(); } });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Przeszukuje wybrane listy tag\u00f3w, regu\u0142, zmiennych, szablon\u00f3w i folder\u00f3w pobranych wersji GTM oraz pokazuje histori\u0119 zmian pojedynczego elementu. Zachowanie strony, ustawienia centralne i uprawnienia wymagaj\u0105 osobnej kontroli.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":9284,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92133],"class_list":["post-9320","page","type-page","status-publish","hentry","tag-google-tag-manager-pl"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Przeszukiwanie list wersji GTM lub \u015bledzenie jednego tagu, aktywatora albo zmiennej przez kolejne wersje po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Przeszukiwanie list wersji GTM po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony ani nie odtwarza uprawnie\u0144.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-23T13:00:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Przeszukiwanie list wersji GTM po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony ani nie odtwarza uprawnie\u0144.\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/gtm-container-history-deep-search-engine\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/gtm-container-history-deep-search-engine\\\/\",\"name\":\"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-09T13:37:31+00:00\",\"dateModified\":\"2026-10-23T13:00:33+00:00\",\"description\":\"Przeszukiwanie list wersji GTM lub \u015bledzenie jednego tagu, aktywatora albo zmiennej przez kolejne wersje po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/gtm-container-history-deep-search-engine\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/gtm-container-history-deep-search-engine\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/gtm-container-history-deep-search-engine\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/pl\\\/narzedzia\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik","description":"Przeszukiwanie list wersji GTM lub \u015bledzenie jednego tagu, aktywatora albo zmiennej przez kolejne wersje po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/","og_locale":"en_US","og_type":"article","og_title":"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik","og_description":"Przeszukiwanie list wersji GTM po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony ani nie odtwarza uprawnie\u0144.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-10-23T13:00:33+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_description":"Przeszukiwanie list wersji GTM po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony ani nie odtwarza uprawnie\u0144.","twitter_misc":{"Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/","url":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/","name":"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-09T13:37:31+00:00","dateModified":"2026-10-23T13:00:33+00:00","description":"Przeszukiwanie list wersji GTM lub \u015bledzenie jednego tagu, aktywatora albo zmiennej przez kolejne wersje po logowaniu do Google. Historia konfiguracji nie ustala zachowania strony.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/gtm-container-history-deep-search-engine\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/pl\/narzedzia\/"},{"@type":"ListItem","position":3,"name":"Historia kontenera GTM i wyszukiwanie pog\u0142\u0119bione"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9320","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=9320"}],"version-history":[{"count":6,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9320\/revisions"}],"predecessor-version":[{"id":27565,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9320\/revisions\/27565"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9284"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=9320"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=9320"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}