Files
packager/index.html
T
2020-10-10 17:16:37 -05:00

377 lines
11 KiB
HTML

<!--
Copyright (c) 2020 Thomas Weber
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
background-color: #eee;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 400;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
margin: 0.5em 0 1em 0;
}
body.loaded .show-loading {
display: none;
}
body:not(.loaded) .hide-loading {
display: none;
}
.card {
max-width: 500px;
margin: auto;
border-radius: 0.3em;
background-color: #fff;
padding: 1em;
margin-top: 1em;
margin-bottom: 1.2em;
box-shadow: 0 0 0.5em 0.1em rgba(0, 0, 0, 0.4);
}
.center {
text-align: center;
}
textarea {
width: 100%;
height: 100px;
}
@media (prefers-color-scheme: dark) {
body {
background: #111;
color: #ddd;
}
a {
color: #4af;
}
.card {
background-color: rgb(29, 29, 29);
}
}
</style>
</head>
<body>
<section class="card">
<h1>The new packager</h1>
<p>This is the new packager for forkphorus & TurboWarp. p3 is a temporary name until something better can be thought of.</p>
</section>
<section class="show-loading card center">
<i>Loading...</i>
</section>
<section class="hide-loading card">
<h2>Select project</h2>
<div>
<label>
<input name="project.source" type="radio" value="id" checked>
Project ID
</label>
<label>
<input name="project.source" type="radio" value="file">
Project File
</label>
</div>
<div if="O.project.source === 'id'">
<label>
Project ID or URL:
<input name="project.id" type="text">
</label>
</div>
<div if="O.project.source === 'file'">
<label>
Project File:
<input name="project.file" type="file" accept=".sb,.sb2,.sb3">
</label>
</div>
</section>
<section class="hide-loading card">
<h2>Environment</h2>
<div>
<label>
Runtime
<select name="environment.runtime">
<option value="turbowarp">TurboWarp</option>
<option value="forkphorus">forkphorus</option>
</select>
</label>
</div>
<div>
<label>
Target Environment
<select name="environment.target">
<option value="html">Plain HTML (Standalone)</option>
<option value="zip">Zip</option>
<option value="nwjs-win64">NW.js (Windows x64)</option>
<option value="nwjs-mac">NW.js (Mac)</option>
</select>
</label>
</div>
</section>
<section if="O.environment.runtime === 'turbowarp'" class="hide-loading card">
<h2>TurboWarp options</h2>
<p>TurboWarp currently does not support any options.</p>
</section>
<section if="O.environment.runtime === 'forkphorus'" class="hide-loading card">
<h2>forkphorus options</h2>
<div>
<label>
Turbo Mode
<input name="forkphorus.playerOptions.turbo" type="checkbox">
</label>
</div>
<div>
<label>
Show Controls
<input name="forkphorus.showControls" type="checkbox" checked>
</label>
</div>
<div>
<label>
Username
<input name="forkphorus.playerOptions.username" type="text">
</label>
</div>
<div>
<label>
Autoplay
<select name="forkphorus.playerOptions.autoplayPolicy">
<option value="always" selected>Always</option>
<option value="never">Never</option>
<option value="if-audio-playable">If audio can be played</option>
</select>
</label>
</div>
<div>
<label>
Framerate
<input name="forkphorus.playerOptions.framerate" value="30" type="number">
</label>
</div>
<div>
<label>
Loading Screen Text
<input name="forkphorus.loadingScreenText" value="forkphorus">
</label>
</div>
<div>
<label>
Cloud Variables
<select name="forkphorus.playerOptions.cloudVariables">
<option value="off">Off</option>
<option value="ws" disabled>Connect to forkphorus clouddata server</option>
<option value="localStorage" selected>Store locally</option>
</select>
</label>
</div>
<div>
<label>
Sprite Fencing
<input name="forkphorus.playerOptions.spriteFencing" type="checkbox">
</label>
</div>
</section>
<section if="O.environment.target.startsWith('nw')" class="hide-loading card">
<h2>NW.js Options</h2>
<div>
<label>
Icon
<input name="nwjs.icon" type="file" accept=".png">
</label>
</div>
<div>
<label>
package.json
<br>
<textarea name="nwjs.manifest">{
"name": "Name of the project",
"main": "project.html",
"window": {
"title": "The title of the window",
"width": 480,
"height": 360,
"resizable": true,
"icon": "icon.png"
}
}</textarea>
</label>
</div>
</section>
<section class="hide-loading card">
<button id="package">Package</button>
</section>
<section id="downloads" class="hide-loading card center">
<i>Downloads will appear here.</i>
</section>
<script src="lib/jszip.min.js"></script>
<script src="lib/icns.js"></script>
<script src="downloader.js"></script>
<script src="parseoptions.js"></script>
<script src="packager.js"></script>
<script>
(function() {
const conditionals = document.querySelectorAll('[if]');
for (const el of conditionals) {
el.visibleIf = new Function('return ' + el.getAttribute('if') + ';');
}
const updateConditionals = () => {
for (const el of conditionals) {
el.hidden = !el.visibleIf();
}
};
O.onchange = updateConditionals;
updateConditionals();
const progressTarget = new EventTarget();
progressTarget.addEventListener('data-progress', (e) => console.log(e.detail));
// progressTarget.addEventListener('asset-fetch', (e) => console.log(e.detail));
// progressTarget.addEventListener('asset-fetched', (e) => console.log(e.detail));
progressTarget.addEventListener('nwjs-progress', (e) => console.log(e.detail));
const loadProject = async () => {
if (O.project.source === 'file') {
if (!O.project.file) {
throw new Error('No file selected');
}
// File is a Blob
return new Packager.Project(O.project.file, await SBDownloader.identifyProjectType(O.project.file));
} else if (O.project.source === 'id') {
const match = O.project.id.match(/\d+/);
if (!match) {
throw new Error('Invalid project ID');
}
const project = await SBDownloader.download(match[0], {
progressTarget
});
return new Packager.Project(await project.asBlob(), project.type);
}
throw new Error('unknown project source');
};
const getRuntime = async (projectType) => {
switch (O.environment.runtime) {
case 'turbowarp': return new Packager.runtimes.TurboWarp();
case 'forkphorus': return new Packager.runtimes.Forkphorus({
playerOptions: {
...O.forkphorus.playerOptions,
fullscreenMode: 'window',
fullscreenPadding: 0,
},
controlsOptions: O.forkphorus.showControls ? { enableFullscreen: false } : null,
loadingScreenText: O.forkphorus.loadingScreenText,
projectType: projectType,
});
}
throw new Error('unknown environment');
};
const getDefaultIcon = () => fetch('default-icon.png')
.then((r) => r.blob());
const getEnvironment = async () => {
switch (O.environment.target) {
case 'html': return new Packager.environments.HTML();
case 'zip': return new Packager.environments.Zip();
case 'nwjs-win64':
case 'nwjs-mac':
return new Packager.environments.NWjs({
icon: O.nwjs.icon || await getDefaultIcon(),
manifest: O.nwjs.manifest,
platform: O.environment.target.substr('nwjs-'.length),
});
}
throw new Error('unknown environment');
};
const addDownloadLink = (blob, fileName) => {
const link = document.createElement('a');
const size = blob.size / 1024 / 1024;
link.href = URL.createObjectURL(blob);
link.textContent = `Download ${fileName} (${size.toFixed(2)} MiB)`;
link.download = fileName;
document.querySelector('#downloads').appendChild(link);
link.click();
};
const removeDownloadsLinks = () => {
const el = document.querySelector('#downloads');
let link = el.firstChild;
while (link) {
const href = link.href;
// Revoke the URLs so that the browser knows it can delete them as our blobs can be large.
if (href) {
URL.revokeObjectURL(href);
}
el.removeChild(link);
link = el.firstChild;
}
};
document.getElementById('package').addEventListener('click', async function() {
try {
removeDownloadsLinks();
const runtime = await getRuntime();
const environment = await getEnvironment();
const projectData = await loadProject();
const result = await environment.package(runtime, projectData, progressTarget);
if (!(result.data instanceof Blob)) {
result.data = new Blob([result.data]);
}
addDownloadLink(result.data, result.filename);
} catch (e) {
console.error(e);
alert(e);
}
});
document.body.classList.add('loaded');
}());
</script>
</body>
</html>