diff --git a/src/media/js/gtm.min.js b/src/media/js/gtm.min.js index 2aa7367..9c5acc8 100644 --- a/src/media/js/gtm.min.js +++ b/src/media/js/gtm.min.js @@ -1 +1 @@ -(()=>{"use strict";const PKG="moko-gtm";const PREFIX=`[${PKG}]`;const WIN=window;const API={};const isDevHost=()=>{const h=WIN.location&&WIN.location.hostname||"";return(h==="localhost"||h==="127.0.0.1"||h.endsWith(".local")||h.endsWith(".test"));};const dntEnabled=()=>{const n=navigator;const v=(n.doNotTrack||n.msDoNotTrack||(n.navigator&&n.navigator.doNotTrack)||"").toString().toLowerCase();return v==="1"||v==="yes";};const getCurrentScript=()=>{const cs=document.currentScript;if(cs)return cs;const scripts=Array.from(document.getElementsByTagName("script"));return scripts.reverse().find(s=>(s.getAttribute("src")||"").includes("/gtm.js"))||null;};const readDatasetCascade=(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 parseBool=(v,fallback=false)=>{if(v==null)return fallback;const s=String(v).trim().toLowerCase();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)=>{if(STATE.debug){try{console.info(PREFIX,...args);}catch(_){}}};const STATE={id: "",dataLayerName: "dataLayer",debug: false,ignoreDNT: false,blockOnDev: true,envAuth: "",envPreview: "",consentDefault:{analytics_storage: "granted",functionality_storage: "granted",security_storage: "granted",ad_storage: "denied",ad_user_data: "denied",ad_personalization: "denied",},pageVars:()=>({})};const mergeOptions=(base,extra={})=>{const out={...base};for(const k in extra){if(!Object.prototype.hasOwnProperty.call(extra,k))continue;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=()=>{const globalOpts=(WIN.MOKO_GTM_OPTIONS&&typeof WIN.MOKO_GTM_OPTIONS==="object")? WIN.MOKO_GTM_OPTIONS :{};const idFromData=readDatasetCascade("id")||WIN.MOKO_GTM_ID||"";const dlFromData=readDatasetCascade("dataLayer")||"";const dbgFromData=readDatasetCascade("debug");const dntFromData=readDatasetCascade("ignoreDnt");const devFromData=readDatasetCascade("blockOnDev");const authFromData=readDatasetCascade("envAuth")||"";const prevFromData=readDatasetCascade("envPreview")||"";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;};const ensureDataLayer=()=>{const l=STATE.dataLayerName;WIN[l]=WIN[l]||[];return WIN[l];};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.isLoaded=()=>{const hasScript=!!document.querySelector('script[src*="googletagmanager.com/gtm.js"]');return hasScript;};API.config=()=>({...STATE});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 injectScript=()=>{if(!STATE.id){debugLog("GTM ID missing;aborting load.");return;}if(API.isLoaded()){debugLog("GTM already loaded;skipping duplicate injection.");return;}const dl=ensureDataLayer();dl.push({"gtm.start": new Date().getTime(),event: "gtm.js"});const f=document.getElementsByTagName("script")[0];const j=document.createElement("script");j.async=true;j.src=`https: if(f&&f.parentNode){f.parentNode.insertBefore(j,f);}else{(document.head||document.documentElement).appendChild(j);}debugLog("Injected GTM script:",j.src);};const applyDefaultConsent=()=>{gtag("consent","default",STATE.consentDefault);debugLog("Applied default consent:",STATE.consentDefault);};const pushInitialVars=()=>{const vars={event: "moko.page_init",page_title: document.title||"",page_language:(document.documentElement&&document.documentElement.lang)||"",...(typeof STATE.pageVars==="function" ?(STATE.pageVars()||{}):{})};gtag(vars);};const shouldLoad=()=>{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;};API.init=(opts={})=>{const detected=detectOptions();const merged=mergeOptions(STATE,mergeOptions(detected,opts));Object.assign(STATE,merged);debugLog("Config:",STATE);ensureDataLayer();applyDefaultConsent();pushInitialVars();if(shouldLoad()){injectScript();}else{debugLog("GTM load prevented by configuration or environment.");}};const autoInit=()=>{const detected=detectOptions();const hasId=!!(detected.id||WIN.MOKO_GTM_ID);if(hasId){API.init();}else{debugLog("No GTM ID detected;awaiting manual init via window.mokoGTM.init({id: 'GTM-XXXXXXX'}).");}};if(document.readyState==="complete"||document.readyState==="interactive"){setTimeout(autoInit,0);}else{document.addEventListener("DOMContentLoaded",autoInit,{once: true});}WIN.mokoGTM=API;try{const detected=detectOptions();if(parseBool(detected.debug,false)){STATE.debug=true;debugLog("Ready. You can call window.mokoGTM.init({id: 'GTM-XXXXXXX'}).");}}catch(_){}})(); \ No newline at end of file +(()=>{"use strict";const PKG="moko-gtm";const PREFIX=`[${PKG}]`;const WIN=window;const API={};const isDevHost=()=>{const h=WIN.location&&WIN.location.hostname||"";return(h==="localhost"||h==="127.0.0.1"||h.endsWith(".local")||h.endsWith(".test"));};const dntEnabled=()=>{const n=navigator;const v=(n.doNotTrack||n.msDoNotTrack||(n.navigator&&n.navigator.doNotTrack)||"").toString().toLowerCase();return v==="1"||v==="yes";};const getCurrentScript=()=>{const cs=document.currentScript;if(cs)return cs;const scripts=Array.from(document.getElementsByTagName("script"));return scripts.reverse().find(s=>(s.getAttribute("src")||"").includes("/gtm.js"))||null;};const readDatasetCascade=(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 parseBool=(v,fallback=false)=>{if(v==null)return fallback;const s=String(v).trim().toLowerCase();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)=>{if(STATE.debug){try{console.info(PREFIX,...args);}catch(_){}}};const STATE={id: "",dataLayerName: "dataLayer",debug: false,ignoreDNT: false,blockOnDev: true,envAuth: "",envPreview: "",consentDefault:{analytics_storage: "granted",functionality_storage: "granted",security_storage: "granted",ad_storage: "denied",ad_user_data: "denied",ad_personalization: "denied",},pageVars:()=>({})};const mergeOptions=(base,extra={})=>{const out={...base};for(const k in extra){if(!Object.prototype.hasOwnProperty.call(extra,k))continue;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=()=>{const globalOpts=(WIN.MOKO_GTM_OPTIONS&&typeof WIN.MOKO_GTM_OPTIONS==="object")? WIN.MOKO_GTM_OPTIONS :{};const idFromData=readDatasetCascade("id")||WIN.MOKO_GTM_ID||"";const dlFromData=readDatasetCascade("dataLayer")||"";const dbgFromData=readDatasetCascade("debug");const dntFromData=readDatasetCascade("ignoreDnt");const devFromData=readDatasetCascade("blockOnDev");const authFromData=readDatasetCascade("envAuth")||"";const prevFromData=readDatasetCascade("envPreview")||"";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;};const ensureDataLayer=()=>{const l=STATE.dataLayerName;WIN[l]=WIN[l]||[];return WIN[l];};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.isLoaded=()=>{const hasScript=!!document.querySelector('script[src*="googletagmanager.com/gtm.js"]');return hasScript;};API.config=()=>({...STATE});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 injectScript=()=>{if(!STATE.id){debugLog("GTM ID missing;aborting load.");return;}if(API.isLoaded()){debugLog("GTM already loaded;skipping duplicate injection.");return;}const dl=ensureDataLayer();dl.push({"gtm.start": new Date().getTime(),event: "gtm.js"});const f=document.getElementsByTagName("script")[0];const j=document.createElement("script");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 applyDefaultConsent=()=>{gtag("consent","default",STATE.consentDefault);debugLog("Applied default consent:",STATE.consentDefault);};const pushInitialVars=()=>{const vars={event: "moko.page_init",page_title: document.title||"",page_language:(document.documentElement&&document.documentElement.lang)||"",...(typeof STATE.pageVars==="function" ?(STATE.pageVars()||{}):{})};gtag(vars);};const shouldLoad=()=>{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;};API.init=(opts={})=>{const detected=detectOptions();const merged=mergeOptions(STATE,mergeOptions(detected,opts));Object.assign(STATE,merged);debugLog("Config:",STATE);ensureDataLayer();applyDefaultConsent();pushInitialVars();if(shouldLoad()){injectScript();}else{debugLog("GTM load prevented by configuration or environment.");}};const autoInit=()=>{const detected=detectOptions();const hasId=!!(detected.id||WIN.MOKO_GTM_ID);if(hasId){API.init();}else{debugLog("No GTM ID detected;awaiting manual init via window.mokoGTM.init({id: 'GTM-XXXXXXX'}).");}};if(document.readyState==="complete"||document.readyState==="interactive"){setTimeout(autoInit,0);}else{document.addEventListener("DOMContentLoaded",autoInit,{once: true});}WIN.mokoGTM=API;try{const detected=detectOptions();if(parseBool(detected.debug,false)){STATE.debug=true;debugLog("Ready. You can call window.mokoGTM.init({id: 'GTM-XXXXXXX'}).");}}catch(_){}})(); \ No newline at end of file diff --git a/src/templates/AssetMinifier.php b/src/templates/AssetMinifier.php index 523c9bb..0cc7212 100644 --- a/src/templates/AssetMinifier.php +++ b/src/templates/AssetMinifier.php @@ -35,8 +35,8 @@ class AssetMinifier // Remove comments $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css); - // Remove whitespace - $css = str_replace(["\r\n", "\r", "\n", "\t", ' ', ' ', ' '], '', $css); + // Remove all whitespace and newlines + $css = preg_replace('/\s+/', ' ', $css); // Remove spaces around selectors and properties $css = preg_replace('/\s*([{}|:;,])\s*/', '$1', $css); @@ -50,21 +50,25 @@ class AssetMinifier /** * Minify JavaScript content * + * Note: This is a basic minifier. For production use, consider using + * a more sophisticated minifier like terser or uglify-js. + * * @param string $js JavaScript content to minify * @return string Minified JavaScript */ public static function minifyJS(string $js): string { - // Remove single-line comments (but preserve URLs) - $js = preg_replace('~//[^\n]*\n~', "\n", $js); + // Remove single-line comments but preserve URLs (https://, http://) + // Only remove comments that start at the beginning of a line or after whitespace + $js = preg_replace('~(?!&|+\-*\/])\s*/', '$1', $js); return trim($js); diff --git a/src/templates/offline.php b/src/templates/offline.php index 66ce0ec..4a312bf 100644 --- a/src/templates/offline.php +++ b/src/templates/offline.php @@ -43,12 +43,12 @@ $direction = $this->direction ?: 'ltr'; /* ----------------------- Load ONLY template.css + colors_*.css (with min toggle) ------------------------ */ -$useMin = !((int) $params->get('developmentmode', 0) === 1); -$assetSuffix = $useMin ? '.min' : ''; +$developmentMode = (int) $params->get('developmentmode', 0) === 1; +$assetSuffix = $developmentMode ? '' : '.min'; // Process assets based on development mode $mediaPath = JPATH_ROOT . '/media/templates/site/moko-cassiopeia'; -AssetMinifier::processAssets($mediaPath, !$useMin); +AssetMinifier::processAssets($mediaPath, $developmentMode); $base = rtrim(Uri::root(true), '/') . '/media/templates/site/moko-cassiopeia/css/';