diff --git a/.gitignore b/.gitignore index ec27914..def2805 100644 --- a/.gitignore +++ b/.gitignore @@ -188,5 +188,9 @@ _Pvt_Extensions/ ModelManifest.xml .vs -/Sunfish/ShareWeb/TestFS ShareWeb +/Sunfish/bin/Debug/* +!/Sunfish +!/Sunfish/bin +!/Sunfish/bin/Debug +!/Sunfish/bin/Debug/$sunfish diff --git a/README.md b/README.md index 85e691d..72ced7d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ Sunfish =============== -A Lightweight & portable file server \ No newline at end of file +A lightweight & portable web & file server in C# + +|![](doc/main.png)| +|:--:| +| Main screen | + diff --git a/Resources/$sunfish/$templates.html b/Resources/$sunfish/$templates.html deleted file mode 100644 index 8da28f7..0000000 --- a/Resources/$sunfish/$templates.html +++ /dev/null @@ -1,39 +0,0 @@ - - - {AppName} {AppVersion} - - - - - - - - - - - - {Include} - - - -
- -
- - -{%:sunfish-header}{%Items:directory-item}{%:sunfish-footer} - -
- - -
{Name}
-
{Description}
-
{@Actions}
{%Actions:action}
{/} -
- -{@Icon}{Icon}{/}{Name} diff --git a/Resources/$sunfish/folder.png b/Resources/$sunfish/folder.png deleted file mode 100644 index 39ec901..0000000 --- a/Resources/$sunfish/folder.png +++ /dev/null Binary files differ diff --git a/Resources/$sunfish/konsole.js b/Resources/$sunfish/konsole.js deleted file mode 100644 index fd5ba0f..0000000 --- a/Resources/$sunfish/konsole.js +++ /dev/null @@ -1,270 +0,0 @@ -function Konsole() { - var k = {}; - - function init() { - buildUI(); - install(); - function inject() { - buildStyles(); - document.body.appendChild(k.root); - } - if (document.body) - inject(); - else - window.addEventListener("load", inject); - } - - function build(def, into) { - var el; - if (!def.$) { - el = document.createTextNode(def); - if (into) - into.appendChild(el); - return el; - } - el = document.createElement(def.$); - var childs = def._; - delete def.$; - delete def._; - for (var i in def) { - if (i == "style") - for (var sn in def.style) - el.style[sn] = def.style[sn]; - else if (i == "event") - for (var en in def.event) - el.addEventListener(en, def.event[en]); - else - el[i] = def[i]; - } - if (childs) - if (typeof childs == "string") - el.appendChild(document.createTextNode(childs)); - else - for (var i in childs) - build(childs[i], el); - if (into) - into.appendChild(el); - return el; - } - - function buildStyles() { - k.style = build({ $: "style" }, document.head); - var rules = ""; - function rule(name, content) { - if (rules) - rules += "\n"; - rules += name + "{" + content + "}"; - } - rule("div#konsole", "all:initial;all:unset;position:fixed;top:0;left:0;width:0;height:0;font-family:sans-serif;z-index:1000"); - rule("div#konsole div#konsole-tab", "position:fixed;top:0;right:15px;width:20px;height:26px;text-align:center;padding-top:4px;background-color:#bd7a00;user-select:none;" + - "color:white;border-bottom-left-radius:10px;border-bottom-right-radius:2px;z-index:10;opacity:0.2;cursor:pointer;transition: opacity 0.2s, background-color 0.2s;" + - "font-size:16px;"); - rule("div#konsole div#konsole-tab:hover", "opacity:1;"); - rule("div#konsole div#konsole-tab.error", "opacity:1;background-color:tomato;"); - rule("div#konsole div#konsole-panel", "position:fixed;top:0;left:50px;right:50px;height:0%;background-color:#bd7a00;color:white;" + - "border-bottom-left-radius:30px;border-bottom-right-radius:5px;font-size:12px;overflow:hidden;transition: height 0.3s"); - rule("div#konsole div#konsole-panel.open", "height:98%"); - rule("div#konsole div#konsole-input-panel", "position:absolute;left:8px;right:8px;bottom:8px;"); - rule("div#konsole input#konsole-input", "width:100%;border-radius:5px;border-bottom-left-radius:20px;border-top-left-radius:10px"); - rule("div#konsole div#konsole-output", "position:absolute;top:0;left:8px;right:8px;bottom:44px;overflow:auto;background-color:white;padding-top:10px;" + - "border-bottom-left-radius:5px;border-bottom-right-radius:5px;color:black"); - rule("div#konsole div.konsole-sign", "display:inline-block;width:16px;text-align:center;font-weight:bold;vertical-align:top;"); - rule("div#konsole div.konsole-sign.error", "color:tomato"); - rule("div#konsole div.konsole-sign.input", "color:#00459e"); - rule("div#konsole div.konsole-sign.output", "color:#7bc012"); - rule("div#konsole div.konsole-block", "display:inline-block"); - k.style.innerHTML = rules; - } - - function buildUI() { - function kTabClick() { - if (k.panel.className == "") - open(); - else - close(); - } - function kInputKeypress(e) { - if (e.keyCode == 13) - try { - var cmd = k.in.value.trim(); - if (cmd == "") - return; - k.in.value = ""; - exec(cmd); - } catch (e) { - error(e); - } - } - k.root = build({ $: "div", id: "konsole" }); - k.tab = build({ $: "div", id: "konsole-tab", title: "Konsole", _: "K", event: { click: kTabClick } }, k.root); - k.panel = build({ $: "div", id: "konsole-panel" }, k.root); - k.inp = build({ $: "div", id: "konsole-input-panel" }, k.panel); - k.in = build({ $: "input", id: "konsole-input", event: { keypress: kInputKeypress } }, k.inp); - k.out = build({ $: "div", id: "konsole-output" }, k.panel); - } - - function install() { - if (!console) - console = {}; - function kk(name, type) { - console["_k_" + name] = console[name]; - console[name] = function (par) { - out(type, par); - console["_k_" + name](par); - } - } - kk("info", "i"); - kk("warn", "w"); - kk("log", "l"); - kk("debug", "d"); - kk("error", "e"); - window.addEventListener('error', function (e) { - k.tab.className = "error"; - var msg = e.message; - if (e.filename) - msg += "\n on " + e.filename + " (" + e.lineno + ":" + e.colno + ")"; - out("!", msg, e); - }); - } - - function rowDiv(type, child) { - var types = { - "!": "error", - ">": "input", - "<": "output", - } - var d = build({ $: "div", _: [{ $: "div", className: "konsole-sign " + types[type], _: type }] }, k.out); - var di = build({ $: "div", className: "konsole-block" }, d); - if (typeof child == "string") - di.innerText = child; - else - di.appendChild(child); - d.scrollIntoView(); - } - - function error(s) { - if (s.stack) - rowDiv("!", s.stack); - else - rowDiv("!", "" + s); - } - - function writeIn(s) { - rowDiv(">", s); - } - - function dump(o, into, expanded) { - if (o === null || o === undefined) { - var d = document.createElement("div"); - d.style.color = "#777777"; - d.innerText = "<" + o + ">"; - into.appendChild(d); - return; - } - switch (typeof o) { - case "string": { - var d = document.createElement("div"); - d.style.color = "#009900"; - d.innerText = '"' + o + '"'; - into.appendChild(d); - } - break; - case "null": - case "undefined": { - var d = document.createElement("div"); - d.style.color = "#999999"; - d.innerText = "<" + o + ">"; - into.appendChild(d); - } - break; - case "number": - case "boolean": { - var d = document.createElement("div"); - d.innerText = o; - into.appendChild(d); - } - break; - case "object": - if (expanded) { - var r = document.createElement("div"); - var d = document.createElement("span"); - d.innerText = "{"; - r.appendChild(d); - for (var k in o) { - d = document.createElement("div"); - var s = document.createElement("span"); - s.style.verticalAlign = "top"; - s.style.color = "#554455"; - s.innerText = k + ":"; - d.appendChild(s); - s = document.createElement("span"); - s.style.display = "inline-block"; - dump(o[k], s, false); - d.appendChild(s); - r.appendChild(d); - } - d = document.createElement("span"); - d.innerText = "}"; - r.appendChild(d); - into.appendChild(r); - } else { - var d = document.createElement("div"); - d.style.backgroundColor = "#C9C9C9"; - d.style.borderRadius = "5px"; - d.style.cursor = "pointer"; - d.innerText = ""; - into.appendChild(d); - d.addEventListener("click", function () { - into.removeChild(d); - dump(o, into, true); - }); - } - break; - case "function": { - var d = document.createElement("div"); - d.style.color = "#000099"; - d.innerText = ""; - into.appendChild(d); - } - break; - default: - throw new Error("Cómo: " + typeof o) - } - } - - function out(type, data, extended) { - var row = build({ $: "div", className: "konsole-block object" }); - dump(data, row, true); - rowDiv(type, row); - } - - function exec(cmd) { - writeIn(cmd); - out("<", eval(cmd)); - } - - function showTab() { - k.root.appendChild(k.tab); - } - - function hideTab() { - k.root.removeChild(k.tab); - } - - function open(obj) { - k.panel.className = "open"; - k.tab.className = ""; - } - - function close() { - k.panel.className = ""; - } - - init(); - this.open = open; - this.close = close; - this.showTab = showTab; - this.hideTab = hideTab; -} - -var konsole = new Konsole(); \ No newline at end of file diff --git a/Resources/$sunfish/logo.png b/Resources/$sunfish/logo.png deleted file mode 100644 index 82fc5d2..0000000 --- a/Resources/$sunfish/logo.png +++ /dev/null Binary files differ diff --git a/Resources/$sunfish/style.css b/Resources/$sunfish/style.css deleted file mode 100644 index 388a3a9..0000000 --- a/Resources/$sunfish/style.css +++ /dev/null @@ -1,355 +0,0 @@ -html { - margin: 0; - padding: 0; - background: #b9b9b9; -} - -body { - margin: 0; - padding: 0; - width: 100%; - background: white; -} - -a { - text-decoration: none; - color: black; -} - -* { - box-sizing: border-box; -} - -a:hover { - color: #5d6962; -} - -#head { - height: 4.7rem; - background: white url(/$sunfish/logo.png) 1rem 1rem no-repeat; - padding: 1rem; - position: relative; -} - -#main-title { - font-family: 'Montserrat', Verdana, sans-serif; - font-size: 2.2rem; - left: 4.5rem; - top: 0.3rem; - position: absolute; -} - -#main-subtitle { - position: absolute; - font-family: 'Montserrat', Verdana, sans-serif; - font-size: 1.1rem; - color: #000000b2; - top: 1.3rem; - left: 13rem; -} - -#main-location { - position: absolute; - bottom: 0; - left: 4.5rem; - right: 0; - font-family: 'Open Sans', sans-serif; - font-size: 0.9rem; - color: #696969; - background: #a6e1ff; - padding: 0.2rem; - border-top-left-radius: 0.6rem; - border-bottom-left-radius: 0.6rem; -} - -#main-location>a::after { - content: " / "; - color: #777777; -} - -#main-location>a>i.material-icons-round { - font-size: 1.3rem; - vertical-align: bottom; -} - -#main-toolbar { - position: absolute; - right: 4rem; - top: 0.8rem; -} - -#main-toolbar i.material-icons-round { - font-size: 26px; - cursor: pointer; - color: dimgrey; -} - -#main-toolbar i.material-icons-round:hover { - font-size: 26px; - cursor: pointer; - color: #818181; -} - -#wrapper { - margin: 0 auto; - padding: 0.5rem; - font-family: 'Roboto Condensed', sans-serif; - text-align: center; -} - -.item { - position: relative; - cursor: pointer; - cursor: hand; - padding: 0.5rem; - margin: 5px 5px 9px 5px; - width: 22.5rem; - max-width: calc(100% - 2rem); - display: inline-block; - border-bottom: 1px solid #DEDEDE; - text-align: initial; - height: 3rem; -} - -.item:hover { - background-color: #ecf9ff; - border-radius: 0.2rem; -} - -.item-icon { - position: absolute; - top: 0.5rem; - left: 0.5rem; - max-width: 32px; - max-height: 32px; -} - -.item-name { - position: absolute; - top: 0.3rem; - left: 3rem; - font-weight: 500; - font-size: 0.9rem; -} - -.item-desc { - position: absolute; - bottom: 0.5rem; - left: 4rem; - font-weight: 400; - font-size: 0.7rem; - color: gray; -} - -.item-actions { - position: absolute; - bottom: 0.1rem; - right: 0rem; - padding-right: 0.5rem; -} - -.item-actions i.material-icons-round { - font-size: 1.1rem; - color: #aaaaaa; - margin-left: 0.3rem; -} - -.test { - background-color: #4f6e05; -} - -.item.active { - cursor: pointer; -} - -.item>a { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.desktop .item-actions { - display: none; -} - -.desktop .item:hover .item-actions { - display: unset; -} - -.popup { - position: fixed; - top: 50%; - left: 50%; - width: 30rem; - max-width: 90%; - background-color: rgba(39, 36, 31, 0.822); - font-family: 'Roboto Condensed', sans-serif; - font-weight: bold; - color: white; - padding: 1rem; - margin-left: -15rem; - margin-top: -5rem; - border-radius: 0.5rem; -} - -.popup.error { - background-color: rgba(116, 11, 3, 0.822); -} - -.popup>.body { - padding-bottom: 1rem; - font-size: 1.1rem; -} - -.popup>.buttons { - text-align: right; -} - -.popup>.body>.input>input { - display: block; - width: 100%; -} - -.popup-wall { - z-index: 200; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.199); -} - -button { - background-color: #e2d9cd; - font-family: 'Open Sans', sans-serif; - font-weight: bold; - color: #424242; - border: 0; - border-radius: 1rem; - margin-left: 0.5rem; - padding: 0.3rem 1rem; - outline: none; -} - -button:hover { - background-color: #beb7ae; -} - -button.warning { - background-color: tomato; -} - -button.warning:hover { - background-color: #d4523b; -} - -input { - border: 0; - background-color: whitesmoke; - border-radius: 1rem; - font-family: 'Open Sans', sans-serif; - margin-top: 1rem; - padding: 0.3rem 1rem; - outline: none; -} - -input:hover { - background-color: #cecece; -} - -.upload-drop { - background-color: rgba(0, 0, 0, 0.75); - display: none; -} - -.upload-drop-icon { - position: absolute; - top: 5%; - height: 90%; - left: 25%; - width: 50%; - bottom: 25%; - border: 10px dashed #c1c1c1; - border-radius: 2rem; - vertical-align: middle; - overflow: hidden; -} - -.upload-drop-icon>i.material-icons-round { - width: 100%; - color: white; - height: 100%; - vertical-align: middle; - font-size: 8rem; - margin-top: 35%; - text-align: center; -} - -.upload-drop-icon>i.material-icons-round::before { - content: "Drop here to upload"; - position: absolute; - top: 58%; - left: 0; - right: 0; - font-family: 'Open Sans'; - font-size: 1rem; -} - -.upload-list { - height: calc( 100% - 4rem); - overflow-y: auto; - padding: 0 1rem; - margin: 1rem 0; -} - -.upload-progress-box { - height: 2rem; - background: #383838; - position: relative; -} - -.upload-progress { - position: absolute; - top: 0; - left: 0; - bottom: 0; - background: #a6e1ff; -} - -.upload-item { - font-family: 'Open Sans'; - color: white; - margin-bottom: 1rem; -} - -.upload-item-progress-box { - height: 1rem; - background: #383838; - border-radius: 0.5rem; - margin-top: 0.2rem; - border: 1px solid black; - position: relative; - overflow: hidden; -} - -.upload-item-progress { - position: absolute; - top: 0; - left: 0; - bottom: 0; - background: #a6e1ff url(/$sunfish/logo.png) 100% 0.05rem/0.8rem 0.8rem no-repeat; -} - -.upload-close { - margin-top: 0.15rem; - background: #6d6d6d; - color: white; - margin-left: calc( 100% - 5rem); -} - -body.dragging .upload-drop{ - display: block; -} \ No newline at end of file diff --git a/Resources/$sunfish/sunfish-directory.js b/Resources/$sunfish/sunfish-directory.js deleted file mode 100644 index 0a6e991..0000000 --- a/Resources/$sunfish/sunfish-directory.js +++ /dev/null @@ -1,327 +0,0 @@ -(function () { - - /** - * Get the item element form any sub element - * @param {*} subElement DOM element child of item - */ - function getItem(subElement) { - while (subElement.className.indexOf("item-row") < 0) - subElement = subElement.parentElement; - return subElement; - } - - sunfish.deleteFile = function (sender) { - //TODO: use the DELTE http method - var ask; - var item = getItem(sender); - var isfolder = item.className.indexOf("directory") >= 0; - var file = item.querySelector("div.item-name").innerText; - if (isfolder) - ask = "Sure to delete folder " + file + " and all its contents? can not be undone."; - else - ask = "Sure to delete " + file + "? can not be undone."; - sunfish.ask(ask, - { b: 'Cancel' }, - { - b: 'Delete', class: 'warning', do: function () { - var unlock = sunfish.lock("deleting..."); - sunfish.ajax(document.location.href + file + "?action=delete", { - ok: function (result) { - if (result == "OK") { - unlock(); - item.parentNode.removeChild(item); - } else { - unlock(); - sunfish.error("Error deleting file", function () { - document.location.reload(); - }); - } - } - }) - } - } - - ); - } - - sunfish.renameFile = function (sender) { - var item = getItem(sender); - var file = item.querySelector("div.item-name").innerText; - sunfish.askString('Rename to:', file, function (newName) { - sunfish.ajax(document.location.href + file + "?action=rename&to=" + newName, { - ok: function (result) { - if (result == "OK") { - item.querySelector("div.item-name").innerText = newName; - } else - sunfish.error("Error renaming file", function () { - document.location.reload(); - }); - } - }); - }); - } - - sunfish.openFile = function (sender) { - var item = getItem(sender); - var file = item.querySelector("div.item-name").innerText; - sunfish.ajax(document.location.href + file + "?action=open", { - ok: function (result) { - if (result != "OK") - sunfish.error("Error opening file in server", function () { - document.location.reload(); - }); - } - }); - } - - sunfish.newFolder = function (sender) { - sunfish.askString('Folder name:', "", function (name) { - if (!name) - return; - sunfish.ajax(document.location.href + name + '/', { - method: "PUT", - ok: function (result) { - if (result != "OK") - sunfish.error("Error creading file", function () { - document.location.reload(); - }); - else - document.location.reload(); - } - }); - }); - } - - sunfish.newFile = function (sender) { - sunfish.askString('File name:', "", function (name) { - if (!name) - return; - sunfish.ajax(document.location.href + name, { - method: "PUT", - ok: function (result) { - if (result != "OK") - sunfish.error("Error creading file", function () { - document.location.reload(); - }); - else - document.location.reload(); - } - }); - }); - } - - sunfish.uploadFile = function (sender) { - var efile = document.createElement("input"); - efile.type = "file"; - efile.onchange = () => { - var fileList = efile.files; - for (var i = 0; i < fileList.length; i++) - upl.upload(fileList[i]); - } - efile.click(); - } - - // UPLOAD task - var upl; - function Uploader() { - var pb, elist, uploads = []; - var edrop = sunfish.build({ - $: "div", className: "popup-wall upload-drop", _: [ - { - $: "div", className: "upload-drop-icon", _: [ - { $: "i", className: "material-icons-round", _: ["upload"] } - ] - } - ] - }); - var ctt = edrop.querySelector("div.upload-drop-icon"); - - document.body.appendChild(edrop); - - function show() { - document.body.appendChild(edrop); - var cbutton; - ctt.innerHTML = ""; - elist = sunfish.build({ $: "div", className: "upload-list" }, ctt); - elist.addItem = function (file, name, to) { - if (!pb.updater) { - pb.updater = function () { - var tpos = 0, tmax = 0; - uploads.forEach(u => { - tpos += u.pos; - tmax += u.length; - }) - if (tmax == 0) - tmax = 100; - pb.style.width = (tpos / tmax * 100) + "%"; - if (tpos < tmax) { - setTimeout(pb.updater, 100); - cbutton.style.display = "none"; - } else { - delete pb.updater; - cbutton.style.display = ""; - } - } - setTimeout(pb.updater, 100); - } - var item = sunfish.build({ - $: "div", className: "upload-item", _: [{ $: "div", _: [to] }, - { $: "div", className: "upload-item-progress-box", _: [{ $: "div", className: "upload-item-progress" }] }] - }, elist); - var itemProgress = item.querySelector(".upload-item-progress"); - var me; - uploads.push(me = { - file: file, - name: name, - to: to, - progress: itemProgress, - length: file.size, - pos: 0, - eof: false - }); - function updateProgress() { - if (me.length <= 0) - return; - var pos = me.pos; - if (pos > me.length) - pos = me.length; - itemProgress.style.width = (pos / me.length) * 100 + "%"; - - } - function ko(info) { - itemProgress.style.width = "150%"; - itemProgress.style.backgroundColor = "tomato"; - if (info) - console.error(info); - me.pos = me.length; - } - function step() { - var length = Math.min(1024 * 512, me.length - me.pos); - var blob = file.slice(me.pos, me.pos + length); - if (blob.length == 0) - return ko("Nothing to send"); - sunfish.ajax(document.location.href + to, { - method: "PUT", - data: blob, - headers: { - "X-Sunfish-Offset": me.pos, - "X-Sunfish-Length": blob.size - }, - ok: function (result) { - if (result != "OK") - ko("Upload is " + result); - else { - me.pos += blob.size; - updateProgress(); - if (me.pos < me.length) - step(); - else { - itemProgress.style.backgroundColor = "#46ad33"; - me.eof = true; - } - } - }, - ko: ko - }); - } - step(); - } - var pbb = sunfish.build({ $: "div", className: "upload-progress-box" }, ctt); - pb = sunfish.build({ - $: "div", className: "upload-progress", _: [ - { - $: "button", className: "upload-close", style: { display: "none" }, _: ["Close"], onclick: function () { - /* - document.body.removeChild(edrop); - uploads = []; - elist.innerHTML=""; - cbutton.style.display = "none"; - pb.style.width="0"; - */ - document.location.reload(); - } - } - ] - }, pbb); - cbutton = pb.querySelector(".upload-close"); - } - - document.body.addEventListener("drop", function (ev) { - function processWKItem(item, path) { - path = path || ""; - if (item.isFile) { - item.file(function (file) { - upload(file, path + file.name); - }); - } else if (item.isDirectory) { - // Get folder contents - var dirReader = item.createReader(); - dirReader.readEntries(function (entries) { - for (var i = 0; i < entries.length; i++) { - processWKItem(entries[i], path + item.name + "/"); - } - }); - } - } - ev.preventDefault(); - if (ev.dataTransfer.items) { - var items = ev.dataTransfer.items; - for (var i = 0; i < items.length; i++) { - var item = items[i]; - if (item.kind === 'file') - if (item.webkitGetAsEntry) - processWKItem(item.webkitGetAsEntry()); - else - upload(item.getAsFile()); - } - } else { - for (var i = 0; i < ev.dataTransfer.files.length; i++) - upload(ev.dataTransfer.files[i].getAsFile()); - } - }); - document.body.addEventListener("dragover", function (ev) { - console.info("drag-over"); - ev.stopPropagation(); - ev.preventDefault(); - var anyfile = false; - if (ev.dataTransfer.items) { - for (var i = 0; i < ev.dataTransfer.items.length; i++) - if (anyfile = ev.dataTransfer.items[i].kind == "file") - break; - } else - anyfile = ev.dataTransfer.files.length > 0; - if (anyfile) - document.body.classList.add("dragging"); - }); - - edrop.addEventListener("dragleave", function (ev) { - console.info("drag-leave"); - ev.stopPropagation(); - ev.preventDefault(); - var anyfile = false; - if (ev.dataTransfer.items) { - for (var i = 0; i < ev.dataTransfer.items.length; i++) - if (anyfile = ev.dataTransfer.items[i].kind == "file") - break; - } else - anyfile = ev.dataTransfer.files.length > 0; - if (anyfile) - document.body.classList.remove("dragging"); - }); - - function upload(file, to) { - document.body.appendChild(edrop); - if (!elist) - show(); - to = to || file.name; - elist.addItem(file, file.name, to); - } - - this.upload = upload; - } - - window.addEventListener("load", function () { - upl = new Uploader(); - }); - -})(); \ No newline at end of file diff --git a/Resources/$sunfish/sunfish.js b/Resources/$sunfish/sunfish.js deleted file mode 100644 index 46684d5..0000000 --- a/Resources/$sunfish/sunfish.js +++ /dev/null @@ -1,183 +0,0 @@ -function Sunfish() { - var mobile; - function init() { - mobileCheck = function () { - var check = false; - (function (a) { - if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; - })(navigator.userAgent || navigator.vendor || window.opera); - return check; - }; - mobile = mobileCheck(); - document.body.className = mobile ? "mobile" : "desktop"; - konsole.showTab(); - } - - function build(def, into) { - var el; - if (!def.$) { - el = document.createTextNode(def); - if (into) - into.appendChild(el); - return el; - } - el = document.createElement(def.$); - var childs = def._; - delete def.$; - delete def._; - for (var i in def) { - if (i == "style") - for (var sn in def.style) - el.style[sn] = def.style[sn]; - else - el[i] = def[i]; - } - if (childs) - if (typeof childs == "string") - el.appendChild(document.createTextNode(childs)); - else - for (var i in childs) - build(childs[i], el); - if (into) - into.appendChild(el); - return el; - } - - function dialog(content, buttons, classes) { - var btdef = []; - for (var i in buttons) { - var bt = buttons[i]; - if (bt.$) - btdef.push(bt); - else - btdef.push({ - $: "button", - className: bt.class, - _: bt.b, - onclick: bt.do - }); - } - - var wall = build({ $: "div", className: "popup-wall" }, document.body); - var def = { - $: "div", className: "popup" + (classes ? " " + classes : ""), _: [ - { $: "div", className: "body", _: content }, - { $: "div", className: "buttons", _: btdef } - ] - }; - var dialog = build(def, wall); - dialog.close = function () { - document.body.removeChild(wall); - } - return dialog; - } - - function ask(question, bt1, bt2, bt3, bt4, bt5, iserror) { - var buttons = []; - var el; - function addbt(bt) { - if (bt) { - if (bt.go) - bt.do = function () { document.location = bt.go }; - buttons.push({ - class: bt.class, - b: bt.b, - do: function () { - bt.do && bt.do(); - el.close(); - } - }); - } - } - addbt(bt1); - addbt(bt2); - addbt(bt3); - addbt(bt4); - addbt(bt5); - return el = dialog([question], buttons, iserror ? "error" : null); - } - - function askString(text, def, cb) { - var input; - var dialog = ask({ $: "div", className: "input", _: [text, { $: "input", id: "askInput", value: def }] }, { b: 'Cancel' }, { - b: 'Ok', class: "ok", do: function () { - var value = input.value; - cb && cb(value); - } - }); - input = dialog.querySelector("input#askInput"); - input.focus(); - input.selectionStart = 0; - input.selectionEnd = input.value.length; - input.onkeypress = function (e) { - if (e.keyCode == 13) - dialog.querySelector("button.ok").click(); - }; - } - - function say(text, cb) { - ask(text, { b: 'Ok', do: cb }); - } - - function error(text, cb) { - ask(text, { b: 'Ok', do: cb }, null, null, null, null, true); - } - - function lock(text) { - var wall = build({ $: "div", className: "popup-wall" }, document.body); - var def = { - $: "div", className: "popup", _: [ - { $: "div", className: "body", _: [text] } - ] - }; - build(def, wall); - return function () { - document.body.removeChild(wall); - } - } - - function go(to) { - document.location = to - } - - function ajax(url, ctrl) { - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) - ctrl.ok && ctrl.ok(xhr.responseText, xhr); - }; - xhr.open(ctrl.method || 'GET', url); - if (ctrl.headers) - for (var k in ctrl.headers) - xhr.setRequestHeader(k, ctrl.headers[k]); - if (ctrl.binary) - xhr.sendAsBinary(ctrl.binary, "binary/octet"); - else - xhr.send(ctrl.data); - } - - if (!XMLHttpRequest.prototype.sendAsBinary) - XMLHttpRequest.prototype.sendAsBinary = function (datastr, contentType) { - var bb = new BlobBuilder(); - var len = datastr.length; - var data = new Uint8Array(len); - for (var i = 0; i < len; i++) { - data[i] = datastr.charCodeAt(i); - } - bb.append(data.buffer); - this.send(bb.getBlob(contentType)); - } - - this.init = init; - this.build = build; - this.dialog = dialog; - this.ask = ask; - this.askString = askString; - this.say = say; - this.error = error; - this.lock = lock; - this.go = go; - this.ajax = ajax; -} - -var sunfish = new Sunfish(); diff --git a/Sunfish/Middleware/VFSFolderFileSystem.cs b/Sunfish/Middleware/VFSFolderFileSystem.cs index 7eef5d5..4d6b60f 100644 --- a/Sunfish/Middleware/VFSFolderFileSystem.cs +++ b/Sunfish/Middleware/VFSFolderFileSystem.cs @@ -15,7 +15,8 @@ public VFSFolderFileSystem(string path) { basePath = path; - while (basePath[basePath.Length - 1] == (Path.DirectorySeparatorChar)) + while (basePath[basePath.Length - 1] == Path.DirectorySeparatorChar || + basePath[basePath.Length - 1] == Path.AltDirectorySeparatorChar) { if (basePath.Length < 2) throw new Exception("Invalid path"); diff --git a/Sunfish/bin/Debug/$sunfish/$templates.html b/Sunfish/bin/Debug/$sunfish/$templates.html new file mode 100644 index 0000000..8da28f7 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/$templates.html @@ -0,0 +1,39 @@ + + + {AppName} {AppVersion} + + + + + + + + + + + + {Include} + + + +
+ +
+ + +{%:sunfish-header}{%Items:directory-item}{%:sunfish-footer} + +
+ + +
{Name}
+
{Description}
+
{@Actions}
{%Actions:action}
{/} +
+ +{@Icon}{Icon}{/}{Name} diff --git a/Sunfish/bin/Debug/$sunfish/folder.png b/Sunfish/bin/Debug/$sunfish/folder.png new file mode 100644 index 0000000..39ec901 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/folder.png Binary files differ diff --git a/Sunfish/bin/Debug/$sunfish/konsole.js b/Sunfish/bin/Debug/$sunfish/konsole.js new file mode 100644 index 0000000..fd5ba0f --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/konsole.js @@ -0,0 +1,270 @@ +function Konsole() { + var k = {}; + + function init() { + buildUI(); + install(); + function inject() { + buildStyles(); + document.body.appendChild(k.root); + } + if (document.body) + inject(); + else + window.addEventListener("load", inject); + } + + function build(def, into) { + var el; + if (!def.$) { + el = document.createTextNode(def); + if (into) + into.appendChild(el); + return el; + } + el = document.createElement(def.$); + var childs = def._; + delete def.$; + delete def._; + for (var i in def) { + if (i == "style") + for (var sn in def.style) + el.style[sn] = def.style[sn]; + else if (i == "event") + for (var en in def.event) + el.addEventListener(en, def.event[en]); + else + el[i] = def[i]; + } + if (childs) + if (typeof childs == "string") + el.appendChild(document.createTextNode(childs)); + else + for (var i in childs) + build(childs[i], el); + if (into) + into.appendChild(el); + return el; + } + + function buildStyles() { + k.style = build({ $: "style" }, document.head); + var rules = ""; + function rule(name, content) { + if (rules) + rules += "\n"; + rules += name + "{" + content + "}"; + } + rule("div#konsole", "all:initial;all:unset;position:fixed;top:0;left:0;width:0;height:0;font-family:sans-serif;z-index:1000"); + rule("div#konsole div#konsole-tab", "position:fixed;top:0;right:15px;width:20px;height:26px;text-align:center;padding-top:4px;background-color:#bd7a00;user-select:none;" + + "color:white;border-bottom-left-radius:10px;border-bottom-right-radius:2px;z-index:10;opacity:0.2;cursor:pointer;transition: opacity 0.2s, background-color 0.2s;" + + "font-size:16px;"); + rule("div#konsole div#konsole-tab:hover", "opacity:1;"); + rule("div#konsole div#konsole-tab.error", "opacity:1;background-color:tomato;"); + rule("div#konsole div#konsole-panel", "position:fixed;top:0;left:50px;right:50px;height:0%;background-color:#bd7a00;color:white;" + + "border-bottom-left-radius:30px;border-bottom-right-radius:5px;font-size:12px;overflow:hidden;transition: height 0.3s"); + rule("div#konsole div#konsole-panel.open", "height:98%"); + rule("div#konsole div#konsole-input-panel", "position:absolute;left:8px;right:8px;bottom:8px;"); + rule("div#konsole input#konsole-input", "width:100%;border-radius:5px;border-bottom-left-radius:20px;border-top-left-radius:10px"); + rule("div#konsole div#konsole-output", "position:absolute;top:0;left:8px;right:8px;bottom:44px;overflow:auto;background-color:white;padding-top:10px;" + + "border-bottom-left-radius:5px;border-bottom-right-radius:5px;color:black"); + rule("div#konsole div.konsole-sign", "display:inline-block;width:16px;text-align:center;font-weight:bold;vertical-align:top;"); + rule("div#konsole div.konsole-sign.error", "color:tomato"); + rule("div#konsole div.konsole-sign.input", "color:#00459e"); + rule("div#konsole div.konsole-sign.output", "color:#7bc012"); + rule("div#konsole div.konsole-block", "display:inline-block"); + k.style.innerHTML = rules; + } + + function buildUI() { + function kTabClick() { + if (k.panel.className == "") + open(); + else + close(); + } + function kInputKeypress(e) { + if (e.keyCode == 13) + try { + var cmd = k.in.value.trim(); + if (cmd == "") + return; + k.in.value = ""; + exec(cmd); + } catch (e) { + error(e); + } + } + k.root = build({ $: "div", id: "konsole" }); + k.tab = build({ $: "div", id: "konsole-tab", title: "Konsole", _: "K", event: { click: kTabClick } }, k.root); + k.panel = build({ $: "div", id: "konsole-panel" }, k.root); + k.inp = build({ $: "div", id: "konsole-input-panel" }, k.panel); + k.in = build({ $: "input", id: "konsole-input", event: { keypress: kInputKeypress } }, k.inp); + k.out = build({ $: "div", id: "konsole-output" }, k.panel); + } + + function install() { + if (!console) + console = {}; + function kk(name, type) { + console["_k_" + name] = console[name]; + console[name] = function (par) { + out(type, par); + console["_k_" + name](par); + } + } + kk("info", "i"); + kk("warn", "w"); + kk("log", "l"); + kk("debug", "d"); + kk("error", "e"); + window.addEventListener('error', function (e) { + k.tab.className = "error"; + var msg = e.message; + if (e.filename) + msg += "\n on " + e.filename + " (" + e.lineno + ":" + e.colno + ")"; + out("!", msg, e); + }); + } + + function rowDiv(type, child) { + var types = { + "!": "error", + ">": "input", + "<": "output", + } + var d = build({ $: "div", _: [{ $: "div", className: "konsole-sign " + types[type], _: type }] }, k.out); + var di = build({ $: "div", className: "konsole-block" }, d); + if (typeof child == "string") + di.innerText = child; + else + di.appendChild(child); + d.scrollIntoView(); + } + + function error(s) { + if (s.stack) + rowDiv("!", s.stack); + else + rowDiv("!", "" + s); + } + + function writeIn(s) { + rowDiv(">", s); + } + + function dump(o, into, expanded) { + if (o === null || o === undefined) { + var d = document.createElement("div"); + d.style.color = "#777777"; + d.innerText = "<" + o + ">"; + into.appendChild(d); + return; + } + switch (typeof o) { + case "string": { + var d = document.createElement("div"); + d.style.color = "#009900"; + d.innerText = '"' + o + '"'; + into.appendChild(d); + } + break; + case "null": + case "undefined": { + var d = document.createElement("div"); + d.style.color = "#999999"; + d.innerText = "<" + o + ">"; + into.appendChild(d); + } + break; + case "number": + case "boolean": { + var d = document.createElement("div"); + d.innerText = o; + into.appendChild(d); + } + break; + case "object": + if (expanded) { + var r = document.createElement("div"); + var d = document.createElement("span"); + d.innerText = "{"; + r.appendChild(d); + for (var k in o) { + d = document.createElement("div"); + var s = document.createElement("span"); + s.style.verticalAlign = "top"; + s.style.color = "#554455"; + s.innerText = k + ":"; + d.appendChild(s); + s = document.createElement("span"); + s.style.display = "inline-block"; + dump(o[k], s, false); + d.appendChild(s); + r.appendChild(d); + } + d = document.createElement("span"); + d.innerText = "}"; + r.appendChild(d); + into.appendChild(r); + } else { + var d = document.createElement("div"); + d.style.backgroundColor = "#C9C9C9"; + d.style.borderRadius = "5px"; + d.style.cursor = "pointer"; + d.innerText = ""; + into.appendChild(d); + d.addEventListener("click", function () { + into.removeChild(d); + dump(o, into, true); + }); + } + break; + case "function": { + var d = document.createElement("div"); + d.style.color = "#000099"; + d.innerText = ""; + into.appendChild(d); + } + break; + default: + throw new Error("Cómo: " + typeof o) + } + } + + function out(type, data, extended) { + var row = build({ $: "div", className: "konsole-block object" }); + dump(data, row, true); + rowDiv(type, row); + } + + function exec(cmd) { + writeIn(cmd); + out("<", eval(cmd)); + } + + function showTab() { + k.root.appendChild(k.tab); + } + + function hideTab() { + k.root.removeChild(k.tab); + } + + function open(obj) { + k.panel.className = "open"; + k.tab.className = ""; + } + + function close() { + k.panel.className = ""; + } + + init(); + this.open = open; + this.close = close; + this.showTab = showTab; + this.hideTab = hideTab; +} + +var konsole = new Konsole(); \ No newline at end of file diff --git a/Sunfish/bin/Debug/$sunfish/logo.png b/Sunfish/bin/Debug/$sunfish/logo.png new file mode 100644 index 0000000..82fc5d2 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/logo.png Binary files differ diff --git a/Sunfish/bin/Debug/$sunfish/style.css b/Sunfish/bin/Debug/$sunfish/style.css new file mode 100644 index 0000000..388a3a9 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/style.css @@ -0,0 +1,355 @@ +html { + margin: 0; + padding: 0; + background: #b9b9b9; +} + +body { + margin: 0; + padding: 0; + width: 100%; + background: white; +} + +a { + text-decoration: none; + color: black; +} + +* { + box-sizing: border-box; +} + +a:hover { + color: #5d6962; +} + +#head { + height: 4.7rem; + background: white url(/$sunfish/logo.png) 1rem 1rem no-repeat; + padding: 1rem; + position: relative; +} + +#main-title { + font-family: 'Montserrat', Verdana, sans-serif; + font-size: 2.2rem; + left: 4.5rem; + top: 0.3rem; + position: absolute; +} + +#main-subtitle { + position: absolute; + font-family: 'Montserrat', Verdana, sans-serif; + font-size: 1.1rem; + color: #000000b2; + top: 1.3rem; + left: 13rem; +} + +#main-location { + position: absolute; + bottom: 0; + left: 4.5rem; + right: 0; + font-family: 'Open Sans', sans-serif; + font-size: 0.9rem; + color: #696969; + background: #a6e1ff; + padding: 0.2rem; + border-top-left-radius: 0.6rem; + border-bottom-left-radius: 0.6rem; +} + +#main-location>a::after { + content: " / "; + color: #777777; +} + +#main-location>a>i.material-icons-round { + font-size: 1.3rem; + vertical-align: bottom; +} + +#main-toolbar { + position: absolute; + right: 4rem; + top: 0.8rem; +} + +#main-toolbar i.material-icons-round { + font-size: 26px; + cursor: pointer; + color: dimgrey; +} + +#main-toolbar i.material-icons-round:hover { + font-size: 26px; + cursor: pointer; + color: #818181; +} + +#wrapper { + margin: 0 auto; + padding: 0.5rem; + font-family: 'Roboto Condensed', sans-serif; + text-align: center; +} + +.item { + position: relative; + cursor: pointer; + cursor: hand; + padding: 0.5rem; + margin: 5px 5px 9px 5px; + width: 22.5rem; + max-width: calc(100% - 2rem); + display: inline-block; + border-bottom: 1px solid #DEDEDE; + text-align: initial; + height: 3rem; +} + +.item:hover { + background-color: #ecf9ff; + border-radius: 0.2rem; +} + +.item-icon { + position: absolute; + top: 0.5rem; + left: 0.5rem; + max-width: 32px; + max-height: 32px; +} + +.item-name { + position: absolute; + top: 0.3rem; + left: 3rem; + font-weight: 500; + font-size: 0.9rem; +} + +.item-desc { + position: absolute; + bottom: 0.5rem; + left: 4rem; + font-weight: 400; + font-size: 0.7rem; + color: gray; +} + +.item-actions { + position: absolute; + bottom: 0.1rem; + right: 0rem; + padding-right: 0.5rem; +} + +.item-actions i.material-icons-round { + font-size: 1.1rem; + color: #aaaaaa; + margin-left: 0.3rem; +} + +.test { + background-color: #4f6e05; +} + +.item.active { + cursor: pointer; +} + +.item>a { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.desktop .item-actions { + display: none; +} + +.desktop .item:hover .item-actions { + display: unset; +} + +.popup { + position: fixed; + top: 50%; + left: 50%; + width: 30rem; + max-width: 90%; + background-color: rgba(39, 36, 31, 0.822); + font-family: 'Roboto Condensed', sans-serif; + font-weight: bold; + color: white; + padding: 1rem; + margin-left: -15rem; + margin-top: -5rem; + border-radius: 0.5rem; +} + +.popup.error { + background-color: rgba(116, 11, 3, 0.822); +} + +.popup>.body { + padding-bottom: 1rem; + font-size: 1.1rem; +} + +.popup>.buttons { + text-align: right; +} + +.popup>.body>.input>input { + display: block; + width: 100%; +} + +.popup-wall { + z-index: 200; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.199); +} + +button { + background-color: #e2d9cd; + font-family: 'Open Sans', sans-serif; + font-weight: bold; + color: #424242; + border: 0; + border-radius: 1rem; + margin-left: 0.5rem; + padding: 0.3rem 1rem; + outline: none; +} + +button:hover { + background-color: #beb7ae; +} + +button.warning { + background-color: tomato; +} + +button.warning:hover { + background-color: #d4523b; +} + +input { + border: 0; + background-color: whitesmoke; + border-radius: 1rem; + font-family: 'Open Sans', sans-serif; + margin-top: 1rem; + padding: 0.3rem 1rem; + outline: none; +} + +input:hover { + background-color: #cecece; +} + +.upload-drop { + background-color: rgba(0, 0, 0, 0.75); + display: none; +} + +.upload-drop-icon { + position: absolute; + top: 5%; + height: 90%; + left: 25%; + width: 50%; + bottom: 25%; + border: 10px dashed #c1c1c1; + border-radius: 2rem; + vertical-align: middle; + overflow: hidden; +} + +.upload-drop-icon>i.material-icons-round { + width: 100%; + color: white; + height: 100%; + vertical-align: middle; + font-size: 8rem; + margin-top: 35%; + text-align: center; +} + +.upload-drop-icon>i.material-icons-round::before { + content: "Drop here to upload"; + position: absolute; + top: 58%; + left: 0; + right: 0; + font-family: 'Open Sans'; + font-size: 1rem; +} + +.upload-list { + height: calc( 100% - 4rem); + overflow-y: auto; + padding: 0 1rem; + margin: 1rem 0; +} + +.upload-progress-box { + height: 2rem; + background: #383838; + position: relative; +} + +.upload-progress { + position: absolute; + top: 0; + left: 0; + bottom: 0; + background: #a6e1ff; +} + +.upload-item { + font-family: 'Open Sans'; + color: white; + margin-bottom: 1rem; +} + +.upload-item-progress-box { + height: 1rem; + background: #383838; + border-radius: 0.5rem; + margin-top: 0.2rem; + border: 1px solid black; + position: relative; + overflow: hidden; +} + +.upload-item-progress { + position: absolute; + top: 0; + left: 0; + bottom: 0; + background: #a6e1ff url(/$sunfish/logo.png) 100% 0.05rem/0.8rem 0.8rem no-repeat; +} + +.upload-close { + margin-top: 0.15rem; + background: #6d6d6d; + color: white; + margin-left: calc( 100% - 5rem); +} + +body.dragging .upload-drop{ + display: block; +} \ No newline at end of file diff --git a/Sunfish/bin/Debug/$sunfish/sunfish-directory.js b/Sunfish/bin/Debug/$sunfish/sunfish-directory.js new file mode 100644 index 0000000..0a6e991 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/sunfish-directory.js @@ -0,0 +1,327 @@ +(function () { + + /** + * Get the item element form any sub element + * @param {*} subElement DOM element child of item + */ + function getItem(subElement) { + while (subElement.className.indexOf("item-row") < 0) + subElement = subElement.parentElement; + return subElement; + } + + sunfish.deleteFile = function (sender) { + //TODO: use the DELTE http method + var ask; + var item = getItem(sender); + var isfolder = item.className.indexOf("directory") >= 0; + var file = item.querySelector("div.item-name").innerText; + if (isfolder) + ask = "Sure to delete folder " + file + " and all its contents? can not be undone."; + else + ask = "Sure to delete " + file + "? can not be undone."; + sunfish.ask(ask, + { b: 'Cancel' }, + { + b: 'Delete', class: 'warning', do: function () { + var unlock = sunfish.lock("deleting..."); + sunfish.ajax(document.location.href + file + "?action=delete", { + ok: function (result) { + if (result == "OK") { + unlock(); + item.parentNode.removeChild(item); + } else { + unlock(); + sunfish.error("Error deleting file", function () { + document.location.reload(); + }); + } + } + }) + } + } + + ); + } + + sunfish.renameFile = function (sender) { + var item = getItem(sender); + var file = item.querySelector("div.item-name").innerText; + sunfish.askString('Rename to:', file, function (newName) { + sunfish.ajax(document.location.href + file + "?action=rename&to=" + newName, { + ok: function (result) { + if (result == "OK") { + item.querySelector("div.item-name").innerText = newName; + } else + sunfish.error("Error renaming file", function () { + document.location.reload(); + }); + } + }); + }); + } + + sunfish.openFile = function (sender) { + var item = getItem(sender); + var file = item.querySelector("div.item-name").innerText; + sunfish.ajax(document.location.href + file + "?action=open", { + ok: function (result) { + if (result != "OK") + sunfish.error("Error opening file in server", function () { + document.location.reload(); + }); + } + }); + } + + sunfish.newFolder = function (sender) { + sunfish.askString('Folder name:', "", function (name) { + if (!name) + return; + sunfish.ajax(document.location.href + name + '/', { + method: "PUT", + ok: function (result) { + if (result != "OK") + sunfish.error("Error creading file", function () { + document.location.reload(); + }); + else + document.location.reload(); + } + }); + }); + } + + sunfish.newFile = function (sender) { + sunfish.askString('File name:', "", function (name) { + if (!name) + return; + sunfish.ajax(document.location.href + name, { + method: "PUT", + ok: function (result) { + if (result != "OK") + sunfish.error("Error creading file", function () { + document.location.reload(); + }); + else + document.location.reload(); + } + }); + }); + } + + sunfish.uploadFile = function (sender) { + var efile = document.createElement("input"); + efile.type = "file"; + efile.onchange = () => { + var fileList = efile.files; + for (var i = 0; i < fileList.length; i++) + upl.upload(fileList[i]); + } + efile.click(); + } + + // UPLOAD task + var upl; + function Uploader() { + var pb, elist, uploads = []; + var edrop = sunfish.build({ + $: "div", className: "popup-wall upload-drop", _: [ + { + $: "div", className: "upload-drop-icon", _: [ + { $: "i", className: "material-icons-round", _: ["upload"] } + ] + } + ] + }); + var ctt = edrop.querySelector("div.upload-drop-icon"); + + document.body.appendChild(edrop); + + function show() { + document.body.appendChild(edrop); + var cbutton; + ctt.innerHTML = ""; + elist = sunfish.build({ $: "div", className: "upload-list" }, ctt); + elist.addItem = function (file, name, to) { + if (!pb.updater) { + pb.updater = function () { + var tpos = 0, tmax = 0; + uploads.forEach(u => { + tpos += u.pos; + tmax += u.length; + }) + if (tmax == 0) + tmax = 100; + pb.style.width = (tpos / tmax * 100) + "%"; + if (tpos < tmax) { + setTimeout(pb.updater, 100); + cbutton.style.display = "none"; + } else { + delete pb.updater; + cbutton.style.display = ""; + } + } + setTimeout(pb.updater, 100); + } + var item = sunfish.build({ + $: "div", className: "upload-item", _: [{ $: "div", _: [to] }, + { $: "div", className: "upload-item-progress-box", _: [{ $: "div", className: "upload-item-progress" }] }] + }, elist); + var itemProgress = item.querySelector(".upload-item-progress"); + var me; + uploads.push(me = { + file: file, + name: name, + to: to, + progress: itemProgress, + length: file.size, + pos: 0, + eof: false + }); + function updateProgress() { + if (me.length <= 0) + return; + var pos = me.pos; + if (pos > me.length) + pos = me.length; + itemProgress.style.width = (pos / me.length) * 100 + "%"; + + } + function ko(info) { + itemProgress.style.width = "150%"; + itemProgress.style.backgroundColor = "tomato"; + if (info) + console.error(info); + me.pos = me.length; + } + function step() { + var length = Math.min(1024 * 512, me.length - me.pos); + var blob = file.slice(me.pos, me.pos + length); + if (blob.length == 0) + return ko("Nothing to send"); + sunfish.ajax(document.location.href + to, { + method: "PUT", + data: blob, + headers: { + "X-Sunfish-Offset": me.pos, + "X-Sunfish-Length": blob.size + }, + ok: function (result) { + if (result != "OK") + ko("Upload is " + result); + else { + me.pos += blob.size; + updateProgress(); + if (me.pos < me.length) + step(); + else { + itemProgress.style.backgroundColor = "#46ad33"; + me.eof = true; + } + } + }, + ko: ko + }); + } + step(); + } + var pbb = sunfish.build({ $: "div", className: "upload-progress-box" }, ctt); + pb = sunfish.build({ + $: "div", className: "upload-progress", _: [ + { + $: "button", className: "upload-close", style: { display: "none" }, _: ["Close"], onclick: function () { + /* + document.body.removeChild(edrop); + uploads = []; + elist.innerHTML=""; + cbutton.style.display = "none"; + pb.style.width="0"; + */ + document.location.reload(); + } + } + ] + }, pbb); + cbutton = pb.querySelector(".upload-close"); + } + + document.body.addEventListener("drop", function (ev) { + function processWKItem(item, path) { + path = path || ""; + if (item.isFile) { + item.file(function (file) { + upload(file, path + file.name); + }); + } else if (item.isDirectory) { + // Get folder contents + var dirReader = item.createReader(); + dirReader.readEntries(function (entries) { + for (var i = 0; i < entries.length; i++) { + processWKItem(entries[i], path + item.name + "/"); + } + }); + } + } + ev.preventDefault(); + if (ev.dataTransfer.items) { + var items = ev.dataTransfer.items; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.kind === 'file') + if (item.webkitGetAsEntry) + processWKItem(item.webkitGetAsEntry()); + else + upload(item.getAsFile()); + } + } else { + for (var i = 0; i < ev.dataTransfer.files.length; i++) + upload(ev.dataTransfer.files[i].getAsFile()); + } + }); + document.body.addEventListener("dragover", function (ev) { + console.info("drag-over"); + ev.stopPropagation(); + ev.preventDefault(); + var anyfile = false; + if (ev.dataTransfer.items) { + for (var i = 0; i < ev.dataTransfer.items.length; i++) + if (anyfile = ev.dataTransfer.items[i].kind == "file") + break; + } else + anyfile = ev.dataTransfer.files.length > 0; + if (anyfile) + document.body.classList.add("dragging"); + }); + + edrop.addEventListener("dragleave", function (ev) { + console.info("drag-leave"); + ev.stopPropagation(); + ev.preventDefault(); + var anyfile = false; + if (ev.dataTransfer.items) { + for (var i = 0; i < ev.dataTransfer.items.length; i++) + if (anyfile = ev.dataTransfer.items[i].kind == "file") + break; + } else + anyfile = ev.dataTransfer.files.length > 0; + if (anyfile) + document.body.classList.remove("dragging"); + }); + + function upload(file, to) { + document.body.appendChild(edrop); + if (!elist) + show(); + to = to || file.name; + elist.addItem(file, file.name, to); + } + + this.upload = upload; + } + + window.addEventListener("load", function () { + upl = new Uploader(); + }); + +})(); \ No newline at end of file diff --git a/Sunfish/bin/Debug/$sunfish/sunfish.js b/Sunfish/bin/Debug/$sunfish/sunfish.js new file mode 100644 index 0000000..46684d5 --- /dev/null +++ b/Sunfish/bin/Debug/$sunfish/sunfish.js @@ -0,0 +1,183 @@ +function Sunfish() { + var mobile; + function init() { + mobileCheck = function () { + var check = false; + (function (a) { + if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; + })(navigator.userAgent || navigator.vendor || window.opera); + return check; + }; + mobile = mobileCheck(); + document.body.className = mobile ? "mobile" : "desktop"; + konsole.showTab(); + } + + function build(def, into) { + var el; + if (!def.$) { + el = document.createTextNode(def); + if (into) + into.appendChild(el); + return el; + } + el = document.createElement(def.$); + var childs = def._; + delete def.$; + delete def._; + for (var i in def) { + if (i == "style") + for (var sn in def.style) + el.style[sn] = def.style[sn]; + else + el[i] = def[i]; + } + if (childs) + if (typeof childs == "string") + el.appendChild(document.createTextNode(childs)); + else + for (var i in childs) + build(childs[i], el); + if (into) + into.appendChild(el); + return el; + } + + function dialog(content, buttons, classes) { + var btdef = []; + for (var i in buttons) { + var bt = buttons[i]; + if (bt.$) + btdef.push(bt); + else + btdef.push({ + $: "button", + className: bt.class, + _: bt.b, + onclick: bt.do + }); + } + + var wall = build({ $: "div", className: "popup-wall" }, document.body); + var def = { + $: "div", className: "popup" + (classes ? " " + classes : ""), _: [ + { $: "div", className: "body", _: content }, + { $: "div", className: "buttons", _: btdef } + ] + }; + var dialog = build(def, wall); + dialog.close = function () { + document.body.removeChild(wall); + } + return dialog; + } + + function ask(question, bt1, bt2, bt3, bt4, bt5, iserror) { + var buttons = []; + var el; + function addbt(bt) { + if (bt) { + if (bt.go) + bt.do = function () { document.location = bt.go }; + buttons.push({ + class: bt.class, + b: bt.b, + do: function () { + bt.do && bt.do(); + el.close(); + } + }); + } + } + addbt(bt1); + addbt(bt2); + addbt(bt3); + addbt(bt4); + addbt(bt5); + return el = dialog([question], buttons, iserror ? "error" : null); + } + + function askString(text, def, cb) { + var input; + var dialog = ask({ $: "div", className: "input", _: [text, { $: "input", id: "askInput", value: def }] }, { b: 'Cancel' }, { + b: 'Ok', class: "ok", do: function () { + var value = input.value; + cb && cb(value); + } + }); + input = dialog.querySelector("input#askInput"); + input.focus(); + input.selectionStart = 0; + input.selectionEnd = input.value.length; + input.onkeypress = function (e) { + if (e.keyCode == 13) + dialog.querySelector("button.ok").click(); + }; + } + + function say(text, cb) { + ask(text, { b: 'Ok', do: cb }); + } + + function error(text, cb) { + ask(text, { b: 'Ok', do: cb }, null, null, null, null, true); + } + + function lock(text) { + var wall = build({ $: "div", className: "popup-wall" }, document.body); + var def = { + $: "div", className: "popup", _: [ + { $: "div", className: "body", _: [text] } + ] + }; + build(def, wall); + return function () { + document.body.removeChild(wall); + } + } + + function go(to) { + document.location = to + } + + function ajax(url, ctrl) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) + ctrl.ok && ctrl.ok(xhr.responseText, xhr); + }; + xhr.open(ctrl.method || 'GET', url); + if (ctrl.headers) + for (var k in ctrl.headers) + xhr.setRequestHeader(k, ctrl.headers[k]); + if (ctrl.binary) + xhr.sendAsBinary(ctrl.binary, "binary/octet"); + else + xhr.send(ctrl.data); + } + + if (!XMLHttpRequest.prototype.sendAsBinary) + XMLHttpRequest.prototype.sendAsBinary = function (datastr, contentType) { + var bb = new BlobBuilder(); + var len = datastr.length; + var data = new Uint8Array(len); + for (var i = 0; i < len; i++) { + data[i] = datastr.charCodeAt(i); + } + bb.append(data.buffer); + this.send(bb.getBlob(contentType)); + } + + this.init = init; + this.build = build; + this.dialog = dialog; + this.ask = ask; + this.askString = askString; + this.say = say; + this.error = error; + this.lock = lock; + this.go = go; + this.ajax = ajax; +} + +var sunfish = new Sunfish(); diff --git a/doc/main.png b/doc/main.png new file mode 100644 index 0000000..3976cf9 --- /dev/null +++ b/doc/main.png Binary files differ