diff --git a/demo/kitchen-sink/doclist.js b/demo/kitchen-sink/doclist.js
index abdc9a3e..a4848d50 100644
--- a/demo/kitchen-sink/doclist.js
+++ b/demo/kitchen-sink/doclist.js
@@ -169,9 +169,8 @@ function loadDoc(name, callback) {
});
}
-// callback is called with the error message from PUT (if any)
function saveDoc(name, callback) {
- var doc = fileCache[name];
+ var doc = fileCache[name] || name;
if (!doc || !doc.session)
return callback("Unknown document: " + name);
@@ -182,9 +181,24 @@ function saveDoc(name, callback) {
else if (parts[0] == "ace")
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 = {
fileCache: fileCache,
docs: sort(prepareDocList(docs)),
diff --git a/lib/ace/lib/net.js b/lib/ace/lib/net.js
index 357fcb9d..bba76df8 100644
--- a/lib/ace/lib/net.js
+++ b/lib/ace/lib/net.js
@@ -9,9 +9,9 @@ define(function(require, exports, module) {
"use strict";
var dom = require("./dom");
-exports.request = function (verb, url, data, callback) {
+exports.get = function (url, callback) {
var xhr = new XMLHttpRequest();
- xhr.open(verb, url, true);
+ xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
@@ -19,11 +19,7 @@ exports.request = function (verb, url, data, callback) {
callback(xhr.responseText);
}
};
- xhr.send(data);
-};
-
-exports.get = function (url, callback) {
- this.request('GET', url, null, callback);
+ xhr.send(null);
};
exports.loadScript = function(path, callback) {
diff --git a/lib/ace/mode/abap_highlight_rules.js b/lib/ace/mode/abap_highlight_rules.js
index 6b232c90..bab1e504 100644
--- a/lib/ace/mode/abap_highlight_rules.js
+++ b/lib/ace/mode/abap_highlight_rules.js
@@ -126,7 +126,7 @@ var AbapHighlightRules = function() {
{token : "string", regex : "`", next : "start"},
{defaultToken : "string"}
]
- }
+ };
};
oop.inherits(AbapHighlightRules, TextHighlightRules);
diff --git a/static.js b/static.js
index 4f7e7248..a711715e 100755
--- a/static.js
+++ b/static.js
@@ -10,37 +10,100 @@ var http = require("http")
// compatibility with node 0.6
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
- , filename = path.join(process.cwd(), uri);
-
- fs.exists(filename, function(exists) {
- if(!exists) {
- response.writeHead(404, {"Content-Type": "text/plain"});
- response.write("404 Not Found\n");
- response.end();
- return;
+http.createServer(function(req, res) {
+ var uri = url.parse(req.url).pathname
+ , filename = path.join(process.cwd(), uri);
+
+ if (req.method == "PUT") {
+ if (!allowSave)
+ return error(res, 404, "Saving not allowed pass --allow-save to enable");
+ save(req, res, filename);
}
- 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(err) {
- response.writeHead(500, {"Content-Type": "text/plain"});
- response.write(err + "\n");
- response.end();
- return;
- }
+ if (fs.statSync(filename).isDirectory()) {
+ var files = fs.readdirSync(filename);
+ res.writeHead(200, {"Content-Type": "text/html"});
+
+ files.push(".", "..");
+ var html = files.map(function(name) {
+ var href = uri + "/" + name;
+ href = href.replace(/[\/\\]+/g, "/").replace(/\/$/g, "");
+ if (fs.statSync(filename + "/" + name + "/").isDirectory())
+ href += "/";
+ return "" + name + "
";
+ });
- var contentType = mime.lookup(filename) || "text/plain";
- response.writeHead(200, {"Content-Type": contentType});
- response.write(file, "binary");
- response.end();
+ 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);
-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);
+
diff --git a/static.py b/static.py
index 913648e2..7a2faf91 100755
--- a/static.py
+++ b/static.py
@@ -263,13 +263,13 @@ def command():
puttable = set(path.abspath(p) for p in
options.puttable.replace(","," ").split())
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)
for p in puttable:
- if not p.startswith(options.rootdir):
- sys.exit("puttable path '%s' not under root '%s'" % (p, options.rootdir))
+ if not p.startswith(options.rootdir):
+ sys.exit("puttable path '%s' not under root '%s'" % (p, options.rootdir))
# cut off root prefix from puttable paths
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)
if puttable:
print("The following paths (relative to server root) may be "+
- "OVERWRITTEN via HTTP PUT.\n"+
- "I HOPE EVERY USER ON THIS SYSTEM IS TRUSTED!")
+ "OVERWRITTEN via HTTP PUT.")
for p in puttable:
print p
make_server(host, port, app).serve_forever()
except KeyboardInterrupt, ki:
- print "Cio, baby!"
+ print "Ciao, baby!"
except:
sys.exit("Problem initializing server: %s" % sys.exc_info()[1])
diff --git a/tool/mode_creator.html b/tool/mode_creator.html
index b4c66715..259ba64e 100644
--- a/tool/mode_creator.html
+++ b/tool/mode_creator.html
@@ -59,7 +59,7 @@
-
+
@@ -71,7 +71,7 @@
-
+
diff --git a/tool/mode_creator.js b/tool/mode_creator.js
index c78345ac..e79c54c6 100644
--- a/tool/mode_creator.js
+++ b/tool/mode_creator.js
@@ -53,7 +53,8 @@ util.bindDropdown("doc", function(value) {
doclist.loadDoc(value, function(session) {
if (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");
util.fillDropdown(modeEl, modelist.modes);
var modeSessions = {};
-var savedLeadingComments = "";
+
util.bindDropdown(modeEl, function(value) {
if (modeSessions[value]) {
editor1.setSession(modeSessions[value]);
@@ -70,17 +71,18 @@ util.bindDropdown(modeEl, function(value) {
}
var hp = "./lib/ace/mode/" + value + "_highlight_rules.js";
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);
session.setUndoManager(new UndoManager());
+
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]);
+ session.getUndoManager().markClean();
+ updateSaveButtonState(null, editor1);
schedule();
});
});
@@ -91,43 +93,47 @@ document.getElementById("syncToMode").onclick = function() {
run();
};
-var uploadEl1 = document.getElementById("uploadToServer1");
-var uploadEl2 = document.getElementById("uploadToServer2");
-uploadEl1.onclick = function() {
- var text = savedLeadingComments + editor1.getValue();
- 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;
-});
+editor1.saveButton = document.getElementById("saveButton1");
+editor2.saveButton = document.getElementById("saveButton2");
+editor1.saveButton.editor = editor1;
+editor2.saveButton.editor = editor2;
-function handle_put_result(text, editor, buttonEl) {
- text = text.trim();
- if (text.length == 0) {
- buttonEl.disabled = true;
- editor.getSession().getUndoManager().markClean();
- } else {
- 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"+
- "\nand run the included static.py web server with the option\n"+
- "--puttable='lib/ace/mode/*_highlight_rules.js,demo/kitchen-sink/docs/*'");
- } else
- log(text);
+editor1.saveButton.onclick = function() {
+ doclist.saveDoc({
+ path: "./lib/ace/mode/" + modeEl.value + "_highlight_rules.js",
+ session: editor1.session
+ }, function(err) {
+ handleSaveResult(err, editor1);
+ });
+};
+editor1.commands.bindKey({
+ win: "Ctrl-S", mac: "Cmd-s"
+}, editor1.saveButton.onclick);
+editor2.saveButton.onclick = function() {
+ 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() {