MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
Created page with "→Any JavaScript here will be loaded for all users on every page load.: mw.hook( 'codeEditor.configure' ).add(function(session) { // Use proper tabs session.setUseSoftTabs(true); session.setTabSize(2); })" |
No edit summary |
||
| (2 intermediate revisions by the same user not shown) | |||
| Line 5: | Line 5: | ||
session.setUseSoftTabs(true); | session.setUseSoftTabs(true); | ||
session.setTabSize(2); | session.setTabSize(2); | ||
}) | }); | ||
//mw.loader.load('https://en.wikipedia.org/w/index.php?title=User:Nardog/CodeEditorAssist.js&action=raw&ctype=text/javascript'); | |||
mw.loader.using([ | |||
'user.options', 'ext.visualEditor.desktopArticleTarget.init' | |||
], function codeEditorAssist() { | |||
let isEdit = mw.config.exists('wgCodeEditorCurrentLanguage') && | |||
['edit', 'submit'].includes(mw.config.get('wgAction')); | |||
let veAvailable = mw.libs.ve.isVisualAvailable; | |||
let isAf = mw.config.exists('aceConfig'); | |||
if (!isEdit && !veAvailable && !isAf) return; | |||
let context, curEditor, defSettingsMap = new WeakMap(); | |||
let getSettings = (editor, isDef) => { | |||
let settings = {}; | |||
let defSettings = !isDef && defSettingsMap.get(editor); | |||
Object.entries(editor.getOptions()).forEach(([k, v]) => { | |||
if (v === undefined) { | |||
v = null; | |||
} | |||
if (isDef || v !== defSettings[k] || | |||
k === 'showInvisibles' || k === 'wrap' | |||
) { | |||
settings[k] = v; | |||
} | |||
}); | |||
delete settings.mode; | |||
delete settings.readOnly; | |||
delete settings.maxLines; | |||
delete settings.minLines; | |||
delete settings.firstLineNumber; | |||
if (isDef) { | |||
settings.showInvisibles = false; | |||
settings.wrap = 'off'; | |||
} | |||
return settings; | |||
}; | |||
let updateToolbar = () => { | |||
if (!context) return; | |||
let names = []; | |||
if (curEditor.getShowInvisibles() !== context.showInvisibleChars) { | |||
names.push('invisibleChars'); | |||
} | |||
if (curEditor.session.getUseWrapMode() !== context.lineWrappingActive) { | |||
names.push('lineWrapping'); | |||
} | |||
names.forEach(name => { | |||
context.modules.toolbar.$toolbar.find(`.tool[rel="${name}"]`) | |||
.data('action').execute(context); | |||
}); | |||
}; | |||
let saveRemoveHandler = function (e) { | |||
this.disabled = true; | |||
let change = 'userjs-codeeditorassist-settings'; | |||
let value; | |||
if (!e.data) { | |||
value = JSON.stringify(getSettings(curEditor)); | |||
change += '=' + value; | |||
} | |||
mw.loader.using('mediawiki.api').then(() => ( | |||
new mw.Api().postWithEditToken({ | |||
action: 'globalpreferences', | |||
change: change | |||
}) | |||
)).always(response => { | |||
this.disabled = false; | |||
if (!response || response.globalpreferences !== 'success') { | |||
mw.notify( | |||
e.data | |||
? `Couldn't remove settings from your global preferences` | |||
: `Couldn't save settings to your global preferences`, | |||
{ type: 'error' } | |||
); | |||
return; | |||
} | |||
if (e.data) { | |||
delete mw.user.options.values['userjs-codeeditorassist-settings']; | |||
mw.notify('Removed settings from your global preferences'); | |||
} else { | |||
mw.user.options.set('userjs-codeeditorassist-settings', value); | |||
mw.notify('Saved settings to your global preferences'); | |||
} | |||
}); | |||
}; | |||
let observing; | |||
let addButtons = () => { | |||
if (observing) return; | |||
observing = true; | |||
let $buttons = $('<div>').addClass('floatright').append( | |||
$('<button>').text('Save').on('click', saveRemoveHandler), | |||
' ', | |||
$('<button>').text('Remove').on('click', true, saveRemoveHandler), | |||
' ', | |||
$('<button>').text('Reset').on('click', () => { | |||
curEditor.setOptions(defSettingsMap.get(curEditor)); | |||
$('#ace_settingsmenu').parent()[0].click(); | |||
curEditor.execCommand('showSettingsMenu'); | |||
}) | |||
); | |||
new MutationObserver(() => { | |||
$buttons.appendTo('#ace_settingsmenu > table > tr:last-child > td'); | |||
updateToolbar(); | |||
}).observe(document.body, { childList: true }); | |||
}; | |||
let onFocus = (e, editor) => { | |||
curEditor = editor; | |||
}; | |||
let initialize = editor => { | |||
if (!window.ace) return; | |||
if (!(editor instanceof ace.Editor)) { | |||
if (context) { | |||
editor = context.codeEditor; | |||
} else { | |||
let el = document.querySelector('.ace_editor'); | |||
if (!el) return; | |||
editor = ace.edit(el); | |||
} | |||
} | |||
if (defSettingsMap.has(editor)) return; | |||
curEditor = editor; | |||
defSettingsMap.set(editor, getSettings(editor, true)); | |||
let savedSettings = mw.user.options.get('userjs-codeeditorassist-settings'); | |||
if (savedSettings) { | |||
savedSettings = JSON.parse(savedSettings); | |||
editor.setOptions(savedSettings); | |||
updateToolbar(); | |||
} | |||
editor.on('focus', onFocus); | |||
addButtons(); | |||
}; | |||
if (isAf) { | |||
$.when($.ready, mw.loader.using('ext.abuseFilter.ace')).then(initialize); | |||
return; | |||
} | |||
if (veAvailable) { | |||
mw.hook('ve.loadModules').add(addPlugin => { | |||
addPlugin(() => { | |||
let setupEditor = ve.ui.MWAceEditorWidget.prototype.setupEditor; | |||
ve.ui.MWAceEditorWidget.prototype.setupEditor = function () { | |||
setupEditor.apply(this, arguments); | |||
initialize(this.editor); | |||
}; | |||
}); | |||
}); | |||
} | |||
if (!isEdit) return; | |||
mw.loader.load('oojs-ui.styles.icons-interactions'); | |||
mw.hook('codeEditor.configure').add(initialize); | |||
let promise = new Promise(resolve => { | |||
mw.hook('codeEditor.configure').add(resolve); | |||
}); | |||
mw.hook('wikiEditor.toolbarReady').add($textarea => { | |||
context = $textarea.data('wikiEditorContext'); | |||
promise.then(() => { | |||
$textarea.wikiEditor('addToToolbar', { | |||
section: 'main', | |||
group: 'codeeditor-style', | |||
tools: { | |||
settings: { | |||
label: 'Open code editor settings', | |||
type: 'button', | |||
oouiIcon: 'settings', | |||
action: { | |||
type: 'callback', | |||
execute: () => { | |||
curEditor.execCommand('showSettingsMenu'); | |||
} | |||
} | |||
} | |||
} | |||
}); | |||
}); | |||
}); | |||
}); | |||
Latest revision as of 07:42, 2 November 2025
/* Any JavaScript here will be loaded for all users on every page load. */
mw.hook( 'codeEditor.configure' ).add(function(session)
{
// Use proper tabs
session.setUseSoftTabs(true);
session.setTabSize(2);
});
//mw.loader.load('https://en.wikipedia.org/w/index.php?title=User:Nardog/CodeEditorAssist.js&action=raw&ctype=text/javascript');
mw.loader.using([
'user.options', 'ext.visualEditor.desktopArticleTarget.init'
], function codeEditorAssist() {
let isEdit = mw.config.exists('wgCodeEditorCurrentLanguage') &&
['edit', 'submit'].includes(mw.config.get('wgAction'));
let veAvailable = mw.libs.ve.isVisualAvailable;
let isAf = mw.config.exists('aceConfig');
if (!isEdit && !veAvailable && !isAf) return;
let context, curEditor, defSettingsMap = new WeakMap();
let getSettings = (editor, isDef) => {
let settings = {};
let defSettings = !isDef && defSettingsMap.get(editor);
Object.entries(editor.getOptions()).forEach(([k, v]) => {
if (v === undefined) {
v = null;
}
if (isDef || v !== defSettings[k] ||
k === 'showInvisibles' || k === 'wrap'
) {
settings[k] = v;
}
});
delete settings.mode;
delete settings.readOnly;
delete settings.maxLines;
delete settings.minLines;
delete settings.firstLineNumber;
if (isDef) {
settings.showInvisibles = false;
settings.wrap = 'off';
}
return settings;
};
let updateToolbar = () => {
if (!context) return;
let names = [];
if (curEditor.getShowInvisibles() !== context.showInvisibleChars) {
names.push('invisibleChars');
}
if (curEditor.session.getUseWrapMode() !== context.lineWrappingActive) {
names.push('lineWrapping');
}
names.forEach(name => {
context.modules.toolbar.$toolbar.find(`.tool[rel="${name}"]`)
.data('action').execute(context);
});
};
let saveRemoveHandler = function (e) {
this.disabled = true;
let change = 'userjs-codeeditorassist-settings';
let value;
if (!e.data) {
value = JSON.stringify(getSettings(curEditor));
change += '=' + value;
}
mw.loader.using('mediawiki.api').then(() => (
new mw.Api().postWithEditToken({
action: 'globalpreferences',
change: change
})
)).always(response => {
this.disabled = false;
if (!response || response.globalpreferences !== 'success') {
mw.notify(
e.data
? `Couldn't remove settings from your global preferences`
: `Couldn't save settings to your global preferences`,
{ type: 'error' }
);
return;
}
if (e.data) {
delete mw.user.options.values['userjs-codeeditorassist-settings'];
mw.notify('Removed settings from your global preferences');
} else {
mw.user.options.set('userjs-codeeditorassist-settings', value);
mw.notify('Saved settings to your global preferences');
}
});
};
let observing;
let addButtons = () => {
if (observing) return;
observing = true;
let $buttons = $('<div>').addClass('floatright').append(
$('<button>').text('Save').on('click', saveRemoveHandler),
' ',
$('<button>').text('Remove').on('click', true, saveRemoveHandler),
' ',
$('<button>').text('Reset').on('click', () => {
curEditor.setOptions(defSettingsMap.get(curEditor));
$('#ace_settingsmenu').parent()[0].click();
curEditor.execCommand('showSettingsMenu');
})
);
new MutationObserver(() => {
$buttons.appendTo('#ace_settingsmenu > table > tr:last-child > td');
updateToolbar();
}).observe(document.body, { childList: true });
};
let onFocus = (e, editor) => {
curEditor = editor;
};
let initialize = editor => {
if (!window.ace) return;
if (!(editor instanceof ace.Editor)) {
if (context) {
editor = context.codeEditor;
} else {
let el = document.querySelector('.ace_editor');
if (!el) return;
editor = ace.edit(el);
}
}
if (defSettingsMap.has(editor)) return;
curEditor = editor;
defSettingsMap.set(editor, getSettings(editor, true));
let savedSettings = mw.user.options.get('userjs-codeeditorassist-settings');
if (savedSettings) {
savedSettings = JSON.parse(savedSettings);
editor.setOptions(savedSettings);
updateToolbar();
}
editor.on('focus', onFocus);
addButtons();
};
if (isAf) {
$.when($.ready, mw.loader.using('ext.abuseFilter.ace')).then(initialize);
return;
}
if (veAvailable) {
mw.hook('ve.loadModules').add(addPlugin => {
addPlugin(() => {
let setupEditor = ve.ui.MWAceEditorWidget.prototype.setupEditor;
ve.ui.MWAceEditorWidget.prototype.setupEditor = function () {
setupEditor.apply(this, arguments);
initialize(this.editor);
};
});
});
}
if (!isEdit) return;
mw.loader.load('oojs-ui.styles.icons-interactions');
mw.hook('codeEditor.configure').add(initialize);
let promise = new Promise(resolve => {
mw.hook('codeEditor.configure').add(resolve);
});
mw.hook('wikiEditor.toolbarReady').add($textarea => {
context = $textarea.data('wikiEditorContext');
promise.then(() => {
$textarea.wikiEditor('addToToolbar', {
section: 'main',
group: 'codeeditor-style',
tools: {
settings: {
label: 'Open code editor settings',
type: 'button',
oouiIcon: 'settings',
action: {
type: 'callback',
execute: () => {
curEditor.execCommand('showSettingsMenu');
}
}
}
}
});
});
});
});