Skill Tracer
Skills › Marketing Skills › conversion-tracking › fluent-forms.js

fluent-forms.js

101 lines · skills/conversion-tracking/assets/snippets/fluent-forms.js at 88c79f6

1/*! Fluent Forms conversion detection (MIT)
2 * Detects: form submission. Pushes dataLayer event: fluent_forms_submitted
3 * From the conversion-tracking skill in Marketing Skills */
4(function () {
5 if (window.__ctFluentForms) return;
6 window.__ctFluentForms = true;
7 window.dataLayer = window.dataLayer || [];
8 var listeners = [], lastEvent = null;
9 // Fluent Forms 6.2.5 reports one success response through three different
10 // event objects. Deduplicate by response-object identity and original form
11 // node; never inspect or retain a response's fields.
12 var seenResponses = typeof WeakMap === 'function' ? new WeakMap() : null;
13 window.__ctFluentSubscribe = function (listener) {
14 if (listener && typeof listener === 'object') listeners.push(listener);
15 };
16 function isForm(form) {
17 try {
18 if (!form || String(form.tagName || '').toLowerCase() !== 'form') return false;
19 if (String(form.className || '').indexOf('frm-fluent-form') !== -1) return true;
20 if (form.closest && form.closest('.fluentform')) return true;
21 return /^fluentform_\d+$/.test(form.id || '');
22 } catch (e) { return false; }
23 }
24 function asForm(candidate) {
25 try {
26 var el = candidate;
27 if (el && el.jquery && el.length) el = el[0];
28 return isForm(el) ? el : null;
29 } catch (e) { return null; }
30 }
31 function notify(phase, form, candidate, event, arg2, arg3) {
32 var handled = false;
33 for (var i=0;i<listeners.length;i++) {
34 try { var fn=listeners[i] && listeners[i][phase]; if(typeof fn==='function' && fn(form,candidate,event,arg2,arg3)===true)handled=true; } catch(e) {}
35 }
36 return handled;
37 }
38 function submit(event) {
39 try { var form=event.target; if(isForm(form)) notify('beforeAttempt',form,false,event); } catch(e) {}
40 }
41 function resolve(event,arg2,arg3) {
42 try {
43 var form=asForm(arg2 && arg2.form) || asForm(arg3) || asForm(arg2);
44 if(form)return form;
45 var oe=event && event.originalEvent;
46 if(oe && oe.detail) return asForm(oe.detail.form);
47 return asForm(event && event.target);
48 } catch(e) { return null; }
49 }
50 function responseFor(event, arg2) {
51 try {
52 var descriptor = arg2 && Object.getOwnPropertyDescriptor(arg2, 'response');
53 var response = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value') ? descriptor.value : null;
54 if (response && typeof response === 'object') return response;
55 var originalEvent = event && event.originalEvent;
56 var detail = originalEvent && originalEvent.detail;
57 descriptor = detail && Object.getOwnPropertyDescriptor(detail, 'response');
58 response = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value') ? descriptor.value : null;
59 return response && typeof response === 'object' ? response : null;
60 } catch(e) { return null; }
61 }
62 function alreadySeen(response, form) {
63 try {
64 if (!seenResponses || !response || !form || typeof response !== 'object' || typeof form !== 'object') return true;
65 var forms = seenResponses.get(response);
66 if (!forms) {
67 forms = typeof WeakSet === 'function' ? new WeakSet() : null;
68 if (!forms) return true;
69 seenResponses.set(response, forms);
70 }
71 if (forms.has(form)) return true;
72 forms.add(form);
73 return false;
74 } catch(e) { return true; }
75 }
76 function success(event,arg2,arg3) {
77 try {
78 if (event && typeof event === 'object' && event === lastEvent) return;
79 lastEvent=event;
80 if (event && typeof event === 'object') setTimeout(function(){if(lastEvent===event)lastEvent=null;},0);
81 var form=resolve(event,arg2,arg3);
82 if(!form)return;
83 var response=responseFor(event,arg2);
84 if(!response || alreadySeen(response,form))return;
85 if(!notify('onCandidate',form,true,event,arg2,arg3)) window.dataLayer.push({event:'fluent_forms_submitted',form_id:form.id || ''});
86 } catch(e) {}
87 }
88 document.addEventListener('submit',submit,true);
89 function hook() {
90 try {
91 if (window.__ctFluentSuccessHooked || !window.jQuery) return false;
92 window.__ctFluentSuccessHooked=true;
93 window.jQuery(document).on('fluentform_submission_success',success);
94 return true;
95 } catch(e) { return false; }
96 }
97 if(!hook()) {
98 document.addEventListener('DOMContentLoaded',hook);
99 window.addEventListener('load',hook);
100 }
101})();