App import/export from the editor.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4786 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2024-01-21 23:56:36 +00:00
parent ab519342e8
commit ad2b49b838
4 changed files with 97 additions and 0 deletions

View File

@ -1029,6 +1029,97 @@ function removeFile() {
} }
} }
async function appExport() {
let JsZip = (await import('/static/jszip.min.js')).default;
let owner = window.location.pathname.split('/')[1].replace('~', '');
let name = window.location.pathname.split('/')[2];
let zip = new JsZip();
zip.file(`${name}.json`, JSON.stringify({
type: "tildefriends-app",
emoji: gApp.emoji || '📦',
}));
for (let file of Object.keys(gFiles)) {
zip.file(`${name}/${file}`, gFiles[file].buffer ?? gFiles[file].doc.doc.toString());
}
let content = await zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
});
let a = document.createElement('a');
a.href = URL.createObjectURL(content);
a.download = `${owner}_${name}.zip`;
a.click();
}
async function save_file_to_blob_id(name, file) {
console.log(`Saving ${name}.`);
let response = await fetch('/save', {
method: 'POST',
headers: {
'Content-Type': 'application/binary',
},
body: file,
});
if (!response.ok) {
throw new Error('Saving "' + name + '": ' + response.status + ' ' + response.statusText);
}
let blob_id = await response.text();
if (blob_id.charAt(0) == '/') {
blob_id = blob_id.substr(1);
}
return blob_id;
}
async function appImport() {
let JsZip = (await import('/static/jszip.min.js')).default;
let input = document.createElement('input');
input.type = 'file';
input.click();
input.onchange = async function() {
try {
for (let file of input.files) {
if (file.type != 'application/zip') {
console.log('This does not look like a .zip.');
continue;
}
let buffer = await file.arrayBuffer();
let zip = new JsZip();
await zip.loadAsync(buffer);
let app_object;
let app_name;
for (let [name, object] of Object.entries(zip.files)) {
if (name.endsWith('.json') && name.indexOf('/') == -1) {
try {
let parsed = JSON.parse(await object.async('text'));
if (parsed.type == 'tildefriends-app') {
app_object = parsed;
app_name = name.substring(0, name.length - '.json'.length);
break;
}
} catch (e) {
console.log(e);
}
}
}
if (app_object) {
app_object.files = {};
for (let [name, object] of Object.entries(zip.files)) {
if (!name.startsWith(app_name + '/') || name.endsWith('/')) {
continue;
}
app_object.files[name.substring(app_name.length + '/'.length)] = await save_file_to_blob_id(name, await object.async('arrayBuffer'));
}
let path = '/' + await save_file_to_blob_id(`${app_name}.json`, JSON.stringify(app_object)) + '/';
console.log('Redirecting to:', path);
window.location.pathname = path;
}
}
} catch (e) {
alert(e.toString());
}
}
}
window.addEventListener("load", function() { window.addEventListener("load", function() {
window.addEventListener("hashchange", hashChange); window.addEventListener("hashchange", hashChange);
window.addEventListener("focus", focus); window.addEventListener("focus", focus);
@ -1040,6 +1131,8 @@ window.addEventListener("load", function() {
document.getElementById('save').addEventListener('click', () => save()); document.getElementById('save').addEventListener('click', () => save());
document.getElementById('icon').addEventListener('click', () => changeIcon()); document.getElementById('icon').addEventListener('click', () => changeIcon());
document.getElementById('delete').addEventListener('click', () => deleteApp()); document.getElementById('delete').addEventListener('click', () => deleteApp());
document.getElementById('export').addEventListener('click', () => appExport());
document.getElementById('import').addEventListener('click', () => appImport());
document.getElementById('trace_button').addEventListener('click', function(event) { document.getElementById('trace_button').addEventListener('click', function(event) {
event.preventDefault(); event.preventDefault();
trace(); trace();

View File

@ -35,6 +35,7 @@ let k_static_files = [
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'}, {uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
{uri: '/client.js', type: 'text/javascript; charset=UTF-8'}, {uri: '/client.js', type: 'text/javascript; charset=UTF-8'},
{uri: '/favicon.png', type: 'image/png'}, {uri: '/favicon.png', type: 'image/png'},
{uri: '/jszip.min.js', type: 'text/javascript; charset=UTF-8'},
{uri: '/robots.txt', type: 'text/plain; charset=UTF-8'}, {uri: '/robots.txt', type: 'text/plain; charset=UTF-8'},
{uri: '/style.css', type: 'text/css; charset=UTF-8'}, {uri: '/style.css', type: 'text/css; charset=UTF-8'},
{uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}}, {uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}},

View File

@ -22,6 +22,8 @@
<button class="w3-bar-item w3-button w3-blue" id="closeEditor" name="closeEditor" accesskey="c" onmouseover="set_access_key_title(event)" data-tip="Close the editor">Close</button> <button class="w3-bar-item w3-button w3-blue" id="closeEditor" name="closeEditor" accesskey="c" onmouseover="set_access_key_title(event)" data-tip="Close the editor">Close</button>
<button class="w3-bar-item w3-button w3-blue" id="save" name="save" accesskey="s" onmouseover="set_access_key_title(event)" data-tip="Save the app under the given path">Save</button> <button class="w3-bar-item w3-button w3-blue" id="save" name="save" accesskey="s" onmouseover="set_access_key_title(event)" data-tip="Save the app under the given path">Save</button>
<button class="w3-bar-item w3-button w3-blue" id="icon" name="icon" accesskey="i" onmouseover="set_access_key_title(event)" data-tip="Set an icon/emoji for the app">📦</button> <button class="w3-bar-item w3-button w3-blue" id="icon" name="icon" accesskey="i" onmouseover="set_access_key_title(event)" data-tip="Set an icon/emoji for the app">📦</button>
<button class="w3-bar-item w3-button w3-blue" id="export" name="export" accesskey="e" onmouseover="set_access_key_title(event)" data-tip="Export app to .zip file">Export</button>
<button class="w3-bar-item w3-button w3-blue" id="import" name="import" accesskey="i" onmouseover="set_access_key_title(event)" data-tip="Import app from .zip file">Import</button>
<input class="w3-bar-item w3-input w3-border w3-blue" type="text" id="name" name="name" style="flex: 1 1; min-width: 1em"></input> <input class="w3-bar-item w3-input w3-border w3-blue" type="text" id="name" name="name" style="flex: 1 1; min-width: 1em"></input>
<button class="w3-bar-item w3-button w3-blue" id="delete" name="delete" accesskey="d" onmouseover="set_access_key_title(event)" data-tip="Delete the app">Delete</button> <button class="w3-bar-item w3-button w3-blue" id="delete" name="delete" accesskey="d" onmouseover="set_access_key_title(event)" data-tip="Delete the app">Delete</button>
<button class="w3-bar-item w3-button w3-blue" id="trace_button" accesskey="t" onmouseover="set_access_key_title(event)" data-tip="Open a performance trace for the server">Trace</button> <button class="w3-bar-item w3-button w3-blue" id="trace_button" accesskey="t" onmouseover="set_access_key_title(event)" data-tip="Open a performance trace for the server">Trace</button>

1
core/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long