1(function( $ ) { 2 'use strict'; 3 4 /** 5 * All of the code for your public-facing JavaScript source 6 * should reside in this file. 7 * 8 * Note: It has been assumed you will write jQuery code here, so the 9 * $ function reference has been prepared for usage within the scope 10 * of this function. 11 * 12 * This enables you to define handlers, for when the DOM is ready: 13 * 14 * $(function() { 15 * 16 * }); 17 * 18 * When the window is loaded: 19 * 20 * $( window ).load(function() { 21 * 22 * }); 23 * 24 * ...and/or other possibilities. 25 * 26 * Ideally, it is not considered best practise to attach more than a 27 * single DOM-ready or window-load handler for a particular page. 28 * Although scripts in the WordPress core, Plugins and Themes may be 29 * practising this, we should strive to set a better example in our own work. 30 */ 31 32 // Shared OTP state (current email, resend-cooldown timer). 33 var f7hOtpState = { email: '', resendInterval: null }; 34 35 // True while a sign-in request is in flight. The login modal reads this via 36 // F7HIsAuthInProgress() and refuses to close, so a request the server is 37 // already processing can't be abandoned half-way. 38 var f7hAuthInProgress = false; 39 var f7hAuthLockTimeout = null; 40 41 /** 42 * Disable / enable all login controls (social + OTP) together while a 43 * request is in flight, so a user can't fire two sign-in attempts at once. 44 */ 45 function f7hDisableAuthControls() { 46 f7hAuthInProgress = true; 47 48 // Safety valve: never leave the modal stuck shut if a future code path 49 // forgets to re-enable. Far longer than any real sign-in round trip. 50 clearTimeout(f7hAuthLockTimeout); 51 f7hAuthLockTimeout = setTimeout(f7hEnableAuthControls, 60000); 52 53 $('.f-login-modal-fancybox .fancybox-close-small').css('pointer-events', 'none'); 54 $('#f7h_wc_myaccount_login_btn').css('pointer-events', 'none'); 55 $('#f7h_firebase_google_login').css('pointer-events', 'none'); 56 $('#appleid-signin').css('pointer-events', 'none'); 57 $('.js-otp-email-input').css('pointer-events', 'none'); 58 $('.js-otp-request-code-btn').css('pointer-events', 'none'); 59 $('.js-otp-verify-code-btn').css('pointer-events', 'none'); 60 $('#f7h-wc-form-register-link').css('pointer-events', 'none'); 61 } 62 63 function f7hEnableAuthControls() { 64 f7hAuthInProgress = false; 65 clearTimeout(f7hAuthLockTimeout); 66 f7hAuthLockTimeout = null; 67 68 $('.f-login-modal-fancybox .fancybox-close-small').css('pointer-events', 'auto'); 69 $('#f7h_wc_myaccount_login_btn').css('pointer-events', 'auto'); 70 $('#f7h_firebase_google_login').css('pointer-events', 'auto'); 71 $('#appleid-signin').css('pointer-events', 'auto'); 72 $('.js-otp-email-input').css('pointer-events', 'auto'); 73 $('.js-otp-request-code-btn').css('pointer-events', 'auto'); 74 $('.js-otp-verify-code-btn').css('pointer-events', 'auto'); 75 $('#f7h-wc-form-register-link').css('pointer-events', 'auto'); 76 } 77 78 // Read by the login modal's fancybox beforeClose guard in the theme. 79 window.F7HIsAuthInProgress = function() { 80 return f7hAuthInProgress; 81 }; 82 83 function f7hShowAuthError(message) { 84 $('.js-social-login-error').text(message).fadeIn(350); 85 } 86 87 function f7hClearAuthError() { 88 $('.js-social-login-error').hide().text(''); 89 } 90 91 function f7hGetAuthErrorMessage(error) { 92 if (error && error.code === 'auth/account-exists-with-different-credential') { 93 return 'You have already signed up with a different auth provider for that email.'; 94 } 95 96 if (error && (error.code === 'auth/popup-closed-by-user' || error.code === 'auth/cancelled-popup-request')) { 97 return 'Sign in was cancelled.'; 98 } 99 100 return error && error.message ? error.message : 'Something went wrong. Please try again.'; 101 } 102 103 /** 104 * Shared post-Firebase-auth handoff for Google, Apple, and Email OTP. 105 * Gets a fresh, verifiable Firebase ID token and posts it to the WP 106 * verified-login callback, which verifies it server-side before creating 107 * any WP session - the client never asserts its own identity. 108 */ 109 function f7hCompleteVerifiedLogin(user, provider, redirectPath, onError, showError) { 110 function handleError(errorData) { 111 if (typeof onError === 'function') onError(); 112 113 if (typeof showError === 'function') { 114 showError(errorData); 115 return; 116 } 117 118 f7hShowAuthError(errorData && errorData.message ? errorData.message : 'Something went wrong. Please try again.'); 119 } 120 121 return user.getIdToken(true).then(function(idToken) { 122 return $.post(f7h_fb_social_login.ajax_url, { 123 action: 'fifty7hours_firebase_verified_login', 124 idToken: idToken, 125 provider: provider, 126 redirect: redirectPath, 127 nonce: f7h_register_user_ajax_obj.nonce 128 }); 129 }).then(function(response) { 130 if (true === response.success) { 131 if (response.data.status === 'register' && typeof f7hTracking !== 'undefined' && f7hTracking.trackFbLead) { 132 // send event to Facebook 133 f7hTracking.trackFbLead(user.email); 134 } 135 136 window.dataLayer = window.dataLayer || []; 137 window.dataLayer.push({ 138 'event': response.data.status === 'register' ? 'User Sign Up' : 'User Log In', 139 'email': user.email, 140 'signup_method': provider === 'google' ? 'Google' : (provider === 'apple' ? 'Apple' : 'Email OTP') 141 }); 142 143 window.location.href = response.data.redirect; 144 } else { 145 handleError(response.data); 146 } 147 }).catch(function(error) { 148 handleError({ message: error && error.message ? error.message : 'Something went wrong. Please try again.' }); 149 }); 150 } 151 152 /** 153 * Google/Apple: trade the provider sign-in for the 57hours account's custom token, 154 * then hand off to the shared verified login like Email OTP does. 155 */ 156 function f7hCompleteSocialLogin(socialUser, provider, redirectPath, onError) { 157 return socialUser.getIdToken(true).then(function(idToken) { 158 return $.post(f7h_fb_social_login.ajax_url, { 159 action: 'fifty7hours_social_verify', 160 idToken: idToken, 161 nonce: f7h_register_user_ajax_obj.nonce 162 }); 163 }).then(function(response) { 164 if (true !== response.success) { 165 throw response.data || {}; 166 } 167 168 return firebase.auth().signInWithCustomToken(response.data.token); 169 }).then(function(result) { 170 return f7hCompleteVerifiedLogin(result.user, provider, redirectPath, onError); 171 }).catch(function(error) { 172 if (typeof onError === 'function') onError(); 173 174 f7hShowAuthError(error && error.message ? error.message : 'Something went wrong. Please try again.'); 175 }); 176 } 177 178 /** 179 * Where to send the visitor after signing in - shared by Email OTP, Google and Apple, 180 * so all three land in the same place. 181 * 182 * The page they are on, query string and all, read at sign-in time rather than from a 183 * `data-redirect` rendered into the page. A rendered one is wrong more often than it 184 * looks: the page may be served from cache, and any param picked up since it loaded - 185 * a chosen date, a coupon, a campaign tag - would be dropped on the way back. 186 * 187 * An explicit `data-redirect` still wins, for the forms that deliberately send the 188 * visitor somewhere other than where they are. 189 * 190 * Read with `.attr()` rather than `.data()` on purpose: jQuery caches a `.data()` read 191 * and stops consulting the attribute afterwards, so a later `setAttribute` elsewhere 192 * would be silently ignored. 193 */ 194 function f7hGetLoginRedirectPath($trigger) { 195 $trigger = ($trigger && $trigger.length) ? $trigger.first() : $(); 196 197 var redirectPath = $trigger.attr('data-redirect') || (window.location.pathname + window.location.search); 198 var triggerEvent = $trigger.data('event') || $('#f-login-modal').data('event') || ''; 199 200 if (triggerEvent) { 201 redirectPath += (redirectPath.indexOf('?') > -1 ? '&' : '?') + 'event=' + encodeURIComponent(triggerEvent); 202 } 203 204 return redirectPath; 205 } 206 207 /** Email OTP step/state helpers **/ 208 function f7hOtpResolveForm($form) { 209 if ($form && $form.length) { 210 return $form.first(); 211 } 212 213 var $visibleForm = $('.js-email-otp-form:visible').first(); 214 215 return $visibleForm.length ? $visibleForm : $('.js-email-otp-form').first(); 216 } 217 218 function f7hOtpEachForm($form, callback) { 219 if ($form && $form.length) { 220 callback($form.first()); 221 return; 222 } 223 224 $('.js-email-otp-form').each(function() { 225 callback($(this)); 226 }); 227 } 228 229 function f7hOtpGetContext($form) { 230 $form = f7hOtpResolveForm($form); 231 232 var $context = $form.closest('#f-login-modal, .f-myaccount-unauthenticated, .site-content'); 233 234 return $context.length ? $context.first() : $form; 235 } 236 237 function f7hOtpUpdateRequestButtonState($form) { 238 f7hOtpEachForm($form, function($currentForm) { 239 var email = $.trim($currentForm.find('.js-otp-email-input').val()); 240 $currentForm.find('.js-otp-request-code-btn').prop('disabled', email === ''); 241 }); 242 } 243
244 function f7hOtpUpdateVerifyButtonState($form) { 245 f7hOtpEachForm($form, function($currentForm) { 246 var code = ''; 247 248 $currentForm.find('.f-otp-code-input').each(function() { 249 code += $(this).val(); 250 }); 251 252 $currentForm.find('.js-otp-verify-code-btn').prop('disabled', code === ''); 253 }); 254 } 255 256 function f7hOtpGetTitleElement() { 257 return $('#f-login-modal .js-f-login-modal-title, #f-login-modal .title').first(); 258 } 259 260 function f7hOtpGetDefaultTitle() { 261 var $title = f7hOtpGetTitleElement(); 262 263 return $title.data('default-title') || 'Sign in to 57hours'; 264 } 265 266 function f7hOtpGetDefaultReason() { 267 var $reason = $('.js-f-login-modal-reason').first(); 268 269 return $reason.data('default-reason') || 'Enter your email to continue. No passwords needed.'; 270 } 271 272 window.F7HSetLoginModalCopy = function(copy) { 273 copy = copy || {}; 274 275 if (copy.title) { 276 f7hOtpGetTitleElement().text(copy.title); 277 f7hOtpState.emailStepTitle = copy.title; 278 } 279 280 if (typeof copy.reason !== 'undefined') { 281 $('.js-f-login-modal-reason').html(copy.reason); 282 } 283 284 if (typeof copy.otpContinueText !== 'undefined') { 285 $('#f-login-modal').data('otp-continue-text', copy.otpContinueText); 286 } 287 }; 288 289 function f7hOtpShowEmailStep($form) { 290 $form = f7hOtpResolveForm($form); 291 var $context = f7hOtpGetContext($form); 292 293 f7hOtpStopResendCooldown(); 294 $context.find('.js-otp-step-code').hide(); 295 $context.find('.js-otp-step-email').show(); 296 $context.find('.js-f-login-modal-reason').show(); 297 $form.find('.js-otp-code-errors, .js-otp-email-errors').hide().html(''); 298 f7hOtpClearCode($form); 299 f7hOtpUpdateRequestButtonState($form); 300 301 var $title = f7hOtpGetTitleElement(); 302 if ($title.length && f7hOtpState.emailStepTitle) { 303 $title.text(f7hOtpState.emailStepTitle); 304 } 305 } 306 307 function f7hOtpShowCodeStep($form, email) { 308 $form = f7hOtpResolveForm($form); 309 var $context = f7hOtpGetContext($form); 310 var $title = f7hOtpGetTitleElement(); 311 312 if ($title.length) { 313 f7hOtpState.emailStepTitle = $title.text(); 314 $title.text('Check your inbox'); 315 } 316 317 var continueText = $('#f-login-modal').data('otp-continue-text') || 'to continue.'; 318 var changeEmail = String($form.data('lock-email')) === '1' ? '' : 319 ' <button type="button" class="f-btn f-btn--tertiary js-otp-use-different-email">Use a different email</button>.'; 320 321 $form.find('.js-otp-code-email-display').html( 322 'We sent a 6-digit code to <strong>' + $('<div>').text(email).html() + '</strong>. ' + 323 'Enter it below ' + continueText + changeEmail 324 ); 325 $context.find('.js-otp-step-email').hide(); 326 $form.find('.js-otp-step-code').show(); 327 $context.find('.js-f-login-modal-reason').hide(); 328 f7hOtpUpdateVerifyButtonState($form); 329 $form.find('.f-otp-code-input[data-otp-index="0"]').focus(); 330 } 331 332 function f7hOtpClearCode($form, keepErrorState) { 333 $form = f7hOtpResolveForm($form); 334 $form.find('.f-otp-code-input').val(''); 335 336 if (!keepErrorState) { 337 $form.find('.js-otp-code-inputs').removeClass('is-error'); 338 } 339 340 f7hOtpUpdateVerifyButtonState($form); 341 } 342 343 function f7hOtpGetCode($form) { 344 $form = f7hOtpResolveForm($form); 345 var code = ''; 346 $form.find('.f-otp-code-input').each(function(){ code += $(this).val(); }); 347 return code; 348 } 349 350 function f7hOtpShowEmailError($form, errorData) { 351 $form = f7hOtpResolveForm($form); 352 var message = (errorData && errorData.message) ? errorData.message : 'Something went wrong. Please try again.'; 353 $form.find('.js-otp-email-errors').text(message).show(); 354 } 355 356 function f7hOtpShowCodeError($form, errorData) { 357 $form = f7hOtpResolveForm($form); 358 var message = (errorData && errorData.message) ? errorData.message : 'Invalid or expired code.'; 359 $form.find('.js-otp-code-inputs').addClass('is-error'); 360 $form.find('.js-otp-code-errors').text(message).show(); 361 } 362 363 function f7hOtpStartResendCooldown($form) { 364 $form = f7hOtpResolveForm($form); 365 var seconds = 30; 366 var $btn = $form.find('.js-otp-resend-btn'); 367 368 f7hOtpStopResendCooldown(); 369 f7hOtpState.resendButton = $btn; 370 371 $btn.data('original-text', $btn.data('original-text') || $btn.text()); 372 $btn.prop('disabled', true).text('Resend (' + seconds + 's)'); 373 374 f7hOtpState.resendInterval = setInterval(function() { 375 seconds--; 376 377 if (seconds <= 0) { 378 f7hOtpStopResendCooldown(); 379 return; 380 } 381 382 $btn.text('Resend (' + seconds + 's)'); 383 }, 1000); 384 } 385 386 function f7hOtpStopResendCooldown() { 387 var $btn = f7hOtpState.resendButton && f7hOtpState.resendButton.length 388 ? f7hOtpState.resendButton 389 : $('.js-otp-resend-btn'); 390 391 if (f7hOtpState.resendInterval) { 392 clearInterval(f7hOtpState.resendInterval); 393 f7hOtpState.resendInterval = null; 394 } 395 396 $btn.prop('disabled', false).text($btn.data('original-text') || 'Resend'); 397 f7hOtpState.resendButton = null; 398 } 399 400 // Reset the OTP form to its initial state - called whenever the login
401 // modal is (re)opened, so a stale code screen never reappears. 402 window.F7HResetLoginModal = function() { 403 var $form = f7hOtpResolveForm($('#f-login-modal .js-email-otp-form')); 404 405 f7hOtpState.emailStepTitle = ''; 406 f7hOtpShowEmailStep($form); 407 f7hOtpGetTitleElement().text(f7hOtpGetDefaultTitle()); 408 $('.js-f-login-modal-reason').html(f7hOtpGetDefaultReason()); 409 $form.find('.js-otp-email-input').val(''); 410 $form.removeData('otp-email'); 411 $('#f-login-modal').removeData('otp-continue-text'); 412 // Cleared here rather than by each opener, so a trigger's `event=` can't follow 413 // the visitor into the next sign-in the modal is reused for. 414 $('#f-login-modal').removeData('event'); 415 $('#f-login-modal').find('form').removeData('event'); 416 f7hOtpUpdateRequestButtonState($form); 417 }; 418 419 $(function(){ 420 // toggle password 421 $(document).on("click",".js-toggle-password", function() { 422 $(this).toggleClass('password-is-visible'); 423 if ($(this).hasClass('password-is-visible')) { 424 $(this).parent().find('input').prop("type", "text"); 425 } else { 426 $(this).parent().find('input').prop("type", "password"); 427 } 428 }); 429 430 /** Email OTP login/signup **/ 431 f7hOtpUpdateRequestButtonState(); 432 433 $(document).on('input', '.js-otp-email-input', function() { 434 f7hOtpUpdateRequestButtonState($(this).closest('.js-email-otp-form')); 435 }); 436 437 $(document).on('click', '.js-otp-request-code-btn', function(e) { 438 e.preventDefault(); 439 440 var $btn = $(this); 441 var $form = $btn.closest('.js-email-otp-form'); 442 var email = $.trim($form.find('.js-otp-email-input').val()); 443 444 $form.find('.js-otp-email-errors').hide().html(''); 445 446 if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { 447 f7hOtpShowEmailError($form, { message: 'Please enter a valid email address.' }); 448 return; 449 } 450 451 // Honeypot - if a bot filled this hidden field, silently no-op. 452 if ($form.find('input[name="user_consent"]').is(':checked')) return; 453 454 $btn.addClass('is-loading'); 455 f7hDisableAuthControls(); 456 457 $.post(f7h_register_user_ajax_obj.ajax_url, { 458 action: 'fifty7hours_email_otp_request_code', 459 email: email, 460 nonce: f7h_register_user_ajax_obj.nonce 461 }, function(response) { 462 $btn.removeClass('is-loading'); 463 f7hEnableAuthControls(); 464 465 if (true === response.success) { 466 f7hOtpState.email = email; 467 $form.data('otp-email', email); 468 f7hOtpShowCodeStep($form, email); 469 f7hOtpStartResendCooldown($form); 470 } else { 471 f7hOtpShowEmailError($form, response.data); 472 } 473 }).fail(function() { 474 $btn.removeClass('is-loading'); 475 f7hEnableAuthControls(); 476 f7hOtpShowEmailError($form, { message: 'Something went wrong. Please try again.' }); 477 }); 478 }); 479 480 $(document).on('click', '.js-otp-resend-btn', function(e) { 481 e.preventDefault(); 482 483 var $btn = $(this); 484 var $form = $btn.closest('.js-email-otp-form'); 485 var email = $form.data('otp-email') || f7hOtpState.email; 486 487 if ($btn.prop('disabled')) return; 488 489 $btn.prop('disabled', true).addClass('is-loading'); 490 491 $.post(f7h_register_user_ajax_obj.ajax_url, { 492 action: 'fifty7hours_email_otp_request_code', 493 email: email, 494 nonce: f7h_register_user_ajax_obj.nonce 495 }, function(response) { 496 $btn.removeClass('is-loading'); 497 498 if (true === response.success) { 499 $form.find('.js-otp-code-errors').hide().html(''); 500 f7hOtpStartResendCooldown($form); 501 } else { 502 f7hOtpShowCodeError($form, response.data); 503 504 if (response.data && response.data.code === 'OtpTooManyRequests') { 505 f7hOtpStartResendCooldown($form); 506 } else { 507 $btn.prop('disabled', false); 508 } 509 } 510 }).fail(function() { 511 $btn.removeClass('is-loading').prop('disabled', false); 512 f7hOtpShowCodeError($form, { message: 'Something went wrong. Please try again.' }); 513 }); 514 }); 515 516 $(document).on('click', '.js-otp-use-different-email', function(e) { 517 e.preventDefault(); 518 f7hOtpShowEmailStep($(this).closest('.js-email-otp-form')); 519 }); 520 521 // 6-digit code inputs - auto-advance / backspace-to-previous / paste support 522 $(document).on('input', '.f-otp-code-input', function() { 523 var $this = $(this); 524 var $form = $this.closest('.js-email-otp-form'); 525 526 $this.val($this.val().replace(/[^0-9]/g, '').slice(0, 1)); 527 $this.closest('.js-otp-code-inputs').removeClass('is-error'); 528 $form.find('.js-otp-code-errors').hide().html(''); 529 530 if ($this.val().length === 1) { 531 var nextIndex = parseInt($this.data('otp-index'), 10) + 1;
532 var $next = $this.closest('.js-otp-code-inputs').find('.f-otp-code-input[data-otp-index="' + nextIndex + '"]'); 533 if ($next.length) $next.focus(); 534 } 535 536 f7hOtpUpdateVerifyButtonState($form); 537 }); 538 539 $(document).on('keydown', '.f-otp-code-input', function(e) { 540 var $this = $(this); 541 542 if (e.key === 'Backspace' && $this.val() === '') { 543 var prevIndex = parseInt($this.data('otp-index'), 10) - 1; 544 var $prev = $this.closest('.js-otp-code-inputs').find('.f-otp-code-input[data-otp-index="' + prevIndex + '"]'); 545 if ($prev.length) $prev.focus(); 546 } 547 }); 548 549 $(document).on('paste', '.f-otp-code-input', function(e) { 550 var $form = $(this).closest('.js-email-otp-form'); 551 var clipboardData = (e.originalEvent || e).clipboardData || window.clipboardData; 552 var pasted = clipboardData ? clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6) : ''; 553 554 if (pasted.length > 1) { 555 e.preventDefault(); 556 557 var $inputs = $(this).closest('.js-otp-code-inputs').find('.f-otp-code-input'); 558 $inputs.closest('.js-otp-code-inputs').removeClass('is-error'); 559 $form.find('.js-otp-code-errors').hide().html(''); 560 pasted.split('').forEach(function(digit, i) { $inputs.eq(i).val(digit); }); 561 $inputs.eq(Math.min(pasted.length, 6) - 1).focus(); 562 f7hOtpUpdateVerifyButtonState($form); 563 } 564 }); 565 566 $(document).on('click', '.js-otp-verify-code-btn', function(e) { 567 e.preventDefault(); 568 569 var $btn = $(this); 570 var $form = $btn.closest('.js-email-otp-form'); 571 var email = $form.data('otp-email') || f7hOtpState.email; 572 var code = f7hOtpGetCode($form); 573 574 if ($btn.prop('disabled')) return; 575 576 $form.find('.js-otp-code-errors').hide().html(''); 577 578 if (code.length !== 6) { 579 f7hOtpShowCodeError($form, { message: 'Please enter the 6-digit code.' }); 580 return; 581 } 582 583 $btn.addClass('is-loading'); 584 f7hDisableAuthControls(); 585 586 $.post(f7h_register_user_ajax_obj.ajax_url, { 587 action: 'fifty7hours_email_otp_verify_code', 588 email: email, 589 code: code, 590 nonce: f7h_register_user_ajax_obj.nonce 591 }, function(response) { 592 if (true !== response.success) { 593 $btn.removeClass('is-loading'); 594 f7hEnableAuthControls(); 595 f7hOtpShowCodeError($form, response.data); 596 f7hOtpClearCode($form, true); 597 $form.find('.f-otp-code-input[data-otp-index="0"]').focus(); 598 return; 599 } 600 601 var customToken = response.data.token; 602 603 firebase.auth().signInWithCustomToken(customToken).then(function(result) { 604 var redirectPath = f7hGetLoginRedirectPath(f7hOtpResolveForm($form)); 605 606 return f7hCompleteVerifiedLogin(result.user, 'email_otp', redirectPath, function() { 607 $btn.removeClass('is-loading'); 608 f7hEnableAuthControls(); 609 }, function(errorData) { 610 f7hOtpShowCodeError($form, errorData); 611 }); 612 }).catch(function(error) { 613 $btn.removeClass('is-loading'); 614 f7hEnableAuthControls(); 615 f7hOtpShowCodeError($form, { message: error.message }); 616 }); 617 }).fail(function() { 618 $btn.removeClass('is-loading'); 619 f7hEnableAuthControls(); 620 f7hOtpShowCodeError($form, { message: 'Something went wrong. Please try again.' }); 621 }); 622 }); 623 624 $('.js-email-otp-form[data-auto-request="1"]').each(function() { 625 var $form = $(this); 626 var email = $.trim($form.find('.js-otp-email-input').val()); 627 628 if (!email || $form.data('auto-requested')) return; 629 630 $form.data('auto-requested', true); 631 $form.find('.js-otp-request-code-btn').trigger('click'); 632 }); 633 634 // Firebase Google login 635 function firebaseGoogleLoginInit() { 636 var firebaseConfig = { 637 apiKey: f7h_fb_social_login.fbSocialAuth.apiKey, 638 authDomain: f7h_fb_social_login.fbSocialAuth.authDomain, 639 databaseURL: f7h_fb_social_login.fbSocialAuth.databaseURL, 640 projectId: f7h_fb_social_login.fbSocialAuth.projectId, 641 storageBucket: f7h_fb_social_login.fbSocialAuth.storageBucket, 642 }; 643 firebase.initializeApp(firebaseConfig); 644 } 645 646 /**/ 647 firebaseGoogleLoginInit(); 648 649 var f7hActiveAppleLoginButton = null; 650 651 function toggleSignIn() { 652 var loginBtn = this.id; 653 var button = $(this); 654 var buttonTextEl = button.find('.f7h-social-login-text'); 655 var buttonText = button.data('text') || $.trim(buttonTextEl.text()) || 'Continue with Google'; 656 var provider = null; 657 658 f7hClearAuthError(); 659 f7hDisableAuthControls(); 660 button.addClass('is-loading'); 661 662 if (loginBtn == 'f7h_firebase_google_login') { 663 provider = new firebase.auth.GoogleAuthProvider(); 664 buttonTextEl.html('<span class="f7h-myaccount-btn-loading-spinner white-btn-spinner"></span>'); 665 } 666 667 if (!provider) { 668 button.removeClass('is-loading'); 669 f7hEnableAuthControls(); 670 f7hShowAuthError('Something went wrong. Please try again.'); 671 return; 672 } 673 674 var redirectPath = f7hGetLoginRedirectPath($(this)); 675 676 provider.addScope('email'); 677 678 firebase.auth().signInWithPopup(provider).then(function(result) { 679 // The signed-in user info. 680 var user = result.user; 681 682 f7hCompleteSocialLogin(user, 'google', redirectPath, function() { 683 button.removeClass('is-loading'); 684 f7hEnableAuthControls(); 685 686 if (loginBtn == 'f7h_firebase_google_login') { 687 buttonTextEl.html(buttonText); 688 } 689 }); 690 }).catch(function(error) { 691 button.removeClass('is-loading'); 692 693 f7hShowAuthError(f7hGetAuthErrorMessage(error)); 694 695 f7hEnableAuthControls(); 696 697 if (loginBtn == 'f7h_firebase_google_login') { 698 buttonTextEl.html(buttonText); 699 } 700 }); 701 } 702 703 $(document).on('click', '#f7h_firebase_google_login', toggleSignIn); 704 705 // Apple sign in init
706 async function f7hAppleSignInInit() { 707 var rawNonce = 'f7h-sign-in-Apple-id'; 708 // Create SHA256 nonce 709 // encode as UTF-8 710 const nonceUint8Array = new TextEncoder('utf-8').encode(rawNonce); 711 // hash the message 712 const hashedNonce = await crypto.subtle.digest("SHA-256", nonceUint8Array); 713 // convert ArrayBuffer to Array 714 const hashedNonceArray = Array.from(new Uint8Array(hashedNonce)); 715 // convert bytes to hex string 716 const hashNonceHexTxt = hashedNonceArray.map(b => ('00' + b.toString(16)).slice(-2)).join(''); 717 718 // Init Apple sign in 719 AppleID.auth.init({ 720 clientId : f7h_fb_social_login.appleIdIdentifier, 721 scope : 'name email', 722 redirectURI : f7h_fb_social_login.appleIdRedirectUrl, 723 nonce : hashNonceHexTxt, 724 usePopup : true 725 }); 726 } 727 728 /**/ 729 f7hAppleSignInInit(); 730 731 $(document).on('click', '#appleid-signin', f7hDisableLoginButtonsOnAppleLogin); 732 733 function f7hDisableLoginButtonsOnAppleLogin() { 734 f7hActiveAppleLoginButton = $(this); 735 f7hClearAuthError(); 736 f7hDisableAuthControls(); 737 738 f7hActiveAppleLoginButton.addClass('is-loading').next('.f-btn').addClass('is-loading'); 739 } 740 741 function f7hEnableLoginButtonsOnAppleLogin() { 742 var appleButton = (f7hActiveAppleLoginButton && f7hActiveAppleLoginButton.length) ? f7hActiveAppleLoginButton : $('#appleid-signin'); 743 744 f7hEnableAuthControls(); 745 746 appleButton.removeClass('is-loading').next('.f-btn').removeClass('is-loading'); 747 } 748 749 // Listen for Apple sign in authorization success 750 document.addEventListener('AppleIDSignInOnSuccess', (event) => { 751 var appleButton = (f7hActiveAppleLoginButton && f7hActiveAppleLoginButton.length) ? f7hActiveAppleLoginButton : $('#appleid-signin').first(); 752 var redirectPath = f7hGetLoginRedirectPath(appleButton); 753 754 var eventData = event.detail; 755 var idToken = eventData && eventData.authorization ? eventData.authorization.id_token : ''; 756 757 if (!idToken) { 758 f7hEnableLoginButtonsOnAppleLogin(); 759 f7hShowAuthError('Something went wrong. Please try again.'); 760 return; 761 } 762 763 var rawNonce = 'f7h-sign-in-Apple-id'; 764 765 // Sing in user on Firebase 766 var provider = new firebase.auth.OAuthProvider('apple.com'); 767 var credential = provider.credential({ 768 idToken: idToken, 769 rawNonce: rawNonce 770 }); 771 772 firebase.auth().signInWithCredential(credential).then(function(result){ 773 var user = result.user; 774 775 f7hCompleteSocialLogin(user, 'apple', redirectPath, f7hEnableLoginButtonsOnAppleLogin); 776 }).catch(function(error){ 777 f7hShowAuthError(f7hGetAuthErrorMessage(error)); 778 f7hEnableLoginButtonsOnAppleLogin(); 779 }); 780 }); 781 782 // Listen for Apple sign in authorization failures 783 document.addEventListener('AppleIDSignInOnFailure', (event) => { 784 // Handle error. 785 f7hEnableLoginButtonsOnAppleLogin(); 786 var appleError = event.detail && event.detail.error ? event.detail.error : ''; 787 788 if ( appleError === 'popup_closed_by_user' ) { 789 f7hShowAuthError("Sign in was cancelled."); 790 } else { 791 f7hShowAuthError('Something went wrong. Please try again.'); 792 } 793 }); 794 795 function f7hHandleUserVisitsCookie() { 796 var loggedIn = ($('body').hasClass('logged-in')) ? true : false; 797 let userVisitsCount; 798 let cookieExpirationTime; 799 let lastSavedUserVisitedPath; 800 801 if ( ! loggedIn && !! f7h_register_user_ajax_obj.is_content_page ) { 802 let currentVisitPath = f7h_register_user_ajax_obj.current_users_visit_path; 803 804 let userVisitsCookie = Cookies.get('f7hUserVisits'); 805 806 if ( userVisitsCookie !== undefined ) { 807 userVisitsCookie = JSON.parse(userVisitsCookie); 808 809 lastSavedUserVisitedPath = userVisitsCookie.last_user_visited_path; 810 userVisitsCount = userVisitsCookie.visit_count; 811 cookieExpirationTime = new Date(userVisitsCookie.expiration_time); 812 813 // check for a page refresh 814 if ( lastSavedUserVisitedPath !== currentVisitPath ) { 815 userVisitsCount++; 816 } 817 } else { 818 userVisitsCount = 1; 819 820 let dateExpires = new Date(); 821 dateExpires.setDate(dateExpires.getDate() + 1); 822 823 cookieExpirationTime = dateExpires; 824 } 825 826 let newCookieValue = { 827 last_user_visited_path: currentVisitPath, 828 visit_count: userVisitsCount, 829 expiration_time: cookieExpirationTime 830 } 831 832 Cookies.set('f7hUserVisits', newCookieValue, { path: '/', expires: cookieExpirationTime }); 833 } 834 } 835 836 f7hHandleUserVisitsCookie(); 837 838 // Show login popup form if user has visited at least 3 content pages 839 if ( true === shouldShowLoginPopUpForm() ) { 840 window.F7HResetLoginModal(); 841
842 $('.js-f-login-modal-reason').html("Hey there! Thank you for reading our content. You've reached your daily limit for non-users. Please sign in to continue enjoying more good reads."); 843 844 $.fancybox.open({ 845 src: $('#f-login-modal'), 846 type: 'html', 847 baseClass: 'f-login-modal-fancybox', 848 toolbar: false, 849 smallBtn: false, 850 clickSlide: false, 851 clickOutside: false, 852 touch: false, 853 modal: true, 854 afterShow: function( instance ) { 855 var targetblock = document.querySelector('.fancybox-slide'); 856 bodyScrollLock.disableBodyScroll(targetblock); 857 }, 858 afterClose: function( instance, current ) { 859 bodyScrollLock.clearAllBodyScrollLocks(); 860 }, 861 }); 862 } 863 864 function shouldShowLoginPopUpForm() { 865 var loggedIn = ($('body').hasClass('logged-in')) ? true : false; 866 867 if (loggedIn) return false; 868 869 let userVisitsCookie = Cookies.get('f7hUserVisits'); 870 871 if ( userVisitsCookie !== undefined ) { 872 userVisitsCookie = JSON.parse(userVisitsCookie); 873 874 let userVisitsNum = userVisitsCookie.visit_count; 875 876 if ( userVisitsNum >= 3 && f7h_register_user_ajax_obj.is_content_page ) return true; 877 } 878 879 return false; 880 } 881 882 const isWebview = (userAgent) => { 883 return /webview|wv|ip((?!.*Safari)|(?=.*like Safari))/i.test(userAgent); 884 }; 885 886 if (isWebview(window.navigator.userAgent)) { 887 $('#f7h_firebase_google_login').hide(); 888 } 889 890 }); 891})( jQuery );
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.