Skip to main content

max / makenotwork

6.6 KB · 169 lines History Blame Raw
1 // actions-tabs.js
2 // Delegated handler functions extracted from inline on* attributes in
3 // templates/partials/tabs/**, for CSP compliance (no script-src 'unsafe-inline').
4 // The dispatcher in the core module invokes each named function with `this` === the
5 // dispatching element and args drawn from data-arg / data-arg2. Bodies are the
6 // verbatim former inline handlers, adjusted only to read args from the element.
7
8 // --- item_overview.html ---
9 window.copyItemLink = function (path) {
10 navigator.clipboard.writeText(window.location.origin + path).then(() => this.textContent = 'Copied!').catch(() => this.textContent = 'Failed');
11 };
12
13 window.openEmbedSection = function () {
14 var d = document.querySelector('details.content-section'); if (d) { d.open = true; d.scrollIntoView({ behavior: 'smooth' }); }
15 };
16
17 // --- item_embed.html ---
18 window.copyEmbedBtn = function () {
19 window.copyWithFeedback(this, this.previousElementSibling.textContent, 'Copied!');
20 };
21
22 // --- project_settings.html ---
23 window.submitProjectInfo = function () {
24 saveProjectInfo({ preventDefault: function () {} }, this.getAttribute('data-project-id'));
25 };
26
27 window.submitProjectPricing = function () {
28 saveProjectPricing({ preventDefault: function () {} }, this.getAttribute('data-project-id'));
29 };
30
31 // --- item_pricing.html ---
32 window.togglePwywSettings = function () {
33 document.getElementById('pwyw-settings').classList.toggle('hidden', !this.checked);
34 };
35
36 window.toggleCustomLicense = function () {
37 document.getElementById('dash-custom-license').classList.toggle('hidden', this.value !== 'custom');
38 };
39
40 window.toggleLicenseKeys = function () {
41 document.getElementById('license-keys-section').classList.toggle('hidden', !this.checked);
42 };
43
44 // --- project_synckit.html / user_synckit.html ---
45 window.syncKitShowSlugFromBtn = function (appId) {
46 syncKitShowSlugForm(appId, this.dataset.slug);
47 };
48
49 window.syncKitDeleteFromBtn = function (appId) {
50 syncKitDeleteApp(appId, this.dataset.name);
51 };
52
53 // --- project_promotions.html ---
54 window.onPromoTypeChange = function () {
55 togglePromoFields(this.value);
56 };
57
58 window.syncTrialDays = function () {
59 document.getElementById('pc-trial-days').value = this.value;
60 };
61
62 // --- library_collections.html ---
63 window.slugifyCollName = function () {
64 document.getElementById('new-coll-slug').value = this.value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
65 };
66
67 // --- library_purchases.html ---
68 window.filterLibraryRows = function () {
69 (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);
70 };
71
72 window.copyKeyCode = function (code) {
73 window.copyWithFeedback(this, code, 'Copied!');
74 };
75
76 window.toggleContextMenuBtn = function (itemId) {
77 toggleContextMenu({ stopPropagation: function () {} }, itemId);
78 };
79
80 window.openCollectionPickerBtn = function (itemId) {
81 openCollectionPicker(itemId, this);
82 };
83
84 // --- user_profile.html ---
85 // The link row's five wrappers are gone: user_profile.html includes
86 // partials/link_row.html rather than respelling it, so the row's one set of
87 // verbs (onMoveLink/onEditLink/onSaveLink/onCancelLink, in actions-partials.js)
88 // serves both the loop and the HTMX-inserted fragment.
89 window.copyElementText = function (id) {
90 window.copyWithFeedback(this, document.getElementById(id).textContent, 'Copied');
91 };
92
93 // This one flashed for 2000ms where every other copy button used 1500. The
94 // semantic-timing work ruled one duration per intent, so it reverts on
95 // `Intent::Revert` with the rest of them now.
96 window.copyFeedUrl = function () {
97 window.copyWithFeedback(this, document.getElementById('feed-url').value, 'Copied!');
98 };
99
100 // --- user_media.html ---
101 window.onMediaFilesChange = function () {
102 mediaUploadFiles(this.files);
103 };
104
105 window.mediaFilterAll = function () {
106 mediaFilterFolder(null, this);
107 };
108
109 window.mediaFilterFolderBtn = function (folder) {
110 mediaFilterFolder(folder, this);
111 };
112
113 window.mediaDeleteFileBtn = function (id) {
114 mediaDeleteFile(id, this.dataset.filename);
115 };
116
117 // --- item_details.html ---
118 window.onSearchTagsInput = function () {
119 searchTags(this.value);
120 };
121
122 window.syncItemHiddenFields = function () {
123 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;
124 };
125
126 // --- user_creator.html ---
127 window.toggleTrialDetails = function () {
128 document.getElementById('trial-details').classList.toggle('hidden', !this.checked);
129 };
130
131 // --- Non-dispatcher delegated listeners ---
132 // The the core module dispatcher only covers click/change/input/submit. The handlers
133 // below cover drag-and-drop (user_media) and conditional submit (user_account),
134 // keyed on data attributes so the inline handlers can be removed.
135
136 // Media upload drop zone (user_media.html): former ondragover/ondragleave/ondrop.
137 document.addEventListener('dragover', function (e) {
138 var el = e.target.closest('[data-media-dropzone]');
139 if (!el) return;
140 e.preventDefault(); el.classList.add('user-media-upload--dragover');
141 });
142 document.addEventListener('dragleave', function (e) {
143 var el = e.target.closest('[data-media-dropzone]');
144 if (!el) return;
145 el.classList.remove('user-media-upload--dragover');
146 });
147 document.addEventListener('drop', function (e) {
148 var el = e.target.closest('[data-media-dropzone]');
149 if (!el) return;
150 mediaHandleDrop(e); el.classList.remove('user-media-upload--dragover');
151 });
152
153 // Conditional submit guard (user_account.html): a former onsubmit that returned
154 // false to block submission. Runs in capture phase so a false result can stop
155 // propagation before htmx's submit handler sees the event.
156 document.addEventListener('submit', function (e) {
157 var el = e.target.closest('[data-check-submit]');
158 if (!el) return;
159 var fn = window[el.getAttribute('data-check-submit')];
160 if (typeof fn === 'function') {
161 var r = fn.call(el, e);
162 if (r === false) { e.preventDefault(); e.stopPropagation(); }
163 }
164 }, true);
165
166 window.checkPasswordMatch = function () {
167 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; }
168 };
169