2020-11-12 22:32:53 +00:00
|
|
|
import { RuffleObject } from "./ruffle-object";
|
|
|
|
import { RuffleEmbed } from "./ruffle-embed";
|
|
|
|
import { install_plugin, FLASH_PLUGIN } from "./plugin-polyfill";
|
|
|
|
import { public_path } from "./public-path";
|
2020-11-17 21:48:34 +00:00
|
|
|
import { Config } from "./config";
|
2020-11-12 22:32:53 +00:00
|
|
|
|
2020-04-02 14:08:33 +00:00
|
|
|
if (!window.RufflePlayer) {
|
2020-05-12 22:24:41 +00:00
|
|
|
window.RufflePlayer = {};
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
let topLevelRuffleConfig: Config;
|
|
|
|
let ruffleScriptSrc = public_path({}, "ruffle.js");
|
2020-04-02 14:08:33 +00:00
|
|
|
if (window.RufflePlayer.config) {
|
2020-11-17 21:55:29 +00:00
|
|
|
topLevelRuffleConfig = window.RufflePlayer.config;
|
|
|
|
ruffleScriptSrc = public_path(window.RufflePlayer.config, "ruffle.js");
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
/* public_path returns the directory where the file is, *
|
|
|
|
* so we need to append the filename. We don't need to *
|
|
|
|
* worry about the directory not having a slash because *
|
|
|
|
* public_path appends a slash. */
|
2020-11-17 21:55:29 +00:00
|
|
|
ruffleScriptSrc += "ruffle.js";
|
2020-03-25 00:16:40 +00:00
|
|
|
|
2019-08-22 01:02:43 +00:00
|
|
|
/**
|
2019-10-16 02:02:03 +00:00
|
|
|
* Polyfill native elements with Ruffle equivalents.
|
2020-05-12 22:24:41 +00:00
|
|
|
*
|
2019-10-16 02:02:03 +00:00
|
|
|
* This polyfill isn't fool-proof: If there's a chance site JavaScript has
|
|
|
|
* access to a pre-polyfill element, then this will break horribly. We can
|
2019-08-22 01:02:43 +00:00
|
|
|
* keep native objects out of the DOM, and thus out of JavaScript's grubby
|
|
|
|
* little hands, but only if we load first.
|
|
|
|
*/
|
2020-11-12 22:32:53 +00:00
|
|
|
let objects: HTMLCollectionOf<HTMLElement>;
|
|
|
|
let embeds: HTMLCollectionOf<HTMLElement>;
|
2020-11-17 21:55:29 +00:00
|
|
|
function replaceFlashInstances(): void {
|
2019-08-25 20:04:40 +00:00
|
|
|
try {
|
2020-03-25 00:16:40 +00:00
|
|
|
// Create live collections to track embed tags.
|
|
|
|
objects = objects || document.getElementsByTagName("object");
|
|
|
|
embeds = embeds || document.getElementsByTagName("embed");
|
2019-08-22 01:02:43 +00:00
|
|
|
|
2020-03-25 00:16:40 +00:00
|
|
|
// Replace <object> first, because <object> often wraps <embed>.
|
2020-11-12 22:32:53 +00:00
|
|
|
for (const elem of Array.from(objects)) {
|
2020-11-16 23:03:23 +00:00
|
|
|
if (RuffleObject.isInterdictable(elem)) {
|
2020-11-17 21:55:29 +00:00
|
|
|
const ruffleObject = RuffleObject.fromNativeObjectElement(elem);
|
|
|
|
elem.replaceWith(ruffleObject);
|
2020-03-25 00:16:40 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-12 22:32:53 +00:00
|
|
|
for (const elem of Array.from(embeds)) {
|
2020-11-17 19:53:08 +00:00
|
|
|
if (RuffleEmbed.isInterdictable(elem)) {
|
2020-11-17 21:55:29 +00:00
|
|
|
const ruffleObject = RuffleEmbed.fromNativeEmbedElement(elem);
|
|
|
|
elem.replaceWith(ruffleObject);
|
2019-08-25 20:04:40 +00:00
|
|
|
}
|
2019-08-22 01:02:43 +00:00
|
|
|
}
|
2019-08-25 20:04:40 +00:00
|
|
|
} catch (err) {
|
2020-05-12 22:24:41 +00:00
|
|
|
console.error(
|
|
|
|
"Serious error encountered when polyfilling native Flash elements: " +
|
|
|
|
err
|
|
|
|
);
|
2019-08-22 01:02:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function polyfillStaticContent(): void {
|
|
|
|
replaceFlashInstances();
|
2019-09-05 02:11:01 +00:00
|
|
|
}
|
2019-08-22 01:02:43 +00:00
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function polyfillDynamicContent(): void {
|
2020-03-25 00:16:40 +00:00
|
|
|
// Listen for changes to the DOM. If nodes are added, re-check for any Flash instances.
|
2020-05-12 21:36:18 +00:00
|
|
|
const observer = new MutationObserver(function (mutationsList) {
|
2020-03-25 00:16:40 +00:00
|
|
|
// If any nodes were added, re-run the polyfill to replace any new instances.
|
2020-11-12 22:32:53 +00:00
|
|
|
const nodesAdded = mutationsList.some(
|
2020-05-12 22:24:41 +00:00
|
|
|
(mutation) => mutation.addedNodes.length > 0
|
|
|
|
);
|
2020-03-25 00:16:40 +00:00
|
|
|
if (nodesAdded) {
|
2020-11-17 21:55:29 +00:00
|
|
|
replaceFlashInstances();
|
2019-08-22 01:02:43 +00:00
|
|
|
}
|
2019-09-05 02:11:01 +00:00
|
|
|
});
|
2019-08-22 01:02:43 +00:00
|
|
|
|
2020-05-12 22:24:41 +00:00
|
|
|
observer.observe(document, { childList: true, subtree: true });
|
2019-09-05 02:11:01 +00:00
|
|
|
}
|
2019-08-25 21:40:40 +00:00
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function loadRufflePlayerIntoFrame(event: Event): void {
|
2020-11-12 22:32:53 +00:00
|
|
|
const currentTarget = event.currentTarget;
|
|
|
|
if (currentTarget != null && "contentWindow" in currentTarget) {
|
|
|
|
loadFrame(currentTarget["contentWindow"]);
|
|
|
|
}
|
2020-05-23 17:26:40 +00:00
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
function loadFrame(currentFrame: Window): void {
|
|
|
|
let frameDocument;
|
2020-04-02 14:08:33 +00:00
|
|
|
try {
|
2020-11-17 21:55:29 +00:00
|
|
|
frameDocument = currentFrame.document;
|
|
|
|
if (!frameDocument) {
|
2020-04-02 14:08:33 +00:00
|
|
|
console.log("Frame has no document.");
|
|
|
|
return;
|
|
|
|
}
|
2020-05-12 22:24:41 +00:00
|
|
|
} catch (e) {
|
2020-04-02 14:08:33 +00:00
|
|
|
console.log("Error Getting Frame: " + e.message);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
if (!currentFrame.RufflePlayer) {
|
2020-04-02 14:08:33 +00:00
|
|
|
/* Make sure we populate the frame's window.RufflePlayer.config */
|
2020-11-17 21:55:29 +00:00
|
|
|
currentFrame.RufflePlayer = {};
|
|
|
|
currentFrame.RufflePlayer.config = topLevelRuffleConfig;
|
|
|
|
const script = frameDocument.createElement("script");
|
|
|
|
script.src = ruffleScriptSrc; /* Load this script(ruffle.js) into the frame */
|
|
|
|
frameDocument.body.appendChild(script);
|
2020-05-12 22:24:41 +00:00
|
|
|
} else {
|
2020-04-02 14:08:33 +00:00
|
|
|
console.log("(i)frame already has RufflePlayer");
|
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
polyfillFramesCommon(currentFrame);
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-12 22:32:53 +00:00
|
|
|
function handleFrames(
|
|
|
|
frameList: HTMLCollectionOf<HTMLIFrameElement | HTMLFrameElement>
|
2020-11-17 21:48:34 +00:00
|
|
|
): void {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2020-11-12 22:32:53 +00:00
|
|
|
let originalOnLoad: ((ev: Event) => any) | null;
|
2020-05-23 17:26:40 +00:00
|
|
|
for (let i = 0; i < frameList.length; i++) {
|
2020-11-17 21:55:29 +00:00
|
|
|
const currentFrame = frameList[i];
|
2020-05-23 17:26:40 +00:00
|
|
|
/* Apparently, using addEventListener attaches the event *
|
2020-04-02 14:08:33 +00:00
|
|
|
* to the dummy document, which is overwritten when the *
|
|
|
|
* iframe is loaded, so we do this. It can only works if *
|
|
|
|
* it's attached to the frame object itself, which is why *
|
|
|
|
* we're using *
|
|
|
|
* depth.document.getElementsByTagName("iframe") instead *
|
|
|
|
* of depth.frames to get the iframes at the depth. *
|
|
|
|
* Also, this way we should be able to handle frame *
|
|
|
|
* frame navigation, which is good. */
|
2020-05-23 17:26:40 +00:00
|
|
|
setTimeout(function () {
|
2020-09-08 14:19:55 +00:00
|
|
|
try {
|
|
|
|
if (
|
2020-11-17 21:55:29 +00:00
|
|
|
currentFrame.contentDocument &&
|
|
|
|
currentFrame.contentDocument.readyState &&
|
|
|
|
currentFrame.contentDocument.readyState == "complete" &&
|
|
|
|
currentFrame.contentWindow
|
2020-09-08 14:19:55 +00:00
|
|
|
) {
|
2020-11-17 21:55:29 +00:00
|
|
|
loadFrame(currentFrame.contentWindow);
|
2020-09-08 14:19:55 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log(
|
|
|
|
"error loading ruffle player into frame: " + e.message
|
|
|
|
);
|
2020-05-23 17:26:40 +00:00
|
|
|
}
|
|
|
|
}, 500);
|
2020-09-08 14:19:55 +00:00
|
|
|
try {
|
2020-11-17 21:55:29 +00:00
|
|
|
if ((originalOnLoad = currentFrame.onload)) {
|
|
|
|
currentFrame.onload = function (event) {
|
2020-11-12 22:32:53 +00:00
|
|
|
if (originalOnLoad != null) {
|
|
|
|
try {
|
|
|
|
originalOnLoad(event);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(
|
|
|
|
"Error calling original onload: " + e.message
|
|
|
|
);
|
|
|
|
}
|
2020-09-08 14:19:55 +00:00
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
loadRufflePlayerIntoFrame(event);
|
2020-09-08 14:19:55 +00:00
|
|
|
};
|
|
|
|
} else {
|
2020-11-17 21:55:29 +00:00
|
|
|
currentFrame.onload = loadRufflePlayerIntoFrame;
|
2020-09-08 14:19:55 +00:00
|
|
|
}
|
2020-11-17 21:55:29 +00:00
|
|
|
const depth = currentFrame.contentWindow;
|
2020-11-12 22:32:53 +00:00
|
|
|
if (depth != null) {
|
2020-11-17 21:55:29 +00:00
|
|
|
polyfillFramesCommon(depth);
|
2020-11-12 22:32:53 +00:00
|
|
|
}
|
2020-09-08 14:19:55 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log("error loading ruffle player into frame: " + e.message);
|
2020-05-23 17:26:40 +00:00
|
|
|
}
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function polyfillFramesCommon(depth: Window): void {
|
2020-05-23 17:26:40 +00:00
|
|
|
handleFrames(depth.document.getElementsByTagName("iframe"));
|
|
|
|
handleFrames(depth.document.getElementsByTagName("frame"));
|
|
|
|
}
|
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function polyfillStaticFrames(): void {
|
|
|
|
polyfillFramesCommon(window);
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function runFrameListener(mutationsList: MutationRecord[]): void {
|
2020-04-02 14:08:33 +00:00
|
|
|
/* Basically the same as the listener for dynamic embeds. */
|
2020-11-12 22:32:53 +00:00
|
|
|
const nodesAdded = mutationsList.some(
|
2020-05-12 22:24:41 +00:00
|
|
|
(mutation) => mutation.addedNodes.length > 0
|
|
|
|
);
|
2020-04-02 14:08:33 +00:00
|
|
|
if (nodesAdded) {
|
2020-11-17 21:55:29 +00:00
|
|
|
polyfillFramesCommon(window);
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 21:55:29 +00:00
|
|
|
function polyfillDynamicFrames(): void {
|
|
|
|
const observer = new MutationObserver(runFrameListener);
|
2020-05-12 22:24:41 +00:00
|
|
|
observer.observe(document, { childList: true, subtree: true });
|
2020-04-02 14:08:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 21:48:34 +00:00
|
|
|
/**
|
|
|
|
* Polyfills the detection of flash plugins in the browser.
|
|
|
|
*/
|
2020-11-17 21:53:04 +00:00
|
|
|
export function pluginPolyfill(): void {
|
2020-08-12 02:22:06 +00:00
|
|
|
install_plugin(FLASH_PLUGIN);
|
2020-11-12 22:32:53 +00:00
|
|
|
}
|
2019-10-13 02:57:03 +00:00
|
|
|
|
2020-11-17 21:48:34 +00:00
|
|
|
/**
|
|
|
|
* Polyfills legacy flash content on the page.
|
|
|
|
*/
|
|
|
|
export function polyfill(): void {
|
2020-11-17 21:55:29 +00:00
|
|
|
polyfillStaticContent();
|
|
|
|
polyfillDynamicContent();
|
|
|
|
polyfillStaticFrames();
|
|
|
|
polyfillDynamicFrames();
|
2020-11-12 22:32:53 +00:00
|
|
|
}
|