MediaWiki:Gadget-CargoHelperCore.js
From Angelina Jordan Wiki
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Gadget-CargoHelperCore.js
Heavy logic: dialog creation, Cargo queries, validation,
insertion, form logic.
Only loaded on demand by Gadget-CargoHelperLoader.js.
*/
(function () {
'use strict';
// Top-level CargoHelper object
window.CargoHelper = window.CargoHelper || {};
// ---------------------- Top-level helpers ----------------------
// Insert text at cursor in textarea
function insertAtCursor(textarea, newText) {
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var original = textarea.value;
textarea.value = original.slice(0, start) + newText + original.slice(end);
textarea.selectionStart = textarea.selectionEnd = start + newText.length;
textarea.scrollTop = textarea.scrollHeight;
}
// Sanitize a string for Lua-friendly ID
function luaCompatibleSanitize(title) {
title = String(title || '');
title = title.replace(/&[#\w]+;/g, '_');
title = title.replace(/[^\w_.-]/g, '_'); // keep alnum, underscore, hyphen, dot
return title;
}
// Compute next local numeric performance ID
function computeNextLocalId(perfs, page) {
var base = luaCompatibleSanitize(page) + '_';
var max = 0;
perfs.forEach(function (p) {
var id = p.perfID || '';
if (id.indexOf(base) !== 0) return;
var tail = id.slice(base.length);
var m = tail.match(/^(\d+)/);
if (!m) return;
var n = parseInt(m[1], 10);
if (!isNaN(n) && n > max) max = n;
});
return String(max + 1);
}
// Validate ISO-leading date
function isValidIsoLeadingDate(s) {
if (!s) return false;
var m = s.match(/^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?(?:\s.*)?$/);
if (!m) return false;
if (m[2]) { var mm = parseInt(m[2], 10); if (mm < 1 || mm > 12) return false; }
if (m[3]) { var dd = parseInt(m[3], 10); if (dd < 1 || dd > 31) return false; }
return true;
}
// Validate URL
function isValidUrl(s) {
if (!s) return false;
return /^(https?:\/\/|\/\/).+/i.test(s.trim());
}
// ---------------------- Main initialization ----------------------
window.CargoHelper.ready = new Promise(function(resolve, reject) {
mw.loader.using([
'oojs-ui-core',
'oojs-ui-windows',
'oojs-ui-widgets',
'mediawiki.util',
'mediawiki.api'
]).then(function () {
// --- shared helpers ---
function isValidIsoLeadingDate(s) {
if (!s) return false;
var m = s.match(/^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?(?:\s.*)?$/);
if (!m) return false;
if (m[2]) {
var mm = parseInt(m[2], 10);
if (mm < 1 || mm > 12) return false;
}
if (m[3]) {
var dd = parseInt(m[3], 10);
if (dd < 1 || dd > 31) return false;
}
return true;
}
function isValidUrl(s) {
if (!s) return false;
return /^(https?:\/\/|\/\/).+/i.test(s.trim());
}
function clearInvalid(widget) {
if (!widget) return;
if (widget.$input) widget.$input.css('outline', '').css('box-shadow', '');
else if (widget.$element) widget.$element.css('outline', '').css('box-shadow', '');
}
function markInvalid(widget) {
if (!widget) return;
if (widget.$input) {
widget.$input.css('outline', '2px solid #d33').css('box-shadow', '0 0 0 2px rgba(211,51,51,0.08)');
widget.$input.one('input', function () { widget.$input.css('outline', '').css('box-shadow', ''); });
} else if (widget.$element) {
widget.$element.css('outline', '2px solid #d33').css('box-shadow', '0 0 0 2px rgba(211,51,51,0.08)');
widget.$element.one('input', function () { widget.$element.css('outline', '').css('box-shadow', ''); });
}
}
function ValidationDialog(config) { ValidationDialog.super.call(this, config); }
function loadSections() {
var cfg = mw.config.get('wgPageSections');
if (Array.isArray(cfg) && cfg.length) return Promise.resolve(cfg);
var api = new mw.Api();
return api.get({ action: 'parse', page: mw.config.get('wgPageName'), prop: 'sections' })
.then(function (data) {
var out = [];
if (data && data.parse && Array.isArray(data.parse.sections)) {
data.parse.sections.forEach(function (s) {
out.push(s);
});
}
return out;
}).catch(function () { return []; });
}
//---------------------------------------------------------
// 1) Submit handler
//---------------------------------------------------------
window.CargoHelper.handleSubmit = function (
modeSelect,
existingPerfDropdown,
existingPerfs,
eventInput,
dateInput,
contextCombo,
typeInput,
posInput,
withInput,
commentPInput,
urlInput,
durationInput,
qualityInput,
commentVInput,
dlg,
wm
)
{
try {
var DEFAULT_NEW_SECTION_TITLE = 'Latest added video/performance (unsorted)';
[ eventInput, dateInput, contextCombo, urlInput, posInput, withInput, commentPInput, commentVInput ].forEach(clearInvalid);
// gather values
var page = mw.config.get('wgTitle');
var mode = modeSelect.getValue();
var pidLocal;
if (mode === 'existing') {
if (!existingPerfDropdown) { alert('No existing performance to choose.'); return; }
var fullPerfID = existingPerfDropdown.getValue();
if (!fullPerfID) { alert('Please select an existing performance.'); return; }
var lastUnd = fullPerfID.lastIndexOf('_');
pidLocal = (lastUnd >= 0) ? fullPerfID.slice(lastUnd + 1) : fullPerfID;
} else {
pidLocal = computeNextLocalId(existingPerfs || [], page);
}
// Validation
var errors = [];
var urlVal = urlInput.getValue() || '';
if (!isValidUrl(urlVal)) { errors.push('Video URL is required and must start with "http://", "https://" or "//".'); markInvalid(urlInput); }
var perfDate = dateInput.getValue() || '';
if (mode === 'new') {
if (!perfDate) { errors.push('Date is required when creating a new performance.'); markInvalid(dateInput); }
else if (!isValidIsoLeadingDate(perfDate)) { errors.push('Date must start with an ISO date (YYYY, YYYY-MM or YYYY-MM-DD).'); markInvalid(dateInput); }
}
var ctxVal = contextCombo.getValue() || '';
if (mode === 'new' && !ctxVal) { errors.push('Context is required when creating a new performance.'); markInvalid(contextCombo); }
if (errors.length) {
// show dialog with errors
OO.inheritClass(ValidationDialog, OO.ui.MessageDialog);
ValidationDialog.static.name = 'ValidationDialog';
ValidationDialog.static.title = 'Validation error';
ValidationDialog.static.actions = [ { action: 'close', label: 'Close', flags: ['primary'] } ];
var wmVal = new OO.ui.WindowManager();
$(document.body).append(wmVal.$element);
var dlgVal = new ValidationDialog();
wmVal.addWindows([dlgVal]);
wmVal.openWindow(dlgVal, { message: errors.join('\n') });
// focus first invalid
var toFocus = null;
if (!isValidUrl(urlVal)) toFocus = urlInput;
else if (mode === 'new' && (!perfDate || !isValidIsoLeadingDate(perfDate))) toFocus = dateInput;
else if (mode === 'new' && !ctxVal) toFocus = contextCombo;
if (toFocus) { setTimeout(function () { if (toFocus.focus) toFocus.focus(); else if (toFocus.$input) toFocus.$input.focus(); }, 50); }
return;
}
// Build perf & video blocks single-line
var perfBlock = '';
if (mode === 'new') {
var perfFields = [];
if (page) perfFields.push('song=' + page);
if (eventInput.getValue()) perfFields.push('event=' + eventInput.getValue());
if (contextCombo.getValue()) perfFields.push('context=' + contextCombo.getValue());
if (dateInput.getValue()) perfFields.push('date=' + dateInput.getValue());
if (typeInput.getValue()) perfFields.push('type=' + typeInput.getValue());
if (posInput.getValue()) perfFields.push('pos=' + posInput.getValue());
if (withInput.getValue()) perfFields.push('with=' + withInput.getValue());
if (commentPInput.getValue()) perfFields.push('comment=' + commentPInput.getValue());
if (pidLocal) perfFields.push('id=' + pidLocal);
perfBlock = '{{' + 'Performance|' + perfFields.join('|') + '}}';
}
var videoFields = [];
if (pidLocal) videoFields.push('pid=' + pidLocal);
if (urlInput.getValue()) videoFields.push('url=' + urlInput.getValue());
if (durationInput.getValue()) videoFields.push('duration=' + durationInput.getValue());
if (qualityInput.getValue()) videoFields.push('quality=' + qualityInput.getValue());
if (commentVInput.getValue()) videoFields.push('comment=' + commentVInput.getValue());
var videoBlock = '{{' + 'Video|' + videoFields.join('|') + '}}';
var combinedForInsert = (mode === 'new' ? '* ' + perfBlock + '\n*: ' : '') + videoBlock;
// If edit textarea present -> insert at cursor
var $textarea = $('#wpTextbox1');
if ($textarea.length) {
var ta = $textarea[0];
var start = ta.selectionStart;
var textBefore = ta.value.slice(0, start);
var atLineStart = textBefore.endsWith('\n') || textBefore === '';
var insertText;
if (atLineStart) insertText = (mode === 'new' ? '* ' + perfBlock + '\n*: ' : '') + videoBlock;
else insertText = (mode === 'new' ? perfBlock : '') + videoBlock;
insertAtCursor(ta, insertText);
wm.closeWindow(dlg);
return;
}
// Otherwise on view page -> ask for section, then API insert
loadSections().then(function (sections) {
// ------------------------- Section Selection Dialog -------------------------
function openSectionSelectDialog(combinedForInsert,avdlg) {
var DEFAULT_NEW_SECTION_TITLE = 'Latest added video/performance (unsorted)';
var page = mw.config.get('wgPageName');
// Fetch sections
function loadSections() {
var cfg = mw.config.get('wgPageSections');
if (Array.isArray(cfg) && cfg.length) return Promise.resolve(cfg);
var api = new mw.Api();
return api.get({
action: 'parse',
page: page,
prop: 'sections'
}).then(function(data) {
if (data && data.parse && Array.isArray(data.parse.sections)) {
return data.parse.sections;
}
return [];
}).catch(() => []);
}
loadSections().then(function(sections) {
// -------------------
// UI widgets
// -------------------
var selectWidget = new OO.ui.DropdownInputWidget({
options: sections.map(function(s) {
var n = String(s.index || s.number || s.section || '');
var label = (s.line || s.heading || '').toString() || ('Section ' + n);
return { data: n, label: n + ' — ' + label };
}).concat([
{ data: 'new', label: 'End of article (create new section)' }
]),
value: 'new'
});
var titleInput = new OO.ui.TextInputWidget({
value: DEFAULT_NEW_SECTION_TITLE
});
var fieldset = new OO.ui.FieldsetLayout({ label: 'Insert at end of' });
fieldset.addItems([
new OO.ui.FieldLayout(selectWidget, {
label: 'Target section'
}),
new OO.ui.FieldLayout(titleInput, {
label: 'New section title (only used for new section)',
align: 'top'
})
]);
// -----------------------
// Button row (identical structure as AddVideoDialog)
// -----------------------
var cancelButton = new OO.ui.ButtonWidget({
label: 'Cancel',
flags: ['destructive']
});
var insertButton = new OO.ui.ButtonWidget({
label: 'Insert',
flags: ['primary']
});
var buttonRow = $('<div>').css({
marginTop: '1em',
marginBottom: '1em',
textAlign: 'right'
})
.append(cancelButton.$element)
.append(' ')
.append(insertButton.$element);
fieldset.$element.append(buttonRow);
// -----------------------
// Panel (this gives the correct left/right padding)
// -----------------------
var panel = new OO.ui.PanelLayout({
expanded: true,
padded: true,
content: [ fieldset ]
});
// -----------------------
// Dialog definition
// -----------------------
function SectionDialog(config) {
SectionDialog.super.call(this, config);
}
OO.inheritClass(SectionDialog, OO.ui.ProcessDialog);
SectionDialog.static.name = 'SectionDialog';
SectionDialog.static.title = 'Choose insertion location';
SectionDialog.static.size = 'medium';
// No actions -> we use manual footer identical to AddVideoDialog
SectionDialog.static.actions = [];
SectionDialog.prototype.initialize = function () {
SectionDialog.super.prototype.initialize.call(this);
this.$body.append(panel.$element);
// Same CSS override as AddVideoDialog
this.$element.css({
width: '900px',
maxWidth: '95%',
left: '50%',
transform: 'translateX(-50%)'
});
};
var wm = new OO.ui.WindowManager();
$(document.body).append(wm.$element);
var dlg = new SectionDialog();
wm.addWindows([dlg]);
wm.openWindow(dlg);
// -----------------------
// Button actions
// -----------------------
cancelButton.on('click', function () {
wm.closeWindow(dlg);
});
insertButton.on('click', function () {
var chosen = selectWidget.getValue();
var title = (titleInput.getValue() || '').trim() || DEFAULT_NEW_SECTION_TITLE;
var api = new mw.Api();
var appendText = combinedForInsert;
api.postWithToken('csrf', {
action: 'edit',
title: page,
section: chosen === 'new' ? 'new' : chosen,
appendtext: '\n' + appendText + '\n',
summary: chosen === 'new' ? title : 'Add video/performance entry via CargoHelper'
}).then(function () {
// Close Section dialog
wm.closeWindow(dlg);
// Close AddVideo dialog (passed as avdlg)
if (avdlg && avdlg.manager) {
avdlg.manager.closeWindow(avdlg);
}
}).catch(function () {
// Still close Section dialog on failure
wm.closeWindow(dlg);
// Still close AddVideo dialog
if (avdlg && avdlg.manager) {
avdlg.manager.closeWindow(avdlg);
}
});
});
});
}
openSectionSelectDialog(combinedForInsert,dlg);
}).catch(function (err) {
console.error('Failed to load sections:', err);
alert('Failed to load sections (see console).');
});
} catch (err) {
console.error('Submit handler failed:', err);
alert('Error while preparing insertion (see console).');
}
};
//---------------------------------------------------------
// 2) Open dialog function
//---------------------------------------------------------
window.CargoHelper.openDialog = function () {
//---------------------------------------------------------
// Helpers: fetch Cargo data
//---------------------------------------------------------
function fetchPerformancesForPage() {
var page = mw.config.get('wgPageName') || '';
page = page.replace(/_/g, ' ');
page = $('<textarea/>').html(page).text();
var api = new mw.Api();
return api.get({
action: 'cargoquery',
tables: 'Performances',
fields: 'perfID,event,date,contextstr',
where: 'song="' + page.replace(/"/g, '\\"') + '"',
order_by: 'perfID',
limit: 5000
}).then(function (data) {
if (!data.cargoquery) return [];
return data.cargoquery.map(r => r.title || {}).map(t => ({
perfID: t.perfID || '',
event: t.event || '',
date: t.date || '',
contextstr: t.contextstr || ''
}));
}).catch(function (err) { console.error('fetchPerformancesForPage failed:', err); return []; });
}
function fetchAllContexts() {
var api = new mw.Api();
return api.get({
action: 'cargoquery',
tables: 'Performances',
fields: 'contextstr',
group_by: 'contextstr',
order_by: 'contextstr',
limit: 5000
}).then(function (data) {
if (!data.cargoquery) return [];
return data.cargoquery.map(r => (r.title && r.title.contextstr) ? r.title.contextstr : '').filter(Boolean);
}).catch(function (err) { console.error('fetchAllContexts failed:', err); return []; });
}
//---------------------------------------------------------
// 3) Open dialog after fetching data
//---------------------------------------------------------
Promise.all([fetchPerformancesForPage(), fetchAllContexts()])
.then(function (res) {
var existingPerfsArray = res[0] || [];
var allContexts = res[1] || [];
openDialog(existingPerfsArray, allContexts);
}).catch(function (err) {
console.error('CargoHelperCore: failed to load data: ', err);
alert('Failed to load data (see console).');
});
//---------------------------------------------------------
// 4) Actual OOUI dialog UI
//---------------------------------------------------------
function openDialog(existingPerfs, allContexts) {
/************************************************************
* WIDGET DEFINITIONS
************************************************************/
var modeSelect = new OO.ui.DropdownInputWidget({
options: [
{ data: 'existing', label: 'Use existing performance' },
{ data: 'new', label: 'Create new performance' }
],
value: existingPerfs.length ? 'existing' : 'new'
});
var existingPerfDropdown = null;
if (existingPerfs.length) {
existingPerfDropdown = new OO.ui.DropdownInputWidget({
options: existingPerfs.map(function (p) {
var localID = p.perfID.replace(/^.*_/, '');
return { data: p.perfID, label: localID + ' — ' + (p.event || '') + ' (' + (p.date || '') + ')' };
}),
placeholder: 'Choose existing performance'
});
}
// PERFORMANCE FIELDS
var eventInput = new OO.ui.TextInputWidget();
var dateInput = new OO.ui.TextInputWidget();
// CONTEXT: ComboBox with dynamic substring filter
var contextCombo = new OO.ui.ComboBoxInputWidget({
options: allContexts.map(function (c) { return { data: c, label: c }; }),
placeholder: 'Type or pick a full context string'
});
// Keep original as the master list
var originalContextOptions = allContexts.map(function (c) {
return { data: c, label: c };
});
function restoreAllContextOptions() {
// If ComboBox supports clearOptions/addOption (some OOUI versions), use them
if (typeof contextCombo.clearOptions === 'function' && typeof contextCombo.addOption === 'function') {
contextCombo.clearOptions();
originalContextOptions.forEach(function (opt) {
contextCombo.addOption({ data: opt.data, label: opt.label });
});
} else {
// Fallback: rebuild the internal menu only (works across OOUI versions)
var menu = contextCombo.getMenu();
if (menu && typeof menu.clearItems === 'function' && typeof menu.addItems === 'function') {
menu.clearItems();
originalContextOptions.forEach(function (opt) {
menu.addItems([
new OO.ui.MenuOptionWidget({
data: opt.data,
label: opt.label
})
]);
});
}
}
// Ensure the visible input text still matches the underlying value (if any)
var cur = contextCombo.getValue();
if (cur) {
// sometimes ComboBox needs the raw input set as well
if (contextCombo.$input && contextCombo.$input.length) {
contextCombo.$input.val(cur);
}
}
}
// TYPE
var typeInput = new OO.ui.DropdownInputWidget({
options: [
{ data: '', label: '—' },
{ data: 'recording', label: 'recording' },
{ data: 'track', label: 'track' },
{ data: 'music-video', label: 'music-video' },
{ data: 'live', label: 'live' },
{ data: 'rehearsal', label: 'rehearsal' },
{ data: 'soundcheck', label: 'soundcheck' },
{ data: 'sing-along', label: 'sing-along' },
{ data: 'lip-sync', label: 'lip-sync' }
]
});
var posInput = new OO.ui.TextInputWidget();
var withInput = new OO.ui.TextInputWidget({ multiline: true });
var commentPInput = new OO.ui.TextInputWidget({ multiline: true });
// VIDEO
var urlInput = new OO.ui.TextInputWidget();
var durationInput = new OO.ui.DropdownInputWidget({
options: [
{ data: '', label: '—' },
{ data: 'full', label: 'full' },
{ data: 'short', label: 'short' },
{ data: 'fragment', label: 'fragment' }
]
});
var qualityInput = new OO.ui.DropdownInputWidget({
options: [
{ data: '', label: '—' },
{ data: 'best', label: 'best' },
{ data: 'good', label: 'good' },
{ data: 'acceptable', label: 'acceptable' },
{ data: 'poor', label: 'poor' }
]
});
var commentVInput = new OO.ui.TextInputWidget({ multiline: true });
/************************************************************
* CONTEXT FILTERING (fix)
************************************************************/
// Use the real input event and rebuild the internal OOUI menu reliably
contextCombo.$input.on('input', function () {
var value = (contextCombo.getValue() || '').toLowerCase();
var menu = contextCombo.getMenu();
// Rebuild menu items from the master list
menu.clearItems();
originalContextOptions.forEach(function (opt) {
if (opt.data.toLowerCase().indexOf(value) !== -1) {
menu.addItems([
new OO.ui.MenuOptionWidget({
data: opt.data,
label: opt.label
})
]);
}
});
// Ensure the dropdown is visible so the user sees the filtered items
if (!menu.isVisible()) {
menu.toggle(true);
}
});
/************************************************************
* FORM LAYOUT
************************************************************/
var form = new OO.ui.FieldsetLayout({ label: 'Add Video / Performance' });
form.addItems([ new OO.ui.FieldLayout(modeSelect, { label: 'Mode' }) ]);
if (existingPerfDropdown) {
form.addItems([
new OO.ui.FieldLayout(existingPerfDropdown, { label: 'Existing performance', align: 'top' })
]);
}
form.addItems([
new OO.ui.FieldLayout(eventInput, { label: 'Event' }),
new OO.ui.FieldLayout(dateInput, { label: 'Date' }),
new OO.ui.FieldLayout(contextCombo, { label: 'Context (complete string, # separated)', align: 'top' }),
new OO.ui.FieldLayout(typeInput, { label: 'Type' }),
new OO.ui.FieldLayout(posInput, { label: 'Position' }),
new OO.ui.FieldLayout(withInput, { label: 'With (partners)' }),
new OO.ui.FieldLayout(commentPInput,{ label: 'Performance comment' }),
// Separator
new OO.ui.LabelWidget({ label: $('<hr>') }),
new OO.ui.FieldLayout(urlInput, { label: 'Video URL' }),
new OO.ui.FieldLayout(durationInput,{ label: 'Duration' }),
new OO.ui.FieldLayout(qualityInput, { label: 'Quality' }),
new OO.ui.FieldLayout(commentVInput,{ label: 'Video comment' })
]);
var submitButton = new OO.ui.ButtonWidget({ label: 'Insert', flags: ['primary'] });
var cancelButton = new OO.ui.ButtonWidget({ label: 'Cancel', flags: ['destructive'] });
form.$element.append(
$('<div>').css({ marginTop: '1em', marginBottom: '1em', textAlign: 'right' })
.append(cancelButton.$element)
.append(' ')
.append(submitButton.$element)
);
var panel = new OO.ui.PanelLayout({ expanded: true, padded: true, content: [ form ] });
/************************************************************
* DIALOG CONSTRUCTION
************************************************************/
function AddVideoDialog(config) {
AddVideoDialog.super.call(this, config);
}
OO.inheritClass(AddVideoDialog, OO.ui.ProcessDialog);
AddVideoDialog.static.name = 'AddVideoDialog';
AddVideoDialog.static.title = 'Add Video / Performance';
AddVideoDialog.static.size = 'medium';
AddVideoDialog.prototype.initialize = function () {
AddVideoDialog.super.prototype.initialize.call(this);
this.$body.append(panel.$element);
this.$element.css({
width: '900px',
maxWidth: '95%',
left: '50%',
transform: 'translateX(-50%)'
});
};
var wm = new OO.ui.WindowManager();
$(document.body).append(wm.$element);
var dlg = new AddVideoDialog();
wm.addWindows([dlg]);
wm.openWindow(dlg);
cancelButton.on('click', function () {
wm.closeWindow(dlg);
});
/************************************************************
* MODE SWITCHING (fixed with context restore/readOnly)
************************************************************/
function updateFields() {
var isExisting = (modeSelect.getValue() === 'existing');
// PERFORMANCE FIELDS read-only
eventInput.setReadOnly(isExisting);
dateInput.setReadOnly(isExisting);
posInput.setReadOnly(isExisting);
withInput.setReadOnly(isExisting);
commentPInput.setReadOnly(isExisting);
typeInput.setDisabled(isExisting); // dropdown cannot be readonly
// CONTEXT special handling
if (isExisting) {
contextCombo.setReadOnly(true);
if (existingPerfDropdown) {
var id = existingPerfDropdown.getValue();
var perf = existingPerfs.find(p => p.perfID === id) || {};
eventInput.setValue(perf.event || '');
dateInput.setValue(perf.date || '');
restoreAllContextOptions();
contextCombo.setValue(perf.contextstr || '');
typeInput.setValue(perf.type || '');
posInput.setValue(perf.pos || '');
withInput.setValue(perf.with || '');
commentPInput.setValue(perf.comment || '');
}
} else {
// NEW PERFORMANCE MODE
// Make fields editable again
eventInput.setReadOnly(false);
dateInput.setReadOnly(false);
posInput.setReadOnly(false);
withInput.setReadOnly(false);
commentPInput.setReadOnly(false);
typeInput.setDisabled(false);
// Restore context dropdown and allow typing/filtering
contextCombo.setReadOnly(false);
restoreAllContextOptions();
// CLEAR ALL PERFORMANCE-RELATED FIELDS (new behavior)
eventInput.setValue('');
dateInput.setValue('');
contextCombo.setValue('');
contextCombo.$input.val(''); // must clear raw input too
typeInput.setValue('');
posInput.setValue('');
withInput.setValue('');
commentPInput.setValue('');
}
// Disable/enable existing perf dropdown
if (existingPerfDropdown) {
existingPerfDropdown.setDisabled(!isExisting);
}
}
modeSelect.on('change', updateFields);
if (existingPerfDropdown) {
existingPerfDropdown.on('change', updateFields);
}
updateFields();
/************************************************************
* SUBMIT HANDLER (unchanged)
************************************************************/
submitButton.on('click', function () {
try {
window.CargoHelper.handleSubmit(
modeSelect,
existingPerfDropdown,
existingPerfs,
eventInput,
dateInput,
contextCombo,
typeInput,
posInput,
withInput,
commentPInput,
urlInput,
durationInput,
qualityInput,
commentVInput,
dlg,
wm
);
} catch (err) {
console.error('Submit handler failed:', err);
alert('Error while preparing insertion (see console).');
}
});
} // end openDialog
};
// Initialization done
resolve(window.CargoHelper);
}).catch(function(err) { reject(err); });
}); // mw.loader.using
}());