PageSourceSearch

https://hotpot.ai/assets/local_redesign-3bd0575e8d8ecd461e8e953f3a177c75751c2efcd26915b2a35ec5f84109a91a.js

js hotpot.ai collected 2026-10-02 03:27:49 UTC 8,900 bytes, 154 lines download raw bytes

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.