PageSourceSearch

https://skylum.com/js/ver2/channel-mapper.js

js skylum.com collected 2026-09-24 08:26:55 UTC 7,396 bytes, 137 lines download raw bytes

1/**
2 * Channel mapper for Smart Script / attribution.
3 * Computes channel from source, medium, campaign (and link). All PPC sub-types map to one: PPC. Fallback: Other.
4 */
5(function (root) {
6    'use strict';
7
8    var seoRoots = [
9        'chatgpt\\.com', 'openai\\.com', 'claude\\.ai', 'perplexity\\.ai', 'copy\\.ai', 'jasper\\.ai',
10        'blackbox\\.ai', 'quillbot\\.com', 'mistral\\.ai', 'deepseek\\.com',
11        'gemini\\.google\\.com', 'copilot\\.microsoft\\.com', 'chat\\.qwenlm\\.ai',
12        'poe\\.com', 'you\\.com', 'character\\.ai', 'huggingface\\.co', 'meta\\.ai', 'reka\\.ai', 'cohere\\.com', 'grok\\.com', 'qwen\\.ai',
13        'edgeservices\\.bing\\.com', 'ai\\.com', 'phind\\.com', 'openrouter\\.ai', 'lmsys\\.org', 'chat\\.lmsys\\.org', 'x\\.ai', 'bard\\.google\\.com',
14        'writesonic\\.com', 'exa\\.ai', 'bnngpt\\.com', 'nimble\\.ai', 'iask\\.ai', 'aitastic\\.app', 'waldo(?:\\.com)?'
15    ];
16
17    function prepareKeys(gaKeys) {
18        var result = Object.assign({}, gaKeys);
19        ['source', 'medium', 'campaign', 'link'].forEach(function (field) {
20            result[field] = (result[field] || '').toLowerCase();
21        });
22        if (result.medium && result.medium.indexOf(',') !== -1) {
23            var temp = result.medium.split(',');
24            if (temp[0]) result.medium = temp[0].trim();
25        }
26        return result;
27    }
28
29    function reBill(keys) { return keys.medium === 'rebill' && keys.source === 'rebill'; }
30    function push(keys) { return /(notification|push|app_push)/i.test(keys.medium); }
31
32    function brandPpc(keys) {
33        return (/^(cpc)$/i.test(keys.medium) && /brand/i.test(keys.campaign)) ||
34            (/utm/i.test(keys.source) && /cpc/i.test(keys.source) && /brand/i.test(keys.source));
35    }
36    function nonBrandPpc(keys) { return /cpc/i.test(keys.medium) || (/utm/i.test(keys.source) && /cpc/i.test(keys.source)); }
37    function facebookPpc(keys) {
38        return (keys.medium === 'sponsored' && /(fb|ig|facebook|\(none\))/i.test(keys.source)) ||
39            (/utm/i.test(keys.source) && /sponsored/i.test(keys.source));
40    }
41    function displayPpcNew(keys) { return keys.medium === 'display' && /(criteo|banner)/i.test(keys.source); }
42    function remarketing(keys) { return /remarketing/i.test(keys.campaign); }
43
44    function ppc(keys) {
45        return brandPpc(keys) || nonBrandPpc(keys) || facebookPpc(keys) || displayPpcNew(keys) || remarketing(keys);
46    }
47
48    function partner(keys) { return /(partner|newsletter|native)/i.test(keys.medium); }
49    function addPartner(keys) { return keys.source === 'newsletter'; }
50
51    function seo(keys) {
52        var searchEngines = /(duckduckgo\.com|bing\.com|yahoo|baidu\.com|ecosia\.org|startpage\.com|naver\.com|yandex|qwant\.com|swisscows\.com|sogou\.com|petalsearch\.com|mojeek\.com|kagi\.com|dogpile\.com|ask\.com|you\.com|search\.seznam\.cz|search\.brave\.com|chatgpt\.com|gemini\.google\.com|edgeservices\.bing\.com|claude\.ai|iask\.ai|perplexity\.ai)/;
53        if (keys.medium === 'organic' || (searchEngines.test(keys.source) && keys.medium === 'referral') ||
54            (keys.source === 'chatgpt.com' && keys.medium === '(none)') || (keys.source === 'perplexity' && 
54keys.medium === '(none)'))
55            return true;
56        try {
57            var pattern = new RegExp('^(?:[\\w-]+\\.)*(?:' + seoRoots.join('|') + ')$', 'i');
58            if (pattern.test(keys.source)) return true;
59        } catch (e) {}
60        return false;
61    }
62
63    function affiliate(keys) {
64        return /cpa|affiliate|hasoffers/i.test(keys.medium) || (/affiliate/i.test(keys.source) && keys.medium !== 'referral');
65    }
66    function email(keys) {
67        return /email/i.test(keys.medium) || (/^ac|activecampaign|iterable|acemail$/i.test(keys.source) && keys.medium !== 'referral' && keys.medium !== 'account_referral');
68    }
69    function social(keys) {
70        return /^(soc|social|social-network|social-media|sm)$/i.test(keys.medium) ||
71            /(instagram|facebook|pinterest|youtube|twitter|tiktok|linkedin)/i.test(keys.source) ||
72            (/linkedin/i.test(keys.source) && keys.medium !== 'cpc');
73    }
74    function newsScreen(keys) { return /(newsscreen|newscreen)/i.test(keys.medium) || /(newsscreen|newscreen)/i.test(keys.source); }
75    function inApp(keys) {
76        return /inapp/i.test(keys.source) || keys.source === 'app' || /app/i.test(keys.medium) ||
77            (keys.link && /marketplace/i.test(keys.link)) ||
78            (keys.link && /web-view/i.test(keys.link) && !/winstore/i.test(keys.link) && !/promo/i.test(keys.link));
79    }
80    function referral(keys) { return /referral/i.test(keys.medium) || /referral/i.test(keys.source); }
81    function direct(keys) {
82        return keys.source === '(direct)' || (keys.source === '' && (keys.medium === '(none)' || keys.medium === '')) ||
83            (keys.medium === '' && keys.source === '(none)') || (keys.medium === '(none)' && keys.source === '(none)');
84    }
85    function insiders(keys) { return keys.source === 'insiders'; }
86
87    var checks = [
88        { name: 'Rebill', method: reBill },
89        { name: 'Push', method: push },
90        { name: 'PPC', method: ppc },
91        { name: 'Partner', method: partner },
92        { name: 'SEO', method: seo },
93        { name: 'Affiliate', method: affiliate },
94        { name: 'Email', method: email },
95        { name: 'Social', method: social },
96        { name: 'NewsScreen', method: newsScreen },
97        { name: 'InApp', method: inApp },
98        { name: 'Referral', method: referral },
99        { name: 'Direct', method: direct },
100        { name: 'Insiders', method: insiders },
101        { name: 'Partner', method: addPartner }
102    ];
103
104    function getChannelName(gaKeys) {
105        var keys = prepareKeys(gaKeys || {});
106        for (var i = 0; i < checks.length; i++) {
107            try {
108                if (checks[i].method(keys)) return checks[i].name;
109            } catch (e) {}
110        }
111        return 'Other';
112    }
113
114    var ChannelMapper = { getChannelName: getChannelName, prepareKeys: prepareKeys };
115    if (typeof module !== 'undefined' && module.exports) module.exports = ChannelMapper;
116    else root.ChannelMapper = ChannelMapper;
117
118    if (typeof root !== 'undefined' && root.__channelMapperTest) {
119        var M = root.ChannelMapper;
120        var tests = [
121            { keys: { source: 'google', medium: 'cpc', campaign: 'brand_luminar' }, expect: 'PPC' },
122            { keys: { source: 'fb', medium: 'sponsored', campaign: 'any' }, expect: 'PPC' },
123            { keys: { source: 'utm', medium: 'cpc', campaign: 'remarketing' }, expect: 'PPC' },
124            { keys: { source: 'google', medium: 'organic' }, expect: 'SEO' },
125            { keys: { source: 'ac', medium: 'email' }, expect: 'Email' },
126            { keys: { source: 'facebook.com', medium: 'referral' }, expect: 'Social' },
127            { keys: { source: '(direct)', medium: '(none)' }, expect: 'Direct' },
128            { keys: { source: 'unknown', medium: 'xyz' }, expect: 'Other' }
129        ];
130        var ok = 0, fail = 0;
131        tests.forEach(function (t) {
132            var got = M.getChannelName(t.keys);
133            if (got === t.expect) { ok++; } else { fail++; console.log('[ChannelMapper] FAIL', t.keys, 'expected', t.expect, 'got', got); }
134        });
135        console.log('[ChannelMapper] tests: ' + ok + ' ok, ' + fail + ' fail');
136    }
137})(typeof window !== 'undefined' ? window : this);

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.