PageSourceSearch

https://www.epsglobal.com/CMSScripts/Custom/set-visitor-source-cookie.js

js epsglobal.com collected 2026-10-02 03:29:19 UTC 18,760 bytes, 410 lines download raw bytes

1(function() {
2    'use strict';
3
4    // ============================================
5    // ATTRIBUTION TRACKING SECTION
6    // ============================================
7
8    var config = {
9        debug: true, // Set to false in production
10        cookieExpiration: 30 * 24 * 60 * 60 * 1000, // 30 days
11        cookieVersion: 'v2' // Must match WordPress site
12    };
13
14    function log(msg, data) {
15        if (config.debug && console && console.log) {
16            console.log('[Attribution]', msg, data || '');
17        }
18    }
19
20    // ============================================
21    // FIXED LISTS — MUST MATCH ODOO
22    // ============================================
23
24    var KNOWN_SOURCES = [
25        'google','bing','yahoo','duckduckgo','yandex','baidu','brave','live',
26        'facebook','linkedin','twitter','tiktok','instagram','youtube','reddit',
27        'chatgpt','perplexity','claude','gemini','copilot',
28        'epsglobal','newsletter','direct','other'
29    ];
30
31    var KNOWN_MEDIUMS = [
32        'organic','cpc','cpm','paid_social','social','email',
33        'referral','affiliate','ai','display','(none)','other'
34    ];
35
36    var SOURCE_CONTAINS_MAP = [
37        { match: 'chatgpt.com',           source: 'chatgpt' },
38        { match: 'chat.openai.com',       source: 'chatgpt' },
39        { match: 'openai.com',            source: 'chatgpt' },
40        { match: 'perplexity.ai',         source: 'perplexity' },
41        { match: 'claude.ai',             source: 'claude' },
42        { match: 'gemini.google.com',     source: 'gemini' },
43        { match: 'copilot.microsoft.com', source: 'copilot' },
44        { match: 'bing.com/chat',         source: 'copilot' },
45        { match: 'epsglobal',             source: 'epsglobal' },
46        { match: 'bing.com',              source: 'bing' },
47        { match: 'live.com',              source: 'live' },
48        { match: 'search.brave.com',      source: 'brave' },
49        { match: 'brave.com',             source: 'brave' },
50        { match: 'facebook.com',          source: 'facebook' },
51        { match: 'fb.com',                source: 'facebook' },
52        { match: 'linkedin.com',          source: 'linkedin' },
53        { match: 'lnkd.in',               source: 'linkedin' },
54        { match: 't.co',                  source: 'twitter' },
55        { match: 'x.com',                 source: 'twitter' },
56        { match: 'twitter.com',           source: 'twitter' },
57        { match: 'reddit.com',            source: 'reddit' },
58        { match: 'youtube.com',           source: 'youtube' },
59        { match: 'youtu.be',              source: 'youtube' },
60        { match: 'instagram.com',         source: 'instagram' },
61        { match: 'tiktok.com',            source: 'tiktok' }
62    ];
63
64    var AI_SOURCES = ['chatgpt','perplexity','claude','gemini','copilot'];
65
66    function resolveSource(raw) {
67        if (!raw) return 'other';
68        var s = raw.toString().toLowerCase().replace(/^www\./, '');
69        if (s === '(direct)' || s === 'direct') return 'direct';
70        if (KNOWN_SOURCES.indexOf(s) > -1) return s;
71        for (var i = 0; i < SOURCE_CONTAINS_MAP.length; i++) {
72            if (s.indexOf(SOURCE_CONTAINS_MAP[i].match) > -1) {
73                return SOURCE_CONTAINS_MAP[i].source;
74            }
75        }
76        if (/(^|\.)google\./.test(s)) return 'google';
77        if (/(^|\.)yahoo\./.test(s)) return 'yahoo';
78        if (/(^|\.)yandex\./.test(s)) return 'yandex';
79        if (/(^|\.)duckduckgo\./.test(s)) return 'duckduckgo';
80        if (/(^|\.)baidu\./.test(s)) return 'baidu';
81        return 'other';
82    }
83
84    function normaliseAttribution(gaReferral) {
85        var src = resolveSource(gaReferral.utmcsr);
86        var med = (gaReferral.utmcmd || '').toString().toLowerCase();
87        gaReferral.utmcsr = src;
88        if (AI_SOURCES.indexOf(src) > -1) {
89            if (med === 'referral' || med === '' || med === 'other' || KNOWN_MEDIUMS.indexOf(med) === -1) {
90                med = 'ai';
91            }
92        }
93        if (src === 'direct') { med = '(none)'; }
94        if (med === '' || KNOWN_MEDIUMS.indexOf(med) === -1) { med = 'other'; }
95        gaReferral.utmcmd = med;
96        return gaReferral;
97    }
98
99    function initAttributionTracking(callback) {
100        try {
101            log('Starting attribution tracking');
102
103            var referrer = document.referrer || '';
104            var gaReferral = {
105                'utmcsr': '(direct)',
106                'utmcmd': '(none)',
107                'utmccn': '(not set)'
108            };
109
110            var thisHostname = document.location.hostname;
111            var thisDomain = getDomain(thisHostname);
112            var referringDomain = getDomain(referrer);
113            var sessionCookie = getCookie('__utmzzses');
114            var cookieExpiration = new Date(Date.now() + config.cookieExpiration);
115            var qs = document.location.search.replace('?', '');
116            var hash = document.location.hash.replace('#', '');
117            var gaParams = parseGoogleParams(qs + '#' + hash);
118            var referringInfo = parseGaReferrer(referrer);
119            var storedVals = getCookie('__utmz') || getCookie('__utmzz');
120            var newCookieVals = [];
121
122            var keyMap = {
123                'utm_source': 'utmcsr',
124                'utm_medium': 'utmcmd',
125                'utm_campaign': 'utmccn',
126                'utm_content': 'utmcct',
127                'utm_term': 'utmctr',
128                'gclid': 'utmgclid',
129                'dclid': 'utmdclid',
130                'msclkid': 'utmmsclid',
131                'fbclid': 'utmfbclid',
132                'ttclid': 'utmttclid',
133                'li_fat_id': 'utmliid',
134                'twclid': 'utmtwclid',
135                'ref': 'utmref'
136            };
137
138            var keyFilter = ['utmcsr','utmcmd','utmccn','utmcct','utmctr','utmgclid','utmdclid','utmmsclid','utmfbclid','utmttclid','utmliid','utmtwclid','utmref'];
139
140            if (sessionCookie && referringDomain === thisDomain) {
141                gaParams = null;
142                referringInfo = null;
143            }
144
145            if (gaParams && (gaParams.utm_source || gaParams.gclid || gaParams.dclid || gaParams.msclkid || gaParams.fbclid || gaParams.ttclid || gaParams.li_fat_id || gaParams.twclid)) {
146                log('Processing URL parameters', gaParams);
147                for (var key in gaParams) {
148                    if (gaParams.hasOwnProperty(key) && typeof gaParams[key] !== 'undefined') {
149                        var keyName = keyMap[key];
150                        if (keyName) { gaReferral[keyName] = gaParams[key]; }
151                    }
152                }
153
154                // Click-ID inference only fills in source/medium when they were
155                // NOT explicitly provided via utm_source — explicit UTMs win.
156                if (!gaParams.utm_source) {
157                    if (gaParams.gclid || gaParams.dclid) {
158                        gaReferral.utmcsr = 'google';
159                        gaReferral.utmcmd = gaParams.gclid ? 'cpc' : 'cpm';
160                    } else if (gaParams.msclkid) {
161                        gaReferral.utmcsr = 'bing';     gaReferral.utmcmd = 'cpc';
162                    } else if (gaParams.fbclid) {
163                        gaReferral.utmcsr = 'facebook'; gaReferral.utmcmd = 'paid_social';
164                    } else if (gaParams.ttclid) {
165                        gaReferral.utmcsr = 'tiktok';   gaReferral.utmcmd = 'paid_social';
166                    } else if (gaParams.li_fat_id) {
167                        gaReferral.utmcsr = 'linkedin'; gaReferral.utmcmd = 'paid_social';
168                    } else if (gaParams.twclid) {
169                        gaReferral.utmcsr = 'twitter';  gaReferral.utmcmd = 'paid_social';
170                    }
171                } else if (!gaParams.utm_medium) {
172                    // utm_source given without utm_medium: infer medium from click ID
173                    if (gaParams.gclid || gaParams.msclkid) {
174                        gaReferral.utmcmd = 'cpc';
175                    } else if (gaParams.dclid) {
176                        gaReferral.utmcmd = 'cpm';
177                    } else if (gaParams.fbclid || gaParams.ttclid || gaParams.li_fat_id || gaParams.twclid) {
178                        gaReferral.utmcmd = 'paid_social';
179                    }
180                }
181            } else if (referringInfo) {
182                log('Processing referrer info', referringInfo);
183                gaReferral.utmcsr = referringInfo.source;
184                gaReferral.utmcmd = referringInfo.medium;
185                if (referringInfo.term) { gaReferral.utmctr = referringInfo.term; }
186            } else if (storedVals) {
187                log('Using stored values');
188                var values = {};
189                var raw = storedVals.split('|');
190                for (var i = 0; i < raw.length; i++) {
191                    // Split on the FIRST '=' only so values containing '=' survive
192                    var eqPos = raw[i].indexOf('=');
193                    if (eqPos > -1) {
194                        var _key = raw[i].substring(0, eqPos).split('.').pop();
195                        values[_key] = raw[i].substring(eqPos + 1);
196                    }
197                }
198                gaReferral = values;
199            }
200
201            gaReferral = normaliseAttribution(gaReferral);
202            log('Normalised attribution', { source: gaReferral.utmcsr, medium: gaReferral.utmcmd });
203
204            for (var key in gaReferral) {
205                if (gaReferral.hasOwnProperty(key) && typeof gaReferral[key] !== 'undefined' && keyFilter.indexOf(key) > -1) {
206                    newCookieVals.push(key + '=' + gaReferral[key]);
207                }
208            }
209
210            var cookieValue = newCookieVals.join('|');
211            var currentCookieName = 'currentTrafficSource_' + config.cookieVersion;
212            var initialCookieName = 'initialTrafficSource_' + config.cookieVersion;
213
214            if (cookieValue) {
215                log('Setting cookies', cookieValue);
216                writeCookie(currentCookieName, cookieValue, cookieExpiration, '/', thisDomain);
217                if (!getCookie(initialCookieName)) {
218                    writeCookie(initialCookieName, cookieValue, cookieExpiration, '/', thisDomain);
219                }
220                writeCookie('__utmzzses', '1', null, '/', thisDomain);
221                log('Attribution tracking complete');
222            }
223
224            if (callback && typeof callback === 'function') { callback(); }
225
226        } catch (error) {
227            console.error('[Attribution Error]', error);
228            if (callback && typeof callback === 'function') { callback(); }
229        }
230    }
231
232    function parseGoogleParams(str) {
233        var campaignParams = ['source','medium','campaign','term','content'];
234        // FIXED: 'msclkid' was previously written as '(d|g|ms)cli
234d', which only
235        // matched 'msclid' — Microsoft's param has a 'k' (msclkid), so Bing paid
236        // clicks were never detected and fell through to bing/organic.
237        // Also anchored to start-of-string or a separator so e.g. 'href=' or
238        // 'xref=' can't false-match the 'ref' parameter.
239        var regex = new RegExp('(^|[?&#])((utm_(' + campaignParams.join('|') + ')|gclid|dclid|msclkid|fbclid|ttclid|li_fat_id|twclid|ref)=([^&#]*))', 'gi');
240        var paramsObj = {};
241        var match;
242
243        while ((match = regex.exec(str)) !== null) {
244            // match[2] is 'name=value'; split on the FIRST '=' only so values
245            // containing '=' (some click IDs, base64-ish tokens) aren't dropped
246            var pair = match[2];
247            var eqPos = pair.indexOf('=');
248            if (eqPos > -1) {
249                var name = pair.substring(0, eqPos).toLowerCase();
250                var value = pair.substring(eqPos + 1);
251                try {
252                    paramsObj[name] = decodeURIComponent(value.replace(/\+/g, ' '));
253                } catch (e) {
254                    paramsObj[name] = value; // malformed encoding — keep raw
255                }
256            }
257        }
258
259        return Object.keys(paramsObj).length > 0 ? paramsObj : null;
260    }
261
262    function parseGaReferrer(referrer) {
263        if (!referrer) return null;
264        var searchEngines = {
265            'google':          { p: 'q',    n: 'google' },
266            'yahoo.com':       { p: 'p',    n: 'yahoo' },
267            'bing.com':        { p: 'q',    n: 'bing' },
268            'baidu.com':       { p: 'wd',   n: 'baidu' },
269            'yandex':          { p: 'text', n: 'yandex' },
270            'duckduckgo.com':  { p: 'q',    n: 'duckduckgo' },
271            'search.brave.com':{ p: 'q',    n: 'brave' }
272        };
273        var a = document.createElement('a');
274        a.href = referrer;
275        var hostname = a.hostname.toLowerCase();
276        var values = null;
277        for (var domain in searchEngines) {
278            if (hostname.indexOf(domain) > -1) {
279                var searchEngine = searchEngines[domain];
280                var termRegex = new RegExp(searchEngine.p + '=([^&#]*)', 'i');
281                var match = a.search.match(termRegex);
282                values = {
283                    source: searchEngine.n,
284                    medium: 'organic',
285                    term: match ? decodeURIComponent(match[1]) : '(not provided)'
286                };
287                break;
288            }
289        }
290        if (!values && hostname && hostname !== document.location.hostname) {
291            values = { source: hostname, medium: 'referral' };
292        }
293        return values;
294    }
295
296    function getDomain(url) {
297        if (!url) return null;
298        try {
299            var a = document.createElement('a');
300            a.href = url;
301            var hostname = a.hostname.toLowerCase().replace(/^www\./, '');
302            var matches = hostname.match(/([^.]+\.(?:co\.uk|com\.au|co\.nz|co\.za|com\.br|co\.jp|co\.kr|co\.in|[^.]+))$/);
303            return matches ? matches[0] : hostname;
304        } catch(e) {
305            return document.location.hostname;
306        }
307    }
308
309    function writeCookie(name, value, expiration, path, domain) {
310        var cookieStr = name + '=' + encodeURIComponent(value);
311        if (expiration) { cookieStr += '; Expires=' + expiration.toUTCString(); }
312        if (path)       { cookieStr += '; Path=' + path; }
313        if (domain)     { cookieStr += '; Domain=' + domain; }
314        cookieStr += '; SameSite=Lax';
315        if (window.location.protocol === 'https:') { cookieStr += '; Secure'; }
316        document.cookie = cookieStr;
317    }
318
319    function getCookie(name) {
320        var nameEQ = name + '=';
321        var ca = document.cookie.split(';');
322        for (var i = 0; i < ca.length; i++) {
323            var c = ca[i].trim();
324            if (c.indexOf(nameEQ) === 0) { return decodeURIComponent(c.substring(nameEQ.length)); }
325        }
326        return null;
327    }
328
329    // ============================================
330    // FORM POPULATION SECTION
331    // ============================================
332
333    function parseCookieValue(cookieValue) {
334        if (!cookieValue) return {};
335        var params = {};
336        var pairs = cookieValue.split('|');
337        for (var i = 0; i < pairs.length; i++) {
338            // Split on the FIRST '=' only so values containing '=' survive
339            var eqPos = pairs[i].indexOf('=');
340            if (eqPos > -1) {
341                var key = pairs[i].substring(0, eqPos);
342                var value = pairs[i].substring(eqPos + 1);
343                params[key] = decodeURIComponent(value.replace(/\+/g, ' '));
344            }
345        }
346        return params;
347    }
348
349    function setFieldValue(fieldId, value) {
350        var field = document.getElementById(fieldId);
351        if (field) {
352            field.value = value || '';
353            log('Set field ' + fieldId + ' to: ' + (value || '(empty)'));
354        } else {
355            log('Field not found: ' + fieldId);
356        }
357    }
358
359    function populateFormFields() {
360        log('Starting form population');
361
362        // Read the versioned initial traffic source cookie (matches WordPress)
363        var cookieValue = getCookie('initialTrafficSource_' + config.cookieVersion);
364        if (!cookieValue) {
365            log('initialTrafficSource_' + config.cookieVersion + ' cookie not found');
366            return;
367        }
368
369        log('Cookie value found:', cookieValue);
370        var params = parseCookieValue(cookieValue);
371        log('Parsed params:', params);
372
373        // Kentico form field IDs — unchanged from original
374        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl01_On_lineForm_plcUp_viewBiz_Os_txtText', params.utmcsr);
375        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl01_On_lineForm_plcUp_viewBiz_Om_txtText', params.utmcmd);
376        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl01_On_lineForm_plcUp_viewBiz_Oc_txtText', params.utmccn);
377        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl01_On_lineForm_plcUp_viewBiz_Ok_txtText', params.utmcct);
378        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl18_On_lineForm2_viewBiz_Os_txtText', params.utmcsr);
379        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl18_On_lineForm2_viewBiz_Om_txtText', params.utmcmd);
380        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl18_On_lineForm2_viewBiz_Oc_txtText', params.utmccn);
381        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl18_On_lineForm2_viewBiz_Ok_txtText', params.utmcct);
382        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl16_On_lineForm2_viewBiz_Os_txtText', params.utmcsr);
383        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl16_On_lineForm2_viewBiz_Om_txtText', params.utmcmd);
384        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl16_On_lineForm2_viewBiz_Oc_txtText', params.utmccn);
385        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl16_On_lineForm2_viewBiz_Ok_txtText', params.utmcct);
386        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl02_On_lineForm_plcUp_viewBiz_Os_txtText', params.utmcsr);
387        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl02_On_lineForm_plcUp_viewBiz_Om_txtText', params.utmcmd);
388        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl02_On_lineForm_plcUp_viewBiz_Oc_txtText', params.utmccn);
389        setFieldValue('p_lt_ctl07_pageplaceholder_p_lt_ctl02_On_lineForm_plcUp_viewBiz_Ok_txtText', params.utmcct);
390
391        log('Form population complete');
392    }
393
394    // ============================================
395    // INITIALIZATION
396    // ============================================
397
398    function init() {
399        initAttributionTracking(function() {
400            if (document.readyState === 'loading') {
401                document.addEventListener('DOMContentLoaded', populateFormFields);
402            } else {
403                populateFormFields();
404            }
405        });
406    }
407
408    init();
409
410})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.