import { Version } from "./version.js"; /** * Represents the Ruffle public API. * * The public API exists primarily to allow multiple installs of Ruffle on a * page (e.g. an extension install and a local one) to cooperate. In an ideal * situation, all Ruffle sources on the page install themselves into a single * public API, and then the public API picks the newest version by default. * * This API *is* versioned, in case we need to upgrade it. However, it must be * backwards- and forwards-compatible with all known sources. */ export class PublicAPI { /** * Construct the Ruffle public API. * * Do not use this function to negotiate a public API. Instead, use * `public_api` to register your Ruffle source with an existing public API * if it exists. * * @param {object} prev What used to be in the public API slot. * * This is used to upgrade from a prior version of the public API, or from * a user-defined configuration object placed in the public API slot. */ constructor(prev) { this.sources = {}; this.config = {}; this.invoked = false; this.newest_name = false; if (prev !== undefined) { if (prev.constructor.name === PublicAPI.name) { /// We're upgrading from a previous API to a new one. this.sources = prev.sources; this.config = prev.config; this.invoked = prev.invoked; this.conflict = prev.conflict; this.newest_name = prev.newest_name; prev.superceded(); } else if (prev.constructor === Object && prev.interdictions !== undefined) { /// We're the first, install user configuration this.config = prev; } else { /// We're the first, but conflicting with someone else. this.conflict = prev; } } window.addEventListener("DOMContentLoaded", this.init.bind(this)); } /** * The version of the public API. * * This allows a page with an old version of the Public API to be upgraded * to a new version of the API. The public API is intended to be changed * very infrequently, if at all, but this provides an escape mechanism for * newer Ruffle sources to upgrade older installations. */ get version() { return "0.1.0"; } /** * Register a given source with the Ruffle Public API. * * @param {string} name The name of the source. * @param {object} api The public API object. This must conform to the shape * of `SourceAPI`. */ register_source(name, api) { this.sources[name] = api; } /** * Determine the name of the newest registered source in the Public API. * * @returns {(string|bool)} The name of the source, or `false` if no source * has yet to be registered. */ newest_source_name() { let newest_name = false, newest_version = Version.from_semver("0.0.0"); for (let k in this.sources) { if (this.sources.hasOwnProperty(k)) { let k_version = Version.from_semver(this.sources[k].version); if (k_version.has_precedence_over(newest_version)) { newest_name = k; newest_version = k_version; } } } return newest_name; } /** * Negotiate and start Ruffle. */ init() { if (!this.invoked) { this.invoked = true; this.newest_name = this.newest_source_name(); if (this.newest_name === false) { throw new Error("No registered Ruffle source!"); } let interdictions = this.config.interdictions; if (interdictions === undefined) { interdictions = ["plugin-detect", "static-content"]; } this.sources[this.newest_name].init(interdictions); } } /** * Indicates that this version of the public API has been superceded by a * newer version. * * This should only be called by a newer version of the Public API. * Identical versions of the Public API should not supercede older versions * of that same API. */ superceded() { this.invoked = true; } /** * Join a source into the public API, if it doesn't already exist. * * @param {*} prev_ruffle The previous iteration of the Ruffle API. * * The `prev_ruffle` param lists the previous object in the RufflePlayer * slot. We perform some checks to see if this is a Ruffle public API or a * conflicting object. If this is conflicting, then a new public API will * be constructed (see the constructor information for what happens to * `prev_ruffle`). * * Note that Public API upgrades are deliberately not enabled in this * version of Ruffle, since there is no Public API to upgrade from. * * @param {string|undefined} source_name The name of this particular * Ruffle source. * * @param {object|undefined} source_api The Ruffle source to add. * * If both parameters are provided they will be used to define a new Ruffle * source to register with the public API. * * @returns {object} The Ruffle Public API. */ static negotiate(prev_ruffle, source_name, source_api) { let public_api; if (prev_ruffle !== undefined && prev_ruffle.constructor.name == PublicAPI.name) { public_api = prev_ruffle; } else { public_api = new PublicAPI(prev_ruffle); } if (source_name !== undefined && source_api !== undefined) { public_api.register_source(source_name, source_api); } return public_api; }; }