This commit is contained in:
nightwing 2015-01-04 02:13:28 +04:00
commit 0054bb8794
7 changed files with 165 additions and 87 deletions

View file

@ -169,9 +169,8 @@ function loadDoc(name, callback) {
}); });
} }
// callback is called with the error message from PUT (if any)
function saveDoc(name, callback) { function saveDoc(name, callback) {
var doc = fileCache[name]; var doc = fileCache[name] || name;
if (!doc || !doc.session) if (!doc || !doc.session)
return callback("Unknown document: " + name); return callback("Unknown document: " + name);
@ -182,9 +181,24 @@ function saveDoc(name, callback) {
else if (parts[0] == "ace") else if (parts[0] == "ace")
path = "lib/" + path; path = "lib/" + path;
net.request('PUT', path, doc.session.getValue(), callback); upload(path, doc.session.getValue(), callback);
} }
function upload(url, data, callback) {
url = net.qualifyURL(url);
if (!/https?:/.test(url))
return callback(new Error("Unsupported url scheme"));
var xhr = new XMLHttpRequest();
xhr.open("PUT", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
callback(!/^2../.test(xhr.status));
}
};
xhr.send(data);
};
module.exports = { module.exports = {
fileCache: fileCache, fileCache: fileCache,
docs: sort(prepareDocList(docs)), docs: sort(prepareDocList(docs)),

View file

@ -9,9 +9,9 @@ define(function(require, exports, module) {
"use strict"; "use strict";
var dom = require("./dom"); var dom = require("./dom");
exports.request = function (verb, url, data, callback) { exports.get = function (url, callback) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open(verb, url, true); xhr.open('GET', url, true);
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
//Do not explicitly handle errors, those should be //Do not explicitly handle errors, those should be
//visible via console output in the browser. //visible via console output in the browser.
@ -19,11 +19,7 @@ exports.request = function (verb, url, data, callback) {
callback(xhr.responseText); callback(xhr.responseText);
} }
}; };
xhr.send(data); xhr.send(null);
};
exports.get = function (url, callback) {
this.request('GET', url, null, callback);
}; };
exports.loadScript = function(path, callback) { exports.loadScript = function(path, callback) {

View file

@ -126,7 +126,7 @@ var AbapHighlightRules = function() {
{token : "string", regex : "`", next : "start"}, {token : "string", regex : "`", next : "start"},
{defaultToken : "string"} {defaultToken : "string"}
] ]
} };
}; };
oop.inherits(AbapHighlightRules, TextHighlightRules); oop.inherits(AbapHighlightRules, TextHighlightRules);

111
static.js
View file

@ -10,37 +10,100 @@ var http = require("http")
// compatibility with node 0.6 // compatibility with node 0.6
if (!fs.exists) if (!fs.exists)
fs.exists = path.exists; fs.exists = path.exists;
http.createServer(function(request, response) { var allowSave = process.argv.indexOf("--allow-save") != -1;
var uri = url.parse(request.url).pathname http.createServer(function(req, res) {
, filename = path.join(process.cwd(), uri); var uri = url.parse(req.url).pathname
, filename = path.join(process.cwd(), uri);
fs.exists(filename, function(exists) { if (req.method == "PUT") {
if(!exists) { if (!allowSave)
response.writeHead(404, {"Content-Type": "text/plain"}); return error(res, 404, "Saving not allowed pass --allow-save to enable");
response.write("404 Not Found\n"); save(req, res, filename);
response.end();
return;
} }
if (fs.statSync(filename).isDirectory()) filename += '/index.html'; fs.exists(filename, function(exists) {
if (!exists)
return error(res, 404, "404 Not Found\n");
fs.readFile(filename, "binary", function(err, file) { if (fs.statSync(filename).isDirectory()) {
if(err) { var files = fs.readdirSync(filename);
response.writeHead(500, {"Content-Type": "text/plain"}); res.writeHead(200, {"Content-Type": "text/html"});
response.write(err + "\n");
response.end();
return;
}
var contentType = mime.lookup(filename) || "text/plain"; files.push(".", "..");
response.writeHead(200, {"Content-Type": contentType}); var html = files.map(function(name) {
response.write(file, "binary"); var href = uri + "/" + name;
response.end(); href = href.replace(/[\/\\]+/g, "/").replace(/\/$/g, "");
if (fs.statSync(filename + "/" + name + "/").isDirectory())
href += "/";
return "<a href='" + href + "'>" + name + "</a><br>";
});
res._hasBody && res.write(html.join(""));
res.end();
return;
}
fs.readFile(filename, "binary", function(err, file) {
if (err) {
res.writeHead(500, { "Content-Type": "text/plain" });
res.write(err + "\n");
res.end();
return;
}
var contentType = mime.lookup(filename) || "text/plain";
res.writeHead(200, { "Content-Type": contentType });
res.write(file, "binary");
res.end();
});
}); });
});
}).listen(port, ip); }).listen(port, ip);
console.log("http://localhost:" + port); function error(res, status, message, error) {
console.error(error || message);
res.writeHead(status, { "Content-Type": "text/plain" });
res.write(message);
res.end();
}
function save(req, res, filePath) {
var data = "";
req.on("data", function(chunk) {
data += chunk;
});
req.on("error", function() {
error(res, 404, "Could't save file");
});
req.on("end", function() {
try {
fs.writeFileSync(filePath, data);
}
catch (e) {
return error(res, 404, "Could't save file", e);
}
res.statusCode = 200;
res.end("OK");
});
}
function getLocalIps() {
var os = require("os");
var interfaces = os.networkInterfaces ? os.networkInterfaces() : {};
var addresses = [];
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2];
if (address.family === "IPv4" && !address.internal) {
addresses.push(address.address);
}
}
}
return addresses;
}
console.log("http://" + (ip == "0.0.0.0" ? getLocalIps()[0] : ip) + ":" + port);

View file

@ -263,13 +263,13 @@ def command():
puttable = set(path.abspath(p) for p in puttable = set(path.abspath(p) for p in
options.puttable.replace(","," ").split()) options.puttable.replace(","," ").split())
if puttable and host not in ('127.0.0.1', 'localhost'): if puttable and host not in ('127.0.0.1', 'localhost'):
sys.exit("Permitting PUT access for non-localhost connections is unwise.") print("Permitting PUT access for non-localhost connections may be unwise.")
options.rootdir = path.abspath(options.rootdir) options.rootdir = path.abspath(options.rootdir)
for p in puttable: for p in puttable:
if not p.startswith(options.rootdir): if not p.startswith(options.rootdir):
sys.exit("puttable path '%s' not under root '%s'" % (p, options.rootdir)) sys.exit("puttable path '%s' not under root '%s'" % (p, options.rootdir))
# cut off root prefix from puttable paths # cut off root prefix from puttable paths
puttable = set(p[len(options.rootdir):] for p in puttable) puttable = set(p[len(options.rootdir):] for p in puttable)
@ -283,13 +283,12 @@ def command():
print "Serving %s to http://%s:%d" % (options.rootdir, host, port) print "Serving %s to http://%s:%d" % (options.rootdir, host, port)
if puttable: if puttable:
print("The following paths (relative to server root) may be "+ print("The following paths (relative to server root) may be "+
"OVERWRITTEN via HTTP PUT.\n"+ "OVERWRITTEN via HTTP PUT.")
"I HOPE EVERY USER ON THIS SYSTEM IS TRUSTED!")
for p in puttable: for p in puttable:
print p print p
make_server(host, port, app).serve_forever() make_server(host, port, app).serve_forever()
except KeyboardInterrupt, ki: except KeyboardInterrupt, ki:
print "Cio, baby!" print "Ciao, baby!"
except: except:
sys.exit("Problem initializing server: %s" % sys.exc_info()[1]) sys.exit("Problem initializing server: %s" % sys.exc_info()[1])

View file

@ -59,7 +59,7 @@
<select id="modeEl" size="1"></select> <select id="modeEl" size="1"></select>
<input type="button" value="&#10227;" title="sync" id="syncToMode"></select> <input type="button" value="&#10227;" title="sync" id="syncToMode"></select>
<span id="tablist"></span> <span id="tablist"></span>
<input type="button" value="Save" id="uploadToServer1"></select> <input type="button" value="Save" id="saveButton1"></select>
<span class="separator-h"></span> <span class="separator-h"></span>
<label for="autorunEl">live preview</label> <label for="autorunEl">live preview</label>
<input type="checkbox" label="autorun" id="autorunEl" checked> <input type="checkbox" label="autorun" id="autorunEl" checked>
@ -71,7 +71,7 @@
<label for="themeEl">Theme</label> <label for="themeEl">Theme</label>
<select id="themeEl" size="1" value="textmate"></select> <select id="themeEl" size="1" value="textmate"></select>
<span class="separator-h"></span> <span class="separator-h"></span>
<input type="button" value="Save" id="uploadToServer2"></select> <input type="button" value="Save" id="saveButton2"></select>
<label for="doc">Document</label> <label for="doc">Document</label>
<select id="doc" size="1"></select> <select id="doc" size="1"></select>
</div> </div>

View file

@ -53,7 +53,8 @@ util.bindDropdown("doc", function(value) {
doclist.loadDoc(value, function(session) { doclist.loadDoc(value, function(session) {
if (session) { if (session) {
editor2.setSession(session); editor2.setSession(session);
uploadEl2.disabled = session.getUndoManager().isClean(); session.getUndoManager().markClean();
updateSaveButtonState(null, editor2);
} }
}); });
}); });
@ -61,7 +62,7 @@ util.bindDropdown("doc", function(value) {
var modeEl = document.getElementById("modeEl"); var modeEl = document.getElementById("modeEl");
util.fillDropdown(modeEl, modelist.modes); util.fillDropdown(modeEl, modelist.modes);
var modeSessions = {}; var modeSessions = {};
var savedLeadingComments = "";
util.bindDropdown(modeEl, function(value) { util.bindDropdown(modeEl, function(value) {
if (modeSessions[value]) { if (modeSessions[value]) {
editor1.setSession(modeSessions[value]); editor1.setSession(modeSessions[value]);
@ -70,17 +71,18 @@ util.bindDropdown(modeEl, function(value) {
} }
var hp = "./lib/ace/mode/" + value + "_highlight_rules.js"; var hp = "./lib/ace/mode/" + value + "_highlight_rules.js";
net.get(hp, function(text) { net.get(hp, function(text) {
uploadEl1.disabled = true;
savedLeadingComments = text;
text = util.stripLeadingComments(text);
savedLeadingComments = savedLeadingComments.substr(0, savedLeadingComments.length - text.length);
var session = new EditSession(text); var session = new EditSession(text);
session.setUndoManager(new UndoManager()); session.setUndoManager(new UndoManager());
modeSessions[value] = session; modeSessions[value] = session;
session.setMode("ace/mode/javascript"); session.setMode("ace/mode/javascript", function() {
if (session.getLine(0).match(/^\s*\//))
session.toggleFoldWidget(0); // fold licence comment
});
editor1.setSession(modeSessions[value]); editor1.setSession(modeSessions[value]);
session.getUndoManager().markClean();
updateSaveButtonState(null, editor1);
schedule(); schedule();
}); });
}); });
@ -91,43 +93,47 @@ document.getElementById("syncToMode").onclick = function() {
run(); run();
}; };
var uploadEl1 = document.getElementById("uploadToServer1"); editor1.saveButton = document.getElementById("saveButton1");
var uploadEl2 = document.getElementById("uploadToServer2"); editor2.saveButton = document.getElementById("saveButton2");
uploadEl1.onclick = function() { editor1.saveButton.editor = editor1;
var text = savedLeadingComments + editor1.getValue(); editor2.saveButton.editor = editor2;
var url = "./lib/ace/mode/" + modeEl.value + "_highlight_rules.js";
net.request('PUT', url, text, function(text) {
handle_put_result(text, editor1, uploadEl1);
});
};
editor1.commands.bindKey("Ctrl-S", uploadEl1.onclick);
uploadEl2.onclick = function() {
doclist.saveDoc(docEl.value, function(text) {
handle_put_result(text, editor2, uploadEl2);
});
};
editor2.commands.bindKey("Ctrl-S", uploadEl2.onclick);
editor1.on('change', function() {
uploadEl1.disabled = false;
});
editor2.on('change', function() {
uploadEl2.disabled = false;
});
function handle_put_result(text, editor, buttonEl) { editor1.saveButton.onclick = function() {
text = text.trim(); doclist.saveDoc({
if (text.length == 0) { path: "./lib/ace/mode/" + modeEl.value + "_highlight_rules.js",
buttonEl.disabled = true; session: editor1.session
editor.getSession().getUndoManager().markClean(); }, function(err) {
} else { handleSaveResult(err, editor1);
if (text.indexOf("405") == 0) { });
log("Write access to this file is disabled.\n"+ };
"To enable saving your changes to disk, clone the Ace repository"+ editor1.commands.bindKey({
"\nand run the included static.py web server with the option\n"+ win: "Ctrl-S", mac: "Cmd-s"
"--puttable='lib/ace/mode/*_highlight_rules.js,demo/kitchen-sink/docs/*'"); }, editor1.saveButton.onclick);
} else editor2.saveButton.onclick = function() {
log(text); doclist.saveDoc(docEl.value, function(err) {
handleSaveResult(err, editor2);
});
};
editor2.commands.bindKey({
win: "Ctrl-S", mac: "Cmd-s"
}, editor2.saveButton.onclick);
function updateSaveButtonState(e, editor){
editor.saveButton.disabled = editor.session.getUndoManager().isClean();
}
editor1.on("input", updateSaveButtonState);
editor2.on("input", updateSaveButtonState);
function handleSaveResult(err, editor) {
if (err) {
return log(
"Write access to this file is disabled.\n"+
"To enable saving your changes to disk, clone the Ace repository\n"+
"and run the included web server with the --allow-write option\n"+
"`node static.js --allow-write` or `static.py --puttable=*`"
);
} }
editor.session.getUndoManager().markClean();
updateSaveButtonState(null, editor);
} }
document.getElementById("perfTest").onclick = function() { document.getElementById("perfTest").onclick = function() {