PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.2
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.2
3.3.2 3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 All 157 releases
notificationx / assets / public / js / nx-elementor-form.js

nx-elementor-form.js in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.3.2, at assets/public/js/nx-elementor-form.js

179 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * NotificationX Elementor Form — submission handler.
3 *
4 * Intercepts the form submit, posts Name / Email / Message to the
5 * notificationx/v1/popup-submit REST endpoint and shows a success or
6 * error message. The selected NX campaign ID is read from data-nx-id.
7 */
8 ( function () {
9 'use strict';
10
11 // ── Per-session submission guard ──────────────────────────────────────
12 // A visitor may submit a given campaign's form only once per browser
13 // session; the flag is stored in sessionStorage keyed by campaign id.
14 function sessionStore() {
15 try { return window.sessionStorage; } catch ( e ) { return null; }
16 }
17 function submittedKey( nxId ) {
18 return 'notificationx_form_submitted_' + nxId;
19 }
20 function hasSubmitted( nxId ) {
21 var store = sessionStore();
22 try { return !! ( store && store.getItem( submittedKey( nxId ) ) ); }
23 catch ( e ) { return false; }
24 }
25 function markSubmitted( nxId ) {
26 var store = sessionStore();
27 try { if ( store ) store.setItem( submittedKey( nxId ), '1' ); }
28 catch ( e ) {}
29 }
30
31 // Disable the form's submit button and show an "already submitted" notice.
32 function lockForm( form, btn, msgEl ) {
33 if ( btn ) btn.disabled = true;
34 if ( msgEl && ! msgEl.textContent ) {
35 msgEl.classList.add( 'is-success' );
36 msgEl.textContent = form.getAttribute( 'data-already' )
37 || 'You have already submitted this form.';
38 }
39 }
40
41 // If the form is rendered inside an Exit Intent popup, ask the popup to
42 // close itself (handled in ExitIntentPopup.tsx) shortly after a success.
43 function closeExitIntent( form, nxId ) {
44 if ( ! form.closest || ! form.closest( '.nx-exit-intent-overlay' ) ) return;
45 setTimeout( function () {
46 document.dispatchEvent( new CustomEvent( 'nx:exit-intent-close', {
47 detail: { nxId: nxId },
48 } ) );
49 }, 1600 );
50 }
51
52 function init( form ) {
53 if ( ! form || form.__nxBound ) return;
54 form.__nxBound = true;
55
56 var msgEl = form.querySelector( '.nx-form-message' );
57 var btn = form.querySelector( '.nx-form-submit' );
58 var nxId = form.getAttribute( 'data-nx-id' );
59
60 // Already submitted this session? Lock the form up-front.
61 if ( nxId && hasSubmitted( nxId ) ) {
62 lockForm( form, btn, msgEl );
63 }
64
65 form.addEventListener( 'submit', function ( ev ) {
66 ev.preventDefault();
67
68 // Guard against repeat submissions within the same session.
69 if ( nxId && hasSubmitted( nxId ) ) {
70 lockForm( form, btn, msgEl );
71 return;
72 }
73
74 if ( msgEl ) {
75 msgEl.textContent = '';
76 msgEl.classList.remove( 'is-success', 'is-error' );
77 }
78
79 // Native required-field validation.
80 if ( typeof form.checkValidity === 'function' && ! form.checkValidity() ) {
81 form.reportValidity();
82 return;
83 }
84
85 var url = form.getAttribute( 'data-rest-url' );
86 if ( ! nxId || ! url ) return;
87
88 var payload = {
89 nx_id: nxId,
90 timestamp: Math.floor( Date.now() / 1000 ),
91 };
92
93 var nameEl = form.querySelector( '[name="name"]' );
94 var emailEl = form.querySelector( '[name="email"]' );
95 var messageEl = form.querySelector( '[name="message"]' );
96
97 if ( nameEl ) payload.name = nameEl.value;
98 if ( emailEl ) payload.email = emailEl.value;
99 if ( messageEl ) payload.message = messageEl.value;
100
101 if ( btn ) btn.disabled = true;
102
103 // popup-submit is a public endpoint (permission_callback => __return_true)
104 // and never verifies a nonce server-side. We deliberately do NOT send an
105 // X-WP-Nonce header: a baked-in wp_rest nonce goes stale in the Elementor
106 // builder and on cached pages, and WordPress core would then reject the
107 // request with "Cookie check failed" (rest_cookie_invalid_nonce). With no
108 // nonce, core treats the submission as anonymous and lets it through.
109 var headers = { 'Content-Type': 'application/json' };
110
111 fetch( url, {
112 method: 'POST',
113 headers: headers,
114 body: JSON.stringify( payload ),
115 } )
116 .then( function ( res ) {
117 return res.json().then( function ( body ) {
118 return { ok: res.ok && body && body.success, body: body };
119 } ).catch( function () {
120 return { ok: res.ok, body: {} };
121 } );
122 } )
123 .then( function ( result ) {
124 if ( result.ok ) {
125 // Remember the submission for the rest of the session and
126 // keep the form locked so it can't be sent again.
127 markSubmitted( nxId );
128 if ( btn ) btn.disabled = true;
129 if ( msgEl ) {
130 msgEl.classList.add( 'is-success' );
131 msgEl.textContent = form.getAttribute( 'data-success' ) || 'Submitted.';
132 }
133 form.reset();
134 // When the form lives inside an Exit Intent popup, dismiss it
135 // after the success message has had a moment to register.
136 closeExitIntent( form, nxId );
137 } else if ( msgEl ) {
138 msgEl.classList.add( 'is-error' );
139 msgEl.textContent = ( result.body && result.body.message )
140 || form.getAttribute( 'data-error' )
141 || 'Something went wrong.';
142 }
143 } )
144 .catch( function () {
145 if ( ! msgEl ) return;
146 msgEl.classList.add( 'is-error' );
147 msgEl.textContent = form.getAttribute( 'data-error' ) || 'Something went wrong.';
148 } )
149 .finally( function () {
150 // Leave the button disabled once a submission has gone through;
151 // only re-enable it after a failed attempt.
152 if ( btn && ! ( nxId && hasSubmitted( nxId ) ) ) btn.disabled = false;
153 } );
154 } );
155 }
156
157 function bindAll( root ) {
158 ( root || document ).querySelectorAll( '.nx-form' ).forEach( init );
159 }
160
161 if ( document.readyState === 'loading' ) {
162 document.addEventListener( 'DOMContentLoaded', function () { bindAll(); } );
163 } else {
164 bindAll();
165 }
166
167 // Re-bind in the Elementor editor preview when widgets re-render.
168 if ( window.jQuery ) {
169 window.jQuery( window ).on( 'elementor/frontend/init', function () {
170 if ( window.elementorFrontend && window.elementorFrontend.hooks ) {
171 window.elementorFrontend.hooks.addAction(
172 'frontend/element_ready/nx-form.default',
173 function ( $scope ) { bindAll( $scope[ 0 ] ); }
174 );
175 }
176 } );
177 }
178 } )();
179