1/* 2 * GrainCheck â universal webâapp install attribution (Tenjin custom "web" channel). 3 * 4 * Every "Install" / store button is routed THROUGH a Tenjin click tracking link, 5 * which records the click (with the inbound source) and redirects to the store. 6 * Works for ANY channel â Meta, Google, TikTok, email, Telegram, referral, organic 7 * â the source is read from UTM tags / click-ids and passed to Tenjin. 8 * 9 * Links come from Tenjin â CONFIGURE â Campaigns (custom channel "web"), per platform. 10 * Meta needs no link here (it's a self-attributing network: S2S callbacks + site pixel). 11 */ 12(function (w, d) { 13 // ===== Tenjin custom "web" channel click links (per platform) + store fallbacks ===== 14 var WEB_LINK = { 15 ios: 'https://track.tenjin.com/v0/click/dtcsfr8fPIbvngpSTvAzZ', 16 android: 'https://track.tenjin.com/v0/click/fOrJP5JZPJ1iow8xWRkc8j' 17 }; 18 var STORE_URL = { 19 ios: 'https://apps.apple.com/app/graincheck/id6761753467', 20 android: 'https://play.google.com/store/apps/details?id=com.graincheck.app' 21 }; 22 var META_PIXEL_ID = '1115565107707861'; 23 // ==================================================================================== 24 25 var q = new URLSearchParams(w.location.search); 26 var get = function (k) { return q.get(k) || ''; }; 27 var SRC = { 28 utm_source: get('utm_source'), utm_medium: get('utm_medium'), 29 utm_campaign: get('utm_campaign'), utm_content: get('utm_content'), utm_term: get('utm_term'), 30 fbclid: get('fbclid'), gclid: get('gclid'), ttclid: get('ttclid') 31 }; 32 function cookie(n) { var m = d.cookie.match('(^|;)\\s*' + n + '\\s*=\\s*([^;]+)'); return m ? m.pop() : ''; } 33 34 // Readable source label so Tenjin groups installs by where they came from. 35 function sourceLabel() { 36 var s = SRC.utm_source || 37 (SRC.fbclid ? 'meta' : SRC.gclid ? 'google' : SRC.ttclid ? 'tiktok' : 'organic'); 38 return SRC.utm_campaign ? s + '_' + SRC.utm_campaign : s; 39 } 40 41 // Full Tenjin click URL for a platform â records click, then redirects to the store. 42 function GC_tenjinClickURL(platform) { 43 var base = WEB_LINK[platform]; 44 if (!base) return STORE_URL[platform] || null; // no tenjin link â go straight to store 45 var sep = base.indexOf('?') === -1 ? '?' : '&'; 46 return base + sep + [ 47 'campaign_name=' + encodeURIComponent(sourceLabel()), 48 'creative_name=' + encodeURIComponent(SRC.utm_content || SRC.utm_medium), 49 'tenjin_parameter_0=' + encodeURIComponent(SRC.fbclid || cookie('_fbc')), 50 'tenjin_parameter_1=' + encodeURIComponent(cookie('_fbp')), 51 'tenjin_parameter_2=' + encodeURIComponent(SRC.utm_source), 52 'tenjin_parameter_3=' + encodeURIComponent(SRC.gclid || SRC.ttclid), 53 'redirect_url=' + encodeURIComponent(STORE_URL[platform]) 54 ].join('&'); 55 } 56 w.GC_tenjinClickURL = GC_tenjinClickURL; // install.html builds its redirect from this 57 58 // Side pings for Meta optimisation + GA (Tenjin attribution is the redirect itself). 59 function GC_fireInstallTracking() { 60 if (META_PIXEL_ID && w.fbq) { try { w.fbq('trackCustom', 'InstallClick', { source: SRC.utm_source || 'web' }); } catch (e) {} } 61 if (w.gtag) { try { w.gtag('event', 'install_click', { source: SRC.utm_source || 'direct', campaign: SRC.utm_campaign }); } catch (e) {} } 62 } 63 w.GC_fireInstallTracking = GC_fireInstallTracking; 64 65 function platformOf(href) { 66 return /apps\.apple\.com/.test(href) ? 'ios' : /play\.google\.com/.test(href) ? 'android' : null; 67 } 68 69 // Point every store button at its Tenjin click link (which redirects to the store). 70 function wire() { 71 var links = d.querySelectorAll('a[href*="play.google.com/store/apps"], a[href*="apps.apple.com"]'); 72 Array.prototype.forEach.call(links, function (a) { 73 var plat = platformOf(a.href); 74 if (!plat) return; 75 a.href = GC_tenjinClickURL(plat); 76 a.addEventListener('click', GC_fireInstallTracking, { passive: true }); 77 }); 78 } 79 80 if (w.GC_NO_AUTOWIRE) return; 81 if (d.readyState !== 'loading') wire(); 82 else d.addEventListener('DOMContentLoaded', wire); 83})(window, document);
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.