/*\ title: js/WikiStore.js \*/ (function(){ /*jslint node: true */ "use strict"; var Tiddler = require("./Tiddler.js").Tiddler, utils = require("./Utils.js"), util = require("util"); /* Creates a new WikiStore object Available options are: shadowStore: An existing WikiStore to use for shadow tiddler storage. Pass null to prevent a default shadow store from being created */ var WikiStore = function WikiStore(options) { options = options || {}; this.tiddlers = {}; this.parsers = {}; this.tiddlerSerializers = {}; this.tiddlerDeserializers = {}; this.sandbox = options.sandbox; this.shadows = options.shadowStore !== undefined ? options.shadowStore : new WikiStore({ shadowStore: null }); }; WikiStore.prototype.registerParser = function(type,parser) { this.parsers[type] = parser; }; WikiStore.prototype.registerTiddlerSerializer = function(extension,mimeType,serializer) { this.tiddlerSerializers[extension] = serializer; this.tiddlerSerializers[mimeType] = serializer; }; WikiStore.prototype.registerTiddlerDeserializer = function(extension,mimeType,deserializer) { this.tiddlerDeserializers[extension] = deserializer; this.tiddlerDeserializers[mimeType] = deserializer; }; WikiStore.prototype.getTiddler = function(title) { var t = this.tiddlers[title]; if(t instanceof Tiddler) { return t; } else if(this.shadows) { return this.shadows.getTiddler(title); } else { return null; } }; WikiStore.prototype.getTiddlerText = function(title) { var t = this.getTiddler(title); return t instanceof Tiddler ? t.fields.text : null; }; WikiStore.prototype.deleteTiddler = function(title) { delete this.tiddlers[title]; }; WikiStore.prototype.tiddlerExists = function(title) { var exists = this.tiddlers[title] instanceof Tiddler; if(exists) { return true; } else if (this.shadows) { return this.shadows.tiddlerExists(title); } return ; }; WikiStore.prototype.addTiddler = function(tiddler) { this.tiddlers[tiddler.fields.title] = tiddler; }; WikiStore.prototype.forEachTiddler = function(/* [sortField,[excludeTag,]]callback */) { var a = 0, sortField = arguments.length > 1 ? arguments[a++] : null, excludeTag = arguments.length > 2 ? arguments[a++] : null, callback = arguments[a++], t, tiddlers = [], tiddler; if(sortField) { for(t in this.tiddlers) { tiddlers.push(this.tiddlers[t]); } tiddlers.sort(function (a,b) { var aa = a.fields[sortField] || 0, bb = b.fields[sortField] || 0; if(aa < bb) { return -1; } else { if(aa > bb) { return 1; } else { return 0; } } }); for(t=0; t" + utils.htmlEncode(tiddler.fields[field]) + ""; case "wikified": return this.renderTiddler("text/html",tiddler.fields.title); case "date": template = template || "DD MMM YYYY"; return utils.htmlEncode(utils.formatDateString(tiddler.fields[field],template)); } } return ""; }; WikiStore.prototype.classesForLink = function(target) { var className = "", externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data):[^\s'"]+(?:\/|\b)/i; if(externalRegExp.test(target)) { className = "linkExternal"; } else if (this.tiddlerExists(target)) { className = "linkInternalResolves"; } else { className = "linkInternalMissing"; } return className !== "" ? " class=\"" + className + "\"" : ""; }; WikiStore.prototype.listTiddlers = function(type,template,emptyMessage) { return "Listing!"; }; /* argOptions: {defaultName:"type"}, handler: function(macroNode,args,title) { var type = args.getValueByName("type","all"), template = args.getValueByName("template",null), templateType = "text/x-tiddlywiki", templateText = "<>", emptyMessage = args.getValueByName("emptyMessage",null); // Get the template to use template = template ? this.store.getTiddler(template) : null; if(template) { templateType = template.fields.type; templateText = template.fields.text; } // Get the handler and the tiddlers var handler = WikiTextRenderer.macros.list.types[type]; handler = handler || WikiTextRenderer.macros.list.types.all; var tiddlers = handler.call(this); // Render them as a list var ul = {type: "ul", children: []}; for(var t=0; t 0) { macroNode.output.push(ul); this.executeMacros(macroNode.output,title); } else if (emptyMessage) { macroNode.output.push({type: "text", value: emptyMessage}); } }, types: { all: function() { return this.store.getTitles("title","excludeLists"); }, missing: function() { return this.store.getMissingTitles(); }, orphans: function() { return this.store.getOrphanTitles(); }, shadowed: function() { return this.store.getShadowTitles(); }, touched: function() { // Server syncing isn't implemented yet return []; }, filter: function() { // Filters aren't implemented yet return []; } } }, */ WikiStore.prototype.parseText = function(type,text) { var parser = this.parsers[type]; if(!parser) { parser = this.parsers["text/x-tiddlywiki"]; } if(parser) { return parser.parse(text); } else { return null; } }; WikiStore.prototype.parseTiddler = function(title) { var tiddler = this.getTiddler(title); if(tiddler) { // Check the cache if(!tiddler.parseTree) { tiddler.parseTree = this.parseText(tiddler.fields.type,tiddler.fields.text); } return tiddler.parseTree; } else { return null; } }; /* Compiles a JavaScript function that renders a tiddler in a particular MIME type */ WikiStore.prototype.compileTiddler = function(title,type) { /*jslint evil: true */ var tiddler = this.getTiddler(title); if(tiddler) { if(!tiddler.renderers[type]) { var tree = this.parseTiddler(title); tiddler.renderers[type] = eval(tree.compile(type)); } return tiddler.renderers[type]; } else { return null; } }; /* Render a tiddler to a particular MIME type. Optionally render it with a different tiddler as the context. This option is used to render a tiddler through a template eg store.renderTiddler("text/html",templateTitle,tiddlerTitle) */ WikiStore.prototype.renderTiddler = function(type,title,asTitle) { var tiddler = this.getTiddler(title), fn = this.compileTiddler(title,type); if(asTitle) { var asTiddler = this.getTiddler(asTitle); return fn(asTiddler,this,utils); } else { if(!tiddler.renditions[type]) { tiddler.renditions[type] = fn(tiddler,this,utils); } return tiddler.renditions[type]; } }; WikiStore.prototype.installMacros = function() { this.macros = { echo: { params: { text: {byPos: 0, type: "text", optional: false} }, code: { "text/html": this.jsParser.parse("return utils.htmlEncode(params.text);"), "text/plain": this.jsParser.parse("return params.text;") } }, view: { params: { field: {byPos: 0, type: "text", optional: false}, format: {byPos: 1, type: "text", optional: true}, template: {byPos: 2, type: "text", optional: true} }, code: { "text/html": this.jsParser.parse("return store.getFormattedTiddlerField(tiddler.fields.title,params.field,params.format,params.template);"), "text/plain": this.jsParser.parse("return store.getFormattedTiddlerField(tiddler.fields.title,params.field,params.format,params.template);") } }, list: { params: { type: {byName: "default", type: "text", optional: false}, template: {byName: true, type: "tiddler", optional: true}, emptyMessage: {byName: true, type: "text", optional: true} }, code: { "text/html": this.jsParser.parse("return store.listTiddlers(params.type,params.template,params.emptyMessage);"), "text/plain": this.jsParser.parse("return store.listTiddlers(params.type,params.template,params.emptyMessage);") } }, version: { params: { }, code: { "text/html": this.jsParser.parse("return '5.0.0';"), "text/plain": this.jsParser.parse("return '5.0.0';") } }, tiddler: { params: { target: {byName: "default", type: "tiddler", optional: false}, "with": {byName: true, type: "text", optional: true, cascade: true} }, code: { "text/html": this.jsParser.parse("return store.renderTiddler('text/html',params.target);"), "text/plain": this.jsParser.parse("return store.renderTiddler('text/plain',params.target);") } } }; }; /* tiddler: { argOptions: {defaultName:"name",cascadeDefaults:true}, handler: function(macroNode,args,title) { var targetTitle = args.getValueByName("name",null), withTokens = args.getValuesByName("with",[]), tiddler = this.store.getTiddler(targetTitle), text = this.store.getTiddlerText(targetTitle,""), t; for(t=0; t