From 0ac55688c48e0220caab219bec8e8bd09f313832 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 5 Apr 2012 12:21:49 +0100 Subject: [PATCH] Browser now syncs changes with server which syncs with the file system A bit rough and ready, but this gives us basic support for editting tiddlers in the browser and updating the original file on the server --- js/App.js | 24 ++-- js/HttpSync.js | 35 ++++++ js/{FileStore.js => LocalFileSync.js} | 45 ++++++- js/Recipe.js | 4 +- js/TiddlerOutput.js | 162 +++++++++++++++----------- js/WikiStore.js | 28 ++++- js/macros/story.js | 2 +- tiddlywiki.js | 43 +++++-- 8 files changed, 249 insertions(+), 94 deletions(-) create mode 100644 js/HttpSync.js rename js/{FileStore.js => LocalFileSync.js} (59%) diff --git a/js/App.js b/js/App.js index 78a902646..42687f854 100644 --- a/js/App.js +++ b/js/App.js @@ -10,6 +10,7 @@ This is the main() function in the browser "use strict"; var WikiStore = require("./WikiStore.js").WikiStore, + HttpSync = require("./HttpSync.js").HttpSync, Tiddler = require("./Tiddler.js").Tiddler, tiddlerInput = require("./TiddlerInput.js"), tiddlerOutput = require("./TiddlerOutput.js"), @@ -81,6 +82,8 @@ var App = function() { var shadowArea = document.getElementById("shadowArea"); this.store.shadows.addTiddlers(this.store.deserializeTiddlers("(DOM)",shadowArea)); } + // Reset pending events on the store so that we don't get events for the initial load + this.store.clearEvents(); // Bit of a hack to set up the macros this.store.installMacro(require("./macros/chooser.js").macro); this.store.installMacro(require("./macros/command.js").macro); @@ -102,8 +105,10 @@ var App = function() { linkInfo.target = encodeURIComponent(linkInfo.target); } }; - // Set up navigation if we're in the browser + // Set up for the browser if(this.isBrowser) { + // Set up HttpSync + this.httpSync = new HttpSync(this.store); // Open the PageTemplate var renderer = this.store.renderMacro("tiddler",{target: "PageTemplate"}); renderer.renderInDom(document.body); @@ -118,18 +123,19 @@ var App = function() { titleRenderer.refresh(changes); document.title = titleRenderer.render("text/plain"); }); - // Set up a timer to change the value of a tiddler - var me = this; - window.setInterval(function() { - me.store.addTiddler(new Tiddler({ - title: "ClockTiddler", - text: "The time was recently " + (new Date()).toString() - })); - },3000); // Listen for navigate events that weren't caught document.addEventListener("tw-navigate",function (event) { renderer.broadcastEvent(event); },false); + // Set up a timer to change the value of a tiddler + var me = this, + s = setInterval || window.setInterval; + s(function() { + me.store.addTiddler(new Tiddler({ + title: "ClockTiddler", + text: "The time was recently " + (new Date()).toString() + })); + },3000); } }; diff --git a/js/HttpSync.js b/js/HttpSync.js new file mode 100644 index 000000000..ae2b668ce --- /dev/null +++ b/js/HttpSync.js @@ -0,0 +1,35 @@ +/*\ +title: js/HttpSync.js + +\*/ +(function(){ + +/*jslint node: true */ +"use strict"; + +function HttpSync(store) { + this.store = store; + this.changeCounts = {}; + store.addEventListener("",function(changes) { + for(var title in changes) { + var tiddler = store.getTiddler(title); + if(tiddler) { + var fieldStrings = tiddler.getFieldStrings(), + fields = {}, + t; + for(t=0; t: + this.changeCounts = {}; // A hashmap of : var self = this; // Set up a queue for loading tiddler files this.loadQueue = async.queue(function(task,callback) { @@ -68,8 +69,8 @@ function FileStore(dirpath,store,callback) { var loadCallback = function(task,tiddlers) { for(var t=0; t lastChangeCount) { + files = self.store.serializeTiddlers([tiddler],"application/x-tiddler"); + for(var t=0; t for(var t=0; t +Output an array of tiddlers as HTML
s out - array to push the output strings tid - the tiddler to be output The fields are in the order title, creator, modifier, created, modified, tags, followed by any others */ -var outputTiddlerDiv = function(tid) { - var result = [], - fields = tid.getFields(), - text = fields.text, - outputAttribute = function(name,transform) { - if(name in fields) { - var value = fields[name]; - if(transform) - value = transform(value); - result.push(" " + name + "=\"" + value + "\""); - delete fields[name]; - } - }; - if(fields.text) { - delete fields.text; +var outputTiddlerDivs = function(tiddlers) { + var result = []; + for(var t=0; t\n
");
+		output.push(utils.htmlEncode(tiddler.text));
+		output.push("
\n
"); + result.push({ + name: tiddler.title, + type: "application/x-tiddler-html-div", + extension: ".tiddler", + data: output.join("") + }); } - result.push("\n
");
-	result.push(utils.htmlEncode(text));
-	result.push("
\n"); - return result.join(""); + return result; }; tiddlerOutput.register = function(store) { - store.registerTiddlerSerializer(".tid","application/x-tiddler",outputTiddler); - store.registerTiddlerSerializer(".tiddler","application/x-tiddler-html-div",outputTiddlerDiv); + store.registerTiddlerSerializer(".tid","application/x-tiddler",outputTiddlers); + store.registerTiddlerSerializer(".tiddler","application/x-tiddler-html-div",outputTiddlerDivs); }; })(); diff --git a/js/WikiStore.js b/js/WikiStore.js index d4d7ffd6f..e5c7676a3 100755 --- a/js/WikiStore.js +++ b/js/WikiStore.js @@ -28,6 +28,7 @@ var WikiStore = function WikiStore(options) { this.parsers = {}; // Hashmap of parsers by accepted MIME type this.macros = {}; // Hashmap of macros by macro name this.caches = {}; // Hashmap of cache objects by tiddler title, each is a hashmap of named caches + this.changeCount = {}; // Hashmap of integer changecount (>1) for each tiddler; persistent across deletions this.tiddlerSerializers = {}; // Hashmap of serializers by target MIME type this.tiddlerDeserializers = {}; // Hashmap of deserializers by accepted MIME type this.eventListeners = []; // Array of {filter:,listener:} @@ -38,6 +39,22 @@ var WikiStore = function WikiStore(options) { }); }; +WikiStore.prototype.incChangeCount = function(title) { + if(this.changeCount.hasOwnProperty(title)) { + this.changeCount[title]++; + } else { + this.changeCount[title] = 1; + } +}; + +WikiStore.prototype.getChangeCount = function(title) { + if(this.changeCount.hasOwnProperty(title)) { + return this.changeCount[title]; + } else { + return 0; + } +}; + WikiStore.prototype.registerParser = function(type,parser) { if(type instanceof Array) { for(var t=0; t 0 ? args[0] : 8000; - // Dumbly, this implementation wastes the recipe processing that happened on the --recipe switch http.createServer(function(request, response) { - response.writeHead(200, {"Content-Type": "text/html"}); - response.end(recipe.cook(), "utf8"); + var path = url.parse(request.url).pathname; + switch(request.method) { + case "PUT": + var data = ""; + request.on("data",function(chunk) { + data += chunk.toString(); + }); + request.on("end",function() { + var title = decodeURIComponent(path.substr(1)); + app.store.addTiddler(new Tiddler(JSON.parse(data),{title: title})); + response.writeHead(204, "OK"); + response.end(); + }); + break; + case "GET": + if(path === "/") { + response.writeHead(200, {"Content-Type": "text/html"}); + response.end(recipe.cook(), "utf8"); + } else { + response.writeHead(404); + response.end(); + } + break; + } }).listen(port); + process.nextTick(function() {callback(null);}); } }, servetiddlers: { @@ -196,11 +219,13 @@ var commandLineSwitches = { response.end(); } }).listen(port); + process.nextTick(function() {callback(null);}); } }, verbose: { args: {min: 0, max: 0}, handler: function(args,callback) { + verbose = true; process.nextTick(function() {callback(null);}); } }, @@ -245,6 +270,7 @@ var commandLineSwitches = { } } } + process.nextTick(function() {callback(null);}); } } }; @@ -259,6 +285,9 @@ var processNextSwitch = function() { if(s.args.length > csw.args.max) { throw "Command line switch --" + s.switchName + " should have a maximum of " + csw.args.max + " arguments"; } + if(verbose) { + console.log("Processing --" + s.switchName + " " + s.args.join(" ")); + } csw.handler(s.args,function (err) { if(err) { throw "Error while executing option '--" + s.switchName + "' was:\n" + err;