Update gtm.js
Unused variable, import, function or class
This commit is contained in:
@@ -19,322 +19,320 @@
|
|||||||
BRIEF: Safe, configurable Google Tag Manager loader for Moko-Cassiopeia.
|
BRIEF: Safe, configurable Google Tag Manager loader for Moko-Cassiopeia.
|
||||||
PATH: ./media/templates/site/moko-cassiopeia/js/gtm.js
|
PATH: ./media/templates/site/moko-cassiopeia/js/gtm.js
|
||||||
NOTE: Place the <noscript> fallback iframe in your HTML template (index.php). A JS file
|
NOTE: Place the <noscript> fallback iframe in your HTML template (index.php). A JS file
|
||||||
cannot provide a true no-JS fallback by definition.
|
cannot provide a true no-JS fallback by definition.
|
||||||
VARIABLES:
|
VARIABLES:
|
||||||
- window.MOKO_GTM_ID (string) // Optional global GTM container ID (e.g., "GTM-XXXXXXX")
|
- window.MOKO_GTM_ID (string) // Optional global GTM container ID (e.g., "GTM-XXXXXXX")
|
||||||
- window.MOKO_GTM_OPTIONS (object) // Optional global options (see JSDoc below)
|
- window.MOKO_GTM_OPTIONS (object) // Optional global options (see JSDoc below)
|
||||||
- data- attributes on the script tag or <html>/<body>:
|
- data- attributes on the script tag or <html>/<body>:
|
||||||
data-gtm-id, data-data-layer, data-debug, data-ignore-dnt,
|
data-gtm-id, data-data-layer, data-debug, data-ignore-dnt,
|
||||||
data-env-auth, data-env-preview, data-block-on-dev
|
data-env-auth, data-env-preview, data-block-on-dev
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global window, document, navigator */
|
/* global window, document, navigator */
|
||||||
(() => {
|
(() => {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} MokoGtmOptions
|
* @typedef {Object} MokoGtmOptions
|
||||||
* @property {string} [id] GTM container ID (e.g., "GTM-XXXXXXX")
|
* @property {string} [id] GTM container ID (e.g., "GTM-XXXXXXX")
|
||||||
* @property {string} [dataLayerName] Custom dataLayer name (default: "dataLayer")
|
* @property {string} [dataLayerName] Custom dataLayer name (default: "dataLayer")
|
||||||
* @property {boolean} [debug] Log debug messages to console (default: false)
|
* @property {boolean} [debug] Log debug messages to console (default: false)
|
||||||
* @property {boolean} [ignoreDNT] Ignore Do Not Track and always load (default: false)
|
* @property {boolean} [ignoreDNT] Ignore Do Not Track and always load (default: false)
|
||||||
* @property {boolean} [blockOnDev] Block loading on localhost/*.test/127.0.0.1 (default: true)
|
* @property {boolean} [blockOnDev] Block loading on localhost/*.test/127.0.0.1 (default: true)
|
||||||
* @property {string} [envAuth] GTM Environment auth string (optional)
|
* @property {string} [envAuth] GTM Environment auth string (optional)
|
||||||
* @property {string} [envPreview] GTM Environment preview name (optional)
|
* @property {string} [envPreview] GTM Environment preview name (optional)
|
||||||
* @property {Record<string,'granted'|'denied'>} [consentDefault]
|
* @property {Record<string,'granted'|'denied'>} [consentDefault]
|
||||||
* Default Consent Mode v2 map. Keys like:
|
* Default Consent Mode v2 map. Keys like:
|
||||||
* analytics_storage, ad_storage, ad_user_data, ad_personalization, functionality_storage, security_storage
|
* analytics_storage, ad_storage, ad_user_data, ad_personalization, functionality_storage, security_storage
|
||||||
* (default: {analytics_storage:'granted', functionality_storage:'granted', security_storage:'granted'})
|
* (default: {analytics_storage:'granted', functionality_storage:'granted', security_storage:'granted'})
|
||||||
* @property {() => (Record<string, any>|void)} [pageVars]
|
* @property {() => (Record<string, any>|void)} [pageVars]
|
||||||
* Function returning extra page variables to push on init (optional)
|
* Function returning extra page variables to push on init (optional)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const PKG = "moko-gtm";
|
const PKG = "moko-gtm";
|
||||||
const PREFIX = `[${PKG}]`;
|
const PREFIX = `[${PKG}]`;
|
||||||
const WIN = window;
|
const WIN = window;
|
||||||
|
|
||||||
// Public API placeholder (attached to window at the end)
|
// Public API placeholder (attached to window at the end)
|
||||||
/** @type {{
|
/** @type {{
|
||||||
* init: (opts?: Partial<MokoGtmOptions>) => void,
|
* init: (opts?: Partial<MokoGtmOptions>) => void,
|
||||||
* setConsent: (updates: Record<string,'granted'|'denied'>) => void,
|
* setConsent: (updates: Record<string,'granted'|'denied'>) => void,
|
||||||
* push: (...args:any[]) => void,
|
* push: (...args:any[]) => void,
|
||||||
* isLoaded: () => boolean,
|
* isLoaded: () => boolean,
|
||||||
* config: () => Required<MokoGtmOptions>
|
* config: () => Required<MokoGtmOptions>
|
||||||
* }} */
|
* }} */
|
||||||
const API = {};
|
const API = {};
|
||||||
|
|
||||||
// ---- Utilities ---------------------------------------------------------
|
// ---- Utilities ---------------------------------------------------------
|
||||||
|
|
||||||
const isDevHost = () => {
|
const isDevHost = () => {
|
||||||
const h = WIN.location && WIN.location.hostname || "";
|
const h = WIN.location && WIN.location.hostname || "";
|
||||||
return (
|
return (
|
||||||
h === "localhost" ||
|
h === "localhost" ||
|
||||||
h === "127.0.0.1" ||
|
h === "127.0.0.1" ||
|
||||||
h.endsWith(".local") ||
|
h.endsWith(".local") ||
|
||||||
h.endsWith(".test")
|
h.endsWith(".test")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dntEnabled = () => {
|
const dntEnabled = () => {
|
||||||
// Different browsers expose DNT differently; treat "1" or "yes" as enabled.
|
// Different browsers expose DNT differently; treat "1" or "yes" as enabled.
|
||||||
const n = navigator;
|
const n = navigator;
|
||||||
const v = (n.doNotTrack || n.msDoNotTrack || (n.navigator && n.navigator.doNotTrack) || "").toString().toLowerCase();
|
const v = (n.doNotTrack || n.msDoNotTrack || (n.navigator && n.navigator.doNotTrack) || "").toString().toLowerCase();
|
||||||
return v === "1" || v === "yes";
|
return v === "1" || v === "yes";
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentScript = () => {
|
const getCurrentScript = () => {
|
||||||
// document.currentScript is best; fallback to last <script> whose src ends with /gtm.js
|
// document.currentScript is best; fallback to last <script> whose src ends with /gtm.js
|
||||||
const cs = document.currentScript;
|
const cs = document.currentScript;
|
||||||
if (cs) return cs;
|
if (cs) return cs;
|
||||||
const scripts = Array.from(document.getElementsByTagName("script"));
|
const scripts = Array.from(document.getElementsByTagName("script"));
|
||||||
return scripts.reverse().find(s => (s.getAttribute("src") || "").includes("/gtm.js")) || null;
|
return scripts.reverse().find(s => (s.getAttribute("src") || "").includes("/gtm.js")) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAttr = (el, name) => el ? el.getAttribute(name) : null;
|
const readDatasetCascade = (name) => {
|
||||||
|
// Check <script>, <html>, <body>, then <meta name="moko:gtm-<name>">
|
||||||
|
const script = getCurrentScript();
|
||||||
|
const html = document.documentElement;
|
||||||
|
const body = document.body;
|
||||||
|
const meta = document.querySelector(`meta[name="moko:gtm-${name}"]`);
|
||||||
|
return (
|
||||||
|
(script && script.dataset && script.dataset[name]) ||
|
||||||
|
(html && html.dataset && html.dataset[name]) ||
|
||||||
|
(body && body.dataset && body.dataset[name]) ||
|
||||||
|
(meta && meta.getAttribute("content")) ||
|
||||||
|
null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const readDatasetCascade = (name) => {
|
const parseBool = (v, fallback = false) => {
|
||||||
// Check <script>, <html>, <body>, then <meta name="moko:gtm-<name>">
|
if (v == null) return fallback;
|
||||||
const script = getCurrentScript();
|
const s = String(v).trim().toLowerCase();
|
||||||
const html = document.documentElement;
|
if (["1","true","yes","y","on"].includes(s)) return true;
|
||||||
const body = document.body;
|
if (["0","false","no","n","off"].includes(s)) return false;
|
||||||
const meta = document.querySelector(`meta[name="moko:gtm-${name}"]`);
|
return fallback;
|
||||||
return (
|
};
|
||||||
(script && script.dataset && script.dataset[name]) ||
|
|
||||||
(html && html.dataset && html.dataset[name]) ||
|
|
||||||
(body && body.dataset && body.dataset[name]) ||
|
|
||||||
(meta && meta.getAttribute("content")) ||
|
|
||||||
null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseBool = (v, fallback = false) => {
|
const debugLog = (...args) => {
|
||||||
if (v == null) return fallback;
|
if (STATE.debug) {
|
||||||
const s = String(v).trim().toLowerCase();
|
try { console.info(PREFIX, ...args); } catch (_) {}
|
||||||
if (["1","true","yes","y","on"].includes(s)) return true;
|
}
|
||||||
if (["0","false","no","n","off"].includes(s)) return false;
|
};
|
||||||
return fallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
const debugLog = (...args) => {
|
// ---- Configuration & State --------------------------------------------
|
||||||
if (STATE.debug) {
|
|
||||||
try { console.info(PREFIX, ...args); } catch (_) {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- Configuration & State --------------------------------------------
|
/** @type {Required<MokoGtmOptions>} */
|
||||||
|
const STATE = {
|
||||||
|
id: "",
|
||||||
|
dataLayerName: "dataLayer",
|
||||||
|
debug: false,
|
||||||
|
ignoreDNT: false,
|
||||||
|
blockOnDev: true,
|
||||||
|
envAuth: "",
|
||||||
|
envPreview: "",
|
||||||
|
consentDefault: {
|
||||||
|
analytics_storage: "granted",
|
||||||
|
functionality_storage: "granted",
|
||||||
|
security_storage: "granted",
|
||||||
|
// The following default to "denied" unless the site explicitly opts-in:
|
||||||
|
ad_storage: "denied",
|
||||||
|
ad_user_data: "denied",
|
||||||
|
ad_personalization: "denied",
|
||||||
|
},
|
||||||
|
pageVars: () => ({})
|
||||||
|
};
|
||||||
|
|
||||||
/** @type {Required<MokoGtmOptions>} */
|
const mergeOptions = (base, extra = {}) => {
|
||||||
const STATE = {
|
const out = {...base};
|
||||||
id: "",
|
for (const k in extra) {
|
||||||
dataLayerName: "dataLayer",
|
if (!Object.prototype.hasOwnProperty.call(extra, k)) continue;
|
||||||
debug: false,
|
const v = extra[k];
|
||||||
ignoreDNT: false,
|
if (v && typeof v === "object" && !Array.isArray(v)) {
|
||||||
blockOnDev: true,
|
out[k] = {...(out[k] || {}), ...v};
|
||||||
envAuth: "",
|
} else if (v !== undefined) {
|
||||||
envPreview: "",
|
out[k] = v;
|
||||||
consentDefault: {
|
}
|
||||||
analytics_storage: "granted",
|
}
|
||||||
functionality_storage: "granted",
|
return out;
|
||||||
security_storage: "granted",
|
};
|
||||||
// The following default to "denied" unless the site explicitly opts-in:
|
|
||||||
ad_storage: "denied",
|
|
||||||
ad_user_data: "denied",
|
|
||||||
ad_personalization: "denied",
|
|
||||||
},
|
|
||||||
pageVars: () => ({})
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergeOptions = (base, extra = {}) => {
|
const detectOptions = () => {
|
||||||
const out = {...base};
|
// 1) Global window options
|
||||||
for (const k in extra) {
|
/** @type {Partial<MokoGtmOptions>} */
|
||||||
if (!Object.prototype.hasOwnProperty.call(extra, k)) continue;
|
const globalOpts = (WIN.MOKO_GTM_OPTIONS && typeof WIN.MOKO_GTM_OPTIONS === "object") ? WIN.MOKO_GTM_OPTIONS : {};
|
||||||
const v = extra[k];
|
|
||||||
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
||||||
out[k] = {...(out[k] || {}), ...v};
|
|
||||||
} else if (v !== undefined) {
|
|
||||||
out[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
const detectOptions = () => {
|
// 2) Dataset / meta
|
||||||
// 1) Global window options
|
const idFromData = readDatasetCascade("id") || WIN.MOKO_GTM_ID || "";
|
||||||
/** @type {Partial<MokoGtmOptions>} */
|
const dlFromData = readDatasetCascade("dataLayer") || "";
|
||||||
const globalOpts = (WIN.MOKO_GTM_OPTIONS && typeof WIN.MOKO_GTM_OPTIONS === "object") ? WIN.MOKO_GTM_OPTIONS : {};
|
const dbgFromData = readDatasetCascade("debug");
|
||||||
|
const dntFromData = readDatasetCascade("ignoreDnt");
|
||||||
|
const devFromData = readDatasetCascade("blockOnDev");
|
||||||
|
const authFromData = readDatasetCascade("envAuth") || "";
|
||||||
|
const prevFromData = readDatasetCascade("envPreview") || "";
|
||||||
|
|
||||||
// 2) Dataset / meta
|
// 3) Combine
|
||||||
const idFromData = readDatasetCascade("id") || WIN.MOKO_GTM_ID || "";
|
/** @type {Partial<MokoGtmOptions>} */
|
||||||
const dlFromData = readDatasetCascade("dataLayer") || "";
|
const detected = {
|
||||||
const dbgFromData = readDatasetCascade("debug");
|
id: idFromData || globalOpts.id || "",
|
||||||
const dntFromData = readDatasetCascade("ignoreDnt");
|
dataLayerName: dlFromData || globalOpts.dataLayerName || undefined,
|
||||||
const devFromData = readDatasetCascade("blockOnDev");
|
debug: parseBool(dbgFromData, !!globalOpts.debug),
|
||||||
const authFromData = readDatasetCascade("envAuth") || "";
|
ignoreDNT: parseBool(dntFromData, !!globalOpts.ignoreDNT),
|
||||||
const prevFromData = readDatasetCascade("envPreview") || "";
|
blockOnDev: parseBool(devFromData, (globalOpts.blockOnDev ?? true)),
|
||||||
|
envAuth: authFromData || globalOpts.envAuth || "",
|
||||||
|
envPreview: prevFromData || globalOpts.envPreview || "",
|
||||||
|
consentDefault: globalOpts.consentDefault || undefined,
|
||||||
|
pageVars: typeof globalOpts.pageVars === "function" ? globalOpts.pageVars : undefined
|
||||||
|
};
|
||||||
|
|
||||||
// 3) Combine
|
return detected;
|
||||||
/** @type {Partial<MokoGtmOptions>} */
|
};
|
||||||
const detected = {
|
|
||||||
id: idFromData || globalOpts.id || "",
|
|
||||||
dataLayerName: dlFromData || globalOpts.dataLayerName || undefined,
|
|
||||||
debug: parseBool(dbgFromData, !!globalOpts.debug),
|
|
||||||
ignoreDNT: parseBool(dntFromData, !!globalOpts.ignoreDNT),
|
|
||||||
blockOnDev: parseBool(devFromData, (globalOpts.blockOnDev ?? true)),
|
|
||||||
envAuth: authFromData || globalOpts.envAuth || "",
|
|
||||||
envPreview: prevFromData || globalOpts.envPreview || "",
|
|
||||||
consentDefault: globalOpts.consentDefault || undefined,
|
|
||||||
pageVars: typeof globalOpts.pageVars === "function" ? globalOpts.pageVars : undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
return detected;
|
// ---- dataLayer / gtag helpers -----------------------------------------
|
||||||
};
|
|
||||||
|
|
||||||
// ---- dataLayer / gtag helpers -----------------------------------------
|
const ensureDataLayer = () => {
|
||||||
|
const l = STATE.dataLayerName;
|
||||||
|
WIN[l] = WIN[l] || [];
|
||||||
|
return WIN[l];
|
||||||
|
};
|
||||||
|
|
||||||
const ensureDataLayer = () => {
|
/** gtag wrapper backed by dataLayer. */
|
||||||
const l = STATE.dataLayerName;
|
const gtag = (...args) => {
|
||||||
WIN[l] = WIN[l] || [];
|
const dl = ensureDataLayer();
|
||||||
return WIN[l];
|
dl.push(arguments.length > 1 ? args : args[0]);
|
||||||
};
|
debugLog("gtag push:", args);
|
||||||
|
};
|
||||||
|
|
||||||
/** gtag wrapper backed by dataLayer. */
|
API.push = (...args) => gtag(...args);
|
||||||
const gtag = (...args) => {
|
|
||||||
const dl = ensureDataLayer();
|
|
||||||
dl.push(arguments.length > 1 ? args : args[0]);
|
|
||||||
debugLog("gtag push:", args);
|
|
||||||
};
|
|
||||||
|
|
||||||
API.push = (...args) => gtag(...args);
|
API.setConsent = (updates) => {
|
||||||
|
gtag("consent", "update", updates || {});
|
||||||
|
};
|
||||||
|
|
||||||
API.setConsent = (updates) => {
|
API.isLoaded = () => {
|
||||||
gtag("consent", "update", updates || {});
|
const hasScript = !!document.querySelector('script[src*="googletagmanager.com/gtm.js"]');
|
||||||
};
|
return hasScript;
|
||||||
|
};
|
||||||
|
|
||||||
API.isLoaded = () => {
|
API.config = () => ({...STATE});
|
||||||
const hasScript = !!document.querySelector('script[src*="googletagmanager.com/gtm.js"]');
|
|
||||||
return hasScript;
|
|
||||||
};
|
|
||||||
|
|
||||||
API.config = () => ({...STATE});
|
// ---- Loader ------------------------------------------------------------
|
||||||
|
|
||||||
// ---- Loader ------------------------------------------------------------
|
const buildEnvQuery = () => {
|
||||||
|
const qp = [];
|
||||||
|
if (STATE.envAuth) qp.push(`gtm_auth=${encodeURIComponent(STATE.envAuth)}`);
|
||||||
|
if (STATE.envPreview) qp.push(`gtm_preview=${encodeURIComponent(STATE.envPreview)}`, "gtm_cookies_win=x");
|
||||||
|
return qp.length ? `&${qp.join("&")}` : "";
|
||||||
|
};
|
||||||
|
|
||||||
const buildEnvQuery = () => {
|
const injectScript = () => {
|
||||||
const qp = [];
|
if (!STATE.id) {
|
||||||
if (STATE.envAuth) qp.push(`gtm_auth=${encodeURIComponent(STATE.envAuth)}`);
|
debugLog("GTM ID missing; aborting load.");
|
||||||
if (STATE.envPreview) qp.push(`gtm_preview=${encodeURIComponent(STATE.envPreview)}`, "gtm_cookies_win=x");
|
return;
|
||||||
return qp.length ? `&${qp.join("&")}` : "";
|
}
|
||||||
};
|
if (API.isLoaded()) {
|
||||||
|
debugLog("GTM already loaded; skipping duplicate injection.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const injectScript = () => {
|
// Standard GTM bootstrap timing event
|
||||||
if (!STATE.id) {
|
const dl = ensureDataLayer();
|
||||||
debugLog("GTM ID missing; aborting load.");
|
dl.push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (API.isLoaded()) {
|
|
||||||
debugLog("GTM already loaded; skipping duplicate injection.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard GTM bootstrap timing event
|
const f = document.getElementsByTagName("script")[0];
|
||||||
const dl = ensureDataLayer();
|
const j = document.createElement("script");
|
||||||
dl.push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
|
j.async = true;
|
||||||
|
j.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(STATE.id)}${STATE.dataLayerName !== "dataLayer" ? `&l=${encodeURIComponent(STATE.dataLayerName)}` : ""}${buildEnvQuery()}`;
|
||||||
|
if (f && f.parentNode) {
|
||||||
|
f.parentNode.insertBefore(j, f);
|
||||||
|
} else {
|
||||||
|
(document.head || document.documentElement).appendChild(j);
|
||||||
|
}
|
||||||
|
debugLog("Injected GTM script:", j.src);
|
||||||
|
};
|
||||||
|
|
||||||
const f = document.getElementsByTagName("script")[0];
|
const applyDefaultConsent = () => {
|
||||||
const j = document.createElement("script");
|
// Consent Mode v2 default
|
||||||
j.async = true;
|
gtag("consent", "default", STATE.consentDefault);
|
||||||
j.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(STATE.id)}${STATE.dataLayerName !== "dataLayer" ? `&l=${encodeURIComponent(STATE.dataLayerName)}` : ""}${buildEnvQuery()}`;
|
debugLog("Applied default consent:", STATE.consentDefault);
|
||||||
if (f && f.parentNode) {
|
};
|
||||||
f.parentNode.insertBefore(j, f);
|
|
||||||
} else {
|
|
||||||
(document.head || document.documentElement).appendChild(j);
|
|
||||||
}
|
|
||||||
debugLog("Injected GTM script:", j.src);
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyDefaultConsent = () => {
|
const pushInitialVars = () => {
|
||||||
// Consent Mode v2 default
|
// Minimal page vars; allow site to add more via pageVars()
|
||||||
gtag("consent", "default", STATE.consentDefault);
|
const vars = {
|
||||||
debugLog("Applied default consent:", STATE.consentDefault);
|
event: "moko.page_init",
|
||||||
};
|
page_title: document.title || "",
|
||||||
|
page_language: (document.documentElement && document.documentElement.lang) || "",
|
||||||
|
...(typeof STATE.pageVars === "function" ? (STATE.pageVars() || {}) : {})
|
||||||
|
};
|
||||||
|
gtag(vars);
|
||||||
|
};
|
||||||
|
|
||||||
const pushInitialVars = () => {
|
const shouldLoad = () => {
|
||||||
// Minimal page vars; allow site to add more via pageVars()
|
if (!STATE.ignoreDNT && dntEnabled()) {
|
||||||
const vars = {
|
debugLog("DNT is enabled; blocking GTM load (set ignoreDNT=true to override).");
|
||||||
event: "moko.page_init",
|
return false;
|
||||||
page_title: document.title || "",
|
}
|
||||||
page_language: (document.documentElement && document.documentElement.lang) || "",
|
if (STATE.blockOnDev && isDevHost()) {
|
||||||
...(typeof STATE.pageVars === "function" ? (STATE.pageVars() || {}) : {})
|
debugLog("Development host detected; blocking GTM load (set blockOnDev=false to override).");
|
||||||
};
|
return false;
|
||||||
gtag(vars);
|
}
|
||||||
};
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const shouldLoad = () => {
|
// ---- Public init -------------------------------------------------------
|
||||||
if (!STATE.ignoreDNT && dntEnabled()) {
|
|
||||||
debugLog("DNT is enabled; blocking GTM load (set ignoreDNT=true to override).");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (STATE.blockOnDev && isDevHost()) {
|
|
||||||
debugLog("Development host detected; blocking GTM load (set blockOnDev=false to override).");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- Public init -------------------------------------------------------
|
API.init = (opts = {}) => {
|
||||||
|
// Merge: defaults <- detected <- passed opts
|
||||||
|
const detected = detectOptions();
|
||||||
|
const merged = mergeOptions(STATE, mergeOptions(detected, opts));
|
||||||
|
|
||||||
API.init = (opts = {}) => {
|
// Commit back to STATE
|
||||||
// Merge: defaults <- detected <- passed opts
|
Object.assign(STATE, merged);
|
||||||
const detected = detectOptions();
|
|
||||||
const merged = mergeOptions(STATE, mergeOptions(detected, opts));
|
|
||||||
|
|
||||||
// Commit back to STATE
|
debugLog("Config:", STATE);
|
||||||
Object.assign(STATE, merged);
|
|
||||||
|
|
||||||
debugLog("Config:", STATE);
|
// Prepare dataLayer/gtag and consent
|
||||||
|
ensureDataLayer();
|
||||||
|
applyDefaultConsent();
|
||||||
|
pushInitialVars();
|
||||||
|
|
||||||
// Prepare dataLayer/gtag and consent
|
// Load GTM if allowed
|
||||||
ensureDataLayer();
|
if (shouldLoad()) {
|
||||||
applyDefaultConsent();
|
injectScript();
|
||||||
pushInitialVars();
|
} else {
|
||||||
|
debugLog("GTM load prevented by configuration or environment.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Load GTM if allowed
|
// ---- Auto-init on DOMContentLoaded (safe even if deferred) -------------
|
||||||
if (shouldLoad()) {
|
|
||||||
injectScript();
|
|
||||||
} else {
|
|
||||||
debugLog("GTM load prevented by configuration or environment.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- Auto-init on DOMContentLoaded (safe even if deferred) -------------
|
const autoInit = () => {
|
||||||
|
// Only auto-init if we have some ID from globals/datasets.
|
||||||
|
const detected = detectOptions();
|
||||||
|
const hasId = !!(detected.id || WIN.MOKO_GTM_ID);
|
||||||
|
if (hasId) {
|
||||||
|
API.init(); // use detected/global defaults
|
||||||
|
} else {
|
||||||
|
debugLog("No GTM ID detected; awaiting manual init via window.mokoGTM.init({ id: 'GTM-XXXXXXX' }).");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const autoInit = () => {
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||||
// Only auto-init if we have some ID from globals/datasets.
|
// Defer to ensure <body> exists for any late consumers.
|
||||||
const detected = detectOptions();
|
setTimeout(autoInit, 0);
|
||||||
const hasId = !!(detected.id || WIN.MOKO_GTM_ID);
|
} else {
|
||||||
if (hasId) {
|
document.addEventListener("DOMContentLoaded", autoInit, { once: true });
|
||||||
API.init(); // use detected/global defaults
|
}
|
||||||
} else {
|
|
||||||
debugLog("No GTM ID detected; awaiting manual init via window.mokoGTM.init({ id: 'GTM-XXXXXXX' }).");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (document.readyState === "complete" || document.readyState === "interactive") {
|
// Expose API
|
||||||
// Defer to ensure <body> exists for any late consumers.
|
WIN.mokoGTM = API;
|
||||||
setTimeout(autoInit, 0);
|
|
||||||
} else {
|
|
||||||
document.addEventListener("DOMContentLoaded", autoInit, { once: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expose API
|
// Helpful console hint (only if debug true after detection)
|
||||||
WIN.mokoGTM = API;
|
try {
|
||||||
|
const detected = detectOptions();
|
||||||
// Helpful console hint (only if debug true after detection)
|
if (parseBool(detected.debug, false)) {
|
||||||
try {
|
STATE.debug = true;
|
||||||
const detected = detectOptions();
|
debugLog("Ready. You can call window.mokoGTM.init({ id: 'GTM-XXXXXXX' }).");
|
||||||
if (parseBool(detected.debug, false)) {
|
}
|
||||||
STATE.debug = true;
|
} catch (_) {}
|
||||||
debugLog("Ready. You can call window.mokoGTM.init({ id: 'GTM-XXXXXXX' }).");
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user