33 lines
4.4 KiB
JavaScript
33 lines
4.4 KiB
JavaScript
/* toolctrl marketing-funnel SDK (Phase 05 #78). Single-file <3KB gz mini-snippet
|
|
for marketing landing-pages. Auto-captures UTMs+gclid+fbclid+referrer on page
|
|
load, stores first-touch attribution in localStorage so multi-page visits
|
|
attribute the same source, and POSTs to /api/leads/capture on demand or via
|
|
bound forms. Usage:
|
|
<script src="https://cdn.example.com/embed/marketing.js"
|
|
data-api-url="https://app.example.com" defer></script>
|
|
<script>
|
|
toolctrlMarketing.bindFormById('lead-form');
|
|
// or imperative:
|
|
toolctrlMarketing.captureLead({ email: 'a@b.co', source: 'blog-cta' });
|
|
</script>
|
|
*/
|
|
(function(){if(typeof window==="undefined"||typeof document==="undefined")return;
|
|
var ATTR_KEY="__tcm_attr",ATTR_KEYS=["utmSource","utmMedium","utmCampaign","utmTerm","utmContent","gclid","fbclid","referrer","landingPage"];
|
|
function s(){try{return document.currentScript||(function(){var a=document.getElementsByTagName("script");for(var i=a.length-1;i>=0;i--){if(a[i].src&&a[i].src.indexOf("/embed/marketing.js")!==-1)return a[i];}return null;})();}catch(e){return null;}}
|
|
var sc=s();
|
|
var apiUrl=(sc&&sc.getAttribute("data-api-url"))||(sc&&sc.src?new URL(sc.src).origin:"");
|
|
function qp(){var o={},u;try{u=new URL(location.href);}catch(e){return o;}var m={utm_source:"utmSource",utm_medium:"utmMedium",utm_campaign:"utmCampaign",utm_term:"utmTerm",utm_content:"utmContent",gclid:"gclid",fbclid:"fbclid"};for(var k in m){if(u.searchParams.has(k))o[m[k]]=u.searchParams.get(k);}return o;}
|
|
function readStored(){try{var raw=localStorage.getItem(ATTR_KEY);if(!raw)return{};return JSON.parse(raw)||{};}catch(e){return{};}}
|
|
function writeStored(a){try{localStorage.setItem(ATTR_KEY,JSON.stringify(a));}catch(e){}}
|
|
function mergeAttr(){var stored=readStored(),fresh=qp(),out={};for(var i=0;i<ATTR_KEYS.length;i++){var k=ATTR_KEYS[i];if(stored[k])out[k]=stored[k];}for(var k2 in fresh){if(!out[k2])out[k2]=fresh[k2];}if(!out.referrer&&document.referrer)out.referrer=document.referrer.slice(0,2048);if(!out.landingPage)out.landingPage=location.href.slice(0,2048);writeStored(out);return out;}
|
|
var attr=mergeAttr();
|
|
function postCapture(payload){if(!apiUrl)return Promise.reject(new Error("api-url missing"));return fetch(apiUrl.replace(/\/$/,"")+"/api/leads/capture",{method:"POST",headers:{"content-type":"application/json"},credentials:"omit",mode:"cors",body:JSON.stringify(payload)});}
|
|
function captureLead(input){if(!input||typeof input.email!=="string")return Promise.reject(new Error("email required"));var body={email:input.email,attribution:attr};for(var k in input){if(k==="email"||k==="attribution")continue;body[k]=input[k];}if(input.attribution){for(var k3 in input.attribution){body.attribution[k3]=input.attribution[k3];}}return postCapture(body).then(function(r){return{ok:r.ok,status:r.status};});}
|
|
function readForm(f){var d={},els=f.elements;for(var i=0;i<els.length;i++){var el=els[i];if(!el.name)continue;var n=el.name;if(el.type==="checkbox"||el.type==="radio"){if(!el.checked)continue;d[n]=el.value;}else{d[n]=el.value;}}return d;}
|
|
function bindForm(f){if(!f||f.__tcm_bound)return;f.__tcm_bound=true;f.addEventListener("submit",function(e){var d=readForm(f);if(!d.email){return;}e.preventDefault();var src=f.getAttribute("data-source")||sc&&sc.getAttribute("data-source")||null;var p={email:d.email,firstName:d.firstName||d.first_name||null,lastName:d.lastName||d.last_name||null,phone:d.phone||null,company:d.company||null,source:src,website_url:d.website_url||""};if(d.turnstileToken||d["cf-turnstile-response"])p.turnstileToken=d.turnstileToken||d["cf-turnstile-response"];captureLead(p).then(function(res){f.dispatchEvent(new CustomEvent("tcm:captured",{detail:res}));if(res.ok)f.reset();}).catch(function(err){f.dispatchEvent(new CustomEvent("tcm:error",{detail:{error:String(err&&err.message||err)}}));});});}
|
|
function bindFormById(id){var f=document.getElementById(id);bindForm(f);return!!f;}
|
|
function bindFormsByAttr(){var ns=document.querySelectorAll("form[data-toolctrl-lead]");for(var i=0;i<ns.length;i++)bindForm(ns[i]);}
|
|
if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",bindFormsByAttr);}else{bindFormsByAttr();}
|
|
window.toolctrlMarketing={captureLead:captureLead,bindFormById:bindFormById,bindFormsByAttr:bindFormsByAttr,getAttribution:function(){return Object.assign({},attr);},resetAttribution:function(){try{localStorage.removeItem(ATTR_KEY);}catch(e){}attr={};}};
|
|
})();
|