extension: Make the player page a local Ruffle demo
This commit is contained in:
parent
a11725c27c
commit
e7df6d890e
|
@ -540,7 +540,7 @@ export interface DataLoadOptions extends BaseLoadOptions {
|
|||
/**
|
||||
* The data to load a movie from.
|
||||
*/
|
||||
data: Iterable<number>;
|
||||
data: ArrayLike<number> | ArrayBufferLike;
|
||||
|
||||
/**
|
||||
* The filename of the SWF movie to provide to ActionScript.
|
||||
|
|
|
@ -150,7 +150,7 @@ export class RufflePlayer extends HTMLElement {
|
|||
private contextMenuSupported = false;
|
||||
|
||||
// The effective config loaded upon `.load()`.
|
||||
private loadedConfig?: URLLoadOptions | DataLoadOptions;
|
||||
public loadedConfig?: URLLoadOptions | DataLoadOptions;
|
||||
|
||||
private swfUrl?: URL;
|
||||
private instance: Ruffle | null;
|
||||
|
|
|
@ -77,6 +77,9 @@
|
|||
"action_reload": {
|
||||
"message": "Reload tab to apply changes"
|
||||
},
|
||||
"open_player_page": {
|
||||
"message": "Open SWF Player"
|
||||
},
|
||||
"open_settings_page": {
|
||||
"message": "Open Settings Page"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,199 @@
|
|||
:root {
|
||||
--ruffle-blue: #37528c;
|
||||
--ruffle-orange: #ffad33;
|
||||
--splash-screen-background: #31497d;
|
||||
}
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Lato, sans-serif;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
background: black;
|
||||
}
|
||||
|
||||
#main {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
border: 8px dashed var(--ruffle-orange);
|
||||
border-radius: 30px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in;
|
||||
margin: 10px 5px;
|
||||
}
|
||||
|
||||
#overlay.drag {
|
||||
opacity: 1;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
#player {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#nav {
|
||||
width: 100%;
|
||||
background: var(--ruffle-blue);
|
||||
box-shadow: 0 3px 6px 5px var(--ruffle-blue);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
color: white;
|
||||
padding: 10px 0 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#title {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
#title:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#title img {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
#file-picker select,
|
||||
#file-picker input,
|
||||
#author {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#local-file-container,
|
||||
#sample-swfs-container {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#local-file {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#local-file-label {
|
||||
color: var(--ruffle-blue);
|
||||
padding: 3px 10px;
|
||||
margin: 5px 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 50px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#local-file-name {
|
||||
min-width: 150px;
|
||||
display: inline-block;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
#sample-swfs {
|
||||
background-color: white;
|
||||
color: var(--ruffle-blue);
|
||||
border: 1px solid white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#author-container {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#author {
|
||||
color: var(--ruffle-orange);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: var(--ruffle-blue);
|
||||
margin: 15vh auto;
|
||||
padding: 20px;
|
||||
border: 2px solid white;
|
||||
width: 300px;
|
||||
height: 270px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#open-modal,
|
||||
#reload-swf {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#metadata {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#metadata td {
|
||||
padding: 2px 1px;
|
||||
border: 1px solid #ddd;
|
||||
color: var(--ruffle-orange);
|
||||
}
|
||||
|
||||
#metadata tr td:nth-child(1) {
|
||||
font-weight: bold;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media only screen and (width <= 800px) {
|
||||
#local-file-container,
|
||||
#sample-swfs-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#local-file-container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (width <= 600px) {
|
||||
#local-file-static-label,
|
||||
#sample-swfs-label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#author-container {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#nav {
|
||||
flex-flow: column;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,86 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Ruffle</title>
|
||||
<title>Ruffle Player</title>
|
||||
<link rel="icon" type="image/png" href="images/icon32.png" />
|
||||
<link rel="stylesheet" href="css/player.css" />
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<div id="nav">
|
||||
<div id="title">
|
||||
<a href="https://ruffle.rs/" target="_blank">
|
||||
<img
|
||||
src="images/logo.svg"
|
||||
alt="Ruffle"
|
||||
data-canonical-src="https://ruffle.rs/assets/logo.svg" />
|
||||
</a>
|
||||
</div>
|
||||
<div id="file-picker">
|
||||
<div id="local-file-container">
|
||||
<span id="local-file-static-label">Local SWF:</span>
|
||||
<input type="file" accept=".swf,.spl" id="local-file"
|
||||
aria-describedby="local-file-static-label" />
|
||||
<label for="local-file" id="local-file-label">Select File</label>
|
||||
<span id="local-file-name">No file selected.</span>
|
||||
</div>
|
||||
<div id="sample-swfs-container">
|
||||
<label for="webURL">Web URL: </label>
|
||||
<input id="webURL" name="webURL" type="text" placeholder="URL of a .swf file on the web">
|
||||
<button type="submit" id="webFormSubmit">Load</button>
|
||||
</div>
|
||||
 
|
||||
<svg width="20px" id="open-modal" viewBox="0 0 416.979 416.979"><path fill="white" d="M356.004 61.156c-81.37-81.47-213.377-81.551-294.848-.182-81.47 81.371-81.552 213.379-.181 294.85 81.369 81.47 213.378 81.551 294.849.181 81.469-81.369 81.551-213.379.18-294.849zM237.6 340.786a5.821 5.821 0 0 1-5.822 5.822h-46.576a5.821 5.821 0 0 1-5.822-5.822V167.885a5.821 5.821 0 0 1 5.822-5.822h46.576a5.82 5.82 0 0 1 5.822 5.822v172.901zm-29.11-202.885c-18.618 0-33.766-15.146-33.766-33.765 0-18.617 15.147-33.766 33.766-33.766s33.766 15.148 33.766 33.766c0 18.619-15.149 33.765-33.766 33.765z"/></g></svg>
|
||||
 
|
||||
<svg id="reload-swf" width="20px" viewBox="0 0 489.711 489.711"><path fill="white" d="M112.156 97.111c72.3-65.4 180.5-66.4 253.8-6.7l-58.1 2.2c-7.5.3-13.3 6.5-13 14 .3 7.3 6.3 13 13.5 13h.5l89.2-3.3c7.3-.3 13-6.2 13-13.5v-1.6l-3.3-88.2c-.3-7.5-6.6-13.3-14-13-7.5.3-13.3 6.5-13 14l2.1 55.3c-36.3-29.7-81-46.9-128.8-49.3-59.2-3-116.1 17.3-160 57.1-60.4 54.7-86 137.9-66.8 217.1 1.5 6.2 7 10.3 13.1 10.3 1.1 0 2.1-.1 3.2-.4 7.2-1.8 11.7-9.1 9.9-16.3-16.8-69.6 5.6-142.7 58.7-190.7zm350.3 98.4c-1.8-7.2-9.1-11.7-16.3-9.9-7.2 1.8-11.7 9.1-9.9 16.3 16.9 69.6-5.6 142.7-58.7 190.7-37.3 33.7-84.1 50.3-130.7 50.3-44.5 0-88.9-15.1-124.7-44.9l58.8-5.3c7.4-.7 12.9-7.2 12.2-14.7s-7.2-12.9-14.7-12.2l-88.9 8c-7.4.7-12.9 7.2-12.2 14.7l8 88.9c.6 7 6.5 12.3 13.4 12.3.4 0 .8 0 1.2-.1 7.4-.7 12.9-7.2 12.2-14.7l-4.8-54.1c36.3 29.4 80.8 46.5 128.3 48.9 3.8.2 7.6.3 11.3.3 55.1 0 107.5-20.2 148.7-57.4 60.4-54.7 86-137.8 66.8-217.1z"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main" aria-label="Select a demo or drag an SWF">
|
||||
<div id="overlay" class="hidden"></div>
|
||||
</div>
|
||||
<div id="metadata-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" id="close-modal">×</span>
|
||||
<table id="metadata">
|
||||
<tr>
|
||||
<td>Uncompressed Length</td>
|
||||
<td><span class="metadata" id="uncompressedLength">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SWF Version</td>
|
||||
<td><span class="metadata" id="swfVersion">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FP Version</td>
|
||||
<td><span class="metadata" id="flashVersion">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ActionScript 3</td>
|
||||
<td><span class="metadata" id="isActionScript3">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Frames</td>
|
||||
<td><span class="metadata" id="numFrames">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Frame Rate</td>
|
||||
<td><span class="metadata" id="frameRate">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SWF Width</td>
|
||||
<td><span class="metadata" id="width">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SWF Height</td>
|
||||
<td><span class="metadata" id="height">Loading</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SWF Background Color</td>
|
||||
<td><input class="metadata" type="color" id="backgroundColor" disabled value="#FFFFFF"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="dist/player.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
</div>
|
||||
<div id="version-text">Ruffle extension</div>
|
||||
<button id="options-button">Settings</button>
|
||||
<button id="player-button">Open SWF Player</button>
|
||||
<button id="reload-button" disabled>Reload</button>
|
||||
<script src="dist/popup.js"></script>
|
||||
</body>
|
||||
|
|
|
@ -1,19 +1,230 @@
|
|||
import * as utils from "./utils";
|
||||
import { PublicAPI } from "ruffle-core";
|
||||
import type { Letterbox } from "ruffle-core";
|
||||
import type {
|
||||
Letterbox,
|
||||
RufflePlayer,
|
||||
DataLoadOptions,
|
||||
URLLoadOptions,
|
||||
} from "ruffle-core";
|
||||
|
||||
const api = PublicAPI.negotiate(window.RufflePlayer!, "local");
|
||||
window.RufflePlayer = api;
|
||||
const ruffle = api.newest()!;
|
||||
let player: RufflePlayer;
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
const url = new URL(window.location.href);
|
||||
// Hash always starts with #, gotta slice that off
|
||||
const swfUrl = url.hash.length > 1 ? url.hash.slice(1) : null;
|
||||
if (!swfUrl) {
|
||||
const main = document.getElementById("main")!;
|
||||
const overlay = document.getElementById("overlay")!;
|
||||
const localFileInput = document.getElementById(
|
||||
"local-file",
|
||||
)! as HTMLInputElement;
|
||||
const localFileName = document.getElementById("local-file-name")!;
|
||||
const closeModal = document.getElementById("close-modal")!;
|
||||
const openModal = document.getElementById("open-modal")!;
|
||||
const reloadSwf = document.getElementById("reload-swf")!;
|
||||
const metadataModal = document.getElementById("metadata-modal")!;
|
||||
|
||||
// Default config used by the player.
|
||||
const defaultConfig = {
|
||||
letterbox: "on" as Letterbox,
|
||||
forceScale: true,
|
||||
forceAlign: true,
|
||||
};
|
||||
|
||||
const swfToFlashVersion: { [key: number]: string } = {
|
||||
1: "1",
|
||||
2: "2",
|
||||
3: "3",
|
||||
4: "4",
|
||||
5: "5",
|
||||
6: "6",
|
||||
7: "7",
|
||||
8: "8",
|
||||
9: "9.0",
|
||||
10: "10.0/10.1",
|
||||
11: "10.2",
|
||||
12: "10.3",
|
||||
13: "11.0",
|
||||
14: "11.1",
|
||||
15: "11.2",
|
||||
16: "11.3",
|
||||
17: "11.4",
|
||||
18: "11.5",
|
||||
19: "11.6",
|
||||
20: "11.7",
|
||||
21: "11.8",
|
||||
22: "11.9",
|
||||
23: "12",
|
||||
24: "13",
|
||||
25: "14",
|
||||
26: "15",
|
||||
27: "16",
|
||||
28: "17",
|
||||
29: "18",
|
||||
30: "19",
|
||||
31: "20",
|
||||
32: "21",
|
||||
33: "22",
|
||||
34: "23",
|
||||
35: "24",
|
||||
36: "25",
|
||||
37: "26",
|
||||
38: "27",
|
||||
39: "28",
|
||||
40: "29",
|
||||
41: "30",
|
||||
42: "31",
|
||||
43: "32",
|
||||
};
|
||||
|
||||
function unload() {
|
||||
if (player) {
|
||||
player.remove();
|
||||
document.querySelectorAll("span.metadata").forEach((el) => {
|
||||
el.textContent = "Loading";
|
||||
});
|
||||
(
|
||||
document.getElementById("backgroundColor")! as HTMLInputElement
|
||||
).value = "#FFFFFF";
|
||||
}
|
||||
}
|
||||
|
||||
function load(options: string | DataLoadOptions | URLLoadOptions) {
|
||||
unload();
|
||||
player = ruffle.createPlayer();
|
||||
player.id = "player";
|
||||
main.append(player);
|
||||
player.load(options);
|
||||
player.addEventListener("loadedmetadata", function () {
|
||||
if (player.metadata) {
|
||||
for (const [key, value] of Object.entries(player.metadata)) {
|
||||
const metadataElement = document.getElementById(key);
|
||||
if (metadataElement) {
|
||||
switch (key) {
|
||||
case "backgroundColor":
|
||||
(metadataElement as HTMLInputElement).value =
|
||||
value ?? "#FFFFFF";
|
||||
break;
|
||||
case "uncompressedLength":
|
||||
metadataElement.textContent = `${value >> 10}Kb`;
|
||||
break;
|
||||
// @ts-expect-error This intentionally falls through to the default case
|
||||
case "swfVersion":
|
||||
document.getElementById(
|
||||
"flashVersion",
|
||||
)!.textContent = swfToFlashVersion[value] ?? null;
|
||||
// falls through and executes the default case as well
|
||||
default:
|
||||
metadataElement.textContent = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadFile(file: File | undefined) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (file.name) {
|
||||
localFileName.textContent = file.name;
|
||||
}
|
||||
const data = await new Response(file).arrayBuffer();
|
||||
load({ data: data, swfFileName: file.name, ...defaultConfig });
|
||||
history.pushState("", document.title, window.location.pathname);
|
||||
}
|
||||
|
||||
localFileInput.addEventListener("change", (event) => {
|
||||
const eventTarget = event.target as HTMLInputElement;
|
||||
if (
|
||||
eventTarget?.files &&
|
||||
eventTarget?.files.length > 0 &&
|
||||
eventTarget.files[0]
|
||||
) {
|
||||
loadFile(eventTarget.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
main.addEventListener("dragenter", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
main.addEventListener("dragleave", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.remove("drag");
|
||||
});
|
||||
main.addEventListener("dragover", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.add("drag");
|
||||
});
|
||||
main.addEventListener("drop", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.remove("drag");
|
||||
if (event.dataTransfer) {
|
||||
localFileInput.files = event.dataTransfer.files;
|
||||
loadFile(event.dataTransfer.files[0]);
|
||||
}
|
||||
});
|
||||
localFileInput.addEventListener("dragleave", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.remove("drag");
|
||||
});
|
||||
localFileInput.addEventListener("dragover", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.add("drag");
|
||||
});
|
||||
localFileInput.addEventListener("drop", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
overlay.classList.remove("drag");
|
||||
if (event.dataTransfer) {
|
||||
localFileInput.files = event.dataTransfer.files;
|
||||
loadFile(event.dataTransfer.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
closeModal.addEventListener("click", () => {
|
||||
metadataModal.style.display = "none";
|
||||
});
|
||||
|
||||
openModal.addEventListener("click", () => {
|
||||
metadataModal.style.display = "block";
|
||||
});
|
||||
|
||||
reloadSwf.addEventListener("click", () => {
|
||||
if (player) {
|
||||
const confirmReload = confirm("Reload the current SWF?");
|
||||
if (confirmReload) {
|
||||
if (player.loadedConfig) {
|
||||
player.load(player.loadedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
if (
|
||||
navigator.userAgent.match(/iPad/i) ||
|
||||
navigator.userAgent.match(/iPhone/i)
|
||||
) {
|
||||
localFileInput.removeAttribute("accept");
|
||||
}
|
||||
overlay.classList.remove("hidden");
|
||||
});
|
||||
|
||||
window.onclick = (event) => {
|
||||
if (event.target === metadataModal) {
|
||||
metadataModal.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
async function loadSwf(swfUrl: string) {
|
||||
try {
|
||||
const pathname = new URL(swfUrl).pathname;
|
||||
document.title = pathname.substring(pathname.lastIndexOf("/") + 1);
|
||||
|
@ -21,19 +232,39 @@ window.addEventListener("DOMContentLoaded", async () => {
|
|||
// Ignore URL parsing errors.
|
||||
}
|
||||
|
||||
const player = ruffle.createPlayer();
|
||||
player.id = "player";
|
||||
document.getElementById("main")!.append(player);
|
||||
|
||||
const options = await utils.getExplicitOptions();
|
||||
|
||||
player.load({
|
||||
localFileName.textContent = document.title;
|
||||
localFileInput.value = "";
|
||||
load({
|
||||
...options,
|
||||
url: swfUrl,
|
||||
base: swfUrl.substring(0, swfUrl.lastIndexOf("/") + 1),
|
||||
// Override some default values when playing in the extension player page.
|
||||
letterbox: "on" as Letterbox,
|
||||
forceAlign: true,
|
||||
forceScale: true,
|
||||
...defaultConfig,
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("pageshow", async () => {
|
||||
const url = new URL(window.location.href);
|
||||
// Hash always starts with #, gotta slice that off
|
||||
const swfUrl = url.hash.length > 1 ? url.hash.slice(1) : null;
|
||||
if (swfUrl) {
|
||||
await loadSwf(swfUrl);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
const webFormSubmit = document.getElementById(
|
||||
"webFormSubmit",
|
||||
) as HTMLButtonElement;
|
||||
if (webFormSubmit) {
|
||||
webFormSubmit.addEventListener("click", function () {
|
||||
const webURL = document.getElementById(
|
||||
"webURL",
|
||||
) as HTMLInputElement;
|
||||
if ((webURL?.value || "") !== "") {
|
||||
loadSwf(webURL.value);
|
||||
window.location.href = "#" + webURL.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -170,6 +170,15 @@ window.addEventListener("DOMContentLoaded", () => {
|
|||
window.close();
|
||||
});
|
||||
|
||||
const playerButton = document.getElementById(
|
||||
"player-button",
|
||||
) as HTMLButtonElement;
|
||||
playerButton.textContent = utils.i18n.getMessage("open_player_page");
|
||||
playerButton.addEventListener("click", async () => {
|
||||
await utils.openPlayerPage();
|
||||
window.close();
|
||||
});
|
||||
|
||||
reloadButton = document.getElementById(
|
||||
"reload-button",
|
||||
) as HTMLButtonElement;
|
||||
|
|
|
@ -63,6 +63,7 @@ export let runtime: {
|
|||
};
|
||||
|
||||
export let openOptionsPage: () => Promise<void>;
|
||||
export let openPlayerPage: () => Promise<void>;
|
||||
|
||||
function promisify<T>(
|
||||
func: (callback: (result: T) => void) => void,
|
||||
|
@ -129,12 +130,20 @@ if (typeof chrome !== "undefined") {
|
|||
promisify((cb: () => void) =>
|
||||
chrome.tabs.create({ url: "/options.html" }, cb),
|
||||
);
|
||||
openPlayerPage = () =>
|
||||
promisify((_cb: () => void) =>
|
||||
chrome.tabs.create({ url: "/player.html" }),
|
||||
);
|
||||
} else if (typeof browser !== "undefined") {
|
||||
i18n = browser.i18n;
|
||||
storage = browser.storage;
|
||||
tabs = browser.tabs;
|
||||
runtime = browser.runtime;
|
||||
openOptionsPage = () => browser.runtime.openOptionsPage();
|
||||
openPlayerPage = () =>
|
||||
promisify((_cb: () => void) =>
|
||||
browser.tabs.create({ url: "/player.html" }),
|
||||
);
|
||||
} else {
|
||||
throw new Error("Extension API not found.");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue