mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 18:06:16 +01:00
886 lines
24 KiB
HTML
886 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<title>ESP Editor</title>
|
|
<link rel="icon" href="data:,">
|
|
<style type="text/css" media="screen">
|
|
.cm {
|
|
z-index: 300;
|
|
position: absolute;
|
|
left: 5px;
|
|
border: 1px solid #444;
|
|
background-color: #F5F5F5;
|
|
display: none;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
|
|
font-size: 12px;
|
|
font-family: sans-serif;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.cm ul {
|
|
list-style: none;
|
|
top: 0;
|
|
left: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.cm li {
|
|
position: relative;
|
|
min-width: 60px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.cm span {
|
|
color: #444;
|
|
display: inline-block;
|
|
padding: 6px;
|
|
}
|
|
|
|
.cm li:hover {
|
|
background: #444;
|
|
}
|
|
|
|
.cm li:hover span {
|
|
color: #EEE;
|
|
}
|
|
|
|
.tvu ul,
|
|
.tvu li {
|
|
padding: 0;
|
|
margin: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.tvu li {
|
|
display: flex;
|
|
justify-content: stretch;
|
|
}
|
|
|
|
.tvu input {
|
|
position: absolute;
|
|
opacity: 0;
|
|
}
|
|
|
|
.tvu {
|
|
font: normal 12px Verdana, Arial, Sans-serif;
|
|
-moz-user-select: none;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
color: #444;
|
|
line-height: 16px;
|
|
}
|
|
|
|
.tvu .icon {
|
|
padding: 0 0 0 5px;
|
|
cursor: pointer;
|
|
display: inline-block;
|
|
width: 20px;
|
|
text-align: center;
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
.tvu .filename {
|
|
padding: 2px 0 0 2px;
|
|
cursor: pointer;
|
|
display: inline-block;
|
|
width: auto;
|
|
flex: 2;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.tvu .size {
|
|
padding: 2px 5px 0 5px;
|
|
cursor: pointer;
|
|
display: inline-block;
|
|
text-align: right;
|
|
}
|
|
|
|
.tvu li:hover {
|
|
background-color:#cccccc;
|
|
}
|
|
|
|
#uploader {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
left: 0;
|
|
background-color: #444;
|
|
color: #EEE;
|
|
height: 45px;
|
|
}
|
|
|
|
button {
|
|
background-color: #eeeeee;
|
|
border: 0px;
|
|
cursor: pointer;
|
|
padding: 3px 5px;
|
|
margin: 10px;
|
|
appearance: none;
|
|
}
|
|
|
|
input {
|
|
background: #ffffff;
|
|
border: 0px;
|
|
appearance: none;
|
|
padding: 2px 5px;
|
|
margin: 10px;
|
|
}
|
|
|
|
input[type="file"] {
|
|
background: none;
|
|
}
|
|
|
|
.uploadpath {
|
|
background: none;
|
|
color: white;
|
|
}
|
|
|
|
#tree {
|
|
position: absolute;
|
|
top: 50px;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 300px;
|
|
}
|
|
|
|
#editor,
|
|
#preview {
|
|
position: absolute;
|
|
top: 45px;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 300px;
|
|
border-left: 1px solid #EEE;
|
|
}
|
|
|
|
#preview {
|
|
background-color: #EEE;
|
|
padding: 5px;
|
|
}
|
|
|
|
#loader {
|
|
position: absolute;
|
|
top: 36%;
|
|
right: 40%;
|
|
}
|
|
|
|
.loader {
|
|
z-index: 10000;
|
|
border: 8px solid #b5b5b5;
|
|
/* Grey */
|
|
border-top: 8px solid #3498db;
|
|
/* Blue */
|
|
border-bottom: 8px solid #3498db;
|
|
/* Blue */
|
|
border-radius: 50%;
|
|
width: 40px;
|
|
height: 40px;
|
|
animation: spin 2s linear infinite;
|
|
display: none;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
</style>
|
|
<script>
|
|
const $ = document.querySelector.bind(document);
|
|
|
|
if (typeof XMLHttpRequest === "undefined") {
|
|
XMLHttpRequest = function () {
|
|
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) { }
|
|
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) { }
|
|
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
|
|
throw new Error("This browser does not support XMLHttpRequest.");
|
|
};
|
|
}
|
|
|
|
function ge(a) {
|
|
return document.getElementById(a);
|
|
}
|
|
function ce(a) {
|
|
return document.createElement(a);
|
|
}
|
|
|
|
function sortByKey(array, key) {
|
|
return array.sort(function (a, b) {
|
|
var x = a[key]; var y = b[key];
|
|
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
|
});
|
|
}
|
|
|
|
|
|
var QueuedRequester = function () {
|
|
this.queue = [];
|
|
this.running = false;
|
|
this.xmlhttp = null;
|
|
}
|
|
QueuedRequester.prototype = {
|
|
_request: function (req) {
|
|
this.running = true;
|
|
if (!req instanceof Object) return;
|
|
var that = this;
|
|
|
|
function ajaxCb(x, d) {
|
|
return function () {
|
|
if (x.readyState == 4) {
|
|
ge("loader").style.display = "none";
|
|
d.callback(x.status, x.responseText);
|
|
if (that.queue.length === 0) that.running = false;
|
|
if (that.running) that._request(that.queue.shift());
|
|
}
|
|
}
|
|
}
|
|
|
|
ge("loader").style.display = "block";
|
|
|
|
var p = "";
|
|
if (req.params instanceof FormData) {
|
|
p = req.params;
|
|
} else if (req.params instanceof Object) {
|
|
for (var key in req.params) {
|
|
if (p === "")
|
|
p += (req.method === "GET") ? "?" : "";
|
|
else
|
|
p += "&";
|
|
p += encodeURIComponent(key) + "=" + encodeURIComponent(req.params[key]);
|
|
};
|
|
}
|
|
|
|
this.xmlhttp = new XMLHttpRequest();
|
|
this.xmlhttp.onreadystatechange = ajaxCb(this.xmlhttp, req);
|
|
if (req.method === "GET") {
|
|
this.xmlhttp.open(req.method, req.url + p, true);
|
|
this.xmlhttp.send();
|
|
} else {
|
|
this.xmlhttp.open(req.method, req.url, true);
|
|
if (p instanceof String)
|
|
this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
this.xmlhttp.send(p);
|
|
}
|
|
},
|
|
stop: function () {
|
|
if (this.running) this.running = false;
|
|
if (this.xmlhttp && this.xmlhttp.readyState < 4) {
|
|
this.xmlhttp.abort();
|
|
}
|
|
},
|
|
add: function (method, url, params, callback) {
|
|
this.queue.push({ url: url, method: method, params: params, callback: callback });
|
|
if (!this.running) {
|
|
this._request(this.queue.shift());
|
|
}
|
|
}
|
|
}
|
|
|
|
var requests = new QueuedRequester();
|
|
|
|
function createFileUploader(element, tree, editor) {
|
|
var xmlHttp;
|
|
|
|
var refresh = ce("button");
|
|
refresh.innerHTML = 'Refresh List';
|
|
ge(element).appendChild(refresh);
|
|
|
|
var path = ce("input");
|
|
path.id = "upload-path";
|
|
path.type = "text";
|
|
path.name = "path";
|
|
path.classList.add("uploadpath");
|
|
path.defaultValue = "/";
|
|
ge(element).appendChild(path);
|
|
|
|
var input = ce("input");
|
|
input.type = "file";
|
|
input.multiple = false;
|
|
input.name = "data";
|
|
input.id = "upload-select";
|
|
ge(element).appendChild(input);
|
|
|
|
/*
|
|
var mkfile = ce("button");
|
|
mkfile.innerHTML = 'Create';
|
|
ge(element).appendChild(mkfile);
|
|
*/
|
|
|
|
var filename = ce("input");
|
|
filename.id = "editor-filename";
|
|
filename.type = "text";
|
|
|
|
filename.size = 20;
|
|
ge(element).appendChild(filename);
|
|
|
|
var button = ce("button");
|
|
button.id = "button";
|
|
button.innerHTML = 'Upload';
|
|
button.style.display = 'none';
|
|
ge(element).appendChild(button);
|
|
|
|
var savefile = ce("button");
|
|
savefile.id = "savefile";
|
|
savefile.innerHTML = ' Save ';
|
|
savefile.style.display = 'none';
|
|
ge(element).appendChild(savefile);
|
|
|
|
function httpPostProcessRequest(status, responseText) {
|
|
if (status != 200)
|
|
alert("ERROR[" + status + "]: " + responseText);
|
|
else
|
|
tree.refreshPath(path.value);
|
|
}
|
|
function createPath(p) {
|
|
var formData = new FormData();
|
|
formData.append("path", p);
|
|
requests.add("PUT", "/edit", formData, httpPostProcessRequest);
|
|
}
|
|
|
|
/*
|
|
mkfile.onclick = function (e) {
|
|
createPath(filename.value);
|
|
editor.loadUrl(filename.value);
|
|
path.value = (filename.value);
|
|
};
|
|
*/
|
|
|
|
savefile.onclick = function (e) {
|
|
editor.execCommand('saveCommand');
|
|
};
|
|
|
|
refresh.onclick = function (e) {
|
|
tree.refreshPath(path.value);
|
|
};
|
|
|
|
button.onclick = function (e) {
|
|
if (input.files.length === 0) {
|
|
return;
|
|
}
|
|
var formData = new FormData();
|
|
formData.append("data", input.files[0], ge("editor-filename").value);
|
|
requests.add("POST", "/edit", formData, httpPostProcessRequest);
|
|
var uploadSelect = ge("upload-select");
|
|
uploadSelect.value = "";
|
|
};
|
|
input.onchange = function (e) {
|
|
if (input.files.length === 0) return;
|
|
var filename = input.files[0].name;
|
|
ge("editor-filename").value = path.value + (path.value=="/" ? "" : "/") + filename;
|
|
button.style.display = 'inline-block';
|
|
savefile.style.display = 'none';
|
|
};
|
|
}
|
|
|
|
function createTree(element, editor) {
|
|
var preview = ge("preview");
|
|
var treeRoot = ce("div");
|
|
treeRoot.className = "tvu";
|
|
ge(element).appendChild(treeRoot);
|
|
|
|
function loadDownload(path) {
|
|
ge('download-frame').src = "/edit?download=" + path;
|
|
}
|
|
|
|
function loadPreview(path) {
|
|
ge("button").style.display = 'none';
|
|
ge("savefile").style.display = 'none';
|
|
|
|
var edfname = ge("editor-filename");
|
|
|
|
var filename = path;
|
|
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
|
|
var name = /(.*)\.[^.]+$/.exec(filename)[1];
|
|
if (typeof name !== undefined) {
|
|
filename = name;
|
|
}
|
|
|
|
edfname.value = filename + "." + ext;
|
|
ge("editor").style.display = "none";
|
|
|
|
preview.style.display = "block";
|
|
if (ext == "raw" || ext == "pending") {
|
|
let storedTagTypes = localStorage.getItem("tagTypes");
|
|
let tagTypes = JSON.parse(storedTagTypes);
|
|
let mac = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('/') + 17);
|
|
let targetDiv = null;
|
|
if (window.opener && !window.opener.closed) {
|
|
targetDiv = window.opener.document.querySelector(`div[data-mac="${mac}"]`);
|
|
} else {
|
|
preview.innerHTML = `<p class=\"tvu\">I cannot reach the window with the tag overview. Close this window, and open it again from the tag overview.</p>`;
|
|
return;
|
|
}
|
|
if (targetDiv) {
|
|
preview.innerHTML = `<p class=\"tvu\">Preview image for tag ${mac}<p><p><canvas id=\"previewimg\"></p>`;
|
|
var hwtype = targetDiv.getAttribute("data-hwtype");
|
|
console.log("data-hwtype:", hwtype);
|
|
|
|
const canvas = $('#previewimg');
|
|
canvas.style.display = 'block';
|
|
fetch(path + "?r=" + Math.random())
|
|
.then(response => response.arrayBuffer())
|
|
.then(buffer => {
|
|
|
|
data = new Uint8ClampedArray(buffer);
|
|
if (tagTypes[hwtype].zlib > 0 && targetDiv.dataset.ver >= tagTypes[hwtype].zlib) {
|
|
data = window.opener.processZlib(data);
|
|
}
|
|
if (data.length > 0 && tagTypes[hwtype].g5 > 0 && targetDiv.dataset.ver >= tagTypes[hwtype].g5) {
|
|
const headerSize = data[0];
|
|
let bufw = (data[2] << 8) | data[1];
|
|
let bufh = (data[4] << 8) | data[3];
|
|
if ((bufw == tagTypes[hwtype].width || bufw == tagTypes[hwtype].height) && (bufh == tagTypes[hwtype].width || bufh == tagTypes[hwtype].height) && (data[5] <= 3)) {
|
|
// valid header for g5 compression
|
|
if (data[5] == 2) bufh *= 2;
|
|
data = window.opener.processG5(data.subarray(headerSize), bufw, bufh);
|
|
}
|
|
}
|
|
|
|
[canvas.width, canvas.height] = [tagTypes[hwtype].width, tagTypes[hwtype].height] || [0, 0];
|
|
if (tagTypes[hwtype].rotatebuffer%2) [canvas.width, canvas.height] = [canvas.height, canvas.width];
|
|
if (tagTypes[hwtype].rotatebuffer>=2) canvas.style.transform='rotate(180deg)';
|
|
const ctx = canvas.getContext('2d');
|
|
const imageData = ctx.createImageData(canvas.width, canvas.height);
|
|
if (data.length == 0) canvas.style.display = 'none';
|
|
|
|
if (tagTypes[hwtype].bpp == 16) {
|
|
const is16Bit = data.length == tagTypes[hwtype].width * tagTypes[hwtype].height * 2;
|
|
for (let i = 0; i < min(tagTypes[hwtype].width * tagTypes[hwtype].height, data.length); i++) {
|
|
const dataIndex = is16Bit ? i * 2 : i;
|
|
const rgb = is16Bit ? (data[dataIndex] << 8) | data[dataIndex + 1] : data[dataIndex];
|
|
|
|
imageData.data[i * 4] = is16Bit ? ((rgb >> 11) & 0x1F) << 3 : (((rgb >> 5) & 0x07) << 5) * 1.13;
|
|
imageData.data[i * 4 + 1] = is16Bit ? ((rgb >> 5) & 0x3F) << 2 : (((rgb >> 2) & 0x07) << 5) * 1.13;
|
|
imageData.data[i * 4 + 2] = is16Bit ? (rgb & 0x1F) << 3 : ((rgb & 0x03) << 6) * 1.3;
|
|
imageData.data[i * 4 + 3] = 255;
|
|
}
|
|
} else if ([3, 4].includes(tagTypes[hwtype].bpp)) {
|
|
const bpp = tagTypes[hwtype].bpp;
|
|
const colorTable = tagTypes[hwtype].colortable;
|
|
let pixelIndex = 0;
|
|
let bitOffset = 0;
|
|
|
|
while (bitOffset < data.length * 8) {
|
|
let byteIndex = bitOffset >> 3;
|
|
let startBit = bitOffset & 7;
|
|
let pixelValue = (data[byteIndex] << 8 | data[byteIndex + 1] || 0) >> (16 - bpp - startBit) & ((1 << bpp) - 1);
|
|
let color = colorTable[pixelValue];
|
|
imageData.data[pixelIndex * 4] = color[0];
|
|
imageData.data[pixelIndex * 4 + 1] = color[1];
|
|
imageData.data[pixelIndex * 4 + 2] = color[2];
|
|
imageData.data[pixelIndex * 4 + 3] = 255;
|
|
pixelIndex++;
|
|
bitOffset += bpp;
|
|
}
|
|
} else {
|
|
|
|
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;
|
|
let pixelValue = 0;
|
|
const colorTable = tagTypes[hwtype].colortable;
|
|
for (let i = 0; i < data.length; i++) {
|
|
for (let j = 0; j < 8; j++) {
|
|
const pixelIndex = i * 8 + j;
|
|
if (offsetRed) {
|
|
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0) | (((data[i + offsetRed] & (1 << (7 - j))) ? 1 : 0) << 1);
|
|
} else {
|
|
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0);
|
|
}
|
|
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
|
|
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
|
|
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
|
|
imageData.data[pixelIndex * 4 + 3] = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.putImageData(imageData, 0, 0);
|
|
})
|
|
.catch(error => {
|
|
console.log(error);
|
|
});
|
|
|
|
} else {
|
|
preview.innerHTML = `<p class=\"tvu\">No current tag found with this mac. You can safely delete this file.</p>`;
|
|
}
|
|
} else {
|
|
preview.innerHTML = '<img src="/edit?edit=' + path + '&_cb=' + Date.now() + '" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
|
|
}
|
|
}
|
|
|
|
function fillFileMenu(el, path) {
|
|
var list = ce("ul");
|
|
el.appendChild(list);
|
|
var action = ce("li");
|
|
list.appendChild(action);
|
|
if (isImageFile(path)) {
|
|
action.innerHTML = "<span>Preview</span>";
|
|
action.onclick = function (e) {
|
|
loadPreview(path);
|
|
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
|
};
|
|
} else if (isTextFile(path)) {
|
|
action.innerHTML = "<span>Edit</span>";
|
|
action.onclick = function (e) {
|
|
editor.loadUrl(path);
|
|
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
|
};
|
|
}
|
|
var download = ce("li");
|
|
list.appendChild(download);
|
|
download.innerHTML = "<span>Download</span>";
|
|
download.onclick = function (e) {
|
|
loadDownload(path);
|
|
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
|
};
|
|
var delFile = ce("li");
|
|
list.appendChild(delFile);
|
|
delFile.innerHTML = "<span>Delete</span>";
|
|
delFile.onclick = function (e) {
|
|
httpDelete(path);
|
|
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
|
|
};
|
|
}
|
|
|
|
function showContextMenu(event, path, isfile) {
|
|
var divContext = ce("div");
|
|
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
|
|
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
|
|
var left = event.clientX + scrollLeft;
|
|
var top = event.clientY + scrollTop;
|
|
divContext.className = 'cm';
|
|
divContext.style.display = 'block';
|
|
divContext.style.left = left + 'px';
|
|
divContext.style.top = top + 'px';
|
|
fillFileMenu(divContext, path);
|
|
document.body.appendChild(divContext);
|
|
var width = divContext.offsetWidth;
|
|
var height = divContext.offsetHeight;
|
|
divContext.onmouseout = function (e) {
|
|
if (e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)) {
|
|
if (document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(divContext);
|
|
}
|
|
};
|
|
}
|
|
|
|
function createTreeLeaf(path, name, size) {
|
|
var leaf = ce("li");
|
|
leaf.id = path + (path != "/" ? "/":"") + name;
|
|
|
|
var icon = ce("span");
|
|
icon.innerHTML = "📄";
|
|
icon.classList.add("icon");
|
|
leaf.appendChild(icon);
|
|
|
|
var label = ce("span");
|
|
label.innerHTML = name;
|
|
label.classList.add("filename");
|
|
leaf.appendChild(label);
|
|
|
|
var filesize = ce("span");
|
|
filesize.innerHTML = formatBytes(size);
|
|
filesize.classList.add("size");
|
|
leaf.appendChild(filesize);
|
|
|
|
leaf.onclick = function (e) {
|
|
if (isTextFile(leaf.id.toLowerCase())) {
|
|
editor.loadUrl(leaf.id);
|
|
} else if (isImageFile(leaf.id.toLowerCase())) {
|
|
loadPreview(leaf.id);
|
|
}
|
|
};
|
|
leaf.oncontextmenu = function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
showContextMenu(e, leaf.id, true);
|
|
};
|
|
return leaf;
|
|
}
|
|
|
|
function createDirLeaf(path, name, size) {
|
|
var leaf = ce("li");
|
|
leaf.id = path + (path != "/" ? "/" : "") + name;
|
|
|
|
var icon = ce("span");
|
|
icon.innerHTML = "📁";
|
|
icon.classList.add("icon");
|
|
leaf.appendChild(icon);
|
|
|
|
var label = ce("span");
|
|
label.innerHTML = name;
|
|
label.classList.add("filename");
|
|
leaf.appendChild(label);
|
|
|
|
var filesize = ce("span");
|
|
filesize.innerHTML = "";
|
|
filesize.classList.add("size");
|
|
leaf.appendChild(filesize);
|
|
|
|
leaf.onclick = function (e) {
|
|
treeRoot.removeChild(treeRoot.childNodes[0]);
|
|
if (name == "..") {
|
|
httpGet(treeRoot, path === "/" ? "/" : path.substring(0, path.lastIndexOf('/')) || "/");
|
|
} else {
|
|
httpGet(treeRoot, path + (path != "/" ? "/" : "") + name);
|
|
}
|
|
};
|
|
leaf.oncontextmenu = function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
showContextMenu(e, leaf.id, true);
|
|
};
|
|
return leaf;
|
|
}
|
|
|
|
function formatBytes(bytes, decimalPlaces = 2) {
|
|
if (bytes === 0) return '';
|
|
const k = 1024;
|
|
const dm = decimalPlaces < 0 ? 0 : decimalPlaces;
|
|
const sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
}
|
|
|
|
function addList(parent, path, items) {
|
|
ge("upload-path").value = path
|
|
ge("editor-filename").value = path
|
|
sortByKey(items, 'name');
|
|
var list = ce("ul");
|
|
parent.appendChild(list);
|
|
var ll = items.length;
|
|
for (var i = 0; i < ll; i++) {
|
|
if (items[i].type === "file") {
|
|
list.appendChild(createTreeLeaf(path, items[i].name, items[i].size));
|
|
}
|
|
if (items[i].type === "dir") {
|
|
list.insertBefore(createDirLeaf(path, items[i].name, items[i].size), list.firstChild);
|
|
}
|
|
}
|
|
if (path != "/") {
|
|
list.insertBefore(createDirLeaf(path, "..", 0), list.firstChild);
|
|
}
|
|
}
|
|
|
|
function isTextFile(path) {
|
|
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
|
|
if (typeof ext !== undefined) {
|
|
switch (ext) {
|
|
case "txt":
|
|
case "htm":
|
|
case "html":
|
|
case "js":
|
|
case "css":
|
|
case "xml":
|
|
case "json":
|
|
case "conf":
|
|
case "ini":
|
|
case "h":
|
|
case "c":
|
|
case "cpp":
|
|
case "php":
|
|
case "hex":
|
|
case "ino":
|
|
case "pde":
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isImageFile(path) {
|
|
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
|
|
if (typeof ext !== undefined) {
|
|
switch (ext) {
|
|
case "png":
|
|
case "jpg":
|
|
case "gif":
|
|
case "bmp":
|
|
case "pending":
|
|
case "raw":
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this.refreshPath = function (path) {
|
|
treeRoot.removeChild(treeRoot.childNodes[0]);
|
|
httpGet(treeRoot, path);
|
|
};
|
|
|
|
function delCb(path) {
|
|
return function (status, responseText) {
|
|
if (status != 200) {
|
|
alert("ERROR[" + status + "]: " + responseText);
|
|
} else {
|
|
var el = ge(path);
|
|
el.parentNode.removeChild(el);
|
|
}
|
|
}
|
|
}
|
|
|
|
function httpDelete(filename) {
|
|
var formData = new FormData();
|
|
formData.append("path", filename);
|
|
requests.add("DELETE", "/edit", formData, delCb(filename));
|
|
}
|
|
|
|
function getCb(parent, path) {
|
|
return function (status, responseText) {
|
|
if (status == 200)
|
|
addList(parent, path, JSON.parse(responseText));
|
|
}
|
|
}
|
|
|
|
function httpGet(parent, path) {
|
|
requests.add("GET", "/edit", { list: path }, getCb(parent, path));
|
|
}
|
|
|
|
httpGet(treeRoot, "/");
|
|
return this;
|
|
}
|
|
|
|
function createEditor(element, file, lang, theme, type) {
|
|
function getLangFromFilename(filename) {
|
|
var lang = "plain";
|
|
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
|
|
if (typeof ext !== undefined) {
|
|
switch (ext) {
|
|
case "txt": lang = "plain"; break;
|
|
case "hex": lang = "plain"; break;
|
|
case "conf": lang = "plain"; break;
|
|
case "htm": lang = "html"; break;
|
|
case "js": lang = "javascript"; break;
|
|
case "h": lang = "c_cpp"; break;
|
|
case "c": lang = "c_cpp"; break;
|
|
case "cpp": lang = "c_cpp"; break;
|
|
case "css":
|
|
case "scss":
|
|
case "php":
|
|
case "html":
|
|
case "json":
|
|
case "xml":
|
|
case "ini": lang = ext;
|
|
}
|
|
}
|
|
return lang;
|
|
}
|
|
|
|
if (typeof file === "undefined") file = "/index.html";
|
|
|
|
if (typeof lang === "undefined") {
|
|
lang = getLangFromFilename(file);
|
|
}
|
|
|
|
if (typeof theme === "undefined") theme = "textmate";
|
|
|
|
if (typeof type === "undefined") {
|
|
type = "text/" + lang;
|
|
if (lang === "c_cpp") type = "text/plain";
|
|
}
|
|
|
|
var editor = ace.edit(element);
|
|
function httpPostProcessRequest(status, responseText) {
|
|
if (status != 200) alert("ERROR[" + status + "]: " + responseText);
|
|
}
|
|
function httpPost(filename, data, type) {
|
|
var formData = new FormData();
|
|
formData.append("data", new Blob([data], { type: type }), filename);
|
|
requests.add("POST", "/edit", formData, httpPostProcessRequest);
|
|
}
|
|
function httpGetProcessRequest(status, responseText) {
|
|
ge("preview").style.display = "none";
|
|
ge("editor").style.display = "block";
|
|
if (status == 200)
|
|
editor.setValue(responseText);
|
|
else
|
|
editor.setValue("");
|
|
editor.clearSelection();
|
|
}
|
|
function httpGet(theUrl) {
|
|
requests.add("GET", "/edit", { edit: theUrl }, httpGetProcessRequest);
|
|
}
|
|
|
|
if (lang !== "plain") editor.getSession().setMode("ace/mode/" + lang);
|
|
editor.setTheme("ace/theme/" + theme);
|
|
editor.$blockScrolling = Infinity;
|
|
editor.getSession().setUseSoftTabs(true);
|
|
editor.getSession().setTabSize(2);
|
|
editor.setHighlightActiveLine(true);
|
|
editor.setShowPrintMargin(false);
|
|
editor.commands.addCommand({
|
|
name: 'saveCommand',
|
|
bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
|
|
exec: function (editor) {
|
|
httpPost(file, editor.getValue() + "", type);
|
|
},
|
|
readOnly: false
|
|
});
|
|
editor.commands.addCommand({
|
|
name: 'undoCommand',
|
|
bindKey: { win: 'Ctrl-Z', mac: 'Command-Z' },
|
|
exec: function (editor) {
|
|
editor.getSession().getUndoManager().undo(false);
|
|
},
|
|
readOnly: false
|
|
});
|
|
editor.commands.addCommand({
|
|
name: 'redoCommand',
|
|
bindKey: { win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z' },
|
|
exec: function (editor) {
|
|
editor.getSession().getUndoManager().redo(false);
|
|
},
|
|
readOnly: false
|
|
});
|
|
editor.loadUrl = function (filename) {
|
|
ge("button").style.display = 'none';
|
|
ge("savefile").style.display = 'inline-block';
|
|
var edfname = ge("editor-filename");
|
|
edfname.value = filename;
|
|
file = filename;
|
|
lang = getLangFromFilename(file);
|
|
type = "text/" + lang;
|
|
if (lang !== "plain") editor.getSession().setMode("ace/mode/" + lang);
|
|
httpGet(file);
|
|
};
|
|
return editor;
|
|
}
|
|
function onBodyLoad() {
|
|
var vars = {};
|
|
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { vars[key] = value; });
|
|
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
|
|
var tree = createTree("tree", editor);
|
|
createFileUploader("uploader", tree, editor);
|
|
if (typeof vars.file === "undefined") vars.file = "/index.htm";
|
|
editor.loadUrl(vars.file);
|
|
};
|
|
</script>
|
|
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.37.2/ace.js" type="text/javascript"
|
|
charset="utf-8"></script>
|
|
<script>
|
|
if (typeof ace.edit == "undefined") {
|
|
var script = document.createElement('script');
|
|
script.src = "/ace.js";
|
|
script.async = false;
|
|
document.head.appendChild(script);
|
|
}
|
|
</script>
|
|
</head>
|
|
|
|
<body onload="onBodyLoad();">
|
|
<div id="loader" class="loader"></div>
|
|
<div id="uploader"></div>
|
|
<div id="tree"></div>
|
|
<div id="editor"></div>
|
|
<div id="preview" style="display:none;"></div>
|
|
<iframe id=download-frame style='display:none;'></iframe>
|
|
</body>
|
|
|
|
</html>
|