PluginProbe
Passster – Password Protect Pages and Content / 4.3.17
Passster – Password Protect Pages and Content v4.3.17
4.3.17 4.3.16 4.3.15 4.3.14 4.3.12 4.3.13 4.3.11 4.3.10 4.3.9 4.3.8 4.3.7 4.3.6 4.3.5 trunk 3.5.4 3.5.5.2 3.5.5.8 3.5.5.9 4.0 4.1.4 4.2.10 4.2.11 4.2.12 4.2.13 4.2.14 All 49 releases
← All changes | assets/public/passster-public.js +308 -351 4.1.4 → 4.3.17 View file →
@@ -1,386 +1,343 @@
1 -jQuery(document).ready(function ($) {
1 +jQuery( document ).ready( function( $ ) {
2 + const restUrl = ps_ajax.rest_url + 'passster/v1';
2 3
3 - // Get cookie duration.
4 - function getDurationBySettings() {
5 - switch (ps_ajax.cookie_duration_unit) {
6 - case 'days':
7 - return parseInt(ps_ajax.cookie_duration);
8 - break;
9 - case 'hours':
10 - return new Date(new Date().getTime() + (ps_ajax.cookie_duration * 10) * 60 * 1000);
11 - break;
12 - case 'minutes':
13 - return new Date(new Date().getTime() + ps_ajax.cookie_duration * 60 * 1000);
14 - break;
15 - default:
16 - return parseInt(ps_ajax.cookie_duration);
17 - }
18 - }
4 + // Get cookie duration.
5 + function getDurationBySettings() {
6 + switch ( ps_ajax.cookie_duration_unit ) {
7 + case 'days':
8 + return parseInt( ps_ajax.cookie_duration );
9 + case 'hours':
10 + return new Date( new Date().getTime() + ( ps_ajax.cookie_duration * 60 ) * 60 * 1000 );
11 + case 'minutes':
12 + return new Date( new Date().getTime() + ps_ajax.cookie_duration * 60 * 1000 );
13 + default:
14 + return parseInt( ps_ajax.cookie_duration );
15 + }
16 + }
19 17
20 - // Get cache busting URL.
21 - function getCacheFriendlyURL() {
22 - if (location.search) {
23 - return (location.origin).concat(location.pathname).concat(location.hash) + location.search + '&pts=' + Math.floor(Date.now() / 1000);
24 - } else {
25 - return (location.origin).concat(location.pathname).concat(location.hash) + '?pts=' + Math.floor(Date.now() / 1000);
26 - }
27 - }
18 + // Get cache busting URL.
19 + function getCacheFriendlyURL() {
20 + const pts = 'pts=' + Math.floor( Date.now() / 1000 );
21 + const base = location.origin + location.pathname;
22 + const search = location.search ? location.search + '&' + pts : '?' + pts;
23 + const hash = location.hash || '';
24 + return base + search + hash;
25 + }
28 26
29 - // Get hashed cookie via Ajax.
30 - function setHashedCookie(password) {
31 - $.ajax({
32 - type: "post", dataType: "json", url: ps_ajax.ajax_url, data: {
33 - 'action': 'hash_password', 'hash_nonce': ps_ajax.hash_nonce, 'password': password,
34 - }, success: function (response) {
35 - Cookies.set('passster', response.password, {
36 - expires: getDurationBySettings(), sameSite: 'strict'
37 - });
38 - }, async: false,
39 - });
40 - }
27 + // Hash password via REST API.
28 + async function hashPassword( password, postId ) {
29 + try {
30 + const response = await fetch( restUrl + '/hash', {
31 + method: 'POST',
32 + credentials: 'same-origin',
33 + headers: { 'Content-Type': 'application/json' },
34 + body: JSON.stringify( { password, post_id: postId } ),
35 + } );
36 + const data = await response.json();
37 + return data.hash || '';
38 + } catch ( e ) {
39 + return '';
40 + }
41 + }
41 42
42 - // Check if we have an unlock link.
43 - if (ps_ajax.link_pass) {
44 - if (!ps_ajax.disable_cookie) {
45 - if (ps_ajax.link_pass.length < 25) {
46 - // It's an old base64 encryption.
47 - setHashedCookie(atob(ps_ajax.link_pass));
48 - } else {
49 - Cookies.set('passster', ps_ajax.link_pass, {
50 - expires: getDurationBySettings(), sameSite: 'strict'
51 - });
52 - }
43 + // Get existing hashes from cookie as array.
44 + function getExistingHashes() {
45 + const existing = Cookies.get( 'passster' );
46 + if ( ! existing ) {
47 + return [];
48 + }
49 + // Support both old (single hash) and new (pipe-separated) format
50 + return existing.split( '|' ).filter( h => h.length > 0 );
51 + }
53 52
54 - // Handle the redirect with cache busting.
55 - window.location.replace(ps_ajax.permalink + '?pts=' + Math.floor(Date.now() / 1000));
56 - }
57 - }
53 + // Set hashed cookie (appends to existing hashes).
54 + async function setHashedCookie( password, postId ) {
55 + const hash = await hashPassword( password, postId );
56 + if ( hash ) {
57 + const hashes = getExistingHashes();
58 + // Only add if not already present
59 + if ( ! hashes.includes( hash ) ) {
60 + hashes.push( hash );
61 + }
62 + Cookies.set( 'passster', hashes.join( '|' ), {
63 + expires: getDurationBySettings(),
64 + sameSite: 'strict',
65 + } );
66 + }
67 + }
58 68
59 - // Passwords
60 - $('.passster-submit').on('click', function (e) {
61 - e.preventDefault();
69 + // Set simple cookie (for captcha) - appends to existing.
70 + function setCookie( value ) {
71 + const hashes = getExistingHashes();
72 + if ( ! hashes.includes( value ) ) {
73 + hashes.push( value );
74 + }
75 + Cookies.set( 'passster', hashes.join( '|' ), {
76 + expires: getDurationBySettings(),
77 + sameSite: 'strict',
78 + } );
79 + }
62 80
63 - // Validate form before submitting ajax.
64 - var form = $(this).parent().parent();
81 + // Handle successful unlock.
82 + function handleSuccess( data, $form, redirect ) {
83 + if ( data.redirect ) {
84 + window.location.replace( data.redirect );
85 + return;
86 + }
65 87
66 - if (!$(form)[0].checkValidity()) {
67 - $(form)[0].reportValidity();
68 - }
88 + if ( ! ps_ajax.unlock_mode ) {
89 + window.location.href = getCacheFriendlyURL();
90 + } else {
91 + $form.find( '.passster-error' ).hide();
92 + if ( data.requires_reload ) {
93 + window.location.href = getCacheFriendlyURL();
94 + return;
95 + }
96 + if ( data.content ) {
97 + let content = data.content;
98 + if ( ps_ajax.shortcodes ) {
99 + $.each( ps_ajax.shortcodes, function( key, value ) {
100 + content = content.replace( key, value );
101 + } );
102 + }
103 + $form.replaceWith( content );
104 + }
105 + if ( redirect ) {
106 + window.location.replace( redirect );
107 + }
108 + }
109 + }
69 110
70 - ps_id = $(this).attr('data-psid');
71 - form = $("#" + ps_id);
72 - password = $("#" + ps_id + ' .passster-password').attr('data-password');
73 - type = $("#" + ps_id + ' .passster-password').attr('data-protection-type');
74 - list = $("#" + ps_id + ' .passster-password').attr('data-list');
75 - area = $("#" + ps_id + ' .passster-password').attr('data-area');
76 - protection = $("#" + ps_id + ' .passster-password').attr('data-protection');
77 - redirect = $(this).attr('data-redirect');
78 - input = $("#" + ps_id + ' .passster-password').val();
79 - acf = $(this).attr('data-acf');
111 + // Show error.
112 + function showError( $form, message ) {
113 + const $error = $form.find( '.passster-error' );
114 + $error.text( message || 'An error occurred.' );
115 + $error.show().fadeOut( 3500 );
116 + }
80 117
81 - $.ajax({
82 - type: "post", dataType: "json", url: ps_ajax.ajax_url, data: {
83 - 'action': 'validate_input',
84 - 'nonce': ps_ajax.nonce,
85 - 'input': input,
86 - 'password': password,
87 - 'post_id': ps_ajax.post_id,
88 - 'type': type,
89 - 'list': list,
90 - 'area': area,
91 - 'protection': protection,
92 - 'acf': acf,
93 - 'redirect': redirect
94 - }, beforeSend: function () {
95 - form.find(".ps-loader").css('display', 'block');
96 - }, success: function (response) {
97 - form.find(".ps-loader").css('display', 'none');
98 - if (true === response.success) {
99 - // if no ajax.
100 - if (!ps_ajax.unlock_mode) {
101 - setHashedCookie(input);
118 + // Check if we have an unlock link.
119 + if ( ps_ajax.link_pass ) {
120 + if ( ! ps_ajax.disable_cookie ) {
121 + if ( ps_ajax.link_pass.length < 25 ) {
122 + setHashedCookie( atob( ps_ajax.link_pass ), ps_ajax.post_id ).then( () => {
123 + window.location.replace( ps_ajax.permalink + '?pts=' + Math.floor( Date.now() / 1000 ) );
124 + } );
125 + } else {
126 + setCookie( ps_ajax.link_pass );
127 + window.location.replace( ps_ajax.permalink + '?pts=' + Math.floor( Date.now() / 1000 ) );
128 + }
129 + }
130 + }
102 131
103 - if (response.redirect) {
104 - window.location.replace(redirect);
105 - } else {
106 - window.location.href = getCacheFriendlyURL();
107 - }
108 - } else {
132 + // Password form submit - using REST API.
133 + $( '.passster-submit' ).on( 'click', async function( e ) {
134 + e.preventDefault();
109 135
110 - // set cookie if activated.
111 - if (!ps_ajax.disable_cookie) {
112 - setHashedCookie(input);
113 - }
114 - form.find('.passster-error').hide();
136 + const $button = $( this );
137 + const psId = $button.attr( 'data-psid' );
138 + const $form = $( '#' + psId );
139 + const $input = $form.find( '.passster-password' );
140 + const postId = parseInt( $button.attr( 'data-post-id' ) ) || ps_ajax.post_id;
115 141
116 - // replace shortcodes.
117 - let content = response.content;
142 + // Validate form.
143 + const formEl = $form.find( 'form' )[ 0 ];
144 + if ( formEl && ! formEl.checkValidity() ) {
145 + formEl.reportValidity();
146 + return;
147 + }
118 148
119 - if (content) {
120 - $.each(ps_ajax.shortcodes, function (key, value) {
121 - content = content.replace(key, value);
122 - });
149 + // Get form data.
150 + const password = $input.val();
151 + const type = ( $input.attr( 'data-protection-type' ) || 'password' ).replace( /-/g, '_' );
152 + const areaId = parseInt( $input.attr( 'data-area' ) ) || 0;
153 + const listId = parseInt( $input.attr( 'data-list' ) ) || 0;
154 + const lists = $input.attr( 'data-lists' ) || '';
155 + const protection = $input.attr( 'data-protection' ) || '';
156 + const acf = $button.attr( 'data-acf' ) || '';
157 + const redirect = $button.attr( 'data-redirect' ) || '';
158 + const termId = parseInt( $button.attr( 'data-term-id' ) ) || 0;
159 + const postType = $button.attr( 'data-post-type' ) || '';
123 160
124 - $("#" + ps_id).replaceWith(content);
125 - }
161 + // Get block ID from parent wrapper if exists.
162 + const $wrapper = $form.closest( '.passster-protected-content' );
163 + const blockId = $wrapper.length ? $wrapper.attr( 'data-block-id' ) : '';
126 164
127 - // Redirect?
128 - if (response.redirect) {
129 - window.location.replace(redirect);
130 - }
165 + $form.find( '.ps-loader' ).css( 'display', 'block' );
131 166
132 - }
133 - } else {
134 - form.find('.passster-error').text(response.error);
135 - form.find('.passster-error').show().fadeOut(3500);
136 - $("#" + ps_id + ' .passster-password').val('');
137 - }
138 - }
139 - });
140 - });
167 + try {
168 + const response = await fetch( restUrl + '/unlock', {
169 + method: 'POST',
170 + credentials: 'same-origin',
171 + headers: { 'Content-Type': 'application/json' },
172 + body: JSON.stringify( {
173 + password,
174 + type,
175 + post_id: postId,
176 + area_id: areaId,
177 + block_id: blockId,
178 + list_id: listId,
179 + lists,
180 + protection,
181 + acf,
182 + redirect,
183 + term_id: termId,
184 + post_type: postType,
185 + } ),
186 + } );
141 187
142 - // Recaptcha v2
143 - if ($('.recaptcha-form-v2').length > 0) {
144 - grecaptcha.ready(function () {
145 - grecaptcha.render('ps-recaptcha-v2', {
146 - 'sitekey': ps_ajax.recaptcha_key, 'callback': function (token) {
147 - ps_id = $('.recaptcha-v2-submit').attr('data-psid');
148 - form = $("#" + ps_id);
149 - protection = $('.recaptcha-v2-submit').attr('data-protection');
150 - acf = $('.recaptcha-v2-submit').attr('data-acf');
151 - area = $("#" + ps_id).find('.recaptcha-v2-submit').attr('data-area');
152 - redirect = $("#" + ps_id).find('.recaptcha-v2-submit').attr('data-redirect');
188 + const data = await response.json();
189 + $form.find( '.ps-loader' ).css( 'display', 'none' );
190 + if ( data.success ) {
191 + handleSuccess( data, $form, redirect );
192 + } else {
193 + showError( $form, data.error );
194 + $input.val( '' );
195 + }
196 + } catch ( err ) {
197 + $form.find( '.ps-loader' ).css( 'display', 'none' );
198 + showError( $form, 'An error occurred. Please try again.' );
199 + }
200 + } );
153 201
154 - $.ajax({
155 - type: "post", dataType: "json", url: ps_ajax.ajax_url, data: {
156 - 'action': 'validate_input',
157 - 'nonce': ps_ajax.nonce,
158 - 'token': token,
159 - 'post_id': ps_ajax.post_id,
160 - 'type': 'recaptcha',
161 - 'protection': protection,
162 - 'captcha_id': ps_id,
163 - 'acf': acf,
164 - 'area': area,
165 - 'redirect': redirect
166 - }, success: function (response) {
167 - if (true === response.success) {
202 + // Captcha validation via REST API.
203 + async function validateCaptcha( token, type, $form, psId, areaId, redirect, protection ) {
204 + try {
205 + const response = await fetch( restUrl + '/captcha', {
206 + method: 'POST',
207 + credentials: 'same-origin',
208 + headers: { 'Content-Type': 'application/json' },
209 + body: JSON.stringify( {
210 + token,
211 + type,
212 + post_id: ps_ajax.post_id,
213 + area_id: areaId,
214 + redirect,
215 + protection: protection || '',
216 + } ),
217 + } );
168 218
169 - // if no ajax.
170 - if (!ps_ajax.unlock_mode) {
171 - Cookies.set('passster', 'recaptcha', {
172 - expires: getDurationBySettings(), sameSite: 'strict'
173 - });
219 + const data = await response.json();
174 220
175 - if (response.redirect) {
176 - window.location.replace(redirect);
177 - } else {
178 - window.location.href = getCacheFriendlyURL();
179 - }
180 - } else {
181 - // set cookie if activated.
182 - if (!ps_ajax.disable_cookie) {
183 - Cookies.set('passster', 'recaptcha', {
184 - expires: getDurationBySettings(), sameSite: 'strict'
185 - });
186 - }
187 - form.find('.passster-error').hide();
221 + if ( data.success ) {
222 + handleSuccess( data, $form, redirect );
223 + } else {
224 + showError( $form, data.error );
225 + }
226 + } catch ( err ) {
227 + showError( $form, 'Captcha validation failed.' );
228 + }
229 + }
188 230
189 - // replace shortcodes.
190 - let content = response.content;
231 + // Recaptcha v2
232 + if ( $( '.recaptcha-form-v2' ).length > 0 && window.grecaptcha ) {
233 + grecaptcha.ready( function() {
234 + grecaptcha.render( 'ps-recaptcha-v2', {
235 + sitekey: ps_ajax.recaptcha_key,
236 + callback( token ) {
237 + const psId = $( '.recaptcha-v2-submit' ).attr( 'data-psid' );
238 + const $form = $( '#' + psId );
239 + const $btn = $form.find( '.recaptcha-v2-submit' );
240 + const areaId = parseInt( $btn.attr( 'data-area' ) ) || 0;
241 + const redirect = $btn.attr( 'data-redirect' ) || '';
242 + const protection = $btn.attr( 'data-protection' ) || '';
191 243
192 - if (content) {
244 + validateCaptcha( token, 'recaptcha_v2', $form, psId, areaId, redirect, protection );
245 + },
246 + } );
247 + } );
248 + }
193 249
194 - $.each(ps_ajax.shortcodes, function (key, value) {
195 - content = content.replace(key, value);
196 - });
250 + // ReCaptcha v3
251 + $( '.recaptcha-form' ).on( 'submit', function( event ) {
252 + event.preventDefault();
197 253
198 - $("#" + ps_id).replaceWith(content);
199 - }
254 + const $thisForm = $( this );
255 + const $btn = $thisForm.find( '.passster-submit-recaptcha' );
256 + const psId = $btn.attr( 'data-psid' );
257 + const $form = $( '#' + psId );
258 + const areaId = parseInt( $btn.attr( 'data-area' ) ) || 0;
259 + const redirect = $btn.attr( 'data-redirect' ) || '';
260 + const protection = $btn.attr( 'data-protection' ) || '';
200 261
201 - // Redirect?
202 - if (response.redirect) {
203 - window.location.replace(redirect);
204 - }
205 - }
206 - } else {
207 - form.find('.passster-error').text(response.error);
208 - form.find('.passster-error').show().fadeOut(3500);
209 - }
210 - }
211 - });
212 - }
213 - });
214 - });
215 - }
262 + if ( window.grecaptcha ) {
263 + grecaptcha.ready( function() {
264 + grecaptcha.execute( ps_ajax.recaptcha_key, { action: 'validate_input' } ).then( function( token ) {
265 + validateCaptcha( token, 'recaptcha_v3', $form, psId, areaId, redirect, protection );
266 + } );
267 + } );
268 + }
269 + } );
216 270
217 - // ReCaptcha v3
218 - $('.recaptcha-form').on('submit', function (event) {
219 - event.preventDefault();
271 + // hCaptcha
272 + $( '.hcaptcha-form' ).on( 'submit', function( event ) {
273 + event.preventDefault();
220 274
221 - ps_id = $(this).find('.passster-submit-recaptcha').attr('data-psid');
222 - form = $("#" + ps_id);
223 - protection = $(this).find('.passster-submit-recaptcha').attr('data-protection');
224 - acf = $(this).find('.passster-submit-recaptcha').attr('data-acf');
225 - area = $(this).find('.passster-submit-recaptcha').attr('data-area');
226 - redirect = $(this).find('.passster-submit-recaptcha').attr('data-redirect');
275 + const $thisForm = $( this );
276 + const $btn = $thisForm.find( '.passster-submit-recaptcha' );
277 + const psId = $btn.attr( 'data-psid' );
278 + const $form = $( '#' + psId );
279 + const areaId = parseInt( $btn.attr( 'data-area' ) ) || 0;
280 + const redirect = $btn.attr( 'data-redirect' ) || '';
281 + const protection = $btn.attr( 'data-protection' ) || '';
227 282
228 - grecaptcha.ready(function () {
229 - grecaptcha.execute(ps_ajax.recaptcha_key, {action: 'validate_input'}).then(function (token) {
283 + if ( window.hcaptcha ) {
284 + hcaptcha.execute( { async: true } )
285 + .then( ( { response } ) => {
286 + validateCaptcha( response, 'hcaptcha', $form, psId, areaId, redirect, protection );
287 + } )
288 + .catch( ( err ) => {
289 + showError( $form, err.message || 'hCaptcha error' );
290 + } );
291 + }
292 + } );
230 293
231 - form.prepend('<input type="hidden" name="token" value="' + token + '">');
232 - form.prepend('<input type="hidden" name="action" value="validate_input">');
294 + // Turnstile
295 + let turnstileToken = '';
296 + if ( $( '.turnstile-form' ).length > 0 && window.turnstile ) {
297 + window.turnstile.ready( function() {
298 + window.turnstile.render( '.passster-turnstile', {
299 + sitekey: ps_ajax.turnstile_key,
300 + callback( token ) {
301 + turnstileToken = token;
302 + },
303 + } );
304 + } );
233 305
234 - $.ajax({
235 - type: "post", dataType: "json", url: ps_ajax.ajax_url, data: {
236 - 'action': 'validate_input',
237 - 'nonce': ps_ajax.nonce,
238 - 'token': token,
239 - 'post_id': ps_ajax.post_id,
240 - 'type': 'recaptcha',
241 - 'protection': protection,
242 - 'captcha_id': ps_id,
243 - 'acf': acf,
244 - 'area': area,
245 - 'redirect': redirect
246 - }, success: function (response) {
247 - if (true === response.success) {
306 + $( '.turnstile-form' ).on( 'submit', function( event ) {
307 + event.preventDefault();
248 308
249 - // if no ajax.
250 - if (!ps_ajax.unlock_mode) {
251 - Cookies.set('passster', 'recaptcha', {
252 - expires: getDurationBySettings(), sameSite: 'strict'
253 - });
309 + const $thisForm = $( this );
310 + const $btn = $thisForm.find( '.passster-submit-turnstile' );
311 + const psId = $btn.attr( 'data-psid' );
312 + const $form = $( '#' + psId );
313 + const areaId = parseInt( $btn.attr( 'data-area' ) ) || 0;
314 + const redirect = $btn.attr( 'data-redirect' ) || '';
315 + const protection = $btn.attr( 'data-protection' ) || '';
254 316
255 - if (response.redirect) {
256 - window.location.replace(redirect);
257 - } else {
258 - window.location.href = getCacheFriendlyURL();
259 - }
260 - } else {
261 - // set cookie if activated.
262 - if (!ps_ajax.disable_cookie) {
263 - Cookies.set('passster', 'recaptcha', {
264 - expires: getDurationBySettings(), sameSite: 'strict'
265 - });
266 - }
267 - form.find('.passster-error').hide();
268 - // replace shortcodes.
269 - let content = response.content;
317 + if ( ! turnstileToken ) {
318 + showError( $form, 'Please complete the verification.' );
319 + return;
320 + }
270 321
271 - if (content) {
272 - $.each(ps_ajax.shortcodes, function (key, value) {
273 - content = content.replace(key, value);
274 - });
322 + validateCaptcha( turnstileToken, 'turnstile', $form, psId, areaId, redirect, protection );
323 + } );
324 + }
275 325
276 - form.replaceWith(content);
277 - }
278 -
279 - // Redirect?
280 - if (response.redirect) {
281 - window.location.replace(redirect);
282 - }
283 - }
284 - } else {
285 - form.find('.passster-error').text(response.error);
286 - form.find('.passster-error').show().fadeOut(3500);
287 - }
288 - }
289 - });
290 - });
291 - });
292 - });
293 -
294 - // hcaptcha
295 - $('.hcaptcha-form').on('submit', function (event) {
296 - event.preventDefault();
297 -
298 - ps_id = $(this).find('.passster-submit-recaptcha').attr('data-psid');
299 - form = $("#" + ps_id);
300 - protection = $(this).find('.passster-submit-recaptcha').attr('data-protection');
301 - acf = $(this).find('.passster-submit-recaptcha').attr('data-acf');
302 - area = $(this).find('.passster-submit-recaptcha').attr('data-area');
303 - redirect = $(this).find('.passster-submit-recaptcha').attr('data-redirect');
304 -
305 - hcaptcha.execute({async: true})
306 - .then(({response, key}) => {
307 - $.ajax({
308 - type: "post", dataType: "json", url: ps_ajax.ajax_url, data: {
309 - 'action': 'validate_input',
310 - 'nonce': ps_ajax.nonce,
311 - 'token': response,
312 - 'post_id': ps_ajax.post_id,
313 - 'type': 'recaptcha',
314 - 'protection': protection,
315 - 'captcha_id': ps_id,
316 - 'acf': acf,
317 - 'area': area,
318 - 'redirect': redirect
319 - }, success: function (response) {
320 - if (true === response.success) {
321 -
322 - // if no ajax.
323 - if (!ps_ajax.unlock_mode) {
324 - Cookies.set('passster', 'recaptcha', {
325 - expires: getDurationBySettings(), sameSite: 'strict'
326 - });
327 -
328 - if (response.redirect) {
329 - window.location.replace(redirect);
330 - } else {
331 - window.location.href = getCacheFriendlyURL();
332 - }
333 - } else {
334 - // set cookie if activated.
335 - if (!ps_ajax.disable_cookie) {
336 - Cookies.set('passster', 'recaptcha', {
337 - expires: getDurationBySettings(), sameSite: 'strict'
338 - });
339 - }
340 - form.find('.passster-error').hide();
341 - // replace shortcodes.
342 - let content = response.content;
343 -
344 - if (content) {
345 - $.each(ps_ajax.shortcodes, function (key, value) {
346 - content = content.replace(key, value);
347 - });
348 -
349 - form.replaceWith(content);
350 - }
351 -
352 - // Redirect?
353 - if (response.redirect) {
354 - window.location.replace(redirect);
355 - }
356 - }
357 - } else {
358 - form.find('.passster-error').text(response.error);
359 - form.find('.passster-error').show().fadeOut(3500);
360 - }
361 - }
362 - });
363 - })
364 - .catch(err => {
365 - form.find('.passster-error').text(err);
366 - form.find('.passster-error').show().fadeOut(3500);
367 - });
368 -
369 - });
370 -
371 - // Concurrent logout.
372 - $(document).on('click', '#ps-logout', function () {
373 - $.ajax({
374 - type: 'post',
375 - dataType: 'json',
376 - url: ps_ajax.ajax_url,
377 - data: {'action': 'handle_logout', 'logout_nonce': ps_ajax.logout_nonce},
378 - success: function (response) {
379 - if (true === response.success) {
380 - Cookies.set('passster', '', {expires: 0, sameSite: 'strict'});
381 - window.location.href = getCacheFriendlyURL();
382 - }
383 - }
384 - });
385 - });
386 -});
326 + // Concurrent logout - using REST API.
327 + $( document ).on( 'click', '#ps-logout', async function() {
328 + try {
329 + const response = await fetch( restUrl + '/logout', {
330 + method: 'POST',
331 + credentials: 'same-origin',
332 + headers: { 'Content-Type': 'application/json' },
333 + } );
334 + const data = await response.json();
335 + if ( data.success ) {
336 + Cookies.set( 'passster', '', { expires: 0, sameSite: 'strict' } );
337 + window.location.href = getCacheFriendlyURL();
338 + }
339 + } catch ( e ) {
340 + window.location.href = getCacheFriendlyURL();
341 + }
342 + } );
343 +} );