Merge "Infrastructure for loading plugins in the MW integration"

This commit is contained in:
jenkins-bot 2013-07-26 19:35:08 +00:00 committed by Gerrit Code Review
commit d477bfeb6c
4 changed files with 112 additions and 7 deletions

View file

@ -121,8 +121,9 @@ class VisualEditorHooks {
* Adds extra variables to the global config
*/
public static function onResourceLoaderGetConfigVars( array &$vars ) {
global $wgVisualEditorEnableExperimentalCode, $wgVisualEditorEnableEventLogging,
$wgVisualEditorTabLayout, $wgVisualEditorDisableForAnons, $wgVisualEditorNamespaces;
global $wgVisualEditorEnableEventLogging, $wgVisualEditorPluginModules,
$wgVisualEditorEnableExperimentalCode, $wgVisualEditorTabLayout,
$wgVisualEditorDisableForAnons, $wgVisualEditorNamespaces;
$vars['wgVisualEditorConfig'] = array(
'enableExperimentalCode' => $wgVisualEditorEnableExperimentalCode,
@ -131,6 +132,7 @@ class VisualEditorHooks {
'disableForAnons' => $wgVisualEditorDisableForAnons,
'namespaces' => $wgVisualEditorNamespaces,
'skins' => self::$supportedSkins,
'pluginModules' => $wgVisualEditorPluginModules,
);
return true;

View file

@ -10,6 +10,10 @@
/* Configuration */
// Array of ResourceLoader module names (strings) that should be loaded when VisualEditor is
// loaded. Other extensions that extend VisualEditor should add to this array.
$wgVisualEditorPluginModules = array();
// URL to the Parsoid instance
// MUST NOT end in a slash due to Parsoid bug
$wgVisualEditorParsoidURL = 'http://localhost:8000';
@ -38,6 +42,7 @@ $wgVisualEditorTabLayout = 'replace';
// upon signup.
// Depends on GuidedTour and EventLogging extensions.
$wgVisualEditorEnableGenderSurvey = false;
/* Setup */
$wgExtensionCredits['other'][] = array(

View file

@ -20,7 +20,8 @@
*/
( function () {
var conf, uri, pageExists, viewUri, veEditUri, isViewPage,
init, support, getTargetDeferred;
init, support, getTargetDeferred,
plugins = [];
/**
* Use deferreds to avoid loading and instantiating Target multiple times.
@ -38,6 +39,9 @@
// Transfer methods
ve.init.mw.ViewPageTarget.prototype.setupSectionEditLinks = init.setupSectionEditLinks;
// Add plugins
target.addPlugins( plugins );
getTargetDeferred.resolve( target );
} )
.fail( getTargetDeferred.reject );
@ -98,6 +102,45 @@
'blackberry': null
},
/**
* Add a plugin module or function.
*
* Plugins are run after VisualEditor is loaded, but before it is initialized. This allows
* plugins to add classes and register them with the factories and registries.
*
* The parameter to this function can be a ResourceLoader module name or a function.
*
* If it's a module name, it will be loaded together with the VisualEditor core modules when
* VE is loaded. No special care is taken to ensure that the module runs after the VE
* classes are loaded, so if this is desired, the module should depend on
* ext.visualEditor.core .
*
* If it's a function, it will be invoked once the VisualEditor core modules and any
* plugin modules registered through this function have been loaded, but before the editor
* is intialized. The function takes one parameter, which is the ve.init.mw.Target instance
* that's initializing, and can optionally return a jQuery.Promise . VisualEditor will
* only be initialized once all promises returned by plugin functions have been resolved.
*
* @example
* // Register ResourceLoader module
* ve.libs.mw.addPlugin( 'ext.gadget.foobar' );
*
* // Register a callback
* ve.libs.mw.addPlugin( function ( target ) {
* ve.dm.Foobar = .....
* } );
*
* // Register a callback that loads another script
* ve.libs.mw.addPlugin( function () {
* return $.getScript( 'http://example.com/foobar.js' );
* } );
*
* @param {string|Function} plugin Module name or callback that optionally returns a promise
*/
addPlugin: function( plugin ) {
plugins.push( plugin );
},
skinSetup: function () {
init.setupTabLayout();
init.setupSectionEditLinks();

View file

@ -41,7 +41,10 @@ ve.init.mw.Target = function VeInitMwTarget( $container, pageName, revisionId )
document.createElementNS && document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ?
['ext.visualEditor.viewPageTarget.icons-vector', 'ext.visualEditor.icons-vector'] :
['ext.visualEditor.viewPageTarget.icons-raster', 'ext.visualEditor.icons-raster']
);
)
.concat( mw.config.get( 'wgVisualEditorConfig' ).pluginModules || [] );
this.pluginCallbacks = [];
this.modulesReady = $.Deferred();
this.loading = false;
this.saving = false;
this.serializing = false;
@ -123,6 +126,28 @@ ve.inheritClass( ve.init.mw.Target, ve.init.Target );
/* Static Methods */
/**
* Handle the RL modules for VE and registered plugin modules being loaded.
*
* This method is called within the context of a target instance. It executes all registered
* plugin callbacks, gathers any promises returned and resolves this.modulesReady when all of
* the gathered promises are resolved.
*/
ve.init.mw.Target.onModulesReady = function () {
var i, len, callbackResult, promises = [];
for ( i = 0, len = this.pluginCallbacks.length; i < len; i++ ) {
callbackResult = this.pluginCallbacks[i]( this );
if ( callbackResult && callbackResult.then ) { // duck-type jQuery.Promise using .then
promises.push( callbackResult );
}
}
// Dereference the callbacks
this.pluginCallbacks = [];
// Create a master promise tracking all the promises we got, and wait for it
// to be resolved
$.when.apply( $, promises ).done( this.modulesReady.resolve ).fail( this.modulesReady.reject );
};
/**
* Handle response to a successful load request.
*
@ -169,8 +194,8 @@ ve.init.mw.Target.onLoad = function ( response ) {
this.baseTimeStamp = data.basetimestamp;
this.startTimeStamp = data.starttimestamp;
this.revid = data.oldid;
// Everything worked, the page was loaded, continue as soon as the module is ready
mw.loader.using( this.modules, ve.bind( ve.init.mw.Target.onReady, this ) );
// Everything worked, the page was loaded, continue as soon as the modules are loaded
this.modulesReady.done( ve.bind( ve.init.mw.Target.onReady, this ) );
}
};
@ -456,6 +481,32 @@ ve.init.mw.Target.onSerializeError = function ( jqXHR, status, error ) {
/* Methods */
/**
* Add a plugin module or callback.
*
* @param {string|Function} plugin Plugin module or callback
*/
ve.init.mw.Target.prototype.addPlugin = function ( plugin ) {
if ( typeof plugin === 'string' ) {
this.modules.push( plugin );
} else if ( $.isFunction( plugin ) ) {
this.pluginCallbacks.push( plugin );
}
};
/**
* Add an array of plugins.
*
* @see #addPlugin
* @param {Array} plugins
*/
ve.init.mw.Target.prototype.addPlugins = function ( plugins ) {
var i, len;
for ( i = 0, len = plugins.length; i < len; i++ ) {
this.addPlugin( plugins[i] );
}
};
/**
* Get HTML to send to Parsoid. This takes a document generated by the converter and
* transplants the head tag from the old document into it, as well as the attributes on the
@ -502,7 +553,11 @@ ve.init.mw.Target.prototype.load = function () {
return false;
}
// Start loading the module immediately
mw.loader.load( this.modules );
mw.loader.using(
// Wait for site and user JS before running plugins
this.modules.concat( [ 'site', 'user' ] ),
ve.bind( ve.init.mw.Target.onModulesReady, this )
);
data = {
'action': 'visualeditor',