1// Local redesign behavior. Authentication and file operations retain their existing providers. 2document.addEventListener('DOMContentLoaded', () => { 3 const $one = selector => document.querySelector(selector); 4 const $all = selector => [...document.querySelectorAll(selector)]; 5 const toggle = $one('.password-toggle'); 6 if (document.body.classList.contains('local-auth')) { 7 const feedback = document.createElement('dialog'); 8 feedback.className = 'auth-feedback'; 9 feedback.setAttribute('aria-labelledby', 'auth-feedback-title'); 10 feedback.innerHTML = '<h2 id="auth-feedback-title"></h2><p></p><form method="dialog"><button class="primary" autofocus>OK</button></form>'; 11 document.body.append(feedback); 12 window.localResetFeedback = (title, html) => { 13 feedback.querySelector('h2').textContent = title; 14 feedback.querySelector('p').textContent = new DOMParser().parseFromString(html, 'text/html').body.textContent; 15 feedback.showModal(); 16 }; 17 } 18 if (toggle) toggle.addEventListener('click', () => { 19 const input = $one('#account-password'); 20 const show = input.type === 'password'; 21 input.type = show ? 'text' : 'password'; 22 toggle.textContent = show ? 'Hide' : 'Show'; 23 toggle.setAttribute('aria-label', show ? 'Hide password' : 'Show password'); 24 toggle.setAttribute('aria-pressed', String(show)); 25 }); 26 27 const packageData = $one('#credit-packages'); 28 if (packageData) { 29 const prices = JSON.parse(packageData.dataset.packages); 30 const params = new URLSearchParams(location.search); 31 let cadence = ['once', 'monthly', 'yearly'].includes(params.get('cadence')) ? params.get('cadence') : 'once'; 32 let index = /^[0-4]$/.test(params.get('package') || '') ? Number(params.get('package')) : 0; 33 const names = {once: 'One-time purchase', monthly: 'Monthly subscription', yearly: 'Yearly subscription'}; 34 const terms = {once: 'No recurring payment.', monthly: 'Billed monthly. Monthly subscriptions require at least two terms.', yearly: 'Billed yearly. Annual subscriptions are cancellable after one term.'}; 35 const updateOrder = () => { 36 const item = prices[cadence][index]; 37 $all('[data-order-quantity]').forEach(el => el.textContent = item.quantity.toLocaleString('en-US')); 38 $all('[data-order-price]').forEach(el => el.textContent = '$' + item.price.toLocaleString('en-US')); 39 $all('[data-order-frequency]').forEach(el => el.textContent = names[cadence]); 40 $all('[data-order-period]').forEach(el => el.textContent = {once: '', monthly: '/ month', yearly: '/ year'}[cadence]); 41 $all('[data-order-terms]').forEach(el => el.textContent = terms[cadence]); 42 $all('[data-price-index]').forEach(el => el.textContent = '$' + prices[cadence][Number(el.dataset.priceIndex)].price.toLocaleString('en-US') + {once: '', monthly: ' / mo', yearly: ' / yr'}[cadence]); 43 const returnURL = new URL(location.href); 44 returnURL.searchParams.set('cadence', cadence); 45 returnURL.searchParams.set('package', String(index)); 46 window.history.replaceState(null, '', returnURL); 47 }; 48 if ($one('input[name="cadence"]')) { 49 $one(`input[name="cadence"][value="${cadence}"]`).checked = true; 50 $one(`input[name="package"][value="${index}"]`).checked = true; 51 $all('input[name="cadence"]').forEach(input => input.addEventListener('change', () => { cadence = input.value; updateOrder(); })); 52 $all('input[name="package"]').forEach(input => input.addEventListener('change', () => { index = Number(input.value); updateOrder(); })); 53 } 54 updateOrder(); 55 const checkout = $one('#continue-checkout'); 56 if (checkout) { 57 const status = $one('#checkout-status'); 58 const setBusy = busy => { 59 checkout.disabled = busy;
60 $all('input[name="cadence"], input[name="package"]').forEach(input => input.disabled = busy); 61 }; 62 checkout.addEventListener('click', async () => { 63 if (checkout.disabled) return; 64 const item = prices[cadence][index]; 65 setBusy(true); 66 status.textContent = 'Opening checkoutâ¦'; 67 try { 68 // The server validates this price and derives its credit allocation. 69 const response = await window.jQuery.post('/checkout/credits', { 70 mode: item.mode, priceID: item.priceID, name: item.quantity + ' credits', 71 quantity: item.quantity, planType: {once: 0, monthly: 1, yearly: 2}[cadence], referer: location.href 72 }); 73 if (!response.redirectURL) throw new Error('No checkout URL returned'); 74 location.assign(response.redirectURL); 75 } catch (error) { 76 status.textContent = 'Checkout could not be opened. Please try again or contact us.'; 77 setBusy(false); 78 } 79 }); 80 window.addEventListener('pageshow', () => { 81 setBusy(false); 82 status.textContent = ''; 83 }); 84 } 85 } 86 87 const custom = $one('#custom-checkout'); 88 if (custom) { 89 const code = new URLSearchParams(location.search).get('promo'); 90 // Match the existing promo amount, including its $1 default; display the actual amount. 91 const amount = code && code.length > 5 ? Number.parseInt(code.substring(1, 6), 10) : 1; 92 const valid = Number.isFinite(amount) && amount > 0; 93 $one('#custom-amount').textContent = valid ? '$' + amount.toLocaleString('en-US') : 'Unavailable'; 94 custom.disabled = !valid; 95 if (!valid) $one('#custom-status').textContent = 'This payment link is invalid. Please contact us.'; 96 custom.addEventListener('click', () => { 97 if (valid) window.CheckoutClass.showPaymentModal(amount * 100, 'Custom Price', null, location.href); 98 }); 99 } 100 101 const history = $one('#historyBox .itemListBox'); 102 if (history) { 103 let view = 'grid'; 104 let loaded = typeof User !== 'undefined' && Boolean(User); 105 const updateFiles = () => { 106 const items = [...history.querySelectorAll('.itemBox')]; 107 const selectedProduct = $one('#productListBox .selected'); 108 const imageCategory = selectedProduct && ['8', '17'].includes(selectedProduct.getAttribute('serviceId')); 109 $all('#productListBox .product').forEach(button => button.setAttribute('aria-pressed', String(button === selectedProduct))); 110 history.classList.toggle('local-grid', view === 'grid' && imageCategory); 111 history.classList.toggle('local-list', view === 'list' || !imageCategory); 112 items.forEach(item => { 113 const image = item.querySelector('img'); 114 if (image && !image.dataset.keyboardReady) { 115 image.dataset.keyboardReady = 'true'; 116 image.tabIndex = 0; 117 image.setAttribute('role', 'button'); 118 image.alt = 'Open ' + (item.querySelector('.name')?.textContent || 'image'); 119 image.addEventListener('keydown', event => { 120 if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); image.click(); } 121 }); 122 } 123 }); 124 const visible = items.filter(item => !item.hidden); 125 const checked = items.filter(item => item.querySelector('.checkbox:checked')); 126 $one('#drive-count').textContent = loaded ? `${visible.length} ${visible.length === 1 ? 'file' : 'files'}` : ''; 127 $one('#controlBox .button.delete').disabled = history.parentElement.getAttribute('aria-busy') === 'true' || !checked.length; 128 const empty = $one('#drive-empty'); 129 empty.hidden = visible.length > 0; 130 empty.querySelector('h2').textContent = !loaded ? 'Loading your filesâ¦' : 'No files here yet'; 131 empty.querySelector('p').textContent = !loaded ? '' : 'Choose another product to see its files.'; 132 $all('[data-drive-view]').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.driveView === view))); 133 }; 134 const originalRender = window.renderHistory; 135 window.renderHistory = function(serviceId) { originalRender(serviceId); loaded = true; updateFiles(); }; 136 const originalOpen = window.didClickItem; 137 const viewer = $one('#drive-viewer'); 138 window.didClickItem = function(item) { 139 const image = item.find('img'); 140 if (!image.length || !viewer) return originalOpen(item); 141 const name = item.find('.name').text(); 142 viewer.querySelector('h2').textContent = name || 'Image'; 143 viewer.querySelector('img').src = image.attr('src'); 144 viewer.querySelector('img').alt = name; 145 viewer.querySelector('[data-open-original]').href = item.find('a').attr('href'); 146 viewer.showModal(); 147 }; 148 if (viewer) viewer.querySelector('[data-close-viewer]').addEventListener('click', () => viewer.close()); 149 $all('[data-drive-view]').forEach(button => button.addEventListener('click', () => { view = button.dataset.driveView; updateFiles(); })); 150 $one('#historyBox').addEventListener('click', () => updateFiles()); 151 new MutationObserver(updateFiles).observe(history, {childList: true}); 152 updateFiles(); 153 } 154});
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.