MediaWiki:Gadget-BetterUpload.js
MediaWiki interface page
More actions
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.
/* jshint
undef: true,
devel: true,
typed: true,
jquery: true,
strict: true,
eqeqeq: true,
freeze: true,
latedef: true,
shadow: outer,
varstmt: true,
quotmark: single,
esversion: 6,
futurehostile: true
*//* global
importArticles
*/
mw.hook('dev.CCM.load').add((cmLoader) => {
'use strict';
(window.dev = window.dev || {}).BetterUpload = window.dev.BetterUpload || {
'default': '==Licensing==\n{{Fairuse}}'
};
// Double load protection and check we're in Special:Upload
if (window.dev.BetterUpload._LOADED) { return; }
else { window.dev.BetterUpload._LOADED = true; }
// Load dependencies and cache
let api = new mw.Api(),
config = mw.config.values,
urlParams = new URLSearchParams(window.location.search),
cm,
// Main class
BU = {
init: (curr) => {
let form = $('#mw-upload-form');
// Override form submit
form.on('submit', (event)=>{
event.preventDefault();
let rd = $('#wpRedirectFile'),
fn = $('#wpDestFile');
if ( rd.length>0 && rd.val().length>0 && fn.val().length>0 ) {
let openRD = ()=>{window.open( mw.config.get('wgServer')+mw.util.getUrl('File:'+fn.val())+'?redirect=no', '_self');},
rdContent = (window.dev.BetterUpload.redirectFormat||'#redirect [[File:%TARGET%]]').replace(/%TARGET%/, rd.val());
api.create('File:'+fn.val(), {recreate: true}, rdContent).then(openRD);
api.edit('File:'+fn.val(), ()=>({ text: rdContent, comment: 'Create redirect' })).then(openRD);
} else {
BU.saveEdit();
BU.attemptUpload();
}
});
if (document.querySelector('input#wpUploadDescription') && document.querySelector('label[for="wpUploadDescription"]')) {
// Change reupload format
document.querySelector('label[for="wpUploadDescription"]').innerHTML = 'Upload summary:';
document.querySelector('label[for="wpUploadDescription"]').setAttribute('for', 'wpUploadSummary');
document.querySelector('input#wpUploadDescription').setAttribute('name', 'wpUploadSummary');
document.querySelector('input#wpUploadDescription').setAttribute('id', 'wpUploadSummary');
} else {
// Add upload comment field for new uploads
let u_comm = $('<tr>', {
'class': 'mw-htmlform-field-HTMLTextField',
html: [
$('<td>', {
'class': 'mw-label',
html: $('<label>', { 'for': 'wpUploadSummary', text: 'Upload summary:' })
}),
$('<td>', {
'class': 'mw-input',
html: $('<input>', { 'id': 'wpUploadSummary', name: 'wpUploadSummary', size: '60' })
})
]
});
$('.mw-htmlform-field-HTMLTextField:has(#wpDestFile)').after(u_comm);
}
let def_Val = (curr!==null && curr!==undefined) ? curr : (BU.detectFromDest() || window.dev.BetterUpload.default || '');
if (!document.querySelector('.mw-htmlform-field-HTMLTextAreaField')) {
// Add page content field for reuploads
let p_cont = $('<tr>', {
'class': 'mw-htmlform-field-HTMLTextAreaField',
html: [
$('<td>', {
'class': 'mw-label',
html: $('<label>', { 'for': 'wpUploadDescription', text: 'Page content:' })
}),
$('<td>', {
'class': 'mw-input',
html: $('<textarea>', {
'id': 'wpUploadDescription',
name: 'wpUploadDescription',
cols: '80',
rows: '8',
style: 'font-family: Consolas, Eupheima UCAS, Ayuthaya, Menlo, monospace;',
val: def_Val
})
})
]
});
$('.mw-htmlform-field-HTMLTextField:has(#wpUploadSummary)').after(p_cont);
} else {
// Change from summary to page content in new uploads
document.querySelector('.mw-htmlform-field-HTMLTextAreaField label[for="wpUploadDescription"]').innerHTML = 'Page content:';
document.querySelector('.mw-htmlform-field-HTMLTextAreaField textarea#wpUploadDescription').addEventListener('change', BU.renderPreview);
document.querySelector('textarea#wpUploadDescription').style['font-family'] = 'Consolas, Eupheima UCAS, Ayuthaya, Menlo, monospace';
document.querySelector('textarea#wpUploadDescription').value = def_Val;
}
// Add quick redirecting option
let rdField = $('<tr>', {
'class': 'mw-htmlform-field-RedirectSourceField',
html: [
$('<td>', {
'class': 'mw-label',
html: $('<label>', { 'for': 'wpRedirectFile', text: 'Redirect to:' })
}),
$('<td>', {
'class': 'mw-input',
html: $('<input>', { 'id': 'wpRedirectFile', name: 'wpRedirectFile', size: '60' })
})
]
});
$('#mw-htmlform-source').append(rdField);
// Add preview container
let pvField = $('<fieldset>', {
html: [
$('<legend>', { text: 'Page Preview' }),
$('<div>', { 'id': 'wpPagePreview' })
]
});
$('fieldset:has(>#mw-htmlform-description)').after(pvField);
// Remove useless things #wpLicense
$(`
fieldset:has(>#mw-htmlform-options),
tr.mw-htmlform-field-Licenses,
.mw-upload-editlicenses
`).remove();
cmLoader($('#wpUploadDescription'));
mw.hook('dev.CCM.ready').add((ccm)=>{
cm = ccm;
BU.renderPreview();
BU.genPreloads();
// try to preview every couple seconds
let last;
setInterval(() => {
let newtext = cm.view.state.sliceDoc();
if (newtext !== last) {BU.renderPreview();}
last = newtext;
}, 1000);
});
// Update "default values" so that the base upload check for leaving the page doesnt have a stroke when nothing actually changed
form.data('origtext', form.serialize());
},
pushCM: (txt, pos)=>{
cm.view.dispatch({
changes: {
from: pos===null ? 0 : cm.view.state.selection.main.from,
to: pos===null ? cm.view.state.doc.length : cm.view.state.selection.main.to,
insert: txt
},
selection: {anchor: cm.view.state.selection.main.from + (pos||0)}
});
cm.view.focus();
},
// if this file is being uploaded to a specific destination specified in the URL,
// iterate through preloads with a specified "pattern" field to attempt to detect one
detectFromDest: () => {
if (!Array.isArray(window.dev.BetterUpload.preloads) || !urlParams.has('wpDestFile')) return;
let filename = urlParams.get('wpDestFile').replace(/_/g, ' ');
let matched = window.dev.BetterUpload.preloads.find((preload) => {
if (preload.pattern
&& 'string' === typeof preload.pattern
&& new RegExp(preload.pattern).test(filename)
) { return true; }
else { return false; }
});
if (matched && matched.preload) {
return matched.preload;
}
},
genPreloads: () => {
if (Array.isArray(window.dev.BetterUpload.preloads) && window.dev.BetterUpload.preloads.length>0) {
$('.mw-htmlform-field-Preloads').remove(); // remove any to avoid double-loading
let pl_row = $('<tr>', {
'class': 'mw-htmlform-field-Preloads',
html: [
$('<td>', {
'class': 'mw-label',
html: $('<label>', { 'for': 'wpPreload', text: 'Preloads:' })
}),
$('<td>', {
'class': 'mw-input',
html: $('<select>', {
'id': 'wpPreload',
name: 'wpPreload',
html: $('<option>', { value: 'None', title: 'None', selected: '', text: 'None selected' })
})
})
]
});
$('.mw-htmlform-field-HTMLTextAreaField').after(pl_row);
let pl_list = pl_row.find('select').get(0);
let pl_opts = pl_list;
window.dev.BetterUpload.preloads.forEach((setting, num) => {
if (setting._group==='0') {
pl_opts = pl_list;
} else if (setting._group) {
pl_opts = document.createElement('optgroup');
pl_opts.setAttribute('label', setting._group);
pl_list.append(pl_opts);
} else if (setting.name && (setting.preload || setting.header)) {
let option = document.createElement('option');
if (setting.header) {
option.setAttribute('disabled', 'disabled');
option.style.color = 'GrayText';
} else if (setting.preload) {
option.setAttribute('value', setting.name);
option.setAttribute('title', setting.name);
}
option.innerHTML = setting.description || setting.name;
option.setAttribute('numref', num);
pl_opts.append(option);
}
});
pl_list.addEventListener('change', (event) => {
let num = pl_list.selectedOptions[0].getAttribute('numref');
let settings = window.dev.BetterUpload.preloads[num];
if (settings && settings.preload) {
$('.mw-htmlform-field-PreloadValues').remove();
if (settings.fillin && /\$\(1\)\$/.test(settings.preload)) {
let plv_row = $('<tr>', {
'class': 'mw-htmlform-field-PreloadValues',
html: [
$('<td>', {
'class': 'mw-label',
html: $('<label>', { 'for': 'wpPreloadValues', text: 'Values:', style: 'vertical-align: top;' })
}),
$('<td>', {
'class': 'mw-input',
html: $('<select>', {
'id': 'wpPreloadValues',
name: 'wpPreloadValues',
html: $('<option>', { value: 'None', title: 'None', selected: '', text: 'None selected' })
})
})
]
});
pl_row.after(plv_row);
let plv_list = plv_row.find('select').get(0);
let plv_opts = plv_list;
settings.fillin.forEach((value, index) => {
if (value._group==='0') {
plv_opts = plv_list;
} else if (value._group) {
plv_opts = document.createElement('optgroup');
plv_opts.setAttribute('label', value._group);
plv_list.append(plv_opts);
} else {
let option = document.createElement('option');
let name = value.name || value.values.join(', ');
if (value.header) {
option.setAttribute('disabled', 'disabled');
option.style.color = 'GrayText';
} else if (value.preload) {
option.setAttribute('value', name);
option.setAttribute('title', name);
}
option.innerHTML = name;
option.setAttribute('numref', index);
plv_opts.append(option);
}
});
plv_list.addEventListener('change', (event2) => {
let preloadNum = pl_list.selectedOptions[0].getAttribute('numref');
let preloadSettings = window.dev.BetterUpload.preloads[preloadNum];
let valnum = plv_list.selectedOptions[0].getAttribute('numref');
let valsettings = preloadSettings.fillin[valnum];
let newpreload = preloadSettings.preload;
valsettings.values.forEach((rep, ind) => {
let regex = new RegExp(/\$\(/.source+(ind+1)+/\)\$/.source, 'g');
if (regex.test(newpreload)) {
newpreload = newpreload.replace(regex, rep);
}
});
$('#wpPreloadValuePreview').remove();
if (valsettings.reference) {
let refPreview = $('<div>', { 'id': 'wpPreloadValueRef', name: 'wpPreloadValueRef' });
api.get({
action: 'parse',
text: valsettings.reference,
prop: 'text',
disablelimitreport: true,
contentmodel: 'wikitext',
pst: true
}).then((data) => {
if (data && data.parse && data.parse.text && data.parse.text['*']) {
refPreview.html(data.parse.text['*']);
$(plv_list).after(refPreview);
}
});
}
BU.pushCM(newpreload, null);
BU.renderPreview();
});
}
BU.pushCM(settings.preload, null);
BU.renderPreview();
} else { alert('Invalid option.'); }
});
}
},
renderPreview: () => {
let filename = document.querySelector('#wpDestFile').value;
let text = cm.view.state.sliceDoc();
let params = {
action: 'parse',
text: text+'__noeditsection__',
prop: 'text',
disablelimitreport: true,
contentmodel: 'wikitext',
pst: true
};
if (filename.length>0) {
params.title = 'File:' + filename;
}
api.get(params).then((data) => {
if (data && data.parse && data.parse.text && data.parse.text['*']) {
$('#wpPagePreview').html(data.parse.text['*']);
}
}).fail(console.log);
},
saveEdit: () => {
if (document.querySelector('input#wpUploadSummary')) {
let filename = document.querySelector('#wpDestFile').value;
let summary = document.querySelector('input#wpUploadSummary');
let params = {
action: 'edit',
title: 'File:'+filename,
ignorewarnings: '1',
format: 'json',
text: cm.view.state.sliceDoc(),
recreate: 1,
token: mw.user.tokens.get('csrfToken')
};
if (summary && summary.value.length>0) {
params.summary = summary.value;
}
if (filename && filename.length>0) {
api.post(params);
} else { alert('Missing file or file name. Could not save page content.'); }
} else {return;}
},
attemptUpload: () => {
let filename = document.querySelector('#wpDestFile').value;
let file = document.querySelector('#wpUploadFile').files[0];
let comment = document.querySelector('input#wpUploadSummary');
let params = {
token: mw.user.tokens.get('csrfToken'),
filename: filename,
ignorewarnings: '1',
format: 'json',
text: cm.view.state.sliceDoc()
};
if (comment && comment.value.length>0) {
params.comment = comment.value;
}
if (file && filename && filename.length>0) {
let handleResponse = (a, b) => {
let data = (typeof b === 'object' && !Array.isArray(b) && b !== null && (b.error || b.upload)) ? b : a;
if (!data) {
console.log('a', a);
console.log('b', b);
}
if (data.error) {
console.log('data', data);
let cont = document.querySelector('.mw-message-box-error');
if (!cont) {
let _temp = $('<h2 class="mw-message-box-error-header">Upload Warning</h2><div class="mw-message-box-error mw-message-box"></div>');
$('#uploadtext').after(_temp);
cont = _temp[1];
}
cont.innerHTML = (data.error.info && data.error.info.length>0) ? data.error.info : 'Unknown error during upload.';
} else if (data.upload && data.upload.result === 'Success') {
let form = $('#mw-upload-form');
form.data('origtext', form.serialize()); // So the form isnt annoying when leaving
window.open(
config.wgServer+mw.util.getUrl('File:'+filename), // URI encoding required
'_self' // load in current tab
);
} else {
console.log('data', data);
alert('Unknown error on upload!');
}
};
api.upload(file, params).then(handleResponse, handleResponse);
} else { alert('Missing file or file name. Could not upload file.'); }
},
// Delay until element exists to run function
waitFor: function(query, callback, extraDelay) {
if ('function' === typeof callback && 'string' === typeof query) {
extraDelay = extraDelay || 0;
if (document.querySelector(query)) {
setTimeout(callback, extraDelay);
} else {
// set up the mutation observer
let observer = new MutationObserver(function (mutations, me) {
if (document.querySelector(query)) {
setTimeout(callback, extraDelay);
me.disconnect(); // stop observing
return;
}
});
// start observing
observer.observe(document, {
childList: true,
subtree: true
});
}
}
},
};
let titles = [
'MediaWiki:Gadget-BetterUpload.json', // Site-wide settings on MediaWiki json page
'User:'+config.wgUserName+'/BetterUpload.json' // User settings if any in "User:NAME/BetterUpload.json"
];
if (document.querySelector('#wpDestFile') && document.querySelector('#wpDestFile').value.length>0) {
titles.push('File:'+document.querySelector('#wpDestFile').value); // File to check if doing a reupload
}
api.get({
action: 'query',
prop: 'revisions',
titles: titles,
rvprop: 'content',
rvslots: '*'
}).then((data) => {
let page = {user: -1, site: -1, curr: null};
Object.keys(data.query.pages).forEach((id) => {
if (data.query.pages[id].ns === 8 && data.query.pages[id].missing!=='') {
page.site = id;
} else if (data.query.pages[id].ns === 2 && data.query.pages[id].missing!=='') {
page.user = id;
} else if (data.query.pages[id].ns === 6 && data.query.pages[id].missing!=='') {
page.curr = data.query.pages[id].revisions[0].slots.main['*'];
}
});
if (page.user !== -1) {
window.dev.BetterUpload = JSON.parse(data.query.pages[page.user].revisions[0].slots.main['*']);
} else if (page.site !== -1) {
window.dev.BetterUpload = JSON.parse(data.query.pages[page.site].revisions[0].slots.main['*']);
}
let setInit = () => {
if (new URL(location.href).searchParams.has('wpForReUpload')) { // Special:Upload?wpForReUpload=1
BU.init(page.curr);
} else { // Special:Upload
BU.init();
}
};
if (document.querySelector('#wpUploadDescription')) {
setInit();
} else {
// set up the mutation observer
let observer = new MutationObserver((_, me) => {
if (document.querySelector('#wpUploadDescription')) {
me.disconnect(); // stop observing
BU.init();
}
});
// start observing
observer.observe(document, {
childList: true,
subtree: true
});
}
});
});
// Start the process if in the right page
if (mw.config.values.wgCanonicalSpecialPageName === 'Upload') {
mw.loader.using('mediawiki.api').then(()=>{
importArticles({
type: 'script',
articles: [ 'u:dev:MediaWiki:CustomCodeMirror.js' ]
});
});
}