From d283f7e661e14d6ae1881fe803e5b4f1ed0689ff Mon Sep 17 00:00:00 2001 From: David Thompson Date: Mon, 24 Jun 2024 13:49:08 -0400 Subject: Add 2024 Guix social talk. --- .../reveal.js/js/controllers/plugins.js | 254 +++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 2024-06-18-guix-social/reveal.js/js/controllers/plugins.js (limited to '2024-06-18-guix-social/reveal.js/js/controllers/plugins.js') diff --git a/2024-06-18-guix-social/reveal.js/js/controllers/plugins.js b/2024-06-18-guix-social/reveal.js/js/controllers/plugins.js new file mode 100644 index 0000000..88f57bf --- /dev/null +++ b/2024-06-18-guix-social/reveal.js/js/controllers/plugins.js @@ -0,0 +1,254 @@ +import { loadScript } from '../utils/loader.js' + +/** + * Manages loading and registering of reveal.js plugins. + */ +export default class Plugins { + + constructor( reveal ) { + + this.Reveal = reveal; + + // Flags our current state (idle -> loading -> loaded) + this.state = 'idle'; + + // An id:instance map of currently registered plugins + this.registeredPlugins = {}; + + this.asyncDependencies = []; + + } + + /** + * Loads reveal.js dependencies, registers and + * initializes plugins. + * + * Plugins are direct references to a reveal.js plugin + * object that we register and initialize after any + * synchronous dependencies have loaded. + * + * Dependencies are defined via the 'dependencies' config + * option and will be loaded prior to starting reveal.js. + * Some dependencies may have an 'async' flag, if so they + * will load after reveal.js has been started up. + */ + load( plugins, dependencies ) { + + this.state = 'loading'; + + plugins.forEach( this.registerPlugin.bind( this ) ); + + 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; + + const scriptLoadedCallback = (s) => { + if( s && typeof s.callback === 'function' ) s.callback(); + + if( --scriptsToLoad === 0 ) { + this.initPlugins().then( resolve ); + } + }; + + // Load synchronous scripts + scripts.forEach( s => { + if( typeof s.id === 'string' ) { + this.registerPlugin( s ); + scriptLoadedCallback( s ); + } + else if( typeof s.src === 'string' ) { + loadScript( s.src, () => scriptLoadedCallback(s) ); + } + else { + console.warn( 'Unrecognized plugin format', s ); + scriptLoadedCallback(); + } + } ); + } + else { + this.initPlugins().then( resolve ); + } + + } ); + + } + + /** + * Initializes our plugins and waits for them to be ready + * before proceeding. + */ + initPlugins() { + + return new Promise( resolve => { + + let pluginValues = Object.values( this.registeredPlugins ); + let pluginsToInitialize = pluginValues.length; + + // If there are no plugins, skip this step + if( pluginsToInitialize === 0 ) { + this.loadAsync().then( resolve ); + } + // ... otherwise initialize plugins + else { + + let initNextPlugin; + + let afterPlugInitialized = () => { + if( --pluginsToInitialize === 0 ) { + this.loadAsync().then( resolve ); + } + else { + initNextPlugin(); + } + }; + + let i = 0; + + // Initialize plugins serially + initNextPlugin = () => { + + let plugin = pluginValues[i++]; + + // If the plugin has an 'init' method, invoke it + if( typeof plugin.init === 'function' ) { + let promise = plugin.init( this.Reveal ); + + // If the plugin returned a Promise, wait for it + if( promise && typeof promise.then === 'function' ) { + promise.then( afterPlugInitialized ); + } + else { + afterPlugInitialized(); + } + } + else { + afterPlugInitialized(); + } + + } + + initNextPlugin(); + + } + + } ) + + } + + /** + * 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 registered plugins to initialize + * before considering itself ready, as long as the plugin + * is registered before calling `Reveal.initialize()`. + */ + registerPlugin( plugin ) { + + // Backwards compatibility to make reveal.js ~3.9.0 + // plugins work with reveal.js 4.0.0 + if( arguments.length === 2 && typeof arguments[0] === 'string' ) { + plugin = arguments[1]; + plugin.id = arguments[0]; + } + // Plugin can optionally be a function which we call + // to create an instance of the plugin + else if( typeof plugin === 'function' ) { + plugin = plugin(); + } + + let id = plugin.id; + + if( typeof id !== 'string' ) { + console.warn( 'Unrecognized plugin format; can\'t find plugin.id', plugin ); + } + else 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( this.Reveal ); + } + } + 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; + + } + + destroy() { + + Object.values( this.registeredPlugins ).forEach( plugin => { + if( typeof plugin.destroy === 'function' ) { + plugin.destroy(); + } + } ); + + this.registeredPlugins = {}; + this.asyncDependencies = []; + + } + +} -- cgit v1.2.3