diff --git a/.gitignore b/.gitignore index bf413e1..26a0422 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ npm-debug.log roadmap *.orig *.*~ + +.brackets.json + +apps/app-defaults/settings/app-defaults.json diff --git a/apps/app-defaults/index.js b/apps/app-defaults/index.js index 1f4ac16..e22607d 100644 --- a/apps/app-defaults/index.js +++ b/apps/app-defaults/index.js @@ -1,15 +1,12 @@ +var methods = Silk.methods; var defaults = {}; var djson = __dirname + "/settings/app-defaults.json"; var windows; -var Silk = { - defaults: {} -}; methods.add({ "windows": function (windows) { initialize(windows); - console.log("Silk.defaults " + JSON.stringify(Silk.defaults)); } }); @@ -20,7 +17,10 @@ function loadDefaults() { fs.exists(fileName, function (exists) { // TODO create file if it doesn't exist - if (!exists) return; + if (!exists) { + fs.writeFileSync(djson, '{}') + + } fs.stat(fileName, function (err, stats) { if (err) return console.log(err); @@ -58,7 +58,6 @@ function saveDefaults() { for (item in defaults) { // TODO only save items that have a default - console.log("contents: " + item); contents[item] = {}; contents[item].default = defaults[item].default; contents[item].available = []; @@ -75,9 +74,6 @@ function saveDefaults() { function initialize(windows) { - defaults = Silk.defaults; - - for (var i = 0; i < windows.length; ++i) { if (!("opens" in windows[i])) continue; var opens = windows[i].opens; @@ -85,19 +81,19 @@ function initialize(windows) { //for each mime listed in windows for (var x = 0; x < opens.length; ++x) { - if (opens[x] in Silk.defaults) { + if (opens[x] in defaults) { // don't add duplicate apps - if (Silk.defaults[opens[x]].available.indexOf(windows[i].title) < 0) { - Silk.defaults[opens[x]].available.push(windows[i].title); + if (defaults[opens[x]].available.indexOf(windows[i].title) < 0) { + defaults[opens[x]].available.push(windows[i].title); } } else { - + // create object for defaults - Silk.defaults[opens[x]] = {}; - Silk.defaults[opens[x]] = { + defaults[opens[x]] = {}; + defaults[opens[x]] = { default: "", available: [windows[i].title] }; @@ -106,23 +102,23 @@ function initialize(windows) { } loadDefaults(); - console.log(JSON.stringify(defaults, null, 4)); + //console.log(JSON.stringify(defaults, null, 4)); methods.add({ "Silk/appDefaults": function (mime) { - if (mime in Silk.defaults) { - return Silk.defaults[mime]; + if (mime in defaults) { + return defaults[mime]; } else { - var ret = Silk.defaults["*"]; + var ret = defaults["*"]; ret.mime = "*"; return ret } }, "Silk/setDefault": function (data) { - if (data.mime in Silk.defaults) { - Silk.defaults[data.mime].default = data.app; + if (data.mime in defaults) { + defaults[data.mime].default = data.app; } else { - Silk.defaults[data.mime] = {}; - Silk.defaults[data.mime] = { + defaults[data.mime] = {}; + defaults[data.mime] = { default: data.app, available: [] }; diff --git a/apps/app-defaults/settings/app-defaults.json b/apps/app-defaults/settings/app-defaults.json index 4b50749..9e26dfe 100644 --- a/apps/app-defaults/settings/app-defaults.json +++ b/apps/app-defaults/settings/app-defaults.json @@ -1 +1 @@ -{"text/plain":{"default":"","available":[]},"*":{"default":"","available":[]},"undefined":{"default":"Text Editor","available":[]},"text/html":{"default":"Text Editor","available":[]}} \ No newline at end of file +{} \ No newline at end of file diff --git a/apps/appManager/index.js b/apps/appManager/index.js new file mode 100644 index 0000000..1ba1968 --- /dev/null +++ b/apps/appManager/index.js @@ -0,0 +1,153 @@ +var request = require('request'); +var fs = require('fs'); +var path = require('path'); +var yauzl = require("yauzl"); +var mkdirp = require('mkdirp'); +var async = require('async'); + +var methods = Silk.methods; +var apps = []; +var appsFolder = __dirname.split(path.sep); +appsFolder = appsFolder.slice(0, appsFolder.length - 1); +appsFolder = appsFolder.join(path.sep); + +methods.add({ + "apps/remove": function (data, call_obj, send) { + send(void(0), "Deleting..."); + + var file = data.folder; + var folder = appsFolder + path.sep + file; + var deleteFolderRecursive = function (path) { + if (fs.existsSync(path)) { + fs.readdirSync(path).forEach(function (file, index) { + var curPath = path + "/" + file; + if (fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + console.log("Finished removing " + file); + send(void(0), " "); + } + }; + deleteFolderRecursive(folder); + }, + "apps/list": function (data, call_obj, send) { + Silk.api.call('apps/list', {}, function(err, data){ + if(err){ + send(err); + } else{ + send(void(0), data); + } + }) + }, + "apps/install": download +}); + +function download(data, call_ob, send) { + + fs.exists(appsFolder + path.sep + data.url.replace("/", "-"), function (exists) { + if (exists === true) { + send(new Error("App already exists")); + return; + } + + if (data.url === "") { + send(new Error("Url can not be empty")); + } + var repo = data.url; + + var url = "https://api.github.com/repos/" + repo + "/zipball"; + var options = { + url: url, + headers: { + 'User-Agent': 'silk-gui' + } + }; + + send(void(0), "Downloading..."); + request(options).on('response', function (response) { + }).on('error', function (err) { + send(err) + }).on("end", function () { + send(void(0), "pending"); + install(data, call_ob, send) + }).pipe(fs.createWriteStream(__dirname + path.sep + 'test.zip')); + + }); +} + +function install(data, call_ob, send) { + send(void(0), "Installing ..."); + var extractTo = appsFolder + path.sep + data.url.replace("/", "-"); + try { + yauzl.open(__dirname + path.sep + 'test.zip', function (err, zipfile) { + if (err) { + send(err); + return; + } + zipfile.once('end', function () { + Silk.api.call('apps/start', data.url.replace('/', '-'), function (err, data) { + console.log(error); + send(void(0), "Finished installing!") + + }); + send(void(0), 'Starting App'); + fs.unlink(__dirname + path.sep + 'test.zip', function (err) { + if (err) { + send(err); + + } + console.log('Installed ' + data.url); + send(void(0), " "); + }) + }); + zipfile.on("entry", function (entry) { + + if (/\/$/.test(entry.fileName)) { + // directory file names end with '/' + return; + } + + var fileName = entry.fileName; + fileName = fileName.split(path.sep); + fileName = fileName.splice(1, fileName.length) + fileName = fileName.join(path.sep); + + entry.fileName = fileName; + var dest = path.join(extractTo, entry.fileName) + var destDir = path.dirname(dest) + // dest = dest.split(path.sep); + // dest = dest.slice(0, dest.length - 1); + + zipfile.openReadStream(entry, function (err, readStream) { + if (err) { + send(err); + return; + } + + mkdirp(destDir, function (err) { + if (err) { + send(err); + } + + + //entry.fileName = data.url.replace("/", "-"); + // ensure parent directory exists, and then: + + readStream.pipe(fs.createWriteStream(dest).on("error", function (err) { + send(err) + })) + }); + + }); + + }); + }); + + } catch (e) { + send(e); + } +} \ No newline at end of file diff --git a/apps/appManager/public/icon.png b/apps/appManager/public/icon.png new file mode 100644 index 0000000..9b80c61 Binary files /dev/null and b/apps/appManager/public/icon.png differ diff --git a/apps/appManager/public/index.html b/apps/appManager/public/index.html new file mode 100644 index 0000000..05ca509 --- /dev/null +++ b/apps/appManager/public/index.html @@ -0,0 +1,224 @@ + + + + App Manager + + + + +
+

Your Apps

+ +
+ +
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/apps/appManager/window.json b/apps/appManager/window.json new file mode 100644 index 0000000..cdb9195 --- /dev/null +++ b/apps/appManager/window.json @@ -0,0 +1,14 @@ +{ + "title": "App Manager", + "url": "index.html", + "icon": "icon.png", + "multipleWindows": false, + "bower_dependencies": { + "jquery": "latest" + }, + "npm_dependencies": { + "request": "*", + "yauzl": "*", + "mkdirp": "*" + } +} \ No newline at end of file diff --git a/apps/fileExplorer/index.js b/apps/fileExplorer/index.js index d394b21..ed2f945 100644 --- a/apps/fileExplorer/index.js +++ b/apps/fileExplorer/index.js @@ -1,6 +1,6 @@ var fs = require('fs'); var mime = require("mime") - +var methods = Silk.methods; function parsePath(path){ var files = fs.readdirSync(path); @@ -60,5 +60,28 @@ methods.add({ } setupWatcher(path,call_ob,next); return parsePath(path); + }, + "fe/create/folder": function (data, call_ob, send){ + var path = data.path; + var name = data.name; + console.log(path); + console.log(name); + console.log(path + "/" + name); + + if(typeof path == "undefined"){ + path = "/" + } + if(typeof name == "undefined"){ + name = "Untitled"; + } + + fs.mkdir(path + "/" + name,function(err) { + if (err) { + if (err.code == 'EEXIST') send("Fodler already exists"); // ignore the error if the folder already exists + else send(err); // something else went wrong + } + else send(void(0), {"status": "done"}); // successfully created folder + }); + } }) diff --git a/apps/fileExplorer/public/css/fileExplorer.css b/apps/fileExplorer/public/css/fileExplorer.css index 5d4de35..dec9d3a 100644 --- a/apps/fileExplorer/public/css/fileExplorer.css +++ b/apps/fileExplorer/public/css/fileExplorer.css @@ -1,5 +1,6 @@ body { font-family: "Trebuchet MS", Helvetica, sans-serif; + margin-bottom: 50px; } h1 a { color: rgb(128, 128, 128); @@ -12,43 +13,53 @@ h1 a { h1 .current_path i:first-child { display: none; } - h1 a:last-child { -color:rgb(69, 159, 224); + color: rgb(69, 159, 224); } .files .directory { /*background-color: #DDD;*/ } .files .directory i { -color: rgb(234, 204, 78); + color: rgb(234, 204, 78); } -h1 .current_path span{ - display:none; +h1 .current_path span { + display: none; } .files { -list-style: none; + list-style: none; } .files li { -padding: 2px; -margin: 5px; -width: 80px; -display: inline-block; -text-align: center; -vertical-align: top; + padding: 2px; + margin: 5px; + width: 80px; + display: inline-block; + text-align: center; + vertical-align: top; } .files li a { -font-size: 12px; -width: 80px; -word-wrap: break-word; -text-decoration: none; -color: grey; + font-size: 12px; + width: 80px; + word-wrap: break-word; + text-decoration: none; + color: grey; } .files li i { -font-size: 40px; -display: block; + font-size: 40px; + display: block; } .files .file i { -font-size: 30px; -margin-top: 3px; -margin-bottom: 7px; + font-size: 30px; + margin-top: 3px; + margin-bottom: 7px; +} +#newFolder { + background-color: 4EBAEA; + border: none; + color: #FFF; + padding: 10px; + font-size: 14px; + position: fixed; + bottom: 5px; + left: 5px; + cursor: pointer; } \ No newline at end of file diff --git a/apps/fileExplorer/public/index.html b/apps/fileExplorer/public/index.html index c3429e0..8dd0c24 100644 --- a/apps/fileExplorer/public/index.html +++ b/apps/fileExplorer/public/index.html @@ -15,6 +15,7 @@

+ diff --git a/apps/fileExplorer/public/js/call.js b/apps/fileExplorer/public/js/call.js index 829c6f6..b5ccd84 100644 --- a/apps/fileExplorer/public/js/call.js +++ b/apps/fileExplorer/public/js/call.js @@ -5,6 +5,11 @@ var socket; try { var host = "ws://0.0.0.0:9999"; + + // change host url if remote + if(/localhost:3000/.test(location.host) | /0.0.0.0:3000/.test(location.host)){{ + host = location.protocol + "//" + location.host + ":9999"; + } socket = new WebSocket(host); socket.onopen = function () { diff --git a/apps/fileExplorer/public/js/fileExplorer.js b/apps/fileExplorer/public/js/fileExplorer.js index f1a1206..e091d63 100644 --- a/apps/fileExplorer/public/js/fileExplorer.js +++ b/apps/fileExplorer/public/js/fileExplorer.js @@ -21,6 +21,16 @@ function file_explorer(elem) { // alert("You're going to have to setup a default view for mimetype: " + $(this).parent().attr("data-mime")); return false; }) + // Create folder + jQuery("#newFolder").click(function (e) { + var name = prompt("Name of Folder"); + methods.listen("fe/create/folder", {path: that.href, name: name}, function (err, result) { + if(err){ + alert(err); + return; + } + }) + }) this.listener = methods.listen("fe/list/path", function (err, list) { if (err) return alert(JSON.stringify(err)); that.processList(that.href, list); @@ -56,7 +66,7 @@ file_explorer.prototype.processList = function (href, list) { var files = []; this.processCD(href); this.files.empty(); - + // add folders and separate files for (var i = 0; i < list.length; i++) { var item = list[i]; @@ -64,24 +74,26 @@ file_explorer.prototype.processList = function (href, list) { var el = jQuery("
  • " + item.name + "
  • "); this.files.append(el); el.addClass("directory"); - + } else { files.push(item); } } - + // add files - for(var i = 0; i < files.length; i++){ + for (var i = 0; i < files.length; i++) { var item = list[i]; - var el = jQuery("
  • " + item.name + "
  • "); - this.files.append(el); - el.addClass("file"); - el.attr("data-mime", item.mime); - + var el = jQuery("
  • " + item.name + "
  • "); + this.files.append(el); + el.addClass("file"); + el.attr("data-mime", item.mime); + } - + } + + jQuery(function ($) { new file_explorer(); }) \ No newline at end of file diff --git a/apps/fileExplorer/public/logo.png b/apps/fileExplorer/public/logo.png index ead7954..ecb9053 100644 Binary files a/apps/fileExplorer/public/logo.png and b/apps/fileExplorer/public/logo.png differ diff --git a/apps/fileExplorer/window.json b/apps/fileExplorer/window.json index 0e09a0b..a87a8f9 100644 --- a/apps/fileExplorer/window.json +++ b/apps/fileExplorer/window.json @@ -1,8 +1,6 @@ { "title": "File Explorer", - "url": "http://0.0.0.0:3000/fileExplorer/index.html", - "running": false, - "minimized": true, + "url": "/fileExplorer/index.html", "icon": "/fileExplorer/logo.png", - "zIndex": 0 + "multipleWindows": true } diff --git a/apps/taskManager/index.js b/apps/taskManager/index.js new file mode 100644 index 0000000..57bde3d --- /dev/null +++ b/apps/taskManager/index.js @@ -0,0 +1,16 @@ + +Silk.methods.add({ + 'taskManager/apps': function(data, call_obj, send){ + Silk.api.call('apps/list', {}, function(err, data){ + console.dir(err); + send(err, data); + }) + }, + 'taskManager/restart': function(data, call_obj, send){ + console.log("will restart + " + data); + Silk.api.call('apps/restart', data.name, function(err, result) { + console.log(err); + console.log(result); + }) + } +}) \ No newline at end of file diff --git a/apps/taskManager/public/icon.png b/apps/taskManager/public/icon.png new file mode 100644 index 0000000..41f2a2b Binary files /dev/null and b/apps/taskManager/public/icon.png differ diff --git a/apps/taskManager/public/index.html b/apps/taskManager/public/index.html new file mode 100644 index 0000000..e88b342 --- /dev/null +++ b/apps/taskManager/public/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/taskManager/window.json b/apps/taskManager/window.json new file mode 100644 index 0000000..6cc150c --- /dev/null +++ b/apps/taskManager/window.json @@ -0,0 +1,6 @@ +{ + "title": "Task Manager", + "url": "/taskManager/index.html", + "icon": "icon.png", + "multipleWindows": false +} \ No newline at end of file diff --git a/apps/terminal/index.js b/apps/terminal/index.js new file mode 100644 index 0000000..3e75702 --- /dev/null +++ b/apps/terminal/index.js @@ -0,0 +1,13 @@ +try { + var tty = require("tty.js"); +} catch (e) { + console.log("Couldn't find tty.js"); + return; +} +var app = tty.createServer({ + shell: 'bash', + port: 8000 +}); + + +app.listen(); \ No newline at end of file diff --git a/apps/terminal/public/icon.png b/apps/terminal/public/icon.png new file mode 100644 index 0000000..901542e Binary files /dev/null and b/apps/terminal/public/icon.png differ diff --git a/apps/terminal/window.json b/apps/terminal/window.json new file mode 100644 index 0000000..fc09daf --- /dev/null +++ b/apps/terminal/window.json @@ -0,0 +1,9 @@ +{ + "title" : "Terminal", + "url" : "//localhost:8000/", + "icon" : "//localhost:3000/terminal/icon.png", + "remote" : { + "enabled": false + }, + "multipleWindows": false +} \ No newline at end of file diff --git a/apps/textEditor/index.js b/apps/textEditor/index.js index 6dc60ec..47e4411 100644 --- a/apps/textEditor/index.js +++ b/apps/textEditor/index.js @@ -1,19 +1,19 @@ - +var methods = Silk.methods; methods.add({ - "te/open": function(file, callObj, send){ + "te/open": function (file, callObj, send) { var fs = require("fs"); var fileName = file; console.log("fileName"); - fs.exists(fileName, function(exists) { + fs.exists(fileName, function (exists) { if (!exists) return send("this file does not exist"); - fs.stat(fileName, function(err, stats) { - if(err) return send(err); - if(stats.isDirectory()) + fs.stat(fileName, function (err, stats) { + if (err) return send(err); + if (stats.isDirectory()) return send(new Error("Editing directories in a text editor is not currently supported")); - fs.readFile(fileName,function(err,data){ - if(err) return send(err); + fs.readFile(fileName, function (err, data) { + if (err) return send(err); var ret = { state: "ready", content: data.toString("utf-8") @@ -22,22 +22,27 @@ methods.add({ }) }); }); - return {state: "loading"} + return { + state: "loading" + } } }); methods.add({ - "te/save" : function(data){ + "te/save": function (data, callObj, send) { var fs = require("fs"); console.log(data); path = data.path; contents = data.contents; console.log("=========="); - // console.log(contents); + // console.log(contents); fs.writeFile(path, contents, function (err) { - if (err) return console.log(err); - console.log("saved: " + path); -}); + if (err) return console.log(err); + console.log("saved: " + path); + }); console.log("finished"); + + // tricks silk into sending return value; + return " "; } -}) +}) \ No newline at end of file diff --git a/apps/textEditor/public/index.html b/apps/textEditor/public/index.html index 38ca72c..c40c8e1 100644 --- a/apps/textEditor/public/index.html +++ b/apps/textEditor/public/index.html @@ -10,9 +10,10 @@
    - + + diff --git a/apps/textEditor/public/js/mousetrap.js b/apps/textEditor/public/js/mousetrap.js new file mode 100644 index 0000000..d5f6af2 --- /dev/null +++ b/apps/textEditor/public/js/mousetrap.js @@ -0,0 +1,9 @@ +/* mousetrap v1.4.6 craig.is/killing/mice */ +(function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;gg||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c=1.9.1" diff --git a/core/client_api.js b/core/client_api.js index f33ddc4..ffe8cfa 100644 --- a/core/client_api.js +++ b/core/client_api.js @@ -6,9 +6,9 @@ var fs = require("fs"); module.exports = function(req,res,next){ res.setHeader('content-type', 'application/javascript'); bowerstatic.raw("/bc/eventEmitter",function(ee){ - console.log(ee); + debug(ee); bowerstatic.raw("/bc/rsvp",function(rsvp){ - console.log(rsvp); + debug(rsvp); async.eachSeries([ ee, rsvp, diff --git a/core/client_api/window/Window2Server_com.js b/core/client_api/window/Window2Server_com.js index 67c809a..3c5b95d 100644 --- a/core/client_api/window/Window2Server_com.js +++ b/core/client_api/window/Window2Server_com.js @@ -66,7 +66,7 @@ if(typeof module != "undefined" && module.exports){ window.DocumentHost = null; (function(url){ url = /^(http[s]?):\/\/([0-9\.]+|[a-z\-.]+)([?::][0-9]+)?([\/][A-Za-z0-9_\-]+)?(\?.*)?/.exec(url); - var port = (typeof wp != "undefined")?wp:(document.cookie.pwp)?document.cookie.pwp:9999+(parseInt(url[3].substring(1))-3000); + var port = (typeof wp != "undefined")?wp:(document.cookie.pwp)?document.cookie.pwp:3000+(parseInt(url[3].substring(1))-3000); console.log(port); window.DocumentHost = new Server(url[2],port); if(url[4]) diff --git a/core/fork_framework/app_loader.js b/core/fork_framework/app_loader.js index 8a5fde6..b7dec51 100644 --- a/core/fork_framework/app_loader.js +++ b/core/fork_framework/app_loader.js @@ -1,7 +1,6 @@ - var util = require("util") var fs = require("fs"); -var events = require("events"); +var events = require("events"); var async = require("async"); var http = require("http"); var https = require("https"); @@ -10,35 +9,36 @@ var router = express.Router(); var url = require("url"); var child_process = require("child_process"); module.exports = AppFactory; -function AppFactory(folder,urlpath,app){ - if(typeof folder == "undefined") + +function AppFactory(folder, urlpath, app) { + if (typeof folder == "undefined") throw Error("In Order to use the abstract, you need to provide a folder to load from"); - if(!/\/$/.test(folder)) folder += "/"; - if(!/\/$/.test(urlpath)) urlpath += "/" + if (!/\/$/.test(folder)) folder += "/"; + if (!/\/$/.test(urlpath)) urlpath += "/" this.folder = folder; this.urlpath = urlpath; this.hashmap = {}; this.clean = [] this.removed = []; this.router = express.Router(); - router.get(urlpath,function(req,res,next){ + router.get(urlpath, function (req, res, next) { var t = url.parse(req.originalURL); t = t.substring(urlpath.length); - t = t.substring(0,t.indexOf("/")||void(0)); - if(removed.indexOf(t) != -1) + t = t.substring(0, t.indexOf("/") || void(0)); + if (removed.indexOf(t) != -1) next(new Error("this application has been removed")) next(); }) var that = this; app.use(router) process.nextTick(this.compileFolder.bind(this)); - this.on("compiledSingle",function(j){ - router.use(urlpath+j.name, express.static(folder+j.name+"/public")); - router.get(urlpath+j.name, function(req,res,next){ - res.redirect(301,j.url); + this.on("compiledSingle", function (j) { + router.use(urlpath + j.name, express.static(folder + j.name + "/public")); + router.get(urlpath + j.name, function (req, res, next) { + res.redirect(301, j.url); }); - if(j.url != "headless"){ - console.log("not headless") + if (j.url != "headless") { + debug("not headless") that.clean.push(j.clean); } delete j.clean; @@ -49,249 +49,321 @@ function AppFactory(folder,urlpath,app){ } util.inherits(AppFactory, events.EventEmitter); -AppFactory.prototype.compileFolder = function(app){ +AppFactory.prototype.compileFolder = function (app) { var that = this; - fs.readdir(this.folder, function(err,files){ - async.filterSeries(files,function(file,next){ - that.getSingle(file,function(err,j){ - if(err){ - console.log(file+" could not be loaded") + fs.readdir(this.folder, function (err, files) { + async.filterSeries(files, function (file, next) { + that.getSingle(file, function (err, j) { + if (err) { + console.log(file + " could not be loaded") console.log(err); return next(false) } next(true) }) - }, function(results){ + }, function (results) { that.completed = results; that.emit("finishedCompiling", results); }); }) } -AppFactory.prototype.fsEvent = function(event, filename){ - console.log(event); +AppFactory.prototype.fsEvent = function (event, filename) { + debug(event); } -AppFactory.prototype.closeSingle = function(window,next){ - if(!window.fork) +AppFactory.prototype.closeSingle = function (window, next) { + if (!window.fork) return next(new Error("This window has no fork")) - if(!window.fork.pid) + if (!window.fork.pid) return next(new Error("This fork is already gone")) - try{ + try { window.fork.kill() - }catch(e){ + } catch (e) { return next(e) } - next(void(0),window); + next(void(0), window); } -AppFactory.prototype.deleteSingle = function(window,next){ - fs.rmdir(window.path, function(err,res){ +AppFactory.prototype.deleteSingle = function (window, next) { + fs.rmdir(window.path, function (err, res) { console.log(JSON.stringify(arguments)); - if(err) next(err) - next(void(0),window); + if (err) next(err) + next(void(0), window); }); } -AppFactory.prototype.hideSingle = function(window,next){ +AppFactory.prototype.hideSingle = function (window, next) { var name = window.name; var i = 0; - async.whilst(function(){ + async.whilst(function () { return this.collection[i] && this.collection[i] != window - }, function(next){ i++; process.nextTick(next)}, function(){ - if(!this.collection[i]) + }, function (next) { + i++; + process.nextTick(next) + }, function () { + if (!this.collection[i]) next(new Error("there can't hide what doesn't exist")) delete this.collection[i]; this.removed.push(name); - next(void(0),window); + next(void(0), window); + }) +} + +AppFactory.prototype.restartSingle = function (window, next) { + var path = window.path; + window.fork.disconnect(); + window.fork.kill(); + var that = this; + this.getSingle(window.name, function (err, j) { + if (err) { + console.log(file + " could not be loaded") + console.log(err); + return next(false) + } + + // remove old item (first) from clean + for(var i = 0; i < that.clean.length; ++i){ + if(that.clean[i].name === j.name){ + that.clean.splice(i, 1); + break; + } + } + + window = j; + var results = []; + // create array of results + for (item in that.hashmap) { + results.push(item); + } + + that.emit("finishedCompiling", results); + console.log("restarted " + window.name); + next(true) }) } -AppFactory.prototype.checkWindowJSON = function(file,next){ +AppFactory.prototype.checkWindowJSON = function (file, next) { var f = this.folder; - fs.exists(f+file+"/window.json",function(boo){ - if(!boo) - return next(new Error(f+file+"/window.json does not exist.")); - fs.readFile(f+file+"/window.json", function(err, contents){ - if(err) + fs.exists(f + file + "/window.json", function (boo) { + if (!boo) + return next(new Error(f + file + "/window.json does not exist.")); + fs.readFile(f + file + "/window.json", function (err, contents) { + if (err) return next(err) - try{ + try { var j = JSON.parse(contents); - }catch(err){ + } catch (err) { return next(err); } - if(!j.url) return next(new Error("URL is always standard")) - if(!j.title) return next(new Error("for now, title is standard")) - if(!j.icon) return next(new Error("for now, Logo is standard")) + if (!j.url) return next(new Error("URL is always standard")) + if (!j.title) return next(new Error("for now, title is standard")) + if (!j.icon) return next(new Error("for now, Logo is standard")) + + // add fields necessary for windows + if (j.url !== 'headless') { + j.zIndex = 0; + j.running = false; + j.minimized = true; + } + j.name = file; j.clean = JSON.parse(JSON.stringify(j)); - j.path = f+file; - next(void(0),j); + j.path = f + file; + next(void(0), j); }) }) } -AppFactory.prototype.checkURIs = function(j,next){ +AppFactory.prototype.checkURIs = function (j, next) { var that = this; - if(j.url == "headless"){ - console.log("headless"); - return next(void(0),j); + if (j.url == "headless") { + debug("headless"); + return next(void(0), j); } - async.each(["url","icon"],function(ns,next){ - j[ns] = url.resolve("http://localhost:3000"+that.urlpath+j.name+"/index.html",j[ns]); + async.each(["url", "icon"], function (ns, next) { + j[ns] = url.resolve("http://localhost:3000" + that.urlpath + j.name + "/index.html", j[ns]); j.clean[ns] = j[ns]; var parsed = url.parse(j[ns]); - if(!url.host){ - fs.exists(j.path,function(boo){ - if(!boo) return next(new Error("local file does not exist")); + if (!url.host) { + fs.exists(j.path, function (boo) { + if (!boo) return next(new Error("local file does not exist")); next(); }) return; } - if(/^https/.test(j[ns])) - https.request(j[ns], function(res){ - if(res.statusCode >= 400) - return next(new Error(j[ns]+" cannot be fulfilled "+res.statusCode)); + if (/^https/.test(j[ns])) + https.request(j[ns], function (res) { + if (res.statusCode >= 400) + return next(new Error(j[ns] + " cannot be fulfilled " + res.statusCode)); }) else - http.request(j[ns],function(res){ - if(res.statusCode >= 400) - return next(new Error(j[ns]+" cannot be fulfilled "+res.statusCode)); + http.request(j[ns], function (res) { + if (res.statusCode >= 400) + return next(new Error(j[ns] + " cannot be fulfilled " + res.statusCode)); }) - },function(err){ - if(err) next(err); - next(void(0),j); + }, function (err) { + if (err) next(err); + next(void(0), j); }) }; -AppFactory.prototype.checkNPMDeps = function(j,next){ - if(!j.npm_dependencies){ +AppFactory.prototype.checkNPMDeps = function (j, next) { + if (!j.npm_dependencies) { j.npm_dependencies = {}; - j.npm_info = {already_install:[],new_install:[],all:[]}; - return next(void(0),j); + j.npm_info = { + already_install: [], + new_install: [], + all: [] + }; + return next(void(0), j); } var ai = []; var ni = []; - async.each(Object.keys(j.npm_dependencies),function(dep,next){ - try{ + async.each(Object.keys(j.npm_dependencies), function (dep, next) { + try { var loc = require.resolve(dep); - ai.push({name:dep,path:loc}); - return next(void(0),dep); - }catch(e){ + ai.push({ + name: dep, + path: loc + }); + return next(void(0), dep); + } catch (e) { console.log("this dependency does not exist"); child_process.exec( - "npm install "+dep+"@"+j.npm_dependencies[dep], - {cwd:__root}, function(err,stout,sterr){ - if(err) return next(err); - try{ + "npm install " + dep + "@" + j.npm_dependencies[dep], { + cwd: __root + }, function (err, stout, sterr) { + if (err) return next(err); + try { var loc = require.resolve(dep); - ni.push({name:dep,path:loc}); - return next(void(0),dep); - }catch(e){ + ni.push({ + name: dep, + path: loc + }); + return next(void(0), dep); + } catch (e) { return next(e); } - }) + }) } - },function(err,results){ - if(err) return next(err); - j.npm_info = {already_install:ai,new_install:ni,all:results}; - return next(void(0),j); + }, function (err, results) { + if (err) return next(err); + j.npm_info = { + already_install: ai, + new_install: ni, + all: results + }; + return next(void(0), j); }) }; -AppFactory.prototype.checkBowerDeps = function(j,next){ - if(!j.bower_dependencies){ +AppFactory.prototype.checkBowerDeps = function (j, next) { + if (!j.bower_dependencies) { j.bower_dependencies = {}; - j.bower_info = {already_install:[],new_install:[],all:[]}; - return next(void(0),j); + j.bower_info = { + already_install: [], + new_install: [], + all: [] + }; + return next(void(0), j); } var bowerJson = require('bower-json'); var ai = []; var ni = []; - async.each(Object.keys(j.bower_dependencies),function(dep,next){ - bowerJson.read(__root+"/bower_components/"+dep,function(err,file){ - if(file){ - ai.push(dep) - return next(void(0),dep); + async.each(Object.keys(j.bower_dependencies), function (dep, next) { + bowerJson.read(__root + "/bower_components/" + dep, function (err, file) { + if (file) { + ai.push(dep) + return next(void(0), dep); } child_process.exec( - "bower install "+dep - +(j.bower_dependencies[dep]=="latest"? - " --force-latest": - "#"+j.bower_dependencies[dep] - ), - {cwd:__root}, function(err,stout,sterr){ - if(err) return next(err); - bowerJson.read(__root+"/bower_components/"+dep,function(err,file){ - if(err) return next(err); - ni.push(dep); - return next(void(0),dep); - }); - }) + "bower install " + dep + (j.bower_dependencies[dep] == "latest" ? + " --force-latest" : + "#" + j.bower_dependencies[dep] + ), { + cwd: __root + }, function (err, stout, sterr) { + if (err) return next(err); + bowerJson.read(__root + "/bower_components/" + dep, function (err, file) { + if (err) return next(err); + ni.push(dep); + return next(void(0), dep); + }); + }) }) - },function(err,results){ - if(err) return next(err); - j.bower_info = {already_install:ai,new_install:ni,all:results}; - return next(void(0),j); + }, function (err, results) { + if (err) return next(err); + j.bower_info = { + already_install: ai, + new_install: ni, + all: results + }; + return next(void(0), j); }) } -AppFactory.prototype.createFork = function(j,next){ +AppFactory.prototype.createFork = function (j, next) { var that = this; - try{ + try { require.resolve(j.path); - try{ + try { var fork = child_process.fork( - __dirname+"/fork_container/fork2server_com.js",[], - {cwd:__root,env:{start:j.path}} + __dirname + "/fork_container/fork2server_com.js", [], { + cwd: __root, + env: { + start: j.path + } + } ); j.fork = fork; - var timeout = setTimeout(function(){ + var timeout = setTimeout(function () { fork.removeAllListeners(); fork.kill(); - return next(new Error(j.title+"'s fork process timed out, this may be due to long syncrounous code on initialization'")); + return next(new Error(j.title + "'s fork process timed out, this may be due to long syncrounous code on initialization'")); }, 5000); - fork.once("message",function(m){ + fork.once("message", function (m) { clearTimeout(timeout); fork.removeAllListeners(); - if(m.cmd != "ready"){ + if (m.cmd != "ready") { fork.kill(); return next(new Error("fork process sending messages before initialization")); } - console.log("forkready"); - that.emit("forked",fork,j); - next(void(0),j); + debug("forkready"); + that.emit("forked", fork, j); + next(void(0), j); }); - fork.once("error",function(e){ + fork.once("error", function (e) { clearTimeout(timeout); fork.removeAllListeners(); return next(e); }); - }catch(e){ + } catch (e) { clearTimeout(timeout); fork.removeAllListeners(); return next(e); } - }catch(e){ - console.log(j.name+" has no serverside scripts but that is ok") - return next(void(0),j); + } catch (e) { + console.log(j.name + " has no serverside scripts but that is ok") + return next(void(0), j); } } -AppFactory.prototype.getSingle = function(file,next){ +AppFactory.prototype.getSingle = function (file, next) { async.waterfall([ - function(next){ - next(void(0),file); + + function (next) { + next(void(0), file); }, this.checkWindowJSON.bind(this), this.checkURIs.bind(this), this.checkNPMDeps.bind(this), this.checkBowerDeps.bind(this), this.createFork.bind(this) - ],function(err,result){ - if(err) return next(err); - this.emit("compiledSingle",result) - next(void(0),result) + ], function (err, result) { + if (err) return next(err); + this.emit("compiledSingle", result) + next(void(0), result) }.bind(this)) -} +} \ No newline at end of file diff --git a/core/fork_framework/fork_container/fork2server_com.js b/core/fork_framework/fork_container/fork2server_com.js index b7550b8..afa87bb 100644 --- a/core/fork_framework/fork_container/fork2server_com.js +++ b/core/fork_framework/fork_container/fork2server_com.js @@ -1,7 +1,7 @@ /* Similar to meteor.methods */ -console.log("in the child"); +var silkMethods = require('./server_api.js'); function MethodCall(message){ this.id = message.id; @@ -77,7 +77,7 @@ process.on("message",function(message){ /* Commands: disconnect: Head is closed - + silkMethod: return value for silk api method */ if(!("cmd" in message)){ var meth = new MethodCall(message); @@ -88,14 +88,16 @@ process.on("message",function(message){ case "close": break; //expected to close, will close forcfully in 5 seconds case "sleep": break; //Head is removed from the window manager so updates are impossible case "minimize": break; //Head is not removed but updates to the head will not be seen - + case "silkMethod": Silk.api.done(message); } }); // make global because it will be used in most files. +global.Silk = {}; +Silk.methods = methods; +Silk.api = silkMethods; global.methods = methods; - process.nextTick(function(){ require(process.env.start); }) diff --git a/core/fork_framework/fork_container/server_api.js b/core/fork_framework/fork_container/server_api.js new file mode 100644 index 0000000..af25695 --- /dev/null +++ b/core/fork_framework/fork_container/server_api.js @@ -0,0 +1,33 @@ +// makes silk api available to fork +var requests = {}; + +var call = function(method, data, cb){ + var id = new Date().getTime() + '-' + Math.random(); + + requests[id] = { + cb: cb + } + + process.send({cmd:"silkMethod",message:{ + id: id, + method: method, + data: data + }}); +} + +var done = function(message){ + var id = message.message.id; + var error = message.message.error; + var result = message.message.result; + try{ + requests[id].cb(error, result); + } catch(e){ + + } +} + +var api = {}; +api.call = call; +api.done = done; + +module.exports = api; \ No newline at end of file diff --git a/core/fork_framework/index.js b/core/fork_framework/index.js index 125b343..eef6a69 100644 --- a/core/fork_framework/index.js +++ b/core/fork_framework/index.js @@ -1,11 +1,11 @@ var WL = require(__dirname+'/app_loader.js') var windows; - require(__dirname+"/ws2fork_com.js"); module.exports = function(app,wss){ windows = new WL(__root+"/apps/","/",app); + Silk.set("apps/appLoader", windows); windows.on("finishedCompiling", function(results){ for(var i in windows.hashmap) windows.hashmap[i].fork.send({name:"windows",data:windows.clean}); @@ -23,18 +23,9 @@ module.exports = function(app,wss){ console.log("child disconnected") }) }) -/* - methods.add({ - "silk/apps/list": function (data) { - console.log("test"); - console.log("received: " + data); - return "this is a vlue returned by the method" - } - }); -*/ app.get("/windows.json",function(req,res,next){ - console.log(windows.clean); + debug(windows.clean); res.type("json").send(windows.clean); }); diff --git a/core/fork_framework/silk_methods.js b/core/fork_framework/silk_methods.js new file mode 100644 index 0000000..37a7160 --- /dev/null +++ b/core/fork_framework/silk_methods.js @@ -0,0 +1,44 @@ +// API for apps. Available in app forks. +var silkMethods = {}; + +var requests = []; + +silkMethods['apps/list'] = function () { + return Silk.get('apps/appLoader').clean; +} + +silkMethods['apps/restart'] = function (name, message) { + Silk.get("apps/appLoader").restartSingle(Silk.get('apps/appLoader').hashmap[name], function (result) { + }); + +} + +silkMethods['apps/start'] = function (folder, message) { + var appLoader = Silk.get('apps/appLoader'); + appLoader.getSingle(folder, function (err, j){ + if(err) console.log(err); + appLoader.emit('finishedCompiling', appLoader.clean); + }) +}; + +module.exports.methods = silkMethods; + +module.exports.call = function (message, fork) { + var error = null; + var result = null; + try { + var result = silkMethods[message.message.method](message.message.data); + } catch (e) { + error = e; + } + fork.send({ + cmd: "silkMethod", + message: { + id: message.message.id, + error: error, + result: result + } + }) + + +} \ No newline at end of file diff --git a/core/fork_framework/ws2fork_com.js b/core/fork_framework/ws2fork_com.js index e5f752a..13fac6b 100644 --- a/core/fork_framework/ws2fork_com.js +++ b/core/fork_framework/ws2fork_com.js @@ -1,8 +1,9 @@ /* - Similar to meteor.methods + Communication between - method calls from the client and app forks. + - Silk api calls from forks and silk methods. */ - +var silkMethods = require('./silk_methods.js'); var methods = { wflag:false, @@ -16,14 +17,14 @@ var methods = { }; methods.add = function(m,fork){ - console.log("adding"); + debug("adding"); this.responders[m.name] = fork; this.fork_resp[fork.pid].push(m.name) } methods.send = function(message){ if(!this.requests[message.id]){ - console.log("user removed or no request"); + debug("user removed or no request"); return; } this.requests[message.id].send(JSON.stringify(message)); @@ -39,14 +40,14 @@ methods.removeFork = function(fork,code,signal){ } methods.addFork = function(fork){ - console.log("adding fork"); + debug("adding fork"); this.fork_resp[fork.pid] = []; this.forks[fork.pid] = fork; fork.on("message", function(message){ -// console.log(JSON.stringify(message)); switch(message.cmd){ case "send": methods.send(message.message);break; case "add": methods.add(message,fork);break; + case "silkMethod": silkMethods.call(message, fork);break; } }.bind(this)) fork.on("error",function(e){ diff --git a/core/public/js/load.js b/core/public/js/load.js deleted file mode 100644 index e82b2a1..0000000 --- a/core/public/js/load.js +++ /dev/null @@ -1,11 +0,0 @@ -$(document).ready(function () { - $(".loader span").css("animationIterationCount", "1"); - $.ajax("/windows.json").done(function(data){ - initializeManager(data); - window.setTimeout(function () { - $(".loader").fadeOut(); - }, 500) - }) - - -}); diff --git a/core/public/js/windowManager.js b/core/public/js/windowManager.js deleted file mode 100644 index b3c766c..0000000 --- a/core/public/js/windowManager.js +++ /dev/null @@ -1,198 +0,0 @@ -var windows = []; - -// channel for each window -var channels = {}; - -function CreateChannel(app) { - app.running = true; - var windowName = app.title; - // allow html to be updated - console.log(windowName); - chan = Channel.build({ - window: $("#desktop .window iframe[data-name='" + windowName + "']")[0].contentWindow, - origin: "*", - scope: "testScope", - onReady: function () { - console.log("channel is ready!"); - console.log("ready"); - - } - }) - - chan.bind("openFile", function (context, params) { - console.log("=============") - console.log(context); - console.log(params); - - // open text editor and include path in url - // find text editor - - methods.call("Silk/appDefaults", params.mime, function (err, data) { - function openWindow(title) { - // find window index - for (var i = 0; i < windows.length; ++i) { - - if (windows[i].title === title) { - - // start the app - if (windows[i].running == false) { - var url = windows[i].url; - url = url.split("?")[0]; - windows[i].url = url + "?file=" + encodeURIComponent(params.path); - windows[i].running = true; - windows[i].minimized = false; - windowOrder.unshift(windows[i].title); - - } else { - - channels[windows[i].title].notify({ - method: "fileToOpen", - params: { - path: params.path, - mime: params.mime - } - }); - - if (windows[i].minimized == true) { - windows[i].minimized = false; - windowOrder.unshift(windows[i].title); - } else { - // move position to top - windowOrder.pop(windows[i].title); - windowOrder.unshift(windows[i].title); - } - - } - - updateOrder(); - - break; - } - } - } - - console.log(data); - - // open using default program - if (data.default !== "") { - openWindow(data.default); - } - // if one app use it if it is not for *. - else if (data.available.length < 2 && data.mime != "*") { - openWindow(data.available[0]); - } - // let user choose program - else { - var html = '
    Choose App To Open
    '; - html += params.path; - html += '
      '; - for (var i = 0; i < data.available.length; ++i) { - html += "
    • "; - html += data.available[i]; - html += "
    • "; - } - html += '
    Always Use This App
    ' - $("#appNotifications").append(html); - $("#appNotifications").css("display", "block"); - - // set up click hander - $("#appNotifications .content button").click(function (e) { - $("#appNotifications").css("display", "none"); - $("#appNotifications").html(""); - }); - $("#appNotifications .content li").click(function (e) { - console.log($(e.target).html()); - openWindow($(e.target).html()); - // if chewckbox is checked, set up default app - if ($('#appNotifications input').is(':checked') == true) { - methods.call("Silk/setDefault", { - mime: params.mime, - app: $(e.target).html() - }, function () {}) - } - $("#appNotifications").css("display", "none"); - $("#appNotifications").html(""); - }); - } - }); - - }); - - channels[windowName] = chan; -} - -// order of open windows. Used to calculate z-index. -var windowOrder = []; - -// update z-index of windows -var updateOrder = function () { - for (var i = 0; i < windows.length; ++i) { - console.log(windows[i].title) - if (windows[i].running === true & windows[i].minimized !== true) { - console.log(windows[i].title + "will be updated"); - var position = windowOrder.indexOf(windows[i].title); - if (position > -1) { - windows[i].zIndex = 200 - position; - } - } - } -} - -var wm; -var taskbar; - -function initializeManager(_windows) { - windows = _windows; - wm = new Vue({ - el: '#desktop', - data: { - windows: windows - }, - methods: { - buildChannel: CreateChannel, - minimize: function (app) { - // put window on top - app.minimized = !app.minimized; - - }, - close: function (app) { - // reset url - app.url = app.url.split("?")[0]; - app.running = false; - app.minimized = true; - windowOrder.pop(app.title); - updateOrder(); - } - } - }); - - taskbar = new Vue({ - el: '#taskbar', - data: { - programs: windows - }, - methods: { - open: function (app) { - app.running = true; - // move to top if behind - if (app.minimized == false) { - if (windowOrder.indexOf(app.title) > 0) { - // move to top - windowOrder.pop(app.title); - windowOrder.unshift(app.title); - updateOrder(); - return - } - } - app.minimized = !app.minimized; - if (app.minimized === false) { - windowOrder.unshift(app.title); - } - if (app.minimized === true) { - windowOrder.pop(app.title); - } - updateOrder(); - } - } - }) -} \ No newline at end of file diff --git a/core/remote.js b/core/remote.js new file mode 100644 index 0000000..4df47a4 --- /dev/null +++ b/core/remote.js @@ -0,0 +1,41 @@ + // make app availalbe outisde nodeos + + start = function (options) { + var remoteDomain = require('domain').create(); + + remoteDomain.on('error', function (err) { + console.log(err); + try { + Silk.get("remote/close")(); + } catch (e) { + console.log(e); + } + }) + + remoteDomain.add(Silk); + + remoteDomain.run(function () { + var localtunnel = require('localtunnel'); + localtunnel(3000, function (err, tunnel) { + if (err) { + console.log(err); + return; + } + + Silk.set("remote/url", tunnel.url); + Silk.set("remote/close", tunnel.close); + + console.log("Go to " + tunnel.url + " to remotely access Silk"); + + tunnel.on("error", function (err) { + console.log(err); + + }); + tunnel.on("close", function () { + console.log("remote closed"); + }) + }); + + }) + } + Silk.set('remote/start', start); \ No newline at end of file diff --git a/package.json b/package.json index d5b72e5..7ec300e 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,24 @@ "author": "zodern", "license": "MIT", "dependencies": { + "async": "~0.9.0", + "bower-json": "latest", + "commander": "2.5.0", "express": "^4.10.1", - "ws": "~0.4.31", + "jspath": "latest", "localtunnel": "1.5.0", "mime": "~1.2.11", - "commander": "2.5.0", - "async": "~0.9.0", - "bower-json": "latest", - "rsvp": "latest" + "os-utils": "0.0.14", + "rsvp": "latest", + "ws": "~0.4.31" + }, + "optionalDependencies": { + "tty.js": "zodern/tty.js" }, "scripts": { "start": "node server.js" }, "bin": { - "silk": "bin.js" - } + "silk": "bin.js" + } } diff --git a/server.js b/server.js index 0e86008..0fd1e0d 100644 --- a/server.js +++ b/server.js @@ -1,22 +1,88 @@ +var http = require('http'); +var express = require('express') +var WebSocketServer = require('ws').Server; +var program = require('commander'); + +// has info and state of various parts of Silk. Used mainly be api. +global.Silk = { + set: function(prop, value){ + if(prop in Silk.data){ + Silk.data[prop].value = value; + } + else{ + Silk.data[prop] = { + value: value, + needUpdates: [] + } + } + }, + get: function(prop){ + return Silk.data[prop].value; + }, + data: {} +}; global.__root = __dirname; -// debug mode -if (global.debug == undefined) { - global.debug = function (message) { - console.log(message); - } -} +var app = express(), + server, + wss, + windows; -var path = require('path'); -var WebSocketServer = require('ws').Server; -var wss = new WebSocketServer({ - port: 9999 +program + .version('0.3.0') + .option('-r, --remote', 'Remotely access Silk') + .option('-d, --dev', 'Show debug messages') + .parse(process.argv); + +program.dev ? global.debug = console.log : global.debug = function(){}; + + +app.get('/', function (req, res) { + res.sendFile(__root + "/window-manager/public/index.html"); }); +// static files for client +app.use(express.static(__dirname + '/window-manager/public')); +app.get(/^\/bc\//, require(__root + "/core/bower_static.js")); +app.get("/api.js", require(__root + "/core/client_api.js")); + +server = app.listen(3000, function () { + var add = server.address(); -debug("web socket is at: " + wss.options.host + ":" + wss.options.port); + // display url in box + var message = 'Silk at http://' + add.address + ":" + add.port; + var length = message.length; + var prespace = " "; + var box = ""; + + for (var i = 0; i < length + 10; ++i) { + box += "="; + } + + var space = " "; + var empty = ""; + + for (var i = 0; i < length + 10 - 2; ++i) { + empty += " "; + } + + console.log(prespace + box); + console.log(prespace + "|" + empty + "|"); + console.log(prespace + "|" + space + message + space + "|"); + console.log(prespace + "|" + empty + "|"); + console.log(prespace + box); + console.log(""); +}) + +wss = new WebSocketServer({ + server: server, + path: '/websocket' +}); + +debug("web socket is at: " + wss.options.host + wss.options.path); wss.on('connection', function (ws) { + debug("connected"); ws.on('message', function (message) { debug("websocket message: " + message); @@ -26,22 +92,10 @@ wss.on('connection', function (ws) { }); }); -var express = require('express') -var app = express() - -app.get('/', function (req, res) { - res.sendFile(path.join(__dirname, 'index.html')); -}) - -// static files for client -app.use(express.static(__dirname + '/core/public')); -app.get(/^\/bc\//, require(__root + "/core/bower_static.js")); -app.get("/api.js", require(__root + "/core/client_api.js")); - var windows = require(__root + "/core/fork_framework")(app, wss); -var server = app.listen(3000, function () { - var add = server.address(); - console.log('Silk at http://%s:%s', add.address, add.port) +require('./core/remote.js'); -}); +if (program.remote) { + Silk.get('remote/start')(); +} \ No newline at end of file diff --git a/core/public/js/call.js b/window-manager/public/JS/call.js similarity index 92% rename from core/public/js/call.js rename to window-manager/public/JS/call.js index 6312e2c..2010111 100644 --- a/core/public/js/call.js +++ b/window-manager/public/JS/call.js @@ -4,7 +4,14 @@ var socket; (function () { try { - var host = "ws://0.0.0.0:9999"; + var host = "ws://0.0.0.0:3000/websocket"; + + // change host url if remote + if(/localhost:3000/.test(location.host) | /0.0.0.0:3000/.test(location.host)){ + } else{ + host = "ws://" + location.host + "/websocket"; + } + console.log(host); socket = new WebSocket(host); socket.onopen = function () { diff --git a/core/public/js/clock.js b/window-manager/public/JS/clock.js similarity index 100% rename from core/public/js/clock.js rename to window-manager/public/JS/clock.js diff --git a/core/public/js/libraries/jquery.js b/window-manager/public/JS/libraries/jquery.js similarity index 100% rename from core/public/js/libraries/jquery.js rename to window-manager/public/JS/libraries/jquery.js diff --git a/core/public/js/libraries/jsChannel.js b/window-manager/public/JS/libraries/jsChannel.js similarity index 100% rename from core/public/js/libraries/jsChannel.js rename to window-manager/public/JS/libraries/jsChannel.js diff --git a/core/public/js/libraries/vue.js b/window-manager/public/JS/libraries/vue.js similarity index 100% rename from core/public/js/libraries/vue.js rename to window-manager/public/JS/libraries/vue.js diff --git a/window-manager/public/JS/load.js b/window-manager/public/JS/load.js new file mode 100644 index 0000000..2ed9878 --- /dev/null +++ b/window-manager/public/JS/load.js @@ -0,0 +1,22 @@ +$(document).ready(function () { + $(".loader span").css("animationIterationCount", "1"); + $.ajax("/windows.json").done(function (data) { + + // fix urls if remote + if(remote.isRemote === true){ + console.log("fixing urls"); + for(var i = 0; i < data.length; ++i){ + data[i].url = remote.fixURL(data[i].url); + data[i].icon = remote.fixURL(data[i].icon); + } + console.log(JSON.stringify(data)); + } + + initializeManager(data); + window.setTimeout(function () { + $(".loader").fadeOut(); + }, 80) + }) + + +}); \ No newline at end of file diff --git a/core/public/css/main.css b/window-manager/public/css/main.css similarity index 70% rename from core/public/css/main.css rename to window-manager/public/css/main.css index 1e2c48d..1e0afaf 100644 --- a/core/public/css/main.css +++ b/window-manager/public/css/main.css @@ -57,15 +57,18 @@ span:nth-child(4) { /* Task Bar */ #taskbar { - z-index: 500; - position: fixed; - bottom: 0px; - left: 0px; - width: 100%; - height: 50px; - background-color: #459fe0; - opacity: .7; - transition: .3s opacity; +-webkit-user-select: none; +z-index: 500; +position: fixed; +bottom: 0px; +left: 0px; +width: 100%; +height: 50px; +background-color: rgba(255, 255, 255, 0.15); +opacity: 1; +transition: .3s opacity; +border-top: 3px solid #FFF; +box-sizing: border-box; } #taskbar *{ -webkit-user-select: none; @@ -81,7 +84,7 @@ span:nth-child(4) { cursor: pointer; } #taskbar .app:hover { - background-color: rgba(0, 0, 0, 0.57); + background-color: rgba(255, 255, 255, 0.3); } #taskbar .app img { height: 40px; @@ -111,6 +114,8 @@ span:nth-child(4) { height: 30px; padding-top: 7px; box-sizing: border-box; + cursor: default; + font-weight: 300; } .window .title .minimize { display: inline-block; @@ -152,7 +157,7 @@ span:nth-child(4) { -webkit-transition: .2s background-color; } .window .title .close:hover { - background-color: rgba(16, 67, 103, 0.32); +background-color: #B01717; } #desktop .window iframe { background-color: white; @@ -198,7 +203,7 @@ bottom: 0px; height: 50px; font-size: 16px; color: rgb(255, 255, 255); -padding-right: 5px; +padding-right: 13px; padding-top: 17px; box-sizing: border-box; cursor: default; @@ -224,4 +229,114 @@ box-sizing: border-box; color: rgb(0, 0, 255); text-decoration: underline; cursor: pointer; +} + +/* Menu */ + +#menu { +position: absolute; +bottom: 0px; +width: 100%; +background-color: rgba(255, 255, 255, 0.8); +height: auto; +color: #3A3939; +display: none; +z-index: 201; +box-sizing: border-box; +padding: 0px; + -webkit-user-select: one; + opacity: 0; + margin-bottom: 50px; +} +#menu h1 { +font-weight: 300; +text-align: left; +font-size: 26px; +cursor: default; +background-color: #459FE0; + color: white; + width: 100%; + padding: 10px; + padding-left: 50px; + margin-top: 0px; + box-sizing: border-box; +} +#menu h1 img { + height: 26px; + position: absolute; + right: 0px; + top: 0px; + cursor: pointer; + padding: 12px; + background-color: #1E5C88; + -webkit-transition: .2s background-color; +} +#menu h1 img:hover { + background-color: #B01717; +} +#menu ul { +padding-left: 0px; +} +#menu li { +display: inline-block; +list-style: none; +vertical-align: middle; +width: 175px; +cursor: pointer; +box-sizing: border-box; +color: #3A3939; +margin-bottom:5px; +transition: .3s all; + padding: 3px; +} +#menu li img { +vertical-align: middle; +height: 30px; + margin-right: 5px; + transition: .3s all; +} +#menu li:hover { +box-shadow: 0px 3px 0px #459FE0; +} + +#taskbar .app.menu { +border-top: 0px; +background-color: #459FE0; +vertical-align: top; +padding-top: 3px; +margin-right: -4px; +} +#taskbar .app.menu:hover{ + background-color: #1E5C88; +} + +/* tooltips */ + +/* remove arrow */ +.hint--top:before { +border-color: transparent; +} + +.hint:after, [data-hint]:after { +background: #FFF; +color: #808080; +box-shadow: 0px 0px 9px #808080; +border: 1px solid #808080; +} + +.hint--top:hover:after, .hint--top:hover:before, .hint--top:focus:after, .hint--top:focus:before{ +-webkit-transform: translateY(-8px) translateX(-50%); +-moz-transform: translateY(-8px) translateX(-50%); +transform: translateY(-8px) translateX(-50%); +margin-left: 0px; +} + +.hint:hover:before, .hint:hover:after, [data-hint]:hover:before, [data-hint]:hover:after{ + -webkit-transition: .3s opacity; + -moz-transition: .3s opacity; + transition: .3s opacity; + -webkit-transition-delay: 300ms; +-moz-transition-delay: 300ms; +transition-delay: 300ms; + } \ No newline at end of file diff --git a/core/public/css/normalize.css b/window-manager/public/css/normalize.css similarity index 100% rename from core/public/css/normalize.css rename to window-manager/public/css/normalize.css diff --git a/window-manager/public/img/Close-48.png b/window-manager/public/img/Close-48.png new file mode 100644 index 0000000..543879d Binary files /dev/null and b/window-manager/public/img/Close-48.png differ diff --git a/window-manager/public/img/menu.png b/window-manager/public/img/menu.png new file mode 100644 index 0000000..2492923 Binary files /dev/null and b/window-manager/public/img/menu.png differ diff --git a/index.html b/window-manager/public/index.html similarity index 56% rename from index.html rename to window-manager/public/index.html index 33717ff..d049392 100644 --- a/index.html +++ b/window-manager/public/index.html @@ -3,8 +3,9 @@ Silk GUI + - + @@ -13,44 +14,46 @@

    Silk

    +
    -
    {{title}} _ × +
    {{title}} _ ×
    - -
    -
    -
    - - + -
    + + - + + + + diff --git a/window-manager/public/js/channel.js b/window-manager/public/js/channel.js new file mode 100644 index 0000000..1f3343e --- /dev/null +++ b/window-manager/public/js/channel.js @@ -0,0 +1,104 @@ +// channel for each window +var channels = {}; + +function CreateChannel(index) { + var app = windows[index]; + app.running = true; + var windowName = app.title; + // allow html to be updated + console.log(windowName); + chan = Channel.build({ + window: $("#desktop .window iframe[data-name='" + windowName + "']")[0].contentWindow, + origin: "*", + scope: "testScope", + onReady: function () { + console.log("channel is ready!"); + console.log("ready"); + + } + }) + + chan.bind("openFile", function (context, params) { + console.log("=============") + console.log(context); + console.log(params); + + // open text editor and include path in url + // find text editor + + methods.call("Silk/appDefaults", params.mime, function (err, data) { + function openWindow(title) { + // find window index + for (var i = 0; i < apps.length; ++i) { + + //TODO correctly handle apps that have multipleWindows : false + if (apps[i].title === title) { + // clone object + var win = JSON.parse(JSON.stringify(apps[i])); + + // open the window + console.log("win.title = " + win.title); + win.running = true; + win.minimized = false; + windows.push(win); + windowOrder.unshift(windows.length - 1); + updateOrder(); + + var url = win.url; + url = url.split("?")[0]; + win.url = url + "?file=" + encodeURIComponent(params.path); + + return; + } + } + } + + console.log(data); + + // open using default program + if (data.default !== "") { + openWindow(data.default); + } + // if one app use it if it is not for *. + else if (data.available.length < 2 && data.mime != "*") { + openWindow(data.available[0]); + } + // let user choose program + else { + var html = '
    Choose App To Open
    '; + html += params.path; + html += '
      '; + for (var i = 0; i < data.available.length; ++i) { + html += "
    • "; + html += data.available[i]; + html += "
    • "; + } + html += '
    Always Use This App
    ' + $("#appNotifications").append(html); + $("#appNotifications").css("display", "block"); + + // set up click hander + $("#appNotifications .content button").click(function (e) { + $("#appNotifications").css("display", "none"); + $("#appNotifications").html(""); + }); + $("#appNotifications .content li").click(function (e) { + console.log($(e.target).html()); + openWindow($(e.target).html()); + // if chewckbox is checked, set up default app + if ($('#appNotifications input').is(':checked') == true) { + methods.call("Silk/setDefault", { + mime: params.mime, + app: $(e.target).html() + }, function () {}) + } + $("#appNotifications").css("display", "none"); + $("#appNotifications").html(""); + }); + } + }); + + }); + + channels[windowName] = chan; +} \ No newline at end of file diff --git a/window-manager/public/js/remote.js b/window-manager/public/js/remote.js new file mode 100644 index 0000000..f7e2c21 --- /dev/null +++ b/window-manager/public/js/remote.js @@ -0,0 +1,26 @@ +var remote = {}; + +(function () { + + // check if url is remote + if(/localhost:3000/.test(location.host) | /0.0.0.0:3000/.test(location.host)){ + // not remote + } else{ + remote.isRemote = true; + } + + // change http://localhost or 0.0.0.0 to remote address + remote.fixURL = function(url){ + if(remote.isRemote === false){ + return url; + } + var http = /^http:/i; + var https = /^https:/i; + + if(/localhost:3000/.test(url) | /0.0.0.0:3000/.test(url)){ + url = location.protocol + "//" + url.replace("http://localhost:3000", location.host); + return url; + } + } + +})() \ No newline at end of file diff --git a/window-manager/public/js/windowManager.js b/window-manager/public/js/windowManager.js new file mode 100644 index 0000000..c9e3324 --- /dev/null +++ b/window-manager/public/js/windowManager.js @@ -0,0 +1,194 @@ +var windows = []; +var apps = []; + +// order of open windows. Used to calculate z-index. +var windowOrder = []; + +// update z-index of windows +var updateOrder = function () { + for (var i = 0; i < windows.length; ++i) { + console.log(windows[i].title) + if (windows[i].running === true & windows[i].minimized !== true) { + console.log(windows[i].title + "will be updated"); + var position = windowOrder.indexOf(i); + if (position > -1) { + windows[i].zIndex = 200 - position; + } + } + } +} + +var wm; +var taskbar; +var menu; + +function initializeManager(_windows) { + apps = _windows; + wm = new Vue({ + el: '#desktop', + data: { + windows: windows + }, + methods: { + buildChannel: CreateChannel, + minimize: function (index) { + windows[index].minimized = true; + var position = windowOrder.indexOf(index); + windowOrder.splice(position, 1); + }, + close: function (index) { + // reset url + var app = windows[index]; + app.url = app.url.split("?")[0]; + + app.running = false; + app.minimized = true; + + var position = windowOrder.indexOf(index); + windowOrder.splice(position, 1); + updateOrder(); + } + } + }); + + taskbar = new Vue({ + el: '#taskbar', + data: { + programs: windows + }, + methods: { + open: function (index) { + app = windows[index]; + app.running = true; + + // move to top if behind + if (app.minimized === false) { + if (windowOrder.indexOf(index) > -1) { + // move to top + var position = windowOrder.indexOf(index); + windowOrder.splice(position, 1); + windowOrder.unshift(index); + updateOrder(); + return + } + } + + app.minimized = !app.minimized; + if (app.minimized === false) { + windowOrder.unshift(index); + } + if (app.minimized === true) { + var position = windowOrder.indexOf(index); + windowOrder.splice(position, 1); + } + updateOrder(); + }, + menu: function () { + // toggle menu + if ($("#menu").css("display") == "block") { + $("#menu").css("overflow", "hidden"); + $("#menu").animate({ + height: 0, + opacity: 0 + }, + 100, 'swing', function () { + $("#menu").css({ + "display": "none", + "height": "auto" + }) + }); + + } else { + var height = $("#menu").height(); + + // $("#taskbar").css("bottom", height); + $("#menu").css("display", "block"); + $("#menu").css("height", 0); + $("#menu").css("overflow", "hidden"); + $("#menu").animate({ + height: height, + overflow: "scroll", + opacity: 1 + }, { + duration: 100 + }); + } + } + } + }); + + menu = new Vue({ + el: '#menu', + data: { + apps: apps + }, + methods: { + open: function (index) { + + // hide menu + $("#menu").css("overflow", "hidden"); + $("#menu").animate({ + height: 0, + opacity: 0 + }, + 100, 'swing', function () { + $("#menu").css({ + "display": "none", + "height": "auto" + }) + }); + + + var app = menu.$data.apps[index]; + + // create new window + if (app.multipleWindows === true) { + // clone object + var win = JSON.parse(JSON.stringify(app)); + win.running = true; + win.minimized = false; + windows.push(win); + windowOrder.unshift(windows.length - 1); + updateOrder(); + + } else { + var win = JSPath.apply('.windows{.title == "' + app.title + '"}', wm.$data); + if (win.length > 0) { + app = win[0]; + } else { + app = windows.push(app); + app = windows[app - 1]; + } + + // open existing window and move to top + if (app.minimized == false) { + // check if window open + + windowOrder.pop(index); + windowOrder.unshift(index); + updateOrder(); + + } else { + app.minimized = false; + app.running = true; + windowOrder.unshift(index); + updateOrder(); + } + } + } + } + }) + $("#menu h1 img").click(function(){ + $("#menu").css("overflow", "hidden"); + $("#menu").animate({ + height: 0, + opacity: 0 + }, + 100, 'swing', function () { + $("#menu").css({ + "display": "none", + "height": "auto" + }) + }); + }) +} \ No newline at end of file