PageSourceSearch

https://chronosphere.io/wp-content/themes/chronosphere/js/bounce.js

js chronosphere.io collected 2026-10-01 10:35:14 UTC 10,129 bytes, 247 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2    if (typeof MktoForms2 === "undefined") return;
3
4    // === CONFIGURATION ===
5    const CONFIG = {
6        ENABLED: true, // This is a global on/off switch for the script's core functionality.
7        FEATURE_TOGGLES: {
8            enableValidation: true,
9            enableAnalytics: true
10        },
11        DEFAULT_SCRIPT_VERSION: "v1.0.0",
12        RETRY_LIMIT: 3,
13        RETRY_DELAY_MS: 500,
14        DEBUG_DEFAULT_ENVIRONMENTS: ["DEV", "STAGE"]
15    };
16
17    let scriptVersion = CONFIG.DEFAULT_SCRIPT_VERSION;
18
19    const currentScript = document.currentScript || (function () {
20        const scripts = document.getElementsByTagName('script');
21        return scripts[scripts.length - 1];
22    })();
23
24    const scriptLocation = currentScript?.src || 'unknown';
25    const scriptName = scriptLocation.split('/').pop() || 'unknown';
26
27    try {
28        const scriptUrl = new URL(scriptLocation);
29        const scriptVerParam = scriptUrl.searchParams.get("ver");
30        if (scriptVerParam) {
31            scriptVersion = scriptVerParam;
32        }
33    } catch (e) {
34        if (typeof debugMode !== "undefined" && debugMode)
35            console.warn("Could not parse scriptLocation for version override:", e);
36    }
37
38    function detectEnvironment() {
39        const hostname = window.location.hostname.toLowerCase();
40        if (hostname.includes("dev.")) return "DEV";
41        if (hostname.includes("stage.")) return "STAGE";
42        if (hostname.includes("www.") || hostname.includes("prod.")) return "PROD";
43        console.warn("[ZB Email Validator] Environment could not be determined from hostname:", hostname);
44        return "UNKNOWN";
45    }
46
47    const environment = detectEnvironment();
48
49// === Email Validation Rule Configuration ===
50// This object defines what is considered a "valid" email address based on
51// ZeroBounce API results. The structure allows for unconditional and conditional logic:
52//
53// - valid_statuses: Email statuses that are always accepted.
54// - conditional_statuses: Emails with specific statuses are only accepted if they meet
55//   additional criteria like allowed sub_status, MX presence, and free provider rules.
56// - require_mx: If true, all accepted emails must have a valid MX record.
57// - allow_free: If false, emails from free providers (e.g., gmail.com) are rejected.
58// Exit early if disabled by config
59
60// Example of conditional_status configuration
61    /*     conditional_statuses: {
62          "unknown": {
63            sub_statuses: ["antispam_system", "greylisted"],
64            require_mx: false,
65            allow_free: true
66          }
67    */
68
69    if (!CONFIG.ENABLED || !CONFIG.FEATURE_TOGGLES.enableValidation) return;
70
71    const ZBValidationRules = {
72        valid_statuses: ["valid", "catch-all"],
73        require_mx: false,
74        allow_free: true,
75        conditional_statuses: {}
76    }
77
78    const debugMode = (() => {
79        const urlParams = new URLSearchParams(window.location.search);
80        const debugFlag = urlParams.get("debug") === "true" || window.ZB_DEBUG_MODE === true;
81        if (CONFIG.DEBUG_DEFAULT_ENVIRONMENTS.includes(environment)) return true;
82        return debugFlag;
83    })();
84
85    const timestamp = new Date().toISOString();
86    // console.group(`[${timestamp}] ZB Email Validator Diagnostics`);
87    // console.info("Debug Mode:", debugMode);
88    // console.info("Environment:", environment);
89    // console.info("Script Version:", scriptVersion);
90    // console.info("Script Name:", scriptName);
91    // console.info("Script Location:", scriptLocation);
92    // console.groupEnd();
93
94    function pushValidationEvent(data) {
95        window.dataLayer = window.dataLayer || [];
96        window.dataLayer.push(data);
97    }
98
99    function isEmailValid(data) {
100        const rules = ZBValidationRules;
101        const unconditional = rules.valid_statuses.includes(data.status);
102        const conditionalRule = rules.conditional_statuses[data.status];
103        const conditional = conditionalRule &&
104            conditionalRule.sub_statuses.includes(data.sub_status) &&
105            (!conditionalRule.requires_mx || data.mx_found) &&
106            (conditionalRule.allow_free || !data.free_email);
107        const passes_mx = !rules.require_mx || data.mx_found;
108        const passes_free = rules.allow_free || !data.free_email;
109        return (unconditional || conditional) && passes_mx && passes_free;
110    }
111
112    function getEmailDomain(email) {
113        return email.includes("@") ? email.split("@")[1].toLowerCase() : "invali
113d";
114    }
115
116    const ZBUtils = {
117        getEmailDomain,
118        buildValidationURL,
119        isEmailValid,
120        reportValidationResult,
121        reportFallbackUX
122    };
123
124    function buildValidationURL(email) {
125        return `https://api.zerobounce.net/v2/validate?api_key=${zeroBounceAPI.apiKey}&email=${encodeURIComponent(email)}`;
126    }
127
128    function reportValidationResult(data, isValid, emailDomain) {
129        let evaluationMode = "unknown";
130        if (data.fallback) {
131            evaluationMode = "accepted_fallback";
132        } else if (ZBValidationRules.valid_statuses.includes(data.status)) {
133            evaluationMode = "accepted_unconditionally";
134        } else if (ZBValidationRules.conditional_statuses[data.status]) {
135            const rule = ZBValidationRules.conditional_statuses[data.status];
136            const subMatch = rule.sub_statuses.includes(data.sub_status);
137            const mxOk = !rule.requires_mx || data.mx_found;
138            const freeOk = rule.allow_free || !data.free_email;
139            evaluationMode = (subMatch && mxOk && freeOk) ? "accepted_conditionally" : "rejected_conditionally";
140        }
141        const uxString = `${emailDomain} | ${data.status} | ${data.sub_status || "none"} | ${isValid ? "clearValidationError" : "showValidationError"} | ${evaluationMode} | attempt_${data.attempt || 1}`;
142        const uxConfig = JSON.stringify({
143            valid_statuses: ZBValidationRules.valid_statuses,
144            conditional_statuses: ZBValidationRules.conditional_statuses,
145            require_mx: ZBValidationRules.require_mx,
146            allow_free: ZBValidationRules.allow_free
147        });
148        if (CONFIG.FEATURE_TOGGLES.enableAnalytics) pushValidationEvent({
149            hostname: window.location.hostname,
150            pageURL: window.location.href,
151            event: "formValidation_zerobounce",
152            scriptName,
153            scriptVersion,
154            scriptLocation,
155            debugMode,
156            UX_Type: uxString,
157            UX_Config: uxConfig,
158            environment
159        });
160    }
161
162    function reportFallbackUX(emailDomain) {
163        const fallbackData = {status: "error", sub_status: "no_response", fallback: true};
164        reportValidationResult(fallbackData, false, emailDomain);
165        showFallbackNotice();
166    }
167
168    MktoForms2.whenReady(function (form) {
169        const formElem = form.getFormElem();
170        const emailField = formElem.find("input[name='Email']");
171        const submitButton = formElem.find("button[type='submit']");
172
173        submitButton.prop("disabled", true).css("opacity", "0.5");
174
175        function showValidationError(message) {
176            emailField.css("border", "2px solid red");
177            formElem.find(".email-validation-error").remove();
178            emailField.after(`<div class="email-validation-error" style="color: red; font-size: 12px; margin-top: 5px;">${message}</div>`);
179            submitButton.prop("disabled", true).css("opacity", "0.5");
180        }
181
182        function clearValidationError() {
183            emailField.css("border", "");
184            formElem.find(".email-validation-error").remove();
185            submitButton.prop("disabled", false).css("opacity", "1");
186        }
187
188        function showFallbackNotice() {
189            emailField.css("border", "");
190            formElem.find(".email-validation-error").remove();
191            if (debugMode) {
192                emailField.after(`<div class="email-validation-error" style="color: orange; font-size: 12px; margin-top: 5px;">Validation service unavailable. Proceeding without verification.</div>`);
193            }
194            submitButton.prop("disabled", false).css("opacity", "1");
195        }
196
197        function validateEmail(email, attempt = 1) {
198            const apiUrl = ZBUtils.buildValidationURL(email);
199            const emailDomain = ZBUtils.getEmailDomain(email);
200
201            if (debugMode) {
202                console.log(`[${scriptName} ${scriptVersion}] Attempt ${attempt}: Validating "${email}"`);
203            }
204
205            fetch(apiUrl)
206                .then(response => response.json())
207                .then(data => {
208                    data.attempt = attempt;
209                    const isValid = ZBUtils.isEmailValid(data);
210                    ZBUtils.reportValidationResult(data, isValid, emailDomain);
211                    isValid ? clearValidationError() : showValidationError("Invalid Email Address. Please enter a valid email.");
212                })
213                .catch(error => {
214                    if (debugMode) {
215                        console.warn(`[${scriptName} ${scriptVersion}] Attempt ${attempt} failed:`, error);
216                    }
217
218                    if (attempt < CONFIG.RETRY_LIMIT) {
219                        setTimeout(() => validateEmail(email, attempt + 1), CONFIG.RETRY_DELAY_MS);
220                    } else {
221                        ZBUtils.reportFallbackUX(emailDomain);
222                    }
223                });
224        }
225
226        emailField.on("keyup", function () {
227            const email = emailField.val().trim();
228            if (email.length > 5) {
229                validateEmail(email);
230            } else {
231                showValidationError("Please enter a valid email address.");
232            }
233        });
234
235        form.onSubmit(function (e) {
236            if (submitButton.prop("disabled")) {
237                e.preventDefault();
238                return false;
239            }
240        });
241
242        form.onSuccess(function (values, followUpUrl) {
243            window.location.href = followUpUrl;
244            return false;
245        });
246    });
247});

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.