Files
packager/index.html
T

538 lines
15 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>
<meta charset="utf-8">
<title>TurboWarp Packager</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 !important;
}
.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 2px 0.5em 0.1em rgba(0, 0, 0, 0.2);
}
.center {
text-align: center;
}
textarea {
width: 100%;
height: 120px;
box-sizing: border-box;
resize: none;
}
#progress-container {
margin: auto;
position: relative;
width: 250px;
height: 7px;
border: 1px solid currentColor;
}
#progress-bar {
position: absolute;
background-color: currentColor;
top: 0;
left: 0;
height: 100%;
}
body:not(.running) #progress-container {
display: none;
}
#status, #download {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
text-align: center;
}
#status-text {
font-style: italic;
}
footer {
text-align: center;
}
[hidden] {
display: none !important;
}
@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>TurboWarp Packager</h1>
<p>Note - This is in a very early stage of development. Use with care.</p>
<p>This packager no longer supports forkphorus. If you'd like to package forkphorus, use the <a href="https://forkphorus.github.io/packager/">forkphorus packager</a>.</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'" class="fold">
<label>
Project ID or URL:
<input name="project.id" type="text">
</label>
</div>
<div if="O.project.source === 'file'" class="fold">
<label>
Project File:
<input name="project.file" type="file" accept=".sb,.sb2,.sb3">
</label>
</div>
</section>
<section class="hide-loading card">
<h2>Runtime</h2>
<div>
<label>
Turbo Mode
<input name="turbowarp.turbo" type="checkbox">
</label>
</div>
<div>
<label>
Framerate
<input name="turbowarp.framerate" type="number" value="30">
</label>
</div>
<div>
<label>
Interpolation
<input name="turbowarp.interpolation" type="checkbox">
</label>
</div>
<div>
<label>
High quality pen
<input name="turbowarp.highQualityPen" type="checkbox">
</label>
</div>
<div>
<label>
Username (will generate random if left empty)
<input name="turbowarp.username" type="text" value="">
</label>
</div>
<div>
Stage size
<input name="turbowarp.stageSize.width" type="number" value="480">
&times;
<input name="turbowarp.stageSize.height" type="number" value="360">
</div>
<div>
<label>
Autoplay
<input name="turbowarp.autoplay" type="checkbox">
</label>
</div>
</section>
<section class="hide-loading card">
<h2>Appearance</h2>
<div>
<label>
No controls
<input name="turbowarp.noControls" type="checkbox">
</label>
</div>
</section>
<section class="hide-loading card">
<h2>Compiler</h2>
<p>You probably don't want to change these.</p>
<div>
<label>
Enable compiler
<input name="turbowarp.compilerOptions.enabled" type="checkbox" checked>
</label>
</div>
<div>
<label>
Warp timer
<input name="turbowarp.compilerOptions.warpTimer" type="checkbox" checked>
</label>
</div>
</section>
<section class="hide-loading card">
<h2>Cloud variables</h2>
<p>TurboWarp will automatically connect to its cloud variable server if packaging a project from an ID. More options will be added later.</p>
</section>
<section class="hide-loading card">
<h2>Environment</h2>
<div>
<label>
Environment
<select name="environment.name">
<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>
<div if="O.environment.name === 'html'" class="fold">
<h2>Plain HTML Options</h2>
<p>No options.</p>
</div>
<div if="O.environment.name === 'zip'" class="fold">
<h2>Zip Options</h2>
<p>No options.</p>
</div>
<div if="O.environment.name.startsWith('nw')" class="fold">
<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>
</div>
</section>
<section class="hide-loading card">
<button id="package">Package</button>
<button id="preview">Preview</button>
<button id="reset">Reset</button>
</section>
<section id="download" if="O.showDownload" class="hide-loading card">
<!-- Download links will be inserted here -->
</section>
<section id="status" if="!O.showDownload" class="hide-loading card">
<div>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<div id="status-text">Status updates will appear here.</div>
</div>
</section>
<footer>
<div>
<a href="https://github.com/TurboWarp/packager">Source Code</a> -
<a href="credits.html">Credits</a>
</div>
</footer>
<script src="lib/jszip.min.js"></script>
<script src="lib/icns.js"></script>
<script src="conditionals.js"></script>
<script src="downloader.js"></script>
<script src="parseoptions.js"></script>
<script src="packager.js"></script>
<script>
(function() {
document.body.classList.add('loaded');
O.onchange = () => {
Conditionals.update();
};
Conditionals.setup();
let totalAssets = 0;
let finishedAssets = 0;
const progressTarget = new EventTarget();
progressTarget.addEventListener('data-progress', (e) => {
totalAssets = 0;
finishedAssets = 0;
setProgress('Loading project data', e.detail);
});
progressTarget.addEventListener('asset-fetch', (e) => {
totalAssets++;
setProgress('Loading assets', finishedAssets / totalAssets);
});
progressTarget.addEventListener('asset-fetched', (e) => {
finishedAssets++;
setProgress('Loading assets', finishedAssets / totalAssets);
});
progressTarget.addEventListener('nwjs-progress', (e) => setProgress('Loading NW.js', e.detail));
progressTarget.addEventListener('turbowarp-progress', (e) => setProgress('Loading TurboWarp', e.detail));
const statusText = document.querySelector('#status-text');
statusText.initialText = statusText.textContent;
const downloadContainer = document.querySelector('#download');
const progressBar = document.querySelector('#progress-bar');
const packagerOptions = {
preview: false
};
const getProjectId = () => {
if (O.project.source !== 'id') {
return '';
}
const match = O.project.id.match(/\d+/);
if (!match) {
return '';
}
return match[0];
};
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 projectId = getProjectId();
if (!projectId) {
throw new Error('No project selected');
}
const project = await SBDownloader.download(projectId, {
progressTarget
});
return new Packager.Project(await project.asBlob(), project.type);
}
throw new Error('unknown project source');
};
const getRuntime = async (projectType) => {
return new Packager.runtimes.TurboWarp({
projectId: getProjectId(),
...O.turbowarp
});
};
const getDefaultIcon = () => fetch('default-icon.png')
.then((r) => r.blob());
const getEnvironment = async () => {
// In preview mode, the only possible format is standalone HTML
if (packagerOptions.preview) {
return new Packager.environments.HTML();
}
switch (O.environment.name) {
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.name.substr('nwjs-'.length),
});
}
throw new Error('unknown environment');
};
const addDownloadLink = (blob, fileName) => {
O.showDownload = true;
const size = blob.size / 1024 / 1024;
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.textContent = `Download ${fileName} (${size.toFixed(2)} MiB)`;
downloadContainer.appendChild(downloadLink);
downloadLink.download = fileName;
// In preview mode, we open the file in a new window.
if (packagerOptions.preview) {
window.open(url);
} else {
downloadLink.click();
}
Conditionals.update();
};
const resetDownloads = () => {
O.showDownload = false;
let link = downloadContainer.firstChild;
while (link) {
if (link.href) {
URL.revokeObjectURL(link.href);
}
downloadContainer.removeChild(link);
link = downloadContainer.firstChild;
}
Conditionals.update();
};
const setProgress = (status, progress) => {
if (status !== setProgress.lastStatus) {
statusText.textContent = status;
setProgress.lastStatus = status;
}
if (progress < 0) progress = 0;
if (progress > 1) progress = 1;
progressBar.style.width = `${progress * 100}%`;
};
setProgress.lastStatus = statusText.initialText;
const resetProgress = () => {
setProgress(statusText.initialText, 0);
};
let isRunning = false;
const setIsRunning = _isRunning => {
isRunning = _isRunning;
if (isRunning) {
document.body.classList.add('running');
} else {
document.body.classList.remove('running');
}
};
const package = async () => {
try {
setIsRunning(true);
resetDownloads();
setProgress('Loading', 0);
const runtime = await getRuntime();
const environment = await getEnvironment();
const projectData = await loadProject();
environment.setRuntime(runtime);
environment.setProgressTarget(progressTarget);
environment.setProjectData(projectData);
const result = await environment.package();
if (!(result.data instanceof Blob)) {
result.data = new Blob([result.data], {
type: 'text/html'
});
}
addDownloadLink(result.data, result.filename);
} catch (e) {
console.error(e);
alert(e);
} finally {
resetProgress();
setIsRunning(false);
}
};
document.getElementById('package').addEventListener('click', () => {
if (isRunning) return;
packagerOptions.preview = false;
package();
});
document.getElementById('preview').addEventListener('click', () => {
if (isRunning) return;
packagerOptions.preview = true;
package();
});
document.getElementById('reset').addEventListener('click', () => {
if (confirm('Reset all preferences and reload?')) {
try {
localStorage.clear();
} catch (e) {
// ignore
}
location.reload(true);
}
})
}());
</script>
</body>
</html>