forked from cory/tildefriends
Merge branches/quickjs to trunk. This is the way.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3621 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
781
core/client.js
781
core/client.js
@ -2,16 +2,10 @@
|
||||
|
||||
var gSocket;
|
||||
var gCredentials;
|
||||
var gErrorCount = 0;
|
||||
var gCommandHistory = [];
|
||||
var gSendKeyEvents = false;
|
||||
var gSendDeviceOrientationEvents = false;
|
||||
var gGeolocatorWatch;
|
||||
|
||||
var gCurrentFile;
|
||||
var gFiles = {};
|
||||
var gEditor;
|
||||
var gBackup;
|
||||
|
||||
var kMaxCommandHistory = 16;
|
||||
|
||||
var kErrorColor = "#dc322f";
|
||||
var kStatusColor = "#fff";
|
||||
@ -35,31 +29,6 @@ window.addEventListener("keydown", function(event) {
|
||||
}
|
||||
});
|
||||
|
||||
function keydown(event) {
|
||||
if (event.keyCode == 13) {
|
||||
gCommandHistory.push(document.getElementById("input").value);
|
||||
if (gCommandHistory.length > kMaxCommandHistory) {
|
||||
gCommandHistory.shift();
|
||||
}
|
||||
send();
|
||||
event.preventDefault();
|
||||
} else if (event.keyCode == 38 && !event.altKey) {
|
||||
if (gCommandHistory.length) {
|
||||
var input = document.getElementById("input");
|
||||
gCommandHistory.unshift(input.value);
|
||||
input.value = gCommandHistory.pop();
|
||||
event.preventDefault();
|
||||
}
|
||||
} else if (event.keyCode == 40 && !event.altKey) {
|
||||
if (gCommandHistory.length) {
|
||||
var input = document.getElementById("input");
|
||||
gCommandHistory.push(input.value);
|
||||
input.value = gCommandHistory.shift();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureLoaded(nodes, callback) {
|
||||
if (!nodes.length) {
|
||||
callback();
|
||||
@ -107,49 +76,42 @@ function edit() {
|
||||
}
|
||||
|
||||
ensureLoaded([
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/codemirror.min.js"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/theme/base16-dark.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/search/matchesonscrollbar.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/dialog/dialog.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/codemirror.min.css"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/edit/trailingspace.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/dialog/dialog.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/search/search.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/search/searchcursor.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/search/jump-to-line.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/search/matchesonscrollbar.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/addon/scroll/annotatescrollbar.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/mode/javascript/javascript.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/codemirror.min.js"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/theme/base16-dark.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/matchesonscrollbar.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/dialog/dialog.min.css"}},
|
||||
{tagName: "link", attributes: {rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/codemirror.min.css"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/edit/trailingspace.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/dialog/dialog.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/search.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/searchcursor.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/jump-to-line.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/search/matchesonscrollbar.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/addon/scroll/annotatescrollbar.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/javascript/javascript.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/css/css.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/xml/xml.min.js"}},
|
||||
{tagName: "script", attributes: {src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.57.0/mode/htmlmixed/htmlmixed.min.js"}},
|
||||
], function() {
|
||||
load();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function load() {
|
||||
function guessMode(name) {
|
||||
return name.endsWith(".js") ? "javascript" :
|
||||
name.endsWith(".html") ? "htmlmixed" :
|
||||
null;
|
||||
}
|
||||
|
||||
function loadFile(name, id) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.addEventListener("loadend", function() {
|
||||
if (request.status == 200 || request.status == 404) {
|
||||
document.getElementById("editPane").style.display = 'flex';
|
||||
if (!gEditor) {
|
||||
gEditor = CodeMirror.fromTextArea(document.getElementById("editor"), {
|
||||
'theme': 'base16-dark',
|
||||
'lineNumbers': true,
|
||||
'tabSize': 4,
|
||||
'indentUnit': 4,
|
||||
'indentWithTabs': true,
|
||||
'showTrailingSpace': true,
|
||||
});
|
||||
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]);
|
||||
}
|
||||
var text;
|
||||
if (request.status == 200) {
|
||||
text = request.responseText;
|
||||
} else {
|
||||
text = '// New script\nterminal.print("Hello, world!");\n';
|
||||
}
|
||||
gEditor.setValue(text);
|
||||
gEditor.focus();
|
||||
gBackup = text;
|
||||
}
|
||||
});
|
||||
request.addEventListener("error", function() {
|
||||
@ -164,7 +126,67 @@ function load() {
|
||||
alert("Loading source aborted.");
|
||||
closeEditor();
|
||||
});
|
||||
request.open("GET", url() + "/view");
|
||||
request.open("GET", "/" + id + "/view");
|
||||
request.send();
|
||||
}
|
||||
|
||||
function load() {
|
||||
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,
|
||||
});
|
||||
}
|
||||
gFiles = {};
|
||||
var text;
|
||||
var isApp = false;
|
||||
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] = {};
|
||||
loadFile(name, json['files'][name]);
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
request.addEventListener("error", function() {
|
||||
alert("Error loading source.");
|
||||
closeEditor();
|
||||
});
|
||||
request.addEventListener("timeout", function() {
|
||||
alert("Timed out loading source.");
|
||||
closeEditor();
|
||||
});
|
||||
request.addEventListener("abort", function() {
|
||||
alert("Loading source aborted.");
|
||||
closeEditor();
|
||||
});
|
||||
request.open("GET", url() + "view");
|
||||
request.send();
|
||||
}
|
||||
|
||||
@ -172,73 +194,114 @@ function closeEditor() {
|
||||
document.getElementById("editPane").style.display = 'none';
|
||||
}
|
||||
|
||||
function revert() {
|
||||
gEditor.setValue(gBackup);
|
||||
}
|
||||
|
||||
function explodePath() {
|
||||
return /^\/~([^\/]+)\/([^\/]+)(.*)/.exec(window.location.pathname);
|
||||
}
|
||||
|
||||
function packageOwner() {
|
||||
return explodePath()[1];
|
||||
}
|
||||
|
||||
function packageName() {
|
||||
return explodePath()[2];
|
||||
}
|
||||
|
||||
function save(newName) {
|
||||
function save() {
|
||||
document.getElementById("save").disabled = true;
|
||||
document.getElementById("saveAs").disabled = true;
|
||||
if (gCurrentFile) {
|
||||
gFiles[gCurrentFile].doc = gEditor.getDoc();
|
||||
}
|
||||
|
||||
var contents = gEditor.getValue();
|
||||
var run = document.getElementById("run").checked;
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
var appFinished = function(success) {
|
||||
document.getElementById("save").disabled = false;
|
||||
}
|
||||
|
||||
var always = function() {
|
||||
document.getElementById("save").disabled = false;
|
||||
document.getElementById("saveAs").disabled = false;
|
||||
var anyUnfinished = Object.values(gFiles).some(x => x.request);
|
||||
var anyUnsaved = Object.values(gFiles).some(x => !x.id);
|
||||
if (!anyUnfinished && !anyUnsaved) {
|
||||
var app = {
|
||||
type: "tildefriends-app",
|
||||
files: Object.fromEntries(Object.keys(gFiles).map(x => [x, gFiles[x].id])),
|
||||
};
|
||||
Object.values(gFiles).forEach(function(file) { delete file.id; });
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.addEventListener("error", function() {
|
||||
alert("Error saving: " + request.responseText);
|
||||
appFinished(false);
|
||||
});
|
||||
request.addEventListener("loadend", function() {
|
||||
if (request.status == 200) {
|
||||
var latest = document.getElementById('latest');
|
||||
latest.href = request.responseText;
|
||||
latest.style.visibility = request.responseText != window.location.path ? 'visible' : 'hidden';
|
||||
if (run) {
|
||||
if (request.responseText) {
|
||||
reconnect(request.responseText);
|
||||
} else {
|
||||
reconnect(window.location.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);
|
||||
});
|
||||
|
||||
var saveTo = null;
|
||||
var name = document.getElementById("name");
|
||||
if (name && name.value) {
|
||||
saveTo = name.value + "save";
|
||||
} else {
|
||||
saveTo = url() + "save";
|
||||
}
|
||||
request.open("POST", saveTo, true);
|
||||
request.setRequestHeader("Content-Type", "text/json");
|
||||
request.send(JSON.stringify(app));
|
||||
} else if (!anyUnfinished) {
|
||||
appFinished(false);
|
||||
}
|
||||
};
|
||||
|
||||
request.addEventListener("error", function() {
|
||||
alert("Error saving: " + request.responseText);
|
||||
always();
|
||||
});
|
||||
request.addEventListener("loadend", function() {
|
||||
if (request.status == 200) {
|
||||
gBackup = contents;
|
||||
if (run) {
|
||||
if (newName) {
|
||||
window.location.href = "/~" + packageOwner() + "/" + newName + hash();
|
||||
} else {
|
||||
reconnect();
|
||||
Object.values(gFiles).forEach(function(file) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
alert("Unable to save: " + request.responseText);
|
||||
}
|
||||
always();
|
||||
});
|
||||
request.addEventListener("timeout", function() {
|
||||
alert("Timed out saving: " + request.responseText);
|
||||
always();
|
||||
});
|
||||
request.addEventListener("abort", function() {
|
||||
alert("Save aborted: " + request.responseText);
|
||||
always();
|
||||
});
|
||||
request.open("POST", newName ? newName + "/save" : packageName() + "/save", true);
|
||||
request.setRequestHeader("Content-Type", "text/plain");
|
||||
request.send(contents);
|
||||
}
|
||||
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();
|
||||
});
|
||||
|
||||
function saveAs() {
|
||||
var newName = prompt("Save as:", packageName());
|
||||
if (newName) {
|
||||
save(newName);
|
||||
}
|
||||
file.request.open("POST", "/save", true);
|
||||
file.request.setRequestHeader("Content-Type", "text/plain");
|
||||
file.request.send(file.doc.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
function url() {
|
||||
@ -260,191 +323,30 @@ function hash() {
|
||||
return window.location.hash != "#" ? window.location.hash : "";
|
||||
}
|
||||
|
||||
function split(container, children) {
|
||||
if (container) {
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
}
|
||||
if (children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (children[i].name) {
|
||||
var node = document.createElement("div");
|
||||
node.setAttribute("id", "terminal_" + children[i].name);
|
||||
var grow = children[i].grow || "1";
|
||||
var shrink = children[i].shrink || "1";
|
||||
var basis = children[i].basis || "auto";
|
||||
var style = children[i].style || "";
|
||||
node.setAttribute("style", style + "; flex: " + grow + " " + shrink + " " + basis);
|
||||
|
||||
var classes = ["terminal"];
|
||||
if (children[i].type == "vertical") {
|
||||
classes.push("vbox");
|
||||
} else if (children[i].type == "horizontal") {
|
||||
classes.push("hbox");
|
||||
}
|
||||
node.setAttribute("class", classes.join(" "));
|
||||
container.appendChild(node);
|
||||
} else if (children[i].type) {
|
||||
node = document.createElement("div");
|
||||
if (children[i].type == "horizontal") {
|
||||
node.setAttribute("class", "hbox");
|
||||
} else if (children[i].type == "vertical") {
|
||||
node.setAttribute("class", "vbox");
|
||||
}
|
||||
container.appendChild(node);
|
||||
split(node, children[i].children);
|
||||
}
|
||||
function receive(message) {
|
||||
if (message && message.action == "session") {
|
||||
setStatusMessage("...Executing...", kStatusColor, true);
|
||||
gCredentials = message.credentials;
|
||||
updateLogin();
|
||||
} else if (message && message.action == "ready") {
|
||||
setStatusMessage(null);
|
||||
if (window.location.hash) {
|
||||
send({event: "hashChange", hash: window.location.hash});
|
||||
}
|
||||
} else if (message && message.action == "setDocument") {
|
||||
var iframe = document.getElementById("document");
|
||||
iframe.srcdoc = message.content;
|
||||
} else if (message && message.action == "postMessage") {
|
||||
var iframe = document.getElementById("document");
|
||||
iframe.contentWindow.postMessage(message.message, "*");
|
||||
} else if (message && message.action == "ping") {
|
||||
gSocket.send(JSON.stringify({action: "pong"}));
|
||||
} else if (message && message.action == "error") {
|
||||
setStatusMessage(message.error.message + '\n' + message.error.stack, '#f00', false);
|
||||
console.log(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
var gCorkCount = 0;
|
||||
var gCorkLines = [];
|
||||
|
||||
function receive(data) {
|
||||
for (var i in data.lines) {
|
||||
var line = data.lines[i];
|
||||
|
||||
if (line && line[0] && line[0].action == "cork"
|
||||
|| line && line.value && line.value[0].action == "cork") {
|
||||
++gCorkCount;
|
||||
continue;
|
||||
} else if (line && line[0] && line[0].action == "uncork"
|
||||
|| line && line.value && line.value[0].action == "uncork") {
|
||||
if (--gCorkCount <= 0) {
|
||||
receive({lines: gCorkLines});
|
||||
gCorkLines.length = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gCorkCount > 0) {
|
||||
gCorkLines.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
var target = document.getElementsByClassName("terminal")[0].id;
|
||||
if (line && line.terminal) {
|
||||
if (document.getElementById("terminal_" + line.terminal)) {
|
||||
target = "terminal_" + line.terminal;
|
||||
}
|
||||
line = line.value;
|
||||
}
|
||||
|
||||
if (line && line.action == "ping") {
|
||||
gSocket.send(JSON.stringify({action: "pong"}));
|
||||
} else if (line && line.action == "session") {
|
||||
setStatusMessage("...Executing...", kStatusColor, true);
|
||||
gCredentials = line.credentials;
|
||||
updateLogin();
|
||||
} else if (line && line[0] && line[0].action == "ready") {
|
||||
setStatusMessage(null);
|
||||
if (window.location.hash) {
|
||||
send({event: "hashChange", hash: window.location.hash});
|
||||
}
|
||||
} else if (line && line[0] && line[0].action == "notify") {
|
||||
if (window.Notification) {
|
||||
new Notification(line[0].title, line[0].options);
|
||||
}
|
||||
} else if (line && line[0] && line[0].action == "setTitle") {
|
||||
window.document.title = line[0].value;
|
||||
} else if (line && line[0] && line[0].action == "setPrompt") {
|
||||
var prompt = document.getElementById("prompt");
|
||||
while (prompt.firstChild) {
|
||||
prompt.removeChild(prompt.firstChild);
|
||||
}
|
||||
prompt.appendChild(document.createTextNode(line[0].value));
|
||||
} else if (line && line[0] && line[0].action == "setPassword") {
|
||||
var prompt = document.getElementById("input");
|
||||
prompt.setAttribute("type", line[0].value ? "password" : "text");
|
||||
} else if (line && line[0] && line[0].action == "setHash") {
|
||||
window.location.hash = line[0].value;
|
||||
} else if (line && line[0] && line[0].action == "update") {
|
||||
document.getElementById("update").setAttribute("Style", "display: inline");
|
||||
} else if (line && line[0] && line[0].action == "split") {
|
||||
split(document.getElementById("terminals"), line[0].options);
|
||||
} else if (line && line[0] && line[0].action == "postMessageToIframe") {
|
||||
var iframe = document.getElementById("iframe_" + line[0].name);
|
||||
if (iframe) {
|
||||
iframe.contentWindow.postMessage(line[0].message, "*");
|
||||
}
|
||||
} else if (line && line[0] && line[0].action == "setSendKeyEvents") {
|
||||
var value = line[0].value;
|
||||
if (value && !gSendKeyEvents) {
|
||||
window.addEventListener("keydown", keyEvent);
|
||||
window.addEventListener("keypress", keyEvent);
|
||||
window.addEventListener("keyup", keyEvent);
|
||||
} else if (!value && gSendKeyEvents) {
|
||||
window.removeEventListener("keydown", keyEvent);
|
||||
window.removeEventListener("keypress", keyEvent);
|
||||
window.removeEventListener("keyup", keyEvent);
|
||||
}
|
||||
gSendKeyEvents = value;
|
||||
} else if (line && line[0] && line[0].action == "getCurrentPosition") {
|
||||
navigator.geolocation.getCurrentPosition(geolocationPosition, geolocationError, line[0].options);
|
||||
} else if (line && line[0] && line[0].action == "watchPosition") {
|
||||
if (navigator && navigator.geolocation && gGeolocatorWatch === undefined) {
|
||||
gGeolocatorWatch = navigator.geolocation.watchPosition(geolocationPosition, geolocationError, line[0].options);
|
||||
}
|
||||
} else if (line && line[0] && line[0].action == "clearWatch") {
|
||||
if (navigator && navigator.geolocation && gGeolocatorWatch !== undefined) {
|
||||
navigator.geolocation.clearWatch(gGeolocatorWatch);
|
||||
}
|
||||
} else if (line && line[0] && line[0].action == "setSendDeviceOrientationEvents") {
|
||||
let value = line[0].value;
|
||||
if (value && !gSendDeviceOrientationEvents) {
|
||||
window.addEventListener("deviceorientation", deviceOrientation);
|
||||
} else if (!value && gSendDeviceOrientationEvents) {
|
||||
window.removeEventListener("deviceorientation", deviceOrientation);
|
||||
}
|
||||
gSendDeviceOrientationEvents = value;
|
||||
} else {
|
||||
print(document.getElementById(target), line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function geolocationPosition(position) {
|
||||
send({
|
||||
event: 'geolocation',
|
||||
position: {
|
||||
timestamp: position.timestamp,
|
||||
coords: {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
altitude: position.coords.altitude,
|
||||
accuracy: position.coords.accuracy,
|
||||
altitudeAccuracy: position.coords.altitudeAccuracy,
|
||||
heading: position.coords.heading,
|
||||
speed: position.coords.speed,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function geolocationError(error) {
|
||||
send({
|
||||
event: 'geolocation',
|
||||
error: {
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function deviceOrientation(event) {
|
||||
send({
|
||||
event: 'deviceorientation',
|
||||
orientation: {
|
||||
alpha: event.alpha,
|
||||
beta: event.beta,
|
||||
gamma: event.gamma,
|
||||
absolute: event.absolute,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function keyEvent(event) {
|
||||
send({
|
||||
event: "key",
|
||||
@ -458,133 +360,6 @@ function keyEvent(event) {
|
||||
});
|
||||
}
|
||||
|
||||
function autoNewLine(terminal) {
|
||||
terminal.appendChild(document.createElement("br"));
|
||||
}
|
||||
|
||||
function print(terminal, data) {
|
||||
autoNewLine(terminal);
|
||||
printStructured(terminal, data);
|
||||
autoScroll(terminal);
|
||||
}
|
||||
|
||||
function printSvg(container, data, name, namespace) {
|
||||
var node;
|
||||
if (typeof data == "string") {
|
||||
node = document.createTextNode(data);
|
||||
} else {
|
||||
node = document.createElementNS("http://www.w3.org/2000/svg", name);
|
||||
for (var i in data.attributes) {
|
||||
node.setAttribute(i, data.attributes[i]);
|
||||
}
|
||||
if (data.children) {
|
||||
for (var i in data.children) {
|
||||
node.appendChild(printSvg(node, data.children[i], data.children[i].name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function printStructured(container, data) {
|
||||
if (typeof data == "string") {
|
||||
container.appendChild(document.createTextNode(data));
|
||||
} else if (data && data[0] !== undefined) {
|
||||
for (var i in data) {
|
||||
printStructured(container, data[i]);
|
||||
}
|
||||
} else if (data && data.action == "clear") {
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
} else if (data) {
|
||||
var node;
|
||||
if (data.href) {
|
||||
node = document.createElement("a");
|
||||
node.setAttribute("href", data.href);
|
||||
node.setAttribute("target", data.target || "_blank");
|
||||
} else if (data.iframe) {
|
||||
node = document.createElement("iframe");
|
||||
if (data.src) {
|
||||
node.setAttribute("src", data.src);
|
||||
} else {
|
||||
node.setAttribute("srcdoc", data.iframe);
|
||||
}
|
||||
node.setAttribute("sandbox", "allow-forms allow-scripts allow-top-navigation allow-same-origin");
|
||||
if (data.width !== null) {
|
||||
node.setAttribute("width", data.width || 320);
|
||||
}
|
||||
if (data.height !== null) {
|
||||
node.setAttribute("height", data.height || 240);
|
||||
}
|
||||
if (data.name) {
|
||||
node.setAttribute("id", "iframe_" + data.name);
|
||||
}
|
||||
} else if (data.svg) {
|
||||
node = printSvg(container, data.svg, "svg");
|
||||
} else if (data.image) {
|
||||
node = document.createElement("img");
|
||||
node.setAttribute("src", data.image);
|
||||
} else if (data.input) {
|
||||
node = document.createElement("input");
|
||||
node.setAttribute("type", data.input);
|
||||
if (data.name) {
|
||||
node.name = data.name;
|
||||
}
|
||||
if (data.input == "submit") {
|
||||
node.onclick = submitButton;
|
||||
}
|
||||
} else {
|
||||
node = document.createElement("span");
|
||||
}
|
||||
|
||||
if (data.style) {
|
||||
node.setAttribute("style", data.style);
|
||||
}
|
||||
if (data.class) {
|
||||
node.setAttribute("class", data.class);
|
||||
}
|
||||
var value = data.value || data.href || data.command || "";
|
||||
if (data.input) {
|
||||
node.value = value;
|
||||
} else if (!value && data.message && data.stackTrace) {
|
||||
printStructured(node, data.message);
|
||||
node.appendChild(document.createElement("br"));
|
||||
printStructured(node, data.fileName + ":" + data.lineNumber + ":");
|
||||
node.appendChild(document.createElement("br"));
|
||||
if (data.stackTrace.length) {
|
||||
for (var i = 0; i < data.stackTrace.length; i++) {
|
||||
printStructured(node, data.stackTrace[i]);
|
||||
node.appendChild(document.createElement("br"));
|
||||
}
|
||||
} else {
|
||||
printStructured(node, data.sourceLine);
|
||||
}
|
||||
} else if (value === undefined) {
|
||||
printStructured(node, JSON.stringify(value));
|
||||
} else {
|
||||
printStructured(node, value);
|
||||
}
|
||||
if (data.command) {
|
||||
node.dataset.command = data.command;
|
||||
node.onclick = commandClick;
|
||||
node.setAttribute("class", "command");
|
||||
}
|
||||
container.appendChild(node);
|
||||
} else {
|
||||
printStructured(container, JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
function commandClick() {
|
||||
send(this.dataset.command);
|
||||
document.getElementById("input").focus();
|
||||
}
|
||||
|
||||
function autoScroll(terminal) {
|
||||
terminal.scrollTop = terminal.scrollHeight - terminal.clientHeight;
|
||||
}
|
||||
|
||||
function setStatusMessage(message, color, keep) {
|
||||
var node = document.getElementById("status");
|
||||
if (!keep) {
|
||||
@ -594,18 +369,13 @@ function setStatusMessage(message, color, keep) {
|
||||
}
|
||||
if (message) {
|
||||
node.appendChild(document.createTextNode(message));
|
||||
node.setAttribute("style", "display: inline; color: " + (color || kErrorColor));
|
||||
node.setAttribute("style", "display: inline-block; vertical-align: top; white-space: pre; color: " + (color || kErrorColor));
|
||||
}
|
||||
}
|
||||
|
||||
function send(command) {
|
||||
var value = command;
|
||||
if (!command) {
|
||||
value = document.getElementById("input").value;
|
||||
document.getElementById("input").value = "";
|
||||
}
|
||||
function send(value) {
|
||||
try {
|
||||
gSocket.send(JSON.stringify({action: "command", command: value}));
|
||||
gSocket.send(JSON.stringify(value));
|
||||
} catch (error) {
|
||||
setStatusMessage("Send failed: " + error.toString(), kErrorColor);
|
||||
}
|
||||
@ -620,15 +390,7 @@ function updateLogin() {
|
||||
var a = document.createElement("a");
|
||||
if (gCredentials && gCredentials.session) {
|
||||
a.appendChild(document.createTextNode("logout " + gCredentials.session.name));
|
||||
if (gCredentials.session.google) {
|
||||
gapi.load("auth2", function() {
|
||||
gapi.auth2.init();
|
||||
});
|
||||
a.setAttribute("onclick", "logoutGoogle()");
|
||||
a.setAttribute("href", "#");
|
||||
} else {
|
||||
a.setAttribute("href", "/login/logout?return=" + encodeURIComponent(url() + hash()));
|
||||
}
|
||||
a.setAttribute("href", "/login/logout?return=" + encodeURIComponent(url() + hash()));
|
||||
} else {
|
||||
a.appendChild(document.createTextNode("login"));
|
||||
a.setAttribute("href", "/login?return=" + encodeURIComponent(url() + hash()));
|
||||
@ -636,12 +398,6 @@ function updateLogin() {
|
||||
login.appendChild(a);
|
||||
}
|
||||
|
||||
function logoutGoogle() {
|
||||
gapi.auth2.getAuthInstance().signOut().then(function() {
|
||||
window.location.href = "/login/logout?return=" + encodeURIComponent(url() + hash());
|
||||
});
|
||||
}
|
||||
|
||||
var gOriginalInput;
|
||||
function dragHover(event) {
|
||||
event.stopPropagation();
|
||||
@ -731,7 +487,6 @@ function enableDragDrop() {
|
||||
var body = document.body;
|
||||
body.addEventListener("dragover", dragHover);
|
||||
body.addEventListener("dragleave", dragHover);
|
||||
|
||||
body.addEventListener("drop", fileDrop);
|
||||
}
|
||||
|
||||
@ -753,77 +508,42 @@ function blur() {
|
||||
}
|
||||
}
|
||||
|
||||
function onMessage(event) {
|
||||
function message(event) {
|
||||
if (event.data && event.data.event == "resizeMe" && event.data.width && event.data.height) {
|
||||
var iframe = document.getElementById("iframe_" + event.data.name);
|
||||
iframe.setAttribute("width", event.data.width);
|
||||
iframe.setAttribute("height", event.data.height);
|
||||
var node = iframe.parentElement;
|
||||
while (node && !node.classList.contains("terminal")) {
|
||||
node = node.parentElement;
|
||||
}
|
||||
if (node) {
|
||||
autoScroll(node);
|
||||
}
|
||||
} else {
|
||||
send({event: "onWindowMessage", message: event.data});
|
||||
send({event: "message", message: event.data});
|
||||
}
|
||||
}
|
||||
|
||||
function submitButton() {
|
||||
var inputs = document.getElementsByTagName("input");
|
||||
var data = {};
|
||||
for (var i in inputs) {
|
||||
var input = inputs[i];
|
||||
if (input.name) {
|
||||
data[input.name] = input.value;
|
||||
}
|
||||
}
|
||||
send({event: "submit", value: data});
|
||||
}
|
||||
|
||||
function reconnect() {
|
||||
function reconnect(path) {
|
||||
let oldSocket = gSocket;
|
||||
gSocket = null
|
||||
oldSocket.onclose = function() {}
|
||||
oldSocket.onmessage = function() {}
|
||||
oldSocket.close();
|
||||
split(document.getElementById("terminals"), [{name: "terminal_"}]);
|
||||
connectSocket();
|
||||
connectSocket(path);
|
||||
}
|
||||
|
||||
function connectSocket() {
|
||||
function connectSocket(path) {
|
||||
if (!gSocket || gSocket.readyState == gSocket.CLOSED) {
|
||||
setStatusMessage("Connecting...", kStatusColor, true);
|
||||
gSocket = new WebSocket(
|
||||
(window.location.protocol == "https:" ? "wss://" : "ws://")
|
||||
+ window.location.hostname
|
||||
+ (window.location.port.length ? ":" + window.location.port : "")
|
||||
+ "/terminal/socket");
|
||||
+ "/app/socket");
|
||||
gSocket.onopen = function() {
|
||||
setStatusMessage("...Authenticating...", kStatusColor, true);
|
||||
gSocket.send(JSON.stringify({
|
||||
action: "hello",
|
||||
path: window.location.pathname,
|
||||
terminalApi: [
|
||||
['clear'],
|
||||
['notify', 'title', 'options'],
|
||||
['postMessageToIframe', 'name', 'message'],
|
||||
['setHash', 'value'],
|
||||
['setPassword', 'value'],
|
||||
['setPrompt', 'value'],
|
||||
['setTitle', 'value'],
|
||||
['split', 'options'],
|
||||
['setSendKeyEvents', 'value'],
|
||||
|
||||
['getCurrentPosition', 'options'],
|
||||
['watchPosition', 'options'],
|
||||
['clearWatch'],
|
||||
|
||||
['cork'],
|
||||
['uncork'],
|
||||
|
||||
['setSendDeviceOrientationEvents', 'value'],
|
||||
path: path,
|
||||
api: [
|
||||
['setDocument', 'content'],
|
||||
['postMessage', 'message'],
|
||||
['error', 'error'],
|
||||
],
|
||||
}));
|
||||
}
|
||||
@ -836,18 +556,71 @@ function connectSocket() {
|
||||
}
|
||||
}
|
||||
|
||||
function openFile(name) {
|
||||
var newDoc = (name && gFiles[name]) ? gFiles[name].doc : new CodeMirror.Doc("", guessMode(name));
|
||||
var oldDoc = gEditor.swapDoc(newDoc);
|
||||
if (gFiles[gCurrentFile]) {
|
||||
gFiles[gCurrentFile].doc = oldDoc;
|
||||
}
|
||||
gCurrentFile = name;
|
||||
updateFiles();
|
||||
gEditor.focus();
|
||||
}
|
||||
|
||||
function onFileClicked(event) {
|
||||
openFile(event.target.textContent);
|
||||
}
|
||||
|
||||
function updateFiles() {
|
||||
var node = document.getElementById("files");
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
|
||||
for (var file of Object.keys(gFiles).sort()) {
|
||||
var li = document.createElement("li");
|
||||
li.onclick = onFileClicked;
|
||||
li.appendChild(document.createTextNode(file));
|
||||
if (file == gCurrentFile) {
|
||||
li.classList.add("current");
|
||||
}
|
||||
node.appendChild(li);
|
||||
}
|
||||
|
||||
gEditor.focus();
|
||||
}
|
||||
|
||||
function makeNewFile(name) {
|
||||
gFiles[name] = {
|
||||
doc: new CodeMirror.Doc("", guessMode(name))
|
||||
};
|
||||
openFile(name);
|
||||
}
|
||||
|
||||
function newFile() {
|
||||
var name = prompt("Name of new file:", "file.js");
|
||||
if (name && !gFiles[name]) {
|
||||
makeNewFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
function removeFile() {
|
||||
if (confirm("Remove " + gCurrentFile + "?")) {
|
||||
delete gFiles[gCurrentFile];
|
||||
openFile(Object.keys(gFiles)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
if (window.Notification) {
|
||||
Notification.requestPermission();
|
||||
}
|
||||
var input = document.getElementById("input");
|
||||
input.addEventListener("keydown", keydown);
|
||||
input.focus();
|
||||
window.addEventListener("hashchange", hashChange);
|
||||
window.addEventListener("focus", focus);
|
||||
window.addEventListener("blur", blur);
|
||||
window.addEventListener("message", onMessage, false);
|
||||
window.addEventListener("message", message, false);
|
||||
window.addEventListener("online", connectSocket);
|
||||
document.getElementById("name").value = window.location.pathname;
|
||||
enableDragDrop();
|
||||
connectSocket();
|
||||
connectSocket(window.location.pathname);
|
||||
});
|
||||
|
Reference in New Issue
Block a user