Jump to content

User:Evad37/Script modules

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Evad37 (talk | contribs) at 01:16, 26 January 2019 (update). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

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, 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, 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(
            	// When done for real, url would have `title=MediaWiki:Module/`
                'https://en.wikipedia.org/w/index.php?title=User:Evad37/Module/' +
                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 is only useful for registered users.