1/** 2 * @file 3 * Behavior for the My Account page: tab switching and the subscriber-only 4 * Billing tab, which launches the Stripe customer portal. 5 * 6 * The Billing tab is hidden by default and only revealed for readers with an 7 * active Pro subscription (determined via window.urbanizeMembership). The Stripe 8 * portal is an external redirect, so the tab hosts a button that opens it. 9 */ 10(function (Drupal) { 11 'use strict'; 12 13 function activateTab(root, name) { 14 root.querySelectorAll('[data-account-tab]').forEach(function (tab) { 15 var active = tab.getAttribute('data-account-tab') === name; 16 tab.classList.toggle('is-active', active); 17 tab.setAttribute('aria-selected', active ? 'true' : 'false'); 18 }); 19 root.querySelectorAll('[data-account-panel]').forEach(function (panel) { 20 var active = panel.getAttribute('data-account-panel') === name; 21 panel.classList.toggle('is-active', active); 22 if (active) { 23 panel.removeAttribute('hidden'); 24 } 25 else { 26 panel.setAttribute('hidden', 'hidden'); 27 } 28 }); 29 } 30 31 function wireBillingPortal(root, userInfo) { 32 var button = root.querySelector('[data-open-billing-portal]'); 33 var note = root.querySelector('[data-billing-note]'); 34 if (!button) { 35 return; 36 } 37 38 if (userInfo.isBulkSubscription) { 39 button.addEventListener('click', function () { 40 if (note) { 41 note.textContent = 'This account is part of a group subscription. To make changes, please contact your group administrator: ' + (userInfo.bulkOwnerEmail || ''); 42 note.removeAttribute('hidden'); 43 } 44 }); 45 return; 46 } 47 48 button.addEventListener('click', function () { 49 var url = '/stripe-account-session/' + userInfo.stripeCustomer + 50 '?return_url=' + encodeURIComponent(window.location.href); 51 // Open the tab synchronously inside the click handler so it isn't blocked 52 // as a popup; we point it at the Stripe URL once the session is created. 53 var portalTab = window.open('', '_blank'); 54 button.disabled = true; 55 fetch(url, { method: 'POST' }) 56 .then(function (response) { 57 return response.ok ? response.json() : null; 58 }) 59 .then(function (data) { 60 if (data && data.url) { 61 if (portalTab) { 62 portalTab.location = data.url; 63 } 64 else { 65 // Popup was blocked; fall back to same-tab navigation. 66 window.location.href = data.url; 67 } 68 } 69 else if (portalTab) { 70 portalTab.close(); 71 } 72 // The portal opens in a new tab, so this page stays put â re-enable 73 // the button so it can be used again. 74 button.disabled = false; 75 }) 76 .catch(function () { 77 if (portalTab) { 78 portalTab.close(); 79 } 80 button.disabled = false; 81 }); 82 }); 83 } 84 85 Drupal.behaviors.urbanizeAccountPage = { 86 attach: function (context) { 87 var root = (context.querySelector && context.querySelector('.account-page')) || 88 document.querySelector('.account-page'); 89 if (!root || root.getAttribute('data-account-init')) { 90 return; 91 } 92 root.setAttribute('data-account-init', '1'); 93 94 root.querySelectorAll('[data-account-tab]').forEach(function (tab) { 95 tab.addEventListener('click', function () { 96 activateTab(root, tab.getAttribute('data-account-tab')); 97 }); 98 }); 99 100 // Reveal + wire the Billing tab only for active subscribers. 101 if (window.urbanizeMembership && typeof window.urbanizeMembership.getCurrentUserInfo === 'function') { 102 window.urbanizeMembership.getCurrentUserInfo().then(function (userInfo) { 103 if (!userInfo || !userInfo.isSubscriber) { 104 return; 105 } 106 var billingTab = root.querySelector('[data-account-tab="billing"]'); 107 if (billingTab) { 108 billingTab.removeAttribute('hidden'); 109 } 110 wireBillingPortal(root, userInfo); 111 }); 112 } 113 } 114 }; 115})(Drupal);
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.