1
0
Fork 0
why-cant-we-deploy-today/js/controllers/plugins.js

220 lines
5.7 KiB
JavaScript

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 = [];
}
}