changes to plugin api; registerPlugin only accepts plugin instance, instance exposes .id

master
Hakim El Hattab 2020-04-08 13:05:28 +02:00
parent 2e8619d300
commit 9522357349
9 changed files with 85 additions and 65 deletions

View File

@ -466,8 +466,7 @@ You can add your own extensions using the same syntax. The following properties
- **callback**: [optional] Function to execute when the script has loaded - **callback**: [optional] Function to execute when the script has loaded
- **condition**: [optional] Function which must return true for the script to be loaded - **condition**: [optional] Function which must return true for the script to be loaded
You can also include dependencies which are bundled/already present on the page. To include a bundled plugin. replace the `src` property with a plugin `id` and a reference to the `plugin` instance: You can also include dependencies which are bundled/already present on the page. To include a bundled plugin. replace the `src` property with a reference to a `plugin` instance:
- **id**: the id of the plugin
- **plugin**: the plugin instance (see [Plugins](#plugins)) - **plugin**: the plugin instance (see [Plugins](#plugins))
### Ready Event ### Ready Event
@ -1403,17 +1402,20 @@ Then:
## Plugins ## Plugins
Plugins should register themselves with reveal.js by calling `Reveal.registerPlugin( 'myPluginID', MyPlugin )`. Registered plugin instances can optionally expose an "init" function that reveal.js will call to initialize them. Plugins should register themselves with reveal.js by calling `Reveal.registerPlugin( MyPlugin )`. Registered plugins _must_ expose a unique `id` property and can optionally expose an `init` function that reveal.js will call to initialize them.
When reveal.js is booted up via `Reveal.initialize()`, it will go through all registered plugins and invoke their "init" methods. If the "init" method returns a Promise, reveal.js will wait for that promise to be fulfilled before finishing the startup sequence and firing the [ready](#ready-event) event. Here's an example of a plugin that does some asynchronous work before reveal.js can proceed: When reveal.js is booted up via `initialize()`, it will go through all registered plugins and invoke their `init` methods. If the `init` method returns a Promise, reveal.js will wait for that promise to be fulfilled before finishing the startup sequence and firing the [ready](#ready-event) event. Here's an example of a plugin that does some asynchronous work before reveal.js can proceed:
```javascript ```javascript
let MyPlugin = { let MyPlugin = {
init: () => new Promise( resolve => setTimeout( resolve, 3000 ) ) id: 'myPlugin',
init: deck => new Promise( resolve => setTimeout( resolve, 3000 ) )
}; };
Reveal.registerPlugin( 'myPlugin', MyPlugin ); Reveal.initialize({
Reveal.on( 'ready', () => console.log( 'Three seconds later...' ) ); dependencies: [ { plugin: MyPlugin } ]
Reveal.initialize(); }).then( () => {
console.log( 'Three seconds later...' )
} );
``` ```
Note that reveal.js will *not* wait for init Promise fulfillment if the plugin is loaded as an [async dependency](#dependencies). If the plugin's init method does _not_ return a Promise, the plugin is considered ready right away and will not hold up the reveal.js startup sequence. Note that reveal.js will *not* wait for init Promise fulfillment if the plugin is loaded as an [async dependency](#dependencies). If the plugin's init method does _not_ return a Promise, the plugin is considered ready right away and will not hold up the reveal.js startup sequence.

4
dist/reveal.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,9 @@ import { loadScript } from '../utils/loader.js'
*/ */
export default class Plugins { export default class Plugins {
constructor() { constructor( reveal ) {
this.Reveal = reveal;
// Flags our current state (idle -> loading -> loaded) // Flags our current state (idle -> loading -> loaded)
this.state = 'idle'; this.state = 'idle';
@ -58,8 +60,8 @@ export default class Plugins {
// Load synchronous scripts // Load synchronous scripts
scripts.forEach( s => { scripts.forEach( s => {
if( s.id ) { if( s.plugin ) {
this.registerPlugin( s.id, s.plugin ); this.registerPlugin( s.plugin );
scriptLoadedCallback( s ); scriptLoadedCallback( s );
} }
else { else {
@ -104,7 +106,7 @@ export default class Plugins {
// If the plugin has an 'init' method, invoke it // If the plugin has an 'init' method, invoke it
if( typeof plugin.init === 'function' ) { if( typeof plugin.init === 'function' ) {
let callback = plugin.init(); let callback = plugin.init( this.Reveal );
// If the plugin returned a Promise, wait for it // If the plugin returned a Promise, wait for it
if( callback && typeof callback.then === 'function' ) { if( callback && typeof callback.then === 'function' ) {
@ -135,9 +137,9 @@ export default class Plugins {
if( this.asyncDependencies.length ) { if( this.asyncDependencies.length ) {
this.asyncDependencies.forEach( s => { this.asyncDependencies.forEach( s => {
if( s.id ) { if( s.plugin ) {
this.registerPlugin( s.id, s.plugin ); this.registerPlugin( s.plugin );
if( typeof s.plugin.init === 'function' ) s.plugin.init(); if( typeof s.plugin.init === 'function' ) s.plugin.init( this.Reveal );
if( typeof s.callback === 'function' ) s.callback(); if( typeof s.callback === 'function' ) s.callback();
} }
else { else {
@ -157,15 +159,20 @@ export default class Plugins {
* before considering itself ready, as long as the plugin * before considering itself ready, as long as the plugin
* is registered before calling `Reveal.initialize()`. * is registered before calling `Reveal.initialize()`.
*/ */
registerPlugin( id, plugin ) { registerPlugin( plugin ) {
if( this.registeredPlugins[id] === undefined ) { let id = plugin.id;
if( typeof id !== 'string' ) {
console.warn( 'reveal.js: plugin.id is not a string' );
}
else if( this.registeredPlugins[id] === undefined ) {
this.registeredPlugins[id] = plugin; this.registeredPlugins[id] = plugin;
// If a plugin is registered after reveal.js is loaded, // If a plugin is registered after reveal.js is loaded,
// initialize it right away // initialize it right away
if( this.state === 'loaded' && typeof plugin.init === 'function' ) { if( this.state === 'loaded' && typeof plugin.init === 'function' ) {
plugin.init(); plugin.init( this.Reveal );
} }
} }
else { else {

View File

@ -69,14 +69,16 @@
var RevealHighlight = { var RevealHighlight = {
id: 'highlight',
HIGHLIGHT_STEP_DELIMITER: '|', HIGHLIGHT_STEP_DELIMITER: '|',
HIGHLIGHT_LINE_DELIMITER: ',', HIGHLIGHT_LINE_DELIMITER: ',',
HIGHLIGHT_LINE_RANGE_DELIMITER: '-', HIGHLIGHT_LINE_RANGE_DELIMITER: '-',
init: function() { init: function( deck ) {
// Read the plugin config options and provide fallbacks // Read the plugin config options and provide fallbacks
var config = Reveal.getConfig().highlight || {}; var config = deck.getConfig().highlight || {};
config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true; config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true;
config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true; config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true;
@ -105,7 +107,7 @@
// If we're printing to PDF, scroll the code highlights of // If we're printing to PDF, scroll the code highlights of
// all blocks in the deck into view at once // all blocks in the deck into view at once
Reveal.on( 'pdf-ready', function() { deck.on( 'pdf-ready', function() {
[].slice.call( document.querySelectorAll( '.reveal pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) { [].slice.call( document.querySelectorAll( '.reveal pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) {
RevealHighlight.scrollHighlightedLineIntoView( block, {}, true ); RevealHighlight.scrollHighlightedLineIntoView( block, {}, true );
} ); } );
@ -416,7 +418,7 @@
} }
Reveal.registerPlugin( 'highlight', RevealHighlight ); Reveal.registerPlugin( RevealHighlight );
return RevealHighlight; return RevealHighlight;

View File

@ -403,14 +403,13 @@
// API // API
var RevealMarkdown = { var RevealMarkdown = {
id: 'markdown',
/** /**
* Starts processing and converting Markdown within the * Starts processing and converting Markdown within the
* current reveal.js deck. * current reveal.js deck.
*
* @param {function} callback function to invoke once
* we've finished loading and parsing Markdown
*/ */
init: function( callback ) { init: function( deck ) {
if( typeof marked === 'undefined' ) { if( typeof marked === 'undefined' ) {
throw 'The reveal.js Markdown plugin requires marked to be loaded'; throw 'The reveal.js Markdown plugin requires marked to be loaded';
@ -425,7 +424,7 @@
} }
// marked can be configured via reveal.js config options // marked can be configured via reveal.js config options
var options = Reveal.getConfig().markdown; var options = deck.getConfig().markdown;
if( options ) { if( options ) {
marked.setOptions( options ); marked.setOptions( options );
} }
@ -443,7 +442,7 @@
// Register our plugin so that reveal.js will call our // Register our plugin so that reveal.js will call our
// plugin 'init' method as part of the initialization // plugin 'init' method as part of the initialization
Reveal.registerPlugin( 'markdown', RevealMarkdown ); Reveal.registerPlugin( RevealMarkdown );
return RevealMarkdown; return RevealMarkdown;

View File

@ -60,7 +60,9 @@ var RevealMath = window.RevealMath || (function(){
} }
return { return {
init: function() { id: 'math',
init: function( deck ) {
defaults( options, defaultOptions ); defaults( options, defaultOptions );
defaults( options.tex2jax, defaultOptions.tex2jax ); defaults( options.tex2jax, defaultOptions.tex2jax );
@ -73,10 +75,10 @@ var RevealMath = window.RevealMath || (function(){
// Typeset followed by an immediate reveal.js layout since // Typeset followed by an immediate reveal.js layout since
// the typesetting process could affect slide height // the typesetting process could affect slide height
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] ); MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
MathJax.Hub.Queue( Reveal.layout ); MathJax.Hub.Queue( deck.layout );
// Reprocess equations in slides when they turn visible // Reprocess equations in slides when they turn visible
Reveal.on( 'slidechanged', function( event ) { deck.on( 'slidechanged', function( event ) {
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] ); MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
@ -89,4 +91,4 @@ var RevealMath = window.RevealMath || (function(){
})(); })();
Reveal.registerPlugin( 'math', RevealMath ); Reveal.registerPlugin( RevealMath );

View File

@ -13,6 +13,8 @@ var RevealNotes = (function() {
var notesPopup = null; var notesPopup = null;
var deck;
function openNotes( notesFilePath ) { function openNotes( notesFilePath ) {
if (notesPopup && !notesPopup.closed) { if (notesPopup && !notesPopup.closed) {
@ -46,7 +48,7 @@ var RevealNotes = (function() {
namespace: 'reveal-notes', namespace: 'reveal-notes',
type: 'connect', type: 'connect',
url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search, url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
state: Reveal.getState() state: deck.getState()
} ), '*' ); } ), '*' );
}, 500 ); }, 500 );
@ -68,7 +70,7 @@ var RevealNotes = (function() {
*/ */
function callRevealApi( methodName, methodArguments, callId ) { function callRevealApi( methodName, methodArguments, callId ) {
var result = Reveal[methodName].apply( Reveal, methodArguments ); var result = deck[methodName].apply( deck, methodArguments );
notesPopup.postMessage( JSON.stringify( { notesPopup.postMessage( JSON.stringify( {
namespace: 'reveal-notes', namespace: 'reveal-notes',
type: 'return', type: 'return',
@ -83,7 +85,7 @@ var RevealNotes = (function() {
*/ */
function post( event ) { function post( event ) {
var slideElement = Reveal.getCurrentSlide(), var slideElement = deck.getCurrentSlide(),
notesElement = slideElement.querySelector( 'aside.notes' ), notesElement = slideElement.querySelector( 'aside.notes' ),
fragmentElement = slideElement.querySelector( '.current-fragment' ); fragmentElement = slideElement.querySelector( '.current-fragment' );
@ -93,7 +95,7 @@ var RevealNotes = (function() {
notes: '', notes: '',
markdown: false, markdown: false,
whitespace: 'normal', whitespace: 'normal',
state: Reveal.getState() state: deck.getState()
}; };
// Look for notes defined in a slide attribute // Look for notes defined in a slide attribute
@ -134,13 +136,13 @@ var RevealNotes = (function() {
function onConnected() { function onConnected() {
// Monitor events that trigger a change in state // Monitor events that trigger a change in state
Reveal.on( 'slidechanged', post ); deck.on( 'slidechanged', post );
Reveal.on( 'fragmentshown', post ); deck.on( 'fragmentshown', post );
Reveal.on( 'fragmenthidden', post ); deck.on( 'fragmenthidden', post );
Reveal.on( 'overviewhidden', post ); deck.on( 'overviewhidden', post );
Reveal.on( 'overviewshown', post ); deck.on( 'overviewshown', post );
Reveal.on( 'paused', post ); deck.on( 'paused', post );
Reveal.on( 'resumed', post ); deck.on( 'resumed', post );
// Post the initial state // Post the initial state
post(); post();
@ -152,7 +154,11 @@ var RevealNotes = (function() {
} }
return { return {
init: function() { id: 'notes',
init: function( revealInstance ) {
deck = revealInstance;
if( !/receiver/i.test( window.location.search ) ) { if( !/receiver/i.test( window.location.search ) ) {
@ -162,7 +168,7 @@ var RevealNotes = (function() {
} }
// Open the notes when the 's' key is hit // Open the notes when the 's' key is hit
Reveal.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() { deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
openNotes(); openNotes();
} ); } );
@ -175,4 +181,4 @@ var RevealNotes = (function() {
})(); })();
Reveal.registerPlugin( 'notes', RevealNotes ); Reveal.registerPlugin( RevealNotes );

View File

@ -2,15 +2,17 @@
var RevealZoom = (function(){ var RevealZoom = (function(){
return { return {
init: function() { id: 'zoom',
Reveal.getRevealElement().addEventListener( 'mousedown', function( event ) { init: function( reveal ) {
reveal.getRevealElement().addEventListener( 'mousedown', function( event ) {
var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt'; var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : defaultModifier ) + 'Key'; var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
var zoomLevel = ( Reveal.getConfig().zoomLevel ? Reveal.getConfig().zoomLevel : 2 ); var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 );
if( event[ modifier ] && !Reveal.isOverview() ) { if( event[ modifier ] && !reveal.isOverview() ) {
event.preventDefault(); event.preventDefault();
zoom.to({ zoom.to({
@ -27,7 +29,7 @@ var RevealZoom = (function(){
})(); })();
Reveal.registerPlugin( 'zoom', RevealZoom ); Reveal.registerPlugin( RevealZoom );
/*! /*!
* zoom.js 0.3 (modified for use with reveal.js) * zoom.js 0.3 (modified for use with reveal.js)

View File

@ -34,15 +34,15 @@
var initCounter = { PluginB: 0, PluginC: 0, PluginD: 0 }; var initCounter = { PluginB: 0, PluginC: 0, PluginD: 0 };
// Plugin with no init method // Plugin with no init method
var PluginA = {}; var PluginA = { id: 'PluginA' };
// Plugin with init method // Plugin with init method
var PluginB = { init: function() { var PluginB = { id: 'PluginB', init: function() {
initCounter['PluginB'] += 1; initCounter['PluginB'] += 1;
} }; } };
// Async plugin with init method // Async plugin with init method
var PluginC = { init: function() { var PluginC = { id: 'PluginC', init: function() {
return new Promise(function( resolve ) { return new Promise(function( resolve ) {
setTimeout( () => { setTimeout( () => {
initCounter['PluginC'] += 1; initCounter['PluginC'] += 1;
@ -52,24 +52,24 @@
} }; } };
// Plugin initialized after reveal.js is ready // Plugin initialized after reveal.js is ready
var PluginD = { init: function() { var PluginD = { id: 'PluginD', init: function() {
initCounter['PluginD'] += 1; initCounter['PluginD'] += 1;
} }; } };
var PluginE = {}; var PluginE = { id: 'PluginE' };
var reveal = new Reveal( document.querySelector( '.reveal' ) ); var reveal = new Reveal( document.querySelector( '.reveal' ) );
reveal.registerPlugin( 'PluginA', PluginA ); reveal.registerPlugin( PluginA );
reveal.registerPlugin( 'PluginB', PluginB ); reveal.registerPlugin( PluginB );
reveal.registerPlugin( 'PluginC', PluginC ); reveal.registerPlugin( PluginC );
reveal.initialize(); reveal.initialize();
QUnit.test( 'Can initialize synchronously', function( assert ) { QUnit.test( 'Can initialize synchronously', function( assert ) {
assert.strictEqual( initCounter['PluginB'], 1 ); assert.strictEqual( initCounter['PluginB'], 1 );
reveal.registerPlugin( 'PluginB', PluginB ); reveal.registerPlugin( PluginB );
assert.strictEqual( initCounter['PluginB'], 1, 'prevents duplicate registration' ); assert.strictEqual( initCounter['PluginB'], 1, 'prevents duplicate registration' );
}); });
@ -84,7 +84,7 @@
assert.strictEqual( initCounter['PluginC'], 1, 'finsihed initializing when reveal.js dispatches "ready"' ); assert.strictEqual( initCounter['PluginC'], 1, 'finsihed initializing when reveal.js dispatches "ready"' );
done(); done();
reveal.registerPlugin( 'PluginD', PluginD ); reveal.registerPlugin( PluginD );
assert.strictEqual( initCounter['PluginD'], 1, 'plugin registered after reveal.js is ready still initiailizes' ); assert.strictEqual( initCounter['PluginD'], 1, 'plugin registered after reveal.js is ready still initiailizes' );
done(); done();
}); });
@ -93,7 +93,7 @@
QUnit.test( 'Can check if plugin is registered', function( assert ) { QUnit.test( 'Can check if plugin is registered', function( assert ) {
assert.strictEqual( reveal.hasPlugin( 'PluginA' ), true ); assert.strictEqual( reveal.hasPlugin( 'PluginA' ), true );
assert.strictEqual( reveal.hasPlugin( 'PluginE' ), false ); assert.strictEqual( reveal.hasPlugin( 'PluginE' ), false );
reveal.registerPlugin( 'PluginE', PluginE ); reveal.registerPlugin( PluginE );
assert.strictEqual( reveal.hasPlugin( 'PluginE' ), true ); assert.strictEqual( reveal.hasPlugin( 'PluginE' ), true );
} ); } );