// actions-tabs.js // Delegated handler functions extracted from inline on* attributes in // templates/partials/tabs/**, for CSP compliance (no script-src 'unsafe-inline'). // The dispatcher in the core module invokes each named function with `this` === the // dispatching element and args drawn from data-arg / data-arg2. Bodies are the // verbatim former inline handlers, adjusted only to read args from the element. // --- item_overview.html --- window.openInNewTab = function (url) { window.open(url, '_blank'); }; window.copyItemLink = function (path) { navigator.clipboard.writeText(window.location.origin + path).then(() => this.textContent = 'Copied!').catch(() => this.textContent = 'Failed'); }; window.openEmbedSection = function () { var d = document.querySelector('details.content-section'); if (d) { d.open = true; d.scrollIntoView({ behavior: 'smooth' }); } }; // --- item_embed.html --- window.copyEmbedBtn = function () { copyEmbed(this); }; // --- project_settings.html --- window.submitProjectInfo = function () { saveProjectInfo({ preventDefault: function () {} }, this.getAttribute('data-project-id')); }; window.submitProjectPricing = function () { saveProjectPricing({ preventDefault: function () {} }, this.getAttribute('data-project-id')); }; // --- item_pricing.html --- window.togglePwywSettings = function () { document.getElementById('pwyw-settings').classList.toggle('hidden', !this.checked); }; window.toggleCustomLicense = function () { document.getElementById('dash-custom-license').classList.toggle('hidden', this.value !== 'custom'); }; window.toggleLicenseKeys = function () { document.getElementById('license-keys-section').classList.toggle('hidden', !this.checked); }; // --- CSV export buttons (project_subscriptions, buyer_contacts, user_payments, user_creator) --- window.exportCsvButton = function (url, filename) { this.textContent = 'Exporting...'; this.disabled = true; var btn = this; fetch(url, { method: 'POST', headers: csrfHeaders() }).then(function (r) { return r.blob(); }).then(function (b) { var a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = filename; a.click(); btn.textContent = 'Export CSV'; btn.disabled = false; }).catch(function () { btn.textContent = 'Export CSV'; btn.disabled = false; showToast('Export failed'); }); }; // --- project_synckit.html / user_synckit.html --- window.syncKitShowSlugFromBtn = function (appId) { syncKitShowSlugForm(appId, this.dataset.slug); }; window.syncKitDeleteFromBtn = function (appId) { syncKitDeleteApp(appId, this.dataset.name); }; // --- project_promotions.html --- window.onPromoTypeChange = function () { togglePromoFields(this.value); }; window.syncTrialDays = function () { document.getElementById('pc-trial-days').value = this.value; }; // --- user_payments.html --- window.submitOwnForm = function () { this.form.requestSubmit(); }; // --- project_content.html --- window.deselectAll = function () { toggleSelectAll(false); }; window.toggleSelectAllFromEl = function () { toggleSelectAll(this.checked); }; window.sortContent = function (colIndex, sortType) { sortContentTable(parseInt(colIndex, 10), sortType); }; // --- library_collections.html --- window.slugifyCollName = function () { document.getElementById('new-coll-slug').value = this.value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); }; // --- library_purchases.html --- window.filterLibraryRows = function () { (function (q) { var rows = document.querySelectorAll('#library-table tbody tr'); q = q.toLowerCase(); rows.forEach(function (r) { r.classList.toggle('hidden', r.textContent.toLowerCase().indexOf(q) === -1); }); })(this.value); }; window.copyKeyCode = function (code) { navigator.clipboard.writeText(code); this.textContent = 'Copied!'; setTimeout(() => this.textContent = code, 1500); }; window.toggleContextMenuBtn = function (itemId) { toggleContextMenu({ stopPropagation: function () {} }, itemId); }; window.openCollectionPickerBtn = function (itemId) { openCollectionPicker(itemId, this); }; // --- user_profile.html --- window.moveLinkUp = function () { moveLink(this, -1); }; window.moveLinkDown = function () { moveLink(this, 1); }; window.editLinkBtn = function () { editLink(this); }; window.saveLinkBtn = function () { saveLink(this); }; window.cancelLinkBtn = function () { cancelLink(this); }; window.copyElementText = function (id) { navigator.clipboard.writeText(document.getElementById(id).textContent).then(() => { this.textContent = 'Copied'; setTimeout(() => this.textContent = 'Copy', 1500); }); }; window.copyFeedUrl = function () { navigator.clipboard.writeText(document.getElementById('feed-url').value).then(() => { this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy URL', 2000); }); }; // --- user_media.html --- window.onMediaFilesChange = function () { mediaUploadFiles(this.files); }; window.mediaFilterAll = function () { mediaFilterFolder(null, this); }; window.mediaFilterFolderBtn = function (folder) { mediaFilterFolder(folder, this); }; window.mediaDeleteFileBtn = function (id) { mediaDeleteFile(id, this.dataset.filename); }; // --- user_settings.html --- window.setSettingsSectionBtn = function () { setSettingsSection(this); }; // --- item_details.html --- window.onSearchTagsInput = function () { searchTags(this.value); }; window.syncItemHiddenFields = function () { document.getElementById('hidden-title').value = document.getElementById('item-name').value; document.getElementById('hidden-desc').value = document.getElementById('item-description').value; document.getElementById('hidden-ai-tier').value = document.getElementById('ai_tier').value; document.getElementById('hidden-ai-disclosure').value = document.getElementById('ai_disclosure').value; }; // --- user_creator.html --- window.toggleTrialDetails = function () { document.getElementById('trial-details').classList.toggle('hidden', !this.checked); }; // --- Non-dispatcher delegated listeners --- // The the core module dispatcher only covers click/change/input/submit. The handlers // below cover drag-and-drop (user_media) and conditional submit (user_account), // keyed on data attributes so the inline handlers can be removed. // Media upload drop zone (user_media.html): former ondragover/ondragleave/ondrop. document.addEventListener('dragover', function (e) { var el = e.target.closest('[data-media-dropzone]'); if (!el) return; e.preventDefault(); el.classList.add('user-media-upload--dragover'); }); document.addEventListener('dragleave', function (e) { var el = e.target.closest('[data-media-dropzone]'); if (!el) return; el.classList.remove('user-media-upload--dragover'); }); document.addEventListener('drop', function (e) { var el = e.target.closest('[data-media-dropzone]'); if (!el) return; mediaHandleDrop(e); el.classList.remove('user-media-upload--dragover'); }); // Conditional submit guard (user_account.html): a former onsubmit that returned // false to block submission. Runs in capture phase so a false result can stop // propagation before htmx's submit handler sees the event. document.addEventListener('submit', function (e) { var el = e.target.closest('[data-check-submit]'); if (!el) return; var fn = window[el.getAttribute('data-check-submit')]; if (typeof fn === 'function') { var r = fn.call(el, e); if (r === false) { e.preventDefault(); e.stopPropagation(); } } }, true); window.checkPasswordMatch = function () { var np = document.getElementById('new-password').value, cp = document.getElementById('confirm-password').value; if (np !== cp) { var s = document.getElementById('password-status'); s.textContent = 'Passwords do not match'; s.classList.add('danger-text'); return false; } };