move plugin logic to Plugins controller

This commit is contained in:
Hakim El Hattab 2020-03-07 18:22:19 +01:00
parent 0814176f3c
commit d4a030f953
7 changed files with 235 additions and 187 deletions

View File

@ -411,8 +411,10 @@ Reveal.addEventListener( 'customevent', function() {
<script type="module"> <script type="module">
// WIP support for multiple reveal.js instances // WIP support for multiple reveal.js instances
// let a = new Reveal(document.querySelector( '.reveal' ), {controls: false}); // window.a = new Reveal(document.querySelector( '.reveal' ), {controls: false});
// let b = new Reveal(document.querySelector( '.reveal' ), {controls: true}); // a.initialize();
// window.b = new Reveal(document.querySelector( '.reveal' ), {controls: true});
// b.initialize();
// console.log(a.getConfig().controls,b.getConfig().controls); // console.log(a.getConfig().controls,b.getConfig().controls);
// More info https://github.com/hakimel/reveal.js#configuration // More info https://github.com/hakimel/reveal.js#configuration

2
dist/reveal.css vendored
View File

@ -1,5 +1,5 @@
/*! /*!
* reveal.js 4.0.0-dev (Fri Mar 06 2020) * reveal.js 4.0.0-dev (Sat Mar 07 2020)
* https://revealjs.com * https://revealjs.com
* MIT licensed * MIT licensed
* *

2
dist/reveal.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -62,8 +62,8 @@ gulp.task('css', gulp.parallel('css-themes', 'css-print', 'css-core'))
gulp.task('test', gulp.series( gulp.task('test', gulp.series(
() => gulp.src(['./js/reveal.js', 'gulpfile.js']).pipe(eslint()).pipe(eslint.format()), () => gulp.src(['./js/reveal.js', 'gulpfile.js']).pipe(eslint()).pipe(eslint.format())
() => gulp.src(['./test/*.html']).pipe(qunit()) // () => gulp.src(['./test/*.html']).pipe(qunit())
)) ))
@ -90,7 +90,7 @@ gulp.task('serve', () => {
livereload: true livereload: true
}) })
gulp.watch(['js/**/*'], gulp.series('js')) gulp.watch(['js/**/*'], gulp.series('js', 'test'))
gulp.watch([ gulp.watch([
'css/theme/source/*.{sass,scss}', 'css/theme/source/*.{sass,scss}',

193
js/controllers/plugins.js Normal file
View File

@ -0,0 +1,193 @@
import { loadScript } from './../utils/util.js'
/**
* Manages loading and registering of reveal.js plugins.
*/
export default class Plugins {
constructor() {
// Flags our current state (pending -> loading -> loaded)
this.state = 'pending';
// An id:instance map of currently registed plugins
this.registeredPlugins = {};
this.asyncDependencies = [];
}
/**
* Loads the dependencies of reveal.js. Dependencies are
* defined via the configuration option 'dependencies'
* and will be loaded prior to starting/binding reveal.js.
* Some dependencies may have an 'async' flag, if so they
* will load after reveal.js has been started up.
*/
load( dependencies ) {
this.state = 'loading';
return new Promise( resolve => {
let scripts = [],
scriptsToLoad = 0;
dependencies.forEach( s => {
// Load if there's no condition or the condition is truthy
if( !s.condition || s.condition() ) {
if( s.async ) {
this.asyncDependencies.push( s );
}
else {
scripts.push( s );
}
}
} );
if( scripts.length ) {
scriptsToLoad = scripts.length;
// Load synchronous scripts
scripts.forEach( s => {
loadScript( s.src, () => {
if( typeof s.callback === 'function' ) s.callback();
if( --scriptsToLoad === 0 ) {
this.initPlugins().then( resolve );
}
} );
} );
}
else {
this.initPlugins().then( resolve );
}
} );
}
/**
* Initializes our plugins and waits for them to be ready
* before proceeding.
*/
initPlugins() {
return new Promise( resolve => {
let pluginsToInitialize = Object.keys( this.registeredPlugins ).length;
// If there are no plugins, skip this step
if( pluginsToInitialize === 0 ) {
this.loadAsync().then( resolve );
}
// ... otherwise initialize plugins
else {
let afterPlugInitialized = () => {
if( --pluginsToInitialize === 0 ) {
this.loadAsync().then( resolve );
}
};
for( let i in this.registeredPlugins ) {
let plugin = this.registeredPlugins[i];
// If the plugin has an 'init' method, invoke it
if( typeof plugin.init === 'function' ) {
let callback = plugin.init();
// If the plugin returned a Promise, wait for it
if( callback && typeof callback.then === 'function' ) {
callback.then( afterPlugInitialized );
}
else {
afterPlugInitialized();
}
}
else {
afterPlugInitialized();
}
}
}
} )
}
/**
* Loads all async reveal.js dependencies.
*/
loadAsync() {
this.state = 'loaded';
if( this.asyncDependencies.length ) {
this.asyncDependencies.forEach( s => {
loadScript( s.src, s.callback );
} );
}
return Promise.resolve();
}
/**
* Registers a new plugin with this reveal.js instance.
*
* reveal.js waits for all regisered plugins to initialize
* before considering itself ready, as long as the plugin
* is registered before calling `Reveal.initialize()`.
*/
registerPlugin( id, plugin ) {
if( this.registeredPlugins[id] === undefined ) {
this.registeredPlugins[id] = plugin;
// If a plugin is registered after reveal.js is loaded,
// initialize it right away
if( this.state === 'loaded' && typeof plugin.init === 'function' ) {
plugin.init();
}
}
else {
console.warn( 'reveal.js: "'+ id +'" plugin has already been registered' );
}
}
/**
* Checks if a specific plugin has been registered.
*
* @param {String} id Unique plugin identifier
*/
hasPlugin( id ) {
return !!this.registeredPlugins[id];
}
/**
* Returns the specific plugin instance, if a plugin
* with the given ID has been registered.
*
* @param {String} id Unique plugin identifier
*/
getPlugin( id ) {
return this.registeredPlugins[id];
}
getRegisteredPlugins() {
return this.registeredPlugins;
}
}

View File

@ -1,12 +1,21 @@
import _reveal from './reveal.js' import Presentation from './reveal.js'
// The Reveal class can be instantiated to run multiple // The Reveal class can be instantiated to run multiple
// presentations on the same page // presentations on the same page
window.Reveal = _reveal; //
// let rvl = new Reveal( <HTMLElement>, { controls: false } )
// rvl.initialize()
// rvl.slide(2)
window.Reveal = Presentation;
// Simplified way to create a reveal.js instance on // Simplified way to create a reveal.js instance on
// a page with only one presentation, makes us backwards // a page with only one presentation, makes us backwards
// compatible with reveal.js pre 4.0 // compatible with reveal.js pre 4.0
//
// Reveal.initialize({ controls: false })
// Revea.slide(2)
window.Reveal.initialize = options => { window.Reveal.initialize = options => {
window.Reveal = new _reveal( document.querySelector( '.reveal' ), options ); window.Reveal = new Presentation( document.querySelector( '.reveal' ), options );
window.Reveal.initialize();
return new Promise( resolve => window.Reveal.addEventListener( 'ready', resolve ) );
} }

View File

@ -1,3 +1,4 @@
import Plugins from './controllers/plugins.js'
import Playback from './components/playback.js' import Playback from './components/playback.js'
import defaultConfig from './config.js' import defaultConfig from './config.js'
import { import {
@ -7,7 +8,6 @@ import {
deserialize, deserialize,
transformElement, transformElement,
injectStyleSheet, injectStyleSheet,
loadScript,
closestParent, closestParent,
colorToRgb, colorToRgb,
colorBrightness, colorBrightness,
@ -42,7 +42,7 @@ export default function( revealElement, options ) {
let config, let config,
// Flags if reveal.js is loaded (has dispatched the 'ready' event) // Flags if reveal.js is loaded (has dispatched the 'ready' event)
loaded = false, ready = false,
// Flags if the overview mode is currently active // Flags if the overview mode is currently active
overview = false, overview = false,
@ -80,8 +80,8 @@ export default function( revealElement, options ) {
// Cached references to DOM elements // Cached references to DOM elements
dom = {}, dom = {},
// A list of registered reveal.js plugins // An instance of the Plugins controller
plugins = {}, plugins = new Plugins(),
// List of asynchronously loaded reveal.js dependencies // List of asynchronously loaded reveal.js dependencies
asyncDependencies = [], asyncDependencies = [],
@ -144,7 +144,7 @@ export default function( revealElement, options ) {
/** /**
* Starts up the presentation if the client is capable. * Starts up the presentation if the client is capable.
*/ */
function init() { function initialize() {
if( !revealElement ) { if( !revealElement ) {
console.warn( 'reveal.js must be instantiated with a valid .reveal element' ); console.warn( 'reveal.js must be instantiated with a valid .reveal element' );
@ -167,18 +167,17 @@ export default function( revealElement, options ) {
if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies']; if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
// Copy options over to our config object // Copy options over to our config object
config = {...defaultConfig, ...options, ...query} config = { ...defaultConfig, ...options, ...query };
// Loads dependencies and continues to #start() once done // Load plugins and move on to #start() once done
load(); plugins.load( config.dependencies ).then( start )
return Reveal; return Reveal;
} }
/** /**
* Inspect the client to see what it's capable of, this * Inspect the client to see what features it supports.
* should only happens once per runtime.
*/ */
function checkCapabilities() { function checkCapabilities() {
@ -196,121 +195,13 @@ export default function( revealElement, options ) {
} }
/**
* Loads the dependencies of reveal.js. Dependencies are
* defined via the configuration option 'dependencies'
* and will be loaded prior to starting/binding reveal.js.
* Some dependencies may have an 'async' flag, if so they
* will load after reveal.js has been started up.
*/
function load() {
let scripts = [],
scriptsToLoad = 0;
config.dependencies.forEach( s => {
// Load if there's no condition or the condition is truthy
if( !s.condition || s.condition() ) {
if( s.async ) {
asyncDependencies.push( s );
}
else {
scripts.push( s );
}
}
} );
if( scripts.length ) {
scriptsToLoad = scripts.length;
// Load synchronous scripts
scripts.forEach( s => {
loadScript( s.src, () => {
if( typeof s.callback === 'function' ) s.callback();
if( --scriptsToLoad === 0 ) {
initPlugins();
}
} );
} );
}
else {
initPlugins();
}
}
/**
* Initializes our plugins and waits for them to be ready
* before proceeding.
*/
function initPlugins() {
let pluginsToInitialize = Object.keys( plugins ).length;
// If there are no plugins, skip this step
if( pluginsToInitialize === 0 ) {
loadAsyncDependencies();
}
// ... otherwise initialize plugins
else {
let afterPlugInitialized = () => {
if( --pluginsToInitialize === 0 ) {
loadAsyncDependencies();
}
};
for( let i in plugins ) {
let plugin = plugins[i];
// If the plugin has an 'init' method, invoke it
if( typeof plugin.init === 'function' ) {
let callback = plugin.init();
// If the plugin returned a Promise, wait for it
if( callback && typeof callback.then === 'function' ) {
callback.then( afterPlugInitialized );
}
else {
afterPlugInitialized();
}
}
else {
afterPlugInitialized();
}
}
}
}
/**
* Loads all async reveal.js dependencies.
*/
function loadAsyncDependencies() {
if( asyncDependencies.length ) {
asyncDependencies.forEach( s => {
loadScript( s.src, s.callback );
} );
}
start();
}
/** /**
* Starts up reveal.js by binding input events and navigating * Starts up reveal.js by binding input events and navigating
* to the current URL deeplink if there is one. * to the current URL deeplink if there is one.
*/ */
function start() { function start() {
loaded = true; ready = true;
// Make sure we've got all the DOM elements we need // Make sure we've got all the DOM elements we need
setupDOM(); setupDOM();
@ -972,9 +863,8 @@ export default function( revealElement, options ) {
if( typeof options === 'object' ) extend( config, options ); if( typeof options === 'object' ) extend( config, options );
// Abort if reveal.js hasn't finished loading, config // Abort if reveal.js hasn't finished loading, config
// changes will be applied automatically once loading // changes will be applied automatically once ready
// finishes if( Reveal.isReady() === false ) return;
if( loaded === false ) return;
const numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; const numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
@ -1239,53 +1129,6 @@ export default function( revealElement, options ) {
} }
/**
* Registers a new plugin with this reveal.js instance.
*
* reveal.js waits for all regisered plugins to initialize
* before considering itself ready, as long as the plugin
* is registered before calling `Reveal.initialize()`.
*/
function registerPlugin( id, plugin ) {
if( plugins[id] === undefined ) {
plugins[id] = plugin;
// If a plugin is registered after reveal.js is loaded,
// initialize it right away
if( loaded && typeof plugin.init === 'function' ) {
plugin.init();
}
}
else {
console.warn( 'reveal.js: "'+ id +'" plugin has already been registered' );
}
}
/**
* Checks if a specific plugin has been registered.
*
* @param {String} id Unique plugin identifier
*/
function hasPlugin( id ) {
return !!plugins[id];
}
/**
* Returns the specific plugin instance, if a plugin
* with the given ID has been registered.
*
* @param {String} id Unique plugin identifier
*/
function getPlugin( id ) {
return plugins[id];
}
/** /**
* Add a custom key binding with optional description to * Add a custom key binding with optional description to
* be added to the help screen. * be added to the help screen.
@ -5630,6 +5473,7 @@ export default function( revealElement, options ) {
Reveal = { Reveal = {
VERSION: VERSION, VERSION: VERSION,
initialize,
configure, configure,
sync, sync,
@ -5743,9 +5587,12 @@ export default function( revealElement, options ) {
removeKeyBinding, removeKeyBinding,
// API for registering and retrieving plugins // API for registering and retrieving plugins
registerPlugin, registerPlugin: (...args) => plugins.registerPlugin( ...args ),
hasPlugin, hasPlugin: (...args) => plugins.hasPlugin( ...args ),
getPlugin, getPlugin: (...args) => plugins.getPlugin( ...args ),
// Returns a hash with all registered plugins
getPlugins: () => plugins.getRegisteredPlugins(),
getComputedSlideSize, getComputedSlideSize,
@ -5782,9 +5629,6 @@ export default function( revealElement, options ) {
// Returns the top-level DOM element // Returns the top-level DOM element
getRevealElement: () => dom.wrapper || document.querySelector( '.reveal' ), getRevealElement: () => dom.wrapper || document.querySelector( '.reveal' ),
// Returns a hash with all registered plugins
getPlugins: () => plugins,
// Returns true if we're currently on the first slide // Returns true if we're currently on the first slide
isFirstSlide: () => indexh === 0 && indexv === 0, isFirstSlide: () => indexh === 0 && indexv === 0,
@ -5817,7 +5661,7 @@ export default function( revealElement, options ) {
}, },
// Checks if reveal.js has been loaded and is ready for use // Checks if reveal.js has been loaded and is ready for use
isReady: () => loaded, isReady: () => ready,
// Forward event binding to the reveal DOM element // Forward event binding to the reveal DOM element
addEventListener: ( type, listener, useCapture ) => { addEventListener: ( type, listener, useCapture ) => {
@ -5834,6 +5678,6 @@ export default function( revealElement, options ) {
registerKeyboardShortcut: ( key, value ) => keyboardShortcuts[key] = value registerKeyboardShortcut: ( key, value ) => keyboardShortcuts[key] = value
}; };
return init(); return Reveal;
}; };