Skip to content

Commit 9a15ef3

Browse files
bcordisclaude
andcommitted
refactor: split admin-only JS out of cwmcore into cwmcore-admin
Extract reference management, image chooser, and file size converter code into a separate cwmcore-admin.es6.js loaded only on admin pages. cwmcore.es6.js now contains only shared code needed on both front and back end: ProclaimA11y focus trap, inline player toggle, print/submit button delegation, and Bootstrap modal a11y. Before: cwmcore.min.js ~5KB loaded on every page After: cwmcore.min.js ~3KB (shared) + cwmcore-admin.min.js ~2KB (admin only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 769e928 commit 9a15ef3

File tree

5 files changed

+185
-157
lines changed

5 files changed

+185
-157
lines changed

admin/api.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@
102102
// to avoid render-blocking CSS/JS on pages that don't need them.
103103
if ($app->isClient('administrator')) {
104104
$wa->useStyle('com_proclaim.cwmcore')
105-
->useScript('com_proclaim.cwmcorejs');
105+
->useScript('com_proclaim.cwmcorejs')
106+
->useScript('com_proclaim.cwmcorejs-admin');
106107
}
107108

108109
// Register lib_cwmscripture web assets (libraries aren't auto-discovered by Joomla)

admin/src/View/Cwmmediafile/HtmlView.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ public function __call(string $name, array $args): mixed
176176
// Needed to load the article field type for the article selector
177177
FormHelper::addFieldPath(JPATH_ADMINISTRATOR . '/components/com_content/models/fields/modal');
178178

179-
// Load the cwmcore script for file size converter
180-
$app->getDocument()->getWebAssetManager()->useScript('com_proclaim.cwmcorejs');
179+
// Load the admin script for file size converter
180+
$app->getDocument()->getWebAssetManager()->useScript('com_proclaim.cwmcorejs-admin');
181181

182182
// Check for errors.
183183
if (\count($errors = $model->getErrors())) {
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/* globals _ */
2+
/**
3+
* Admin-only utilities for Proclaim backend forms.
4+
*
5+
* Handles reference management, image chooser previews, and the
6+
* file size converter modal on the media file edit screen.
7+
*
8+
* @package Proclaim.Admin
9+
* @since 10.3.0
10+
*/
11+
(function (window, document) {
12+
'use strict';
13+
14+
document.addEventListener('DOMContentLoaded', () => {
15+
// --- Reference management ---
16+
const addReferenceBtn = document.getElementById('addReference');
17+
if (addReferenceBtn) {
18+
addReferenceBtn.addEventListener('click', (e) => {
19+
e.preventDefault();
20+
const referenceTemplate = document.getElementById('reference');
21+
if (!referenceTemplate) { return; }
22+
23+
const newReference = referenceTemplate.cloneNode(true);
24+
25+
const deleteButton = document.createElement('a');
26+
deleteButton.href = '#';
27+
deleteButton.className = 'referenceDelete';
28+
deleteButton.textContent = 'Delete';
29+
30+
const textInput = newReference.querySelector('#text');
31+
if (textInput) {
32+
textInput.value = '';
33+
textInput.setAttribute('value', '');
34+
}
35+
36+
const scriptureSelect = newReference.querySelector('#scripture');
37+
if (scriptureSelect) {
38+
scriptureSelect.value = '0';
39+
}
40+
41+
newReference.appendChild(deleteButton);
42+
const referencesContainer = document.getElementById('references');
43+
if (referencesContainer) {
44+
referencesContainer.appendChild(newReference);
45+
}
46+
});
47+
}
48+
49+
// Use event delegation for dynamically added delete buttons
50+
const referencesContainer = document.getElementById('references');
51+
if (referencesContainer) {
52+
referencesContainer.addEventListener('click', (e) => {
53+
if (e.target.classList.contains('referenceDelete')) {
54+
e.preventDefault();
55+
const parent = e.target.closest('#reference');
56+
if (parent) {
57+
parent.remove();
58+
}
59+
}
60+
});
61+
}
62+
63+
// --- Image chooser preview ---
64+
document.querySelectorAll('.imgChoose').forEach((el) => {
65+
el.addEventListener('change', function () {
66+
const targetImage = document.getElementById(`img${this.id}`);
67+
if (!targetImage) { return; }
68+
69+
const src = targetImage.getAttribute('src');
70+
let activeDir = [];
71+
if (src) {
72+
activeDir = src.split('/');
73+
activeDir.pop();
74+
}
75+
76+
if (parseInt(this.value, 10) === 0) {
77+
targetImage.style.display = 'none';
78+
} else {
79+
targetImage.style.display = '';
80+
}
81+
82+
// Safely escape the value
83+
const escapeHtml = (str) => {
84+
if (typeof _ !== 'undefined' && typeof _.escape === 'function') {
85+
return _.escape(str);
86+
}
87+
const entityMap = {
88+
'&': '&amp;',
89+
'<': '&lt;',
90+
'>': '&gt;',
91+
'"': '&quot;',
92+
"'": '&#039;',
93+
};
94+
return String(str).replace(/[&<>"']/g, (s) => entityMap[s]);
95+
};
96+
97+
if (activeDir.length > 0) {
98+
targetImage.setAttribute('src', `${activeDir.join('/')}/${escapeHtml(this.value)}`);
99+
}
100+
});
101+
});
102+
103+
// --- File size converter modal ---
104+
const converterInput = document.getElementById('Text1');
105+
if (converterInput) {
106+
converterInput.addEventListener('change', function () {
107+
decOnly(this);
108+
});
109+
}
110+
111+
const transferBtn = document.getElementById('btn-transfer-filesize');
112+
if (transferBtn) {
113+
transferBtn.addEventListener('click', () => {
114+
transferFileSize();
115+
});
116+
}
117+
});
118+
119+
function decOnly(i) {
120+
let t = i.value;
121+
if (t.length > 0) {
122+
t = t.replace(/[^\d.]+/g, '');
123+
}
124+
125+
const s = t.split('.');
126+
if (s.length > 1) {
127+
s[1] = `${s[0]}.${s[1]}`;
128+
s.shift();
129+
}
130+
131+
i.value = s.join('');
132+
}
133+
134+
function bandwidth(bytees, type) {
135+
let value = bytees;
136+
if (!Number.isNaN(Number(value)) && (value !== '')) {
137+
switch (type.toUpperCase()) {
138+
case 'KB':
139+
value *= 1024;
140+
break;
141+
case 'MB':
142+
value *= 1024 ** 2;
143+
break;
144+
case 'GB':
145+
value *= 1024 ** 3;
146+
break;
147+
default:
148+
return 'error';
149+
}
150+
151+
return parseInt(value, 10);
152+
}
153+
return 'error';
154+
}
155+
156+
function transferFileSize() {
157+
const size = document.getElementById('Text1').value;
158+
const ty = document.getElementById('Select1').value;
159+
const ss = bandwidth(size, ty);
160+
if (ss === 'error') {
161+
if (typeof Joomla !== 'undefined' && Joomla.renderMessages) {
162+
Joomla.renderMessages({ warning: ['Numbers only please.'] });
163+
}
164+
return false;
165+
}
166+
document.getElementById('jform_params_size').value = ss;
167+
return true;
168+
}
169+
170+
}(window, window.document));

build/media_source/js/cwmcore.es6.js

Lines changed: 0 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* globals _ */
21
(function (window, document) {
32
'use strict';
43

@@ -175,94 +174,7 @@
175174
});
176175
});
177176

178-
const addReferenceBtn = document.getElementById('addReference');
179-
if (addReferenceBtn) {
180-
addReferenceBtn.addEventListener('click', (e) => {
181-
e.preventDefault();
182-
const referenceTemplate = document.getElementById('reference');
183-
if (!referenceTemplate) { return; }
184-
185-
const newReference = referenceTemplate.cloneNode(true);
186-
187-
const deleteButton = document.createElement('a');
188-
deleteButton.href = '#';
189-
deleteButton.className = 'referenceDelete';
190-
deleteButton.textContent = 'Delete';
191-
192-
const textInput = newReference.querySelector('#text');
193-
if (textInput) {
194-
textInput.value = '';
195-
textInput.setAttribute('value', '');
196-
}
197-
198-
const scriptureSelect = newReference.querySelector('#scripture');
199-
if (scriptureSelect) {
200-
scriptureSelect.value = '0';
201-
}
202-
203-
newReference.appendChild(deleteButton);
204-
const referencesContainer = document.getElementById('references');
205-
if (referencesContainer) {
206-
referencesContainer.appendChild(newReference);
207-
}
208-
});
209-
}
210-
211-
// Use event delegation for dynamically added delete buttons
212-
const referencesContainer = document.getElementById('references');
213-
if (referencesContainer) {
214-
referencesContainer.addEventListener('click', (e) => {
215-
if (e.target.classList.contains('referenceDelete')) {
216-
e.preventDefault();
217-
const parent = e.target.closest('#reference');
218-
if (parent) {
219-
parent.remove();
220-
}
221-
}
222-
});
223-
}
224-
225-
document.querySelectorAll('.imgChoose').forEach((el) => {
226-
el.addEventListener('change', function () {
227-
const targetImage = document.getElementById(`img${this.id}`);
228-
if (!targetImage) { return; }
229-
230-
const src = targetImage.getAttribute('src');
231-
let activeDir = [];
232-
if (src) {
233-
activeDir = src.split('/');
234-
activeDir.pop();
235-
}
236-
237-
if (parseInt(this.value, 10) === 0) {
238-
targetImage.style.display = 'none';
239-
} else {
240-
targetImage.style.display = '';
241-
}
242-
243-
// Safely escape the value - use underscore if available, otherwise basic HTML escape
244-
const escapeHtml = (str) => {
245-
if (typeof _ !== 'undefined' && typeof _.escape === 'function') {
246-
return _.escape(str);
247-
}
248-
const entityMap = {
249-
'&': '&amp;',
250-
'<': '&lt;',
251-
'>': '&gt;',
252-
'"': '&quot;',
253-
"'": '&#039;',
254-
};
255-
return String(str).replace(/[&<>"']/g, (s) => entityMap[s]);
256-
};
257-
258-
if (activeDir.length > 0) {
259-
targetImage.setAttribute('src', `${activeDir.join('/')}/${escapeHtml(this.value)}`);
260-
}
261-
});
262-
});
263-
264177
// Auto-initialize focus trap for Bootstrap modals
265-
// Listen for Bootstrap modal events
266178
document.addEventListener('shown.bs.modal', (e) => {
267179
ProclaimA11y.trapFocus(e.target);
268180
});
@@ -287,72 +199,6 @@
287199
Joomla.submitbutton(btn.dataset.submitTask);
288200
}
289201
});
290-
291-
// Converter modal: decimal-only input and transfer button
292-
const converterInput = document.getElementById('Text1');
293-
if (converterInput) {
294-
converterInput.addEventListener('change', function () {
295-
decOnly(this);
296-
});
297-
}
298-
299-
const transferBtn = document.getElementById('btn-transfer-filesize');
300-
if (transferBtn) {
301-
transferBtn.addEventListener('click', () => {
302-
transferFileSize();
303-
});
304-
}
305202
});
306203

307-
function decOnly(i) {
308-
let t = i.value;
309-
if (t.length > 0) {
310-
t = t.replace(/[^\d.]+/g, '');
311-
}
312-
313-
const s = t.split('.');
314-
if (s.length > 1) {
315-
s[1] = `${s[0]}.${s[1]}`;
316-
s.shift();
317-
}
318-
319-
i.value = s.join('');
320-
}
321-
322-
function bandwidth(bytees, type) {
323-
let value = bytees;
324-
if (!Number.isNaN(Number(value)) && (value !== '')) {
325-
switch (type.toUpperCase()) {
326-
case 'KB':
327-
value *= 1024;
328-
break;
329-
case 'MB':
330-
value *= 1024 ** 2;
331-
break;
332-
case 'GB':
333-
value *= 1024 ** 3;
334-
break;
335-
default:
336-
return 'error';
337-
}
338-
339-
return parseInt(value, 10);
340-
}
341-
return 'error';
342-
}
343-
344-
function transferFileSize() {
345-
const size = document.getElementById('Text1').value;
346-
const ty = document.getElementById('Select1').value;
347-
const ss = bandwidth(size, ty);
348-
if (ss === 'error') {
349-
if (typeof Joomla !== 'undefined' && Joomla.renderMessages) {
350-
Joomla.renderMessages({ warning: ['Numbers only please.'] });
351-
}
352-
return false;
353-
}
354-
document.getElementById('jform_params_size').value = ss;
355-
return true;
356-
}
357-
358204
}(window, window.document));

media/joomla.asset.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,17 @@
484484
"core"
485485
]
486486
},
487+
{
488+
"name": "com_proclaim.cwmcorejs-admin",
489+
"type": "script",
490+
"uri": "com_proclaim/cwmcore-admin.min.js",
491+
"attributes": {
492+
"defer": true
493+
},
494+
"dependencies": [
495+
"core"
496+
]
497+
},
487498
{
488499
"name": "com_proclaim.fancybox-vendor-css",
489500
"type": "style",

0 commit comments

Comments
 (0)