(function(){
'use strict';
if(location.href.includes('/wp-admin/')||location.href.includes('/admin.php')){
return;
}
function initCM(){
try {
if(typeof cmAjax==='undefined'||!cmAjax||!cmAjax.url){
if(typeof initCM.retries==='undefined') initCM.retries=0;
if(initCM.retries++ < 20){
setTimeout(initCM, 10);
}else{
startCM();
}
return;
}
startCM();
} catch (e){
if(window.console&&console.error){
console.error('CM init error:', e);
}}
}
function startCM(){
try {
let sessionId;
try {
sessionId=localStorage.getItem('cmSession')||(crypto.randomUUID ? crypto.randomUUID():Math.random().toString(36).substr(2, 9));
localStorage.setItem('cmSession', sessionId);
} catch (e){
sessionId=crypto.randomUUID ? crypto.randomUUID():'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
let startTime=Date.now();
let maxScroll=0;
let scrollEvents=0;
let lastScrollTime=0;
let currentPage=location.href;
function trackScroll(){
try {
const scrollPercent=Math.round((window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100
);
maxScroll=Math.max(maxScroll, scrollPercent);
scrollEvents++;
lastScrollTime=Date.now();
} catch (e){}}
window.addEventListener('scroll', trackScroll, { passive: true });
function getGa4Cookies(){
try {
const cookies=document.cookie.split(';');
let clientId='';
let sessionId='';
for (let i=0; i < cookies.length; i++){
const parts=cookies[i].trim().split('=');
const name=parts[0].trim();
const value=parts.slice(1).join('=').trim();
if(name==='_ga'&&value){
const p=value.split('.');
if(p.length >=4) clientId=p[2] + '.' + p[3];
}
if(name.startsWith('_ga_')&&value&&!sessionId){
const p=value.split('.');
if(p.length >=3) sessionId=p[2];
}}
return { clientId: clientId, sessionId: sessionId };} catch (e){
return { clientId: '', sessionId: '' };}}
function parseUtm(){
try {
const params=new URLSearchParams(location.search);
const utm={};
['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid'].forEach(k=> {
if(params.has(k)) utm[k]=params.get(k);
});
return Object.keys(utm).length ? utm:null;
} catch (e){
return null;
}}
function classifySource(docReferrer, currentUrl){
try {
const host=(new URL(currentUrl)).host;
const ref=docReferrer||'';
if(!ref) return 'Direct';
const refHost=(new URL(ref)).host||'';
if(refHost.endsWith(host)) return 'Direct';
const h=refHost.toLowerCase();
const refLower=ref.toLowerCase();
if(h.includes('linkedin.com')||h.includes('linked.in')) return 'LinkedIn';
if(h.includes('instagram.com')||h.includes('instagr.am')) return 'Instagram';
if(h.includes('facebook.com')||h.includes('fb.com')||h.includes('m.facebook.com')) return 'Facebook';
if(h.includes('twitter.com')||h.includes('t.co')||h.includes('x.com')) return 'Twitter/X';
if(h.includes('youtube.com')||h.includes('youtu.be')) return 'YouTube';
if(h.includes('pinterest.com')||h.includes('pin.it')) return 'Pinterest';
if(h.includes('tiktok.com')) return 'TikTok';
if(h.includes('reddit.com')) return 'Reddit';
if(h.includes('google.com')){
if(refLower.includes('maps')||refLower.includes('gmb')||refLower.includes('business.google.com')){
return 'Google My Business';
}
if(refLower.includes('ads')||refLower.includes('adwords')){
return 'Google Ads';
}
return 'Google (SEO)';
}
if(h.includes('bing.com')) return 'Bing';
if(h.includes('yahoo.com')) return 'Yahoo';
if(h.includes('duckduckgo.com')) return 'DuckDuckGo';
if(h.includes('mail.')||h.includes('email')||refLower.includes('mailto:')) return 'Email';
if(h.includes('newsletter')||h.includes('campaign')) return 'Newsletter';
return 'Referral';
} catch (e){
return 'Direct';
}}
let ga4ClientId='';
let ga4SessionId='';
try {
ga4ClientId=sessionStorage.getItem('cm_ga4_cid')||'';
ga4SessionId=sessionStorage.getItem('cm_ga4_sid')||'';
} catch(e){}
if(!ga4ClientId){
const ga4Now=getGa4Cookies();
ga4ClientId=ga4Now.clientId;
ga4SessionId=ga4Now.sessionId;
if(ga4ClientId){
try {
sessionStorage.setItem('cm_ga4_cid', ga4ClientId);
sessionStorage.setItem('cm_ga4_sid', ga4SessionId);
} catch(e){}}
}
if(!ga4ClientId){
setTimeout(function(){
try {
const ga4Retry=getGa4Cookies();
if(ga4Retry.clientId){
ga4ClientId=ga4Retry.clientId;
ga4SessionId=ga4Retry.sessionId;
sessionStorage.setItem('cm_ga4_cid', ga4ClientId);
sessionStorage.setItem('cm_ga4_sid', ga4SessionId);
}} catch(e){}}, 1500);
}
let existingUtm, sessionUtm, storedSource, detectedSource;
try {
existingUtm=sessionStorage.getItem('cmUtm');
if(!existingUtm){
const currentUtm=parseUtm();
if(currentUtm) sessionStorage.setItem('cmUtm', JSON.stringify(currentUtm));
}
sessionUtm=(()=> {
try {
return JSON.parse(sessionStorage.getItem('cmUtm'));
} catch (e){
return null;
}})();
storedSource=sessionStorage.getItem('cmSource');
if(sessionUtm&&sessionUtm.utm_source){
const utmSource=sessionUtm.utm_source.toLowerCase();
if(utmSource.includes('google')||utmSource.includes('gclid')){
detectedSource=sessionUtm.gclid ? 'Google Ads':'Google (SEO)';
}else if(utmSource.includes('facebook')||utmSource.includes('fb')){
detectedSource='Facebook';
}else if(utmSource.includes('linkedin')){
detectedSource='LinkedIn';
}else if(utmSource.includes('instagram')||utmSource.includes('ig')){
detectedSource='Instagram';
}else if(utmSource.includes('twitter')||utmSource.includes('x.com')){
detectedSource='Twitter/X';
}else{
detectedSource=sessionUtm.utm_source.charAt(0).toUpperCase() + sessionUtm.utm_source.slice(1);
}}else{
detectedSource=storedSource||classifySource(document.referrer, location.href);
}
sessionStorage.setItem('cmSource', detectedSource);
} catch (e){
sessionUtm=null;
detectedSource=classifySource(document.referrer, location.href);
}
function detectDevice(){
const ua=navigator.userAgent.toLowerCase();
const isMobile=/mobi|iphone|android|ipod|ipad/.test(ua)||(screen.width <=812&&'ontouchstart' in window);
const isTablet=/ipad|tablet|playbook|silk/.test(ua)||(screen.width > 812&&screen.width <=1024&&'ontouchstart' in window);
if(isTablet) return 'Tablet';
return isMobile ? 'Smartphone':'Desktop';
}
function detectBrowser(){
const ua=navigator.userAgent.toLowerCase();
if(ua.includes('chrome')&&!ua.includes('edg')) return 'Chrome';
if(ua.includes('firefox')) return 'Firefox';
if(ua.includes('safari')&&!ua.includes('chrome')) return 'Safari';
if(ua.includes('edg')) return 'Edge';
return 'Unknown';
}
function detectBotSignals(){
const reasons=[];
let score=0;
try {
if(navigator.webdriver===true){
reasons.push({ reason: 'WebDriver detected (navigator.webdriver=true)', score: 30 });
score +=30;
}
if(window.chrome===undefined&&navigator.vendor==='Google Inc.'){
reasons.push({ reason: 'Chrome vendor without chrome object (headless indicator)', score: 20 });
score +=20;
}
if(navigator.plugins&&navigator.plugins.length===0){
reasons.push({ reason: 'No browser plugins detected (headless indicator)', score: 15 });
score +=15;
}
if(screen.width===0||screen.height===0){
reasons.push({ reason: 'Invalid screen dimensions (0x0)', score: 25 });
score +=25;
}
if(window.innerWidth===0||window.innerHeight===0){
reasons.push({ reason: 'Invalid viewport dimensions (0x0)', score: 25 });
score +=25;
}
if(window.phantom||window.callPhantom){
reasons.push({ reason: 'PhantomJS detected', score: 50 });
score +=50;
}
if(window.__nightmare){
reasons.push({ reason: 'Nightmare.js detected', score: 50 });
score +=50;
}
if(window.Buffer&&typeof window.Buffer==='function'){
reasons.push({ reason: 'Node.js Buffer detected in browser context', score: 10 });
score +=10;
}
if(navigator.hardwareConcurrency===0){
reasons.push({ reason: 'No CPU cores detected (hardwareConcurrency=0)', score: 10 });
score +=10;
}
if(navigator.deviceMemory===0){
reasons.push({ reason: 'No device memory detected (deviceMemory=0)', score: 10 });
score +=10;
}
if(!navigator.cookieEnabled){
reasons.push({ reason: 'Cookies disabled (unusual for real browsers)', score: 10 });
score +=10;
}
if(!navigator.languages||navigator.languages.length===0){
reasons.push({ reason: 'No browser languages detected', score: 10 });
score +=10;
}
if(window.domAutomation||window.domAutomationController){
reasons.push({ reason: 'DOM automation detected', score: 40 });
score +=40;
}
const ua=navigator.userAgent.toLowerCase();
if(/headless|phantom|selenium|webdriver|puppeteer|playwright/i.test(ua)){
reasons.push({ reason: 'Bot pattern detected in user agent', score: 40 });
score +=40;
}
if(!window.chrome&&!window.safari&&!window.opr&&!window.msBrowser){
if(ua.includes('chrome')){
reasons.push({ reason: 'Chrome user agent but no chrome object', score: 15 });
score +=15;
}}
} catch (e){
}
return {
score: Math.min(100, score),
reasons: reasons,
signals: {
webdriver: navigator.webdriver||false,
plugins: navigator.plugins ? navigator.plugins.length:0,
screenWidth: screen.width,
screenHeight: screen.height,
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
hardwareConcurrency: navigator.hardwareConcurrency||0,
deviceMemory: navigator.deviceMemory||0,
cookieEnabled: navigator.cookieEnabled,
languages: navigator.languages ? navigator.languages.length:0
}};}
const queue=[];
let sending=false;
let lastSentAt=0;
const SEND_INTERVAL_MS=500;
const MAX_BATCH=5;
function enqueue(payload){
queue.push(payload);
scheduleSend();
}
function scheduleSend(){
if(sending) return;
const delay=Math.max(0, SEND_INTERVAL_MS - (Date.now() - lastSentAt));
sending=true;
setTimeout(flushQueue, delay);
}
function flushQueue(){
const batch=queue.splice(0, MAX_BATCH);
if(batch.length===0){
sending=false;
return;
}
lastSentAt=Date.now();
const body=batch.length===1 ? batch[0]:{ batch };
const dataStr=JSON.stringify(body);
const blob=new Blob([dataStr], { type: 'application/json' });
if(navigator.sendBeacon&&batch.length <=10){
if(navigator.sendBeacon(cmAjax.url + '?action=cm_log', blob)){
sending=false;
if(queue.length) scheduleSend();
return;
}}
fetch(cmAjax.url + '?action=cm_log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: dataStr,
keepalive: true
}).then(response=> {
if(!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
}).then(data=> {
if(!data||!data.success) throw new Error('Server error');
}).catch(error=> {
queue.unshift(...batch);
}).finally(()=> {
sending=false;
if(queue.length) scheduleSend();
});
}
function logCM(type, details){
enqueue({ session_id: sessionId, type, details, ga_client_id: ga4ClientId, ga_session_id: ga4SessionId });
}
let lastLoggedUrl=null;
let lastLoggedAt=0;
function shouldProcess(url){
if(document.visibilityState==='hidden'){
return { shouldLog: false, traffic_quality: 'hidden' };}
if(window.top!==window.self){
return { shouldLog: false, traffic_quality: 'iframe' };}
const ua=navigator.userAgent.toLowerCase();
const isBot=/bot|crawl|spider|headless|phantom|googlebot|bingbot/i.test(ua);
const now=Date.now();
const isDuplicate=lastLoggedUrl===url&&(now - lastLoggedAt) < 1000;
lastLoggedUrl=url;
lastLoggedAt=now;
let traffic_quality='valid';
if(isBot){
traffic_quality='bot';
}else if(isDuplicate){
traffic_quality='duplicate';
}
return { shouldLog: true, traffic_quality: traffic_quality };}
const processResult=shouldProcess(currentPage);
if(processResult.shouldLog){
let storedEntry;
try {
storedEntry=sessionStorage.getItem('cmEntrySet');
} catch (e){
storedEntry=null;
}
const device=detectDevice();
const browser=detectBrowser();
let loadTime=0;
if(window.performance&&window.performance.timing){
const perf=window.performance.timing;
const navigationStart=perf.navigationStart||0;
if(perf.loadEventEnd > 0&&navigationStart > 0){
loadTime=perf.loadEventEnd - navigationStart;
}
else if(perf.domInteractive > 0&&navigationStart > 0){
loadTime=perf.domInteractive - navigationStart;
}
else if(perf.domContentLoadedEventEnd > 0&&navigationStart > 0){
loadTime=perf.domContentLoadedEventEnd - navigationStart;
}
else if(window.performance.now){
loadTime=Math.round(window.performance.now());
}
loadTime=Math.max(0, loadTime);
}else if(window.performance&&window.performance.now){
loadTime=Math.round(window.performance.now());
}
const botSignals=detectBotSignals();
const pageData={
url: currentPage,
referrer: document.referrer||'',
source: detectedSource,
utm: sessionUtm||null,
is_entry: !storedEntry,
device: device,
browser: browser,
viewport_width: window.innerWidth,
viewport_height: window.innerHeight,
load_time_ms: loadTime,
traffic_quality: processResult.traffic_quality,
bot_signals: botSignals
};
logCM('page_load', pageData);
if(!storedEntry){
try {
sessionStorage.setItem('cmEntrySet', '1');
} catch (e){
}}
}
let exitDataSent=false;
function sendExitData(){
if(exitDataSent) return;
if(typeof cmAjax==='undefined'||!cmAjax||!cmAjax.url) return;
exitDataSent=true;
const timeOnPage=Math.round((Date.now() - startTime) / 1000);
const hasEngagement=scrollEvents > 0||timeOnPage > 2||maxScroll > 5;
const exitData={
time_on_page: timeOnPage,
exit_page: location.href,
max_scroll: Math.round(maxScroll),
scroll_events: scrollEvents,
has_engagement: hasEngagement
};
const ga4Exit=getGa4Cookies();
if(ga4Exit.clientId&&!ga4ClientId){
ga4ClientId=ga4Exit.clientId;
ga4SessionId=ga4Exit.sessionId;
try {
sessionStorage.setItem('cm_ga4_cid', ga4ClientId);
sessionStorage.setItem('cm_ga4_sid', ga4SessionId);
} catch(e){}}
const data=JSON.stringify({
session_id: sessionId,
type: 'page_end',
details: exitData,
ga_client_id: ga4ClientId,
ga_session_id: ga4SessionId
});
navigator.sendBeacon(cmAjax.url + '?action=cm_log', data);
}
window.addEventListener('beforeunload', sendExitData);
const originalPushState=history.pushState;
history.pushState=function(){
if(!exitDataSent) sendExitData();
exitDataSent=false;
originalPushState.apply(this, arguments);
currentPage=location.href;
startTime=Date.now();
maxScroll=0;
scrollEvents=0;
lastScrollTime=0;
const processResult=shouldProcess(currentPage);
if(processResult.shouldLog){
setTimeout(()=> {
const botSignals=detectBotSignals();
logCM('page_load', {
url: currentPage,
device: detectDevice(),
browser: detectBrowser(),
viewport_width: window.innerWidth,
viewport_height: window.innerHeight,
load_time_ms: 0,
traffic_quality: processResult.traffic_quality,
bot_signals: botSignals
});
}, 100);
}};
window.addEventListener('popstate', ()=> {
if(!exitDataSent) sendExitData();
exitDataSent=false;
currentPage=location.href;
startTime=Date.now();
maxScroll=0;
scrollEvents=0;
lastScrollTime=0;
const processResult=shouldProcess(currentPage);
if(processResult.shouldLog){
setTimeout(()=> {
const botSignals=detectBotSignals();
logCM('page_load', {
url: currentPage,
device: detectDevice(),
browser: detectBrowser(),
viewport_width: window.innerWidth,
viewport_height: window.innerHeight,
load_time_ms: 0,
traffic_quality: processResult.traffic_quality,
bot_signals: botSignals
});
}, 100);
}});
const _cmConvFired={};
function fireCMConversion(type, details){
const now=Date.now();
if(_cmConvFired[type]&&(now - _cmConvFired[type]) < 2000) return;
_cmConvFired[type]=now;
const payload=JSON.stringify({
session_id:    sessionId,
type:          'conversion',
details:       Object.assign({ conversion_type: type, page_url: location.href }, details),
ga_client_id:  ga4ClientId,
ga_session_id: ga4SessionId
});
const sent=navigator.sendBeacon
? navigator.sendBeacon(cmAjax.url + '?action=cm_log', new Blob([payload], { type: 'application/json' }))
: false;
if(!sent){
fetch(cmAjax.url + '?action=cm_log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: payload,
keepalive: true
}).catch(function(){});
}}
document.addEventListener('click', function(e){
try {
const link=e.target.closest('a[href^="tel:"]');
if(link&&!link.closest('[class*="qlwapp"]')){
fireCMConversion('phone_click', {
phone: link.getAttribute('href').replace('tel:', '').trim()
});
}} catch(err){}}, true);
document.addEventListener('click', function(e){
try {
const inWidget=e.target.closest('[class*="qlwapp"]');
if(inWidget){
fireCMConversion('whatsapp_click', {});
return;
}
const waLink=e.target.closest('a[href*="wa.me"], a[href*="whatsapp.com/send"], a[href*="whatsapp.com/chat"]'
);
if(waLink){
fireCMConversion('whatsapp_click', {});
}} catch(err){}}, true);
try {
const _cmFormObserver=new MutationObserver(function(mutations){
for (let i=0; i < mutations.length; i++){
const added=mutations[i].addedNodes;
for (let j=0; j < added.length; j++){
const node=added[j];
if(node.nodeType!==1) continue;
const isSuccess=node.classList&&node.classList.contains('elementor-message-success');
const hasSuccess=node.querySelector&&node.querySelector('.elementor-message-success');
if(isSuccess||hasSuccess){
fireCMConversion('form_submit_success', {});
}}
}});
_cmFormObserver.observe(document.body, { childList: true, subtree: true });
} catch(err){}
try {
if(window.jQuery){
jQuery(document).on('submit_success', function(){
fireCMConversion('form_submit_success', {});
});
}} catch(err){}
document.addEventListener('submit', function(e){
try {
const form=e.target;
if(!form) return;
if(form.classList&&form.classList.contains('elementor-form')) return;
fireCMConversion('form_submit_success', {
form_id: form.id||form.getAttribute('name')||''
});
} catch(err){}}, true);
} catch (e){
if(window.console&&console.error){
console.error('CM tracking error:', e);
}}
}
try {
initCM();
} catch (e){
if(window.console&&console.error){
console.error('CM startup error:', e);
}}
})();