// 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 () { window.copyWithFeedback(this, this.previousElementSibling.textContent, 'Copied!'); }; // --- 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); }; // --- 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) { window.copyWithFeedback(this, code, 'Copied!'); }; window.toggleContextMenuBtn = function (itemId) { toggleContextMenu({ stopPropagation: function () {} }, itemId); }; window.openCollectionPickerBtn = function (itemId) { openCollectionPicker(itemId, this); }; // --- user_profile.html --- // The link row's five wrappers are gone: user_profile.html includes // partials/link_row.html rather than respelling it, so the row's one set of // verbs (onMoveLink/onEditLink/onSaveLink/onCancelLink, in actions-partials.js) // serves both the loop and the HTMX-inserted fragment. window.copyElementText = function (id) { window.copyWithFeedback(this, document.getElementById(id).textContent, 'Copied'); }; // This one flashed for 2000ms where every other copy button used 1500. The // semantic-timing work ruled one duration per intent, so it reverts on // `Intent::Revert` with the rest of them now. window.copyFeedUrl = function () { window.copyWithFeedback(this, document.getElementById('feed-url').value, 'Copied!'); }; // --- 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); }; // --- 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; } };