From 4bb095e81f655eecd8356e55e67a734a35ab7dd8 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Thu, 17 Feb 2022 02:29:04 +0000 Subject: [PATCH] Got rid of all of the XMLHttpRequests in favor of fetch(). git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3842 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/client.js | 374 +++++++++++++++++++------------------------------ 1 file changed, 143 insertions(+), 231 deletions(-) diff --git a/core/client.js b/core/client.js index a668a4e3..db87512e 100644 --- a/core/client.js +++ b/core/client.js @@ -106,15 +106,21 @@ function edit() { {tagName: "script", attributes: {src: "/static/codemirror/xml.min.js"}}, {tagName: "script", attributes: {src: "/static/codemirror/htmlmixed.min.js"}}, ], function() { - load(); + load().catch(function(error) { + alert(error); + closeEditor(); + }); }); } function trace() { - var request = new XMLHttpRequest(); - request.addEventListener("loadend", function() { - if (request.status == 200) { - /* The trace is loaded. */ + fetch('/trace') + .then(function(response) { + if (!response.ok) { + throw new Error('Request failed: ' + response.status + ' ' + response.statusText); + } + return response.arrayBuffer(); + }).then(function(data) { var perfetto = window.open('/perfetto/'); var done = false; if (perfetto) { @@ -122,7 +128,7 @@ function trace() { if (message.data == 'PONG') { perfetto.postMessage({ perfetto: { - buffer: request.response, + buffer: data, title: 'Tilde Friends Trace', url: window.location.href, } @@ -143,22 +149,9 @@ function trace() { } else { alert("Unable to open perfetto."); } - } else { - alert("Failed to load trace: " + request.status + "."); - } - }); - request.addEventListener("error", function() { - alert("Error loading trace."); - }); - request.addEventListener("timeout", function() { - alert("Timed out loading trace."); - }); - request.addEventListener("abort", function() { - alert("Loading trace aborted."); - }); - request.responseType = 'arraybuffer'; - request.open("GET", "/trace"); - request.send(); + }).catch(function(error) { + alert('Failed to load trace: ' + error); + }); } function stats() { @@ -180,113 +173,65 @@ function guessMode(name) { } function loadFile(name, id) { - return new Promise(function(resolve, reject) { - var request = new XMLHttpRequest(); - request.addEventListener("loadend", function() { - if (request.status == 200) { - gFiles[name].doc = new CodeMirror.Doc(request.responseText, guessMode(name)); - if (!Object.values(gFiles).some(x => !x.doc)) { - document.getElementById("editPane").style.display = 'flex'; - openFile(Object.keys(gFiles).sort()[0]); - resolve(); - } - } else { - reject('Error loading source.'); - } - }); - request.addEventListener("error", function() { - alert("Error loading source."); - closeEditor(); - reject('Error loading source.'); - }); - request.addEventListener("timeout", function() { - alert("Timed out loading source."); - closeEditor(); - reject('Timed out loading source.'); - }); - request.addEventListener("abort", function() { - alert("Loading source aborted."); - closeEditor(); - reject('Loading source aborted.'); - }); - request.open("GET", "/" + id + "/view"); - request.send(); + return fetch('/' + id + '/view').then(function(response) { + if (!response.ok) { + throw new Error('Request failed: ' + response.status + ' ' + response.statusText); + } + return response.text(); + }).then(function(text) { + gFiles[name].doc = new CodeMirror.Doc(text, guessMode(name)); + if (!Object.values(gFiles).some(x => !x.doc)) { + document.getElementById("editPane").style.display = 'flex'; + openFile(Object.keys(gFiles).sort()[0]); + } }); } function load(path) { - return new Promise(function(resolve, reject) { - var request = new XMLHttpRequest(); - request.addEventListener("loadend", function() { - if (request.status == 200 || request.status == 404) { - if (!gEditor) { - gEditor = CodeMirror.fromTextArea(document.getElementById("editor"), { - 'theme': 'base16-dark', - 'lineNumbers': true, - 'tabSize': 4, - 'indentUnit': 4, - 'indentWithTabs': true, - 'showTrailingSpace': true, - }); - gEditor.on('changes', function() { - updateFiles(); - }); - } - gFiles = {}; - var text; - var isApp = false; - var promises = []; - if (request.status == 200) { - text = request.responseText; - try { - var json = JSON.parse(text); - if (json && json['type'] == 'tildefriends-app') { - isApp = true; - Object.keys(json['files']).forEach(function(name) { - gFiles[name] = {}; - promises.push(loadFile(name, json['files'][name])); - }); - if (Object.keys(json['files']).length == 0) { - document.getElementById("editPane").style.display = 'flex'; - } - gApp = JSON.parse(text); - } - } catch { - } - } else { - reject('Load failed.'); - } - if (!isApp) { - document.getElementById("editPane").style.display = 'flex'; - if (!text) { - text = '// New script.\n'; - } - gCurrentFile = 'app.js'; - gFiles[gCurrentFile] = { - doc: new CodeMirror.Doc(text, guessMode(gCurrentFile)), - }; - openFile(gCurrentFile); - } - Promise.all(promises).then(resolve).catch(reject); + return fetch((path || url()) + 'view').then(function(response) { + if (!response.ok) { + throw new Error(response.status + ' ' + response.statusText); + } + return response.json(); + }).then(function(json) { + if (!gEditor) { + gEditor = CodeMirror.fromTextArea(document.getElementById("editor"), { + 'theme': 'base16-dark', + 'lineNumbers': true, + 'tabSize': 4, + 'indentUnit': 4, + 'indentWithTabs': true, + 'showTrailingSpace': true, + }); + gEditor.on('changes', function() { + updateFiles(); + }); + } + gFiles = {}; + var isApp = false; + var promises = []; + + if (json && json['type'] == 'tildefriends-app') { + isApp = true; + Object.keys(json['files']).forEach(function(name) { + gFiles[name] = {}; + promises.push(loadFile(name, json['files'][name])); + }); + if (Object.keys(json['files']).length == 0) { + document.getElementById("editPane").style.display = 'flex'; } - }); - request.addEventListener("error", function() { - alert("Error loading source."); - closeEditor(); - reject('Error loading source.'); - }); - request.addEventListener("timeout", function() { - alert("Timed out loading source."); - closeEditor(); - reject('Timed out loading source.'); - }); - request.addEventListener("abort", function() { - alert("Loading source aborted."); - closeEditor(); - reject('Loading source aborted.'); - }); - request.open("GET", (path || url()) + "view"); - request.send(); + gApp = json; + } + if (!isApp) { + document.getElementById("editPane").style.display = 'flex'; + var text = '// New script.\n'; + gCurrentFile = 'app.js'; + gFiles[gCurrentFile] = { + doc: new CodeMirror.Doc(text, guessMode(gCurrentFile)), + }; + openFile(gCurrentFile); + } + return Promise.all(promises); }); } @@ -314,18 +259,8 @@ function save(save_to) { gFiles[gCurrentFile].doc = gEditor.getDoc(); } - var appFinished = function(success) { - document.getElementById("save").disabled = false; - document.getElementById("push_to_parent").disabled = false; - document.getElementById("pull_from_parent").disabled = false; - Object.values(gFiles).forEach(function(file) { - file.generation = file.doc.changeGeneration(); - }); - updateFiles(); - } - var save_path = save_to; - if (!save_to) { + if (!save_path) { var name = document.getElementById("name"); if (name && name.value) { save_path = name.value; @@ -334,102 +269,75 @@ function save(save_to) { } } - var always = function() { - var anyUnfinished = Object.values(gFiles).some(x => x.request); - var anyUnsaved = Object.values(gFiles).some(x => !x.doc.isClean(x.generation) && !x.id); - - if (!anyUnfinished && !anyUnsaved) { - var app = { - type: "tildefriends-app", - files: Object.fromEntries(Object.keys(gFiles).map(x => [x, gFiles[x].id || gApp.files[x]])), - }; - Object.values(gFiles).forEach(function(file) { delete file.id; }); - gApp = JSON.parse(JSON.stringify(app)); - - var request = new XMLHttpRequest(); - request.addEventListener("error", function() { - alert("Error saving: " + request.responseText); - appFinished(false); - }); - request.addEventListener("loadend", function() { - if (request.status == 200) { - if (save_path != window.location.pathname) { - alert('Saved to ' + save_path + '.'); - } else { - reconnect(save_path); - } - appFinished(true); - } else { - alert("Unable to save: " + request.responseText); - appFinished(false); - } - }); - request.addEventListener("timeout", function() { - alert("Timed out saving: " + request.responseText); - appFinished(false); - }); - request.addEventListener("abort", function() { - alert("Save aborted: " + request.responseText); - appFinished(false); - }); - - request.open("POST", save_path + 'save', true); - request.setRequestHeader("Content-Type", "text/json"); - request.send(JSON.stringify(app)); - } else if (!anyUnfinished) { - appFinished(false); - } - }; - - var anySkipped = false; - Object.values(gFiles).forEach(function(file) { + var promises = []; + for (let name of Object.keys(gFiles)) { + let file = gFiles[name]; if (file.doc.isClean(file.generation)) { - anySkipped = true; - return; + continue; } delete file.id; - file.request = new XMLHttpRequest(); - file.request.addEventListener("error", function() { - alert("Error saving: " + file.request.responseText); - file.request = null; - always(); - }); - file.request.addEventListener("loadend", function() { - if (file.request.status == 200) { - file.id = file.request.responseText; - if (file.id.charAt(0) == '/') { - file.id = file.id.substr(1); - } - } else { - alert("Unable to save: " + file.request.responseText); + promises.push(fetch('/save', { + method: 'POST', + headers: { + 'Content-Type': 'text/plain', + }, + body: file.doc.getValue(), + }).then(function(response) { + if (!response.ok) { + throw new Error('Saving "' + name + '": ' + response.status + ' ' + response.statusText); } - file.request = null; - always(); - }); - file.request.addEventListener("timeout", function() { - alert("Timed out saving: " + file.request.responseText); - file.request = null; - always(); - }); - file.request.addEventListener("abort", function() { - alert("Save aborted: " + file.request.responseText); - file.request = null; - always(); - }); - - file.request.open("POST", "/save", true); - file.request.setRequestHeader("Content-Type", "text/plain"); - file.request.send(file.doc.getValue()); - }); - - if (anySkipped) { - always(); + return response.text(); + }).then(function(text) { + file.id = text; + if (file.id.charAt(0) == '/') { + file.id = file.id.substr(1); + } + })); } + + return Promise.all(promises).then(function() { + var app = { + type: "tildefriends-app", + files: Object.fromEntries(Object.keys(gFiles).map(x => [x, gFiles[x].id || gApp.files[x]])), + }; + Object.values(gFiles).forEach(function(file) { delete file.id; }); + gApp = JSON.parse(JSON.stringify(app)); + + return fetch(save_path + 'save', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(app), + }).then(function(response) { + if (!response.ok) { + throw new Error(response.status + ' ' + response.statusText); + } + + if (save_path != window.location.pathname) { + alert('Saved to ' + save_path + '.'); + } else { + reconnect(save_path); + } + }); + }).catch(function(error) { + alert(error); + }).finally(function() { + document.getElementById("save").disabled = false; + document.getElementById("push_to_parent").disabled = false; + document.getElementById("pull_from_parent").disabled = false; + Object.values(gFiles).forEach(function(file) { + file.generation = file.doc.changeGeneration(); + }); + updateFiles(); + }); } function pullFromParent() { - load(gParentApp ? gParentApp.path : null).then(x => save()); + load(gParentApp ? gParentApp.path : null).then(x => save()).catch(function(error) { + alert(error) + }); } function pushToParent() { @@ -687,17 +595,21 @@ function message(event) { } else if (event.data && event.data.action == "setHash") { window.location.hash = event.data.hash; } else if (event.data && event.data.action == 'storeBlob') { - var request = new XMLHttpRequest(); - request.addEventListener("loadend", function() { - if (request.status == 200) { - var iframe = document.getElementById("document"); - iframe.contentWindow.postMessage({'storeBlobComplete': {name: event.data.blob.name, path: request.responseText, type: event.data.blob.type}}, '*'); + fetch('/save', { + method: 'POST', + headers: { + 'Content-Type': 'application/binary', + }, + body: event.data.blob.buffer, + }).then(function(response) { + if (!response.ok) { + throw new Error(response.status + ' ' + response.statusText); } + return response.text(); + }).then(function(text) { + var iframe = document.getElementById("document"); + iframe.contentWindow.postMessage({'storeBlobComplete': {name: event.data.blob.name, path: text, type: event.data.blob.type}}, '*'); }); - request.open('POST', "/save", true); - request.setRequestHeader("Content-Type", "application/binary"); - console.log('storing', event.data.blob.buffer); - request.send(event.data.blob.buffer); } else { send({event: "message", message: event.data}); }