User:Evad37/Script modules
![]() | The following is a draft working towards a proposal for adoption as a Wikipedia policy, guideline, or process. The proposal must not be taken to represent consensus, but is still in development and under discussion, and has not yet reached the process of gathering consensus for adoption. Thus references or links to this page should not describe it as policy, guideline, nor yet even as a proposal. |
Script modules are bits of Javascript code intended to be easily reused by userscripts. Script modules are to be stored in the MediaiWiki: namespace, as subpages of MediaWiki:Script modules/. Draft proposal for reusable Javascript code to be stored in MediaWiki: namespace and be easily used in userscripts.
The basics
A new gadget would add the functions exportScriptModule and importScriptModule to the Window object.
Script modules use the syntax:
Window.exportScriptModule('
name
', IIFE
);
where name
is the name of script module (subpage name without the .js suffix), and IIFE
is an immediately invoked function expression which returns the function or object to be exported.
Userscripts use the syntax:
Window.importScriptModule('
name
').then(function(localVar
) {
/* ... code using the script module's export ... */
});
where name
is the name of script module (subpage name without the .js suffix), and localVar
is a local variable name for whatever the script module exported.
Just like mw.loader.using, the importScriptModule function expresses the intent that a script module be loaded. A new network request is not actually sent if that script module has already been loaded by another script.
Worked example
Suppose you want some code to parse wikitext for templates, and return an array of nicely formatted objects like:
{
name: 'nameOfTemplate',
wikitext: '{{nameOfTemplate|foo|2=1+1=2|bar=qux}}',
parameters: [
{name: 1, value:'foo'},
{name:'2', value:'1+1=2'},
{name:'bar', value:'qux'}
]
}
The module code would stored at MediaWiki:Script modules/templateParser.js and contain something like:
Window.exportScriptModule('templateParser', function() {
/* ... helper functions omitted ... */
var templateParser = function(wikitext) {
/* ... lots of lines omitted ... */
return result;
};
return templateParser;
})());
A userscript would access the template parser using code like this:
importScriptModule('templateParser').then(function(tParser) {
// `tParser` is our local variable name for the module's `templateParser` function. We can choose any name we want.
// We can now use the code defined in that module, e.g.
var templateInfo = tParser('==See also==\n{{Commons category}}\n{{Portal-inline|JavaScript|size=tiny}}\n*[[JavaScript templating]]');
/* `templateInfo` now evaluates to:
[
{
name: 'Commons category',
wikitext: '{{Commons category}}',
parameters: []
},
{
name: 'Portal-inline',
wikitext: '{{Portal-inline|JavaScript|size=tiny}}',
parameters: [
{name: 1, value:'JavaScript'},
{name:'size', value:'tiny'}
]
}
]
*/
};
// `tParser` is not available out here. The scope is limited to the function within .then()
Loading multiple script modules
To load several modules, use $.when()
, e.g.
$.when(
importScriptModule('templateParser'),
importScriptModule('multiButtonConfirm')
).then(function(tParser, multiConfirm) {
/* ... code using these modules goes here ... */
});
Loading errors
If there is an error loading a script module, the promise returned by importScriptModule will be rejected with an error message. Access the error message using a "failFilter" function in .then() e.g.
importScriptModule('moduleWithAnError').then(
function(foo) {
/* ... code that would be executed, if not for the loading error ... */
}),
function(failMessage) {
/* Show an error message to the user. Perhaps using bubble notifications from mw.notify */
}
);
The code
The code for the script enabling the importScriptModule function would be
Window.exportScriptModule = function(name, exported) {
Window.exportScriptModule[name] = exported;
};
Window.importScriptModule = function(module) {
return mw.loader.using('mediawiki.util').then(function() {
if ( Window.exportScriptModule[module] === undefined ) {
// Load module
return $.getScript(
'https://en.wikipedia.org/w/index.php?title=MediaWiki:Script modules' +
mw.util.wikiUrlencode(module) +
'.js&action=raw&ctype=text/javascript'
);
}
// else module has already been loaded
return true;
})
.then(
function() {
if ( Window.exportScriptModule[module] === undefined ) {
throw new Error("Script module " + module + " did not export anything!");
}
return Window.exportScriptModule[module];
},
function() {
throw new Error("Failed to load script module " + module);
}
);
};
This should probably be an on-by-default gadget, as it needs to be loaded before any userscripts are loaded, but is only useful for registered users.
Demos
Here are some demonstrations, using Evad37's subpages instead of MediaWiki namespace.
- ScriptModule import/export code: User:Evad37/ScriptModules.js
- "hello" script module: User:Evad37/Module/hello.js
- "goodbye" script module: User:Evad37/Module/goodbye.js
- Script using "hello" script module: User:Evad37/ScriptModulesDemo.js
- Script using "hello" and "goodbye" script modules: User:Evad37/ScriptModulesDemo2.js
Add the following code to your Special:MyPage/common.js to see these demonstrations working:
$.getScript('https://en.wikipedia.org/w/index.php?title=User:Evad37/ScriptModules.js&action=raw&ctype=text/javascript')
.then(function() {
importScript('User:Evad37/ScriptModulesDemo.js');
importScript('User:Evad37/ScriptModulesDemo2.js');
});