mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 03:57:21 +00:00
Refactor the Tiddler object to enforce immutability
And in the process move the fields out of the fields member
This commit is contained in:
parent
2ff603da0e
commit
b898afe3e5
@ -16,7 +16,7 @@ var BitmapParseTree = function() {
|
||||
|
||||
BitmapParseTree.prototype.compile = function(type) {
|
||||
if(type === "text/html") {
|
||||
return "(function (tiddler,store,utils) {return '<img src=\"data:' + tiddler.fields.type + ';base64,' + tiddler.fields.text + '\">';})";
|
||||
return "(function (tiddler,store,utils) {return '<img src=\"data:' + tiddler.type + ';base64,' + tiddler.text + '\">';})";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
24
js/Recipe.js
24
js/Recipe.js
@ -132,7 +132,7 @@ Recipe.prototype.loadTiddlerFiles = function(recipeLine) {
|
||||
filename = path.basename(filepath), // eg *.js
|
||||
posStar = filename.indexOf("*");
|
||||
if(posStar !== -1) {
|
||||
var fileRegExp = new RegExp("^" + filename.replace(/[-[\]{}()+?.,\\^$|#\s]/g, "\\$&").replace("*",".*") + "$");
|
||||
var fileRegExp = new RegExp("^" + filename.replace(/[\-\[\]{}()+?.,\\\^$|#\s]/g, "\\$&").replace("*",".*") + "$");
|
||||
var files = fs.readdirSync(path.resolve(path.dirname(recipeLine.contextPath),filedir));
|
||||
for(var f=0; f<files.length; f++) {
|
||||
if(fileRegExp.test(files[f])) {
|
||||
@ -341,7 +341,7 @@ Recipe.tiddlerOutputter = {
|
||||
// Lines starting with //# are removed from javascript tiddlers
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
var tid = this.store.getTiddler(tiddlers[t]),
|
||||
text = tid.fields.text;
|
||||
text = tid.text;
|
||||
// For compatibility with cook.rb, remove one trailing \n from tiddler
|
||||
text = text.charAt(text.length-1) === "\n" ? text.substr(0,text.length-1) : text;
|
||||
var lines = text.split("\n");
|
||||
@ -370,7 +370,7 @@ Recipe.tiddlerOutputter = {
|
||||
tid = this.store.getTiddler(title);
|
||||
out.push("<" + "script type=\"application/javascript\">");
|
||||
out.push("define(\"" + title + "\",function(require,exports,module) {");
|
||||
out.push(tid.fields.text);
|
||||
out.push(tid.text);
|
||||
out.push("});");
|
||||
out.push("</" + "script>");
|
||||
}
|
||||
@ -388,24 +388,24 @@ Recipe.prototype.cookRss = function() {
|
||||
return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
|
||||
},
|
||||
tiddlerToRssItem = function(tiddler,uri) {
|
||||
var s = "<title" + ">" + utils.htmlEncode(tiddler.fields.title) + "</title" + ">\n";
|
||||
s += "<description>" + utils.htmlEncode(me.store.renderTiddler("text/html",tiddler.fields.title)) + "</description>\n";
|
||||
var s = "<title" + ">" + utils.htmlEncode(tiddler.title) + "</title" + ">\n";
|
||||
s += "<description>" + utils.htmlEncode(me.store.renderTiddler("text/html",tiddler.title)) + "</description>\n";
|
||||
var i;
|
||||
if(tiddler.fields.tags) {
|
||||
for(i=0; i<tiddler.fields.tags.length; i++) {
|
||||
s += "<category>" + tiddler.fields.tags[i] + "</category>\n";
|
||||
if(tiddler.tags) {
|
||||
for(i=0; i<tiddler.tags.length; i++) {
|
||||
s += "<category>" + tiddler.tags[i] + "</category>\n";
|
||||
}
|
||||
}
|
||||
s += "<link>" + uri + "#" + encodeURIComponent(encodeTiddlyLink(tiddler.fields.title)) + "</link>\n";
|
||||
if(tiddler.fields.modified) {
|
||||
s +="<pubDate>" + tiddler.fields.modified.toUTCString() + "</pubDate>\n";
|
||||
s += "<link>" + uri + "#" + encodeURIComponent(encodeTiddlyLink(tiddler.title)) + "</link>\n";
|
||||
if(tiddler.modified) {
|
||||
s +="<pubDate>" + tiddler.modified.toUTCString() + "</pubDate>\n";
|
||||
}
|
||||
return s;
|
||||
},
|
||||
getRssTiddlers = function(sortField,excludeTag) {
|
||||
var r = [];
|
||||
me.store.forEachTiddler(sortField,excludeTag,function(title,tiddler) {
|
||||
if(!tiddler.hasTag(excludeTag) && tiddler.fields.modified !== undefined) {
|
||||
if(!tiddler.hasTag(excludeTag) && tiddler.modified !== undefined) {
|
||||
r.push(tiddler);
|
||||
}
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ var SVGParseTree = function() {
|
||||
|
||||
SVGParseTree.prototype.compile = function(type) {
|
||||
if(type === "text/html") {
|
||||
return "(function (tiddler,store,utils) {return tiddler.fields.text;})";
|
||||
return "(function (tiddler,store,utils) {return tiddler.text;})";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -21,25 +21,57 @@ var utils = require("./Utils.js"),
|
||||
ArgParser = require("./ArgParser.js").ArgParser;
|
||||
|
||||
var Tiddler = function(/* tiddler,fields */) {
|
||||
this.parseTree = null; // Caches the parse tree for the tiddler
|
||||
this.renderers = {}; // Caches rendering functions for this tiddler (indexed by MIME type)
|
||||
this.renditions = {}; // Caches the renditions produced by those functions (indexed by MIME type)
|
||||
this.fields = {};
|
||||
for(var c=0; c<arguments.length; c++) {
|
||||
var arg = arguments[c],
|
||||
this.cache = {}; // Expose the cache object
|
||||
var fields = {}, // Keep the fields private, later we'll expose getters for them
|
||||
tags, // Keep the tags separately because they're the only Array field
|
||||
f,t,c,arg,src;
|
||||
// Accumulate the supplied fields
|
||||
for(c=0; c<arguments.length; c++) {
|
||||
arg = arguments[c];
|
||||
src = null;
|
||||
if(arg instanceof Tiddler) {
|
||||
src = arg.fields;
|
||||
} else {
|
||||
src = arg;
|
||||
}
|
||||
for(var t in src) {
|
||||
var f = this.parseTiddlerField(t,src[t]);
|
||||
for(t in src) {
|
||||
f = Tiddler.parseTiddlerField(t,src[t]);
|
||||
if(f !== null) {
|
||||
this.fields[t] = f;
|
||||
fields[t] = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pull out the tags
|
||||
if(fields.tags) {
|
||||
tags = fields.tags;
|
||||
delete fields.tags;
|
||||
}
|
||||
// Expose the fields as read only properties
|
||||
for(f in fields) {
|
||||
Object.defineProperty(this,f,{value: fields[f], writeable: false});
|
||||
}
|
||||
// Expose the tags as a getter
|
||||
Object.defineProperty(this,"tags",{get: function() {return tags ? tags.slice(0) : [];}});
|
||||
// Other methods that need access to the fields
|
||||
this.getFields = function() {
|
||||
var r = {};
|
||||
for(var f in fields) {
|
||||
var v = fields[f];
|
||||
if(v instanceof Array) {
|
||||
r[f] = v.slice(0);
|
||||
} else {
|
||||
r[f] = v;
|
||||
}
|
||||
}
|
||||
if(tags) {
|
||||
r.tags = tags;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
};
|
||||
|
||||
Tiddler.prototype.hasTag = function(tag) {
|
||||
return this.tags.indexOf(tag) !== -1;
|
||||
};
|
||||
|
||||
Tiddler.standardFields = {
|
||||
@ -57,18 +89,21 @@ Tiddler.isStandardField = function(name) {
|
||||
return name in Tiddler.standardFields;
|
||||
};
|
||||
|
||||
Tiddler.prototype.hasTag = function(tag) {
|
||||
if(this.tags) {
|
||||
for(var t=0; t<this.tags.length; t++) {
|
||||
if(this.tags[t] === tag) {
|
||||
return true;
|
||||
Tiddler.compareTiddlerFields = function(a,b,sortField) {
|
||||
var aa = a[sortField] || 0,
|
||||
bb = b[sortField] || 0;
|
||||
if(aa < bb) {
|
||||
return -1;
|
||||
} else {
|
||||
if(aa > bb) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Tiddler.prototype.parseTiddlerField = function(name,value) {
|
||||
Tiddler.parseTiddlerField = function(name,value) {
|
||||
var type = Tiddler.specialTiddlerFields[name];
|
||||
if(type) {
|
||||
return Tiddler.specialTiddlerFieldParsers[type](value);
|
||||
|
@ -34,29 +34,30 @@ var outputTiddler = function(tid) {
|
||||
var result = [],
|
||||
outputAttribute = function(name,value) {
|
||||
result.push(name + ": " + value + "\n");
|
||||
};
|
||||
for(var t in tid.fields) {
|
||||
},
|
||||
fields = tid.getFields();
|
||||
for(var t in fields) {
|
||||
switch(t) {
|
||||
case "text":
|
||||
// Ignore the text field
|
||||
break;
|
||||
case "tags":
|
||||
// Output tags as a list
|
||||
outputAttribute(t,tiddlerOutput.stringifyTags(tid.fields.tags));
|
||||
outputAttribute(t,tiddlerOutput.stringifyTags(fields.tags));
|
||||
break;
|
||||
case "modified":
|
||||
case "created":
|
||||
// Output dates in YYYYMMDDHHMM
|
||||
outputAttribute(t,utils.convertToYYYYMMDDHHMM(tid.fields[t]));
|
||||
outputAttribute(t,utils.convertToYYYYMMDDHHMM(fields[t]));
|
||||
break;
|
||||
default:
|
||||
// Output other attributes raw
|
||||
outputAttribute(t,tid.fields[t]);
|
||||
outputAttribute(t,fields[t]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.push("\n");
|
||||
result.push(tid.fields.text);
|
||||
result.push(fields.text);
|
||||
return result.join("");
|
||||
};
|
||||
|
||||
@ -68,23 +69,19 @@ The fields are in the order title, creator, modifier, created, modified, tags, f
|
||||
*/
|
||||
var outputTiddlerDiv = function(tid) {
|
||||
var result = [],
|
||||
attributes = {},
|
||||
outputAttribute = function(name,transform,dontDelete) {
|
||||
if(name in attributes) {
|
||||
var value = attributes[name];
|
||||
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 + "\"");
|
||||
if(!dontDelete) {
|
||||
delete attributes[name];
|
||||
}
|
||||
delete fields[name];
|
||||
}
|
||||
};
|
||||
for(var t in tid.fields) {
|
||||
attributes[t] = tid.fields[t];
|
||||
}
|
||||
if(attributes.text) {
|
||||
delete attributes.text;
|
||||
if(fields.text) {
|
||||
delete fields.text;
|
||||
}
|
||||
result.push("<div");
|
||||
// Output the standard attributes in the correct order
|
||||
@ -95,11 +92,11 @@ var outputTiddlerDiv = function(tid) {
|
||||
outputAttribute("modified", function(v) {return utils.convertToYYYYMMDDHHMM(v);});
|
||||
outputAttribute("tags", function(v) {return stringifyTags(v);});
|
||||
// Output any other attributes
|
||||
for(t in attributes) {
|
||||
for(var t in fields) {
|
||||
outputAttribute(t,null,true);
|
||||
}
|
||||
result.push(">\n<pre>");
|
||||
result.push(utils.htmlEncode(tid.fields.text));
|
||||
result.push(utils.htmlEncode(text));
|
||||
result.push("</pre>\n</div>");
|
||||
return result.join("");
|
||||
};
|
||||
|
@ -1,6 +1,12 @@
|
||||
/*\
|
||||
title: js/WikiStore.js
|
||||
|
||||
WikiStore uses the .cache member of tiddlers to store the following information:
|
||||
|
||||
parseTree: Caches the parse tree for the tiddler
|
||||
renderers: Caches rendering functions for this tiddler (indexed by MIME type)
|
||||
renditions: Caches the renditions produced by those functions (indexed by MIME type)
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
@ -125,7 +131,7 @@ WikiStore.prototype.getTiddler = function(title) {
|
||||
|
||||
WikiStore.prototype.getTiddlerText = function(title) {
|
||||
var t = this.getTiddler(title);
|
||||
return t instanceof Tiddler ? t.fields.text : null;
|
||||
return t instanceof Tiddler ? t.text : null;
|
||||
};
|
||||
|
||||
WikiStore.prototype.deleteTiddler = function(title) {
|
||||
@ -143,8 +149,8 @@ WikiStore.prototype.tiddlerExists = function(title) {
|
||||
};
|
||||
|
||||
WikiStore.prototype.addTiddler = function(tiddler) {
|
||||
this.tiddlers[tiddler.fields.title] = tiddler;
|
||||
this.touchTiddler("modified",tiddler.fields.title);
|
||||
this.tiddlers[tiddler.title] = tiddler;
|
||||
this.touchTiddler("modified",tiddler.title);
|
||||
};
|
||||
|
||||
WikiStore.prototype.forEachTiddler = function(/* [sortField,[excludeTag,]]callback */) {
|
||||
@ -159,22 +165,10 @@ WikiStore.prototype.forEachTiddler = function(/* [sortField,[excludeTag,]]callba
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
tiddlers.sort(function (a,b) {return Tiddler.compareTiddlerFields(a,b,sortField);});
|
||||
for(t=0; t<tiddlers.length; t++) {
|
||||
if(!tiddlers[t].hasTag(excludeTag)) {
|
||||
callback.call(this,tiddlers[t].fields.title,tiddlers[t]);
|
||||
callback.call(this,tiddlers[t].title,tiddlers[t]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -286,10 +280,10 @@ 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);
|
||||
if(!tiddler.cache.parseTree) {
|
||||
tiddler.cache.parseTree = this.parseText(tiddler.type,tiddler.text);
|
||||
}
|
||||
return tiddler.parseTree;
|
||||
return tiddler.cache.parseTree;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -311,11 +305,14 @@ 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));
|
||||
if(!tiddler.cache.renderers) {
|
||||
tiddler.cache.renderers = {};
|
||||
}
|
||||
return tiddler.renderers[type];
|
||||
if(!tiddler.cache.renderers[type]) {
|
||||
var tree = this.parseTiddler(title);
|
||||
tiddler.cache.renderers[type] = eval(tree.compile(type));
|
||||
}
|
||||
return tiddler.cache.renderers[type];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -343,10 +340,13 @@ WikiStore.prototype.renderTiddler = function(targetType,title,asTitle) {
|
||||
var asTiddler = this.getTiddler(asTitle);
|
||||
return fn(asTiddler,this,utils);
|
||||
} else {
|
||||
if(!tiddler.renditions[targetType]) {
|
||||
tiddler.renditions[targetType] = fn(tiddler,this,utils);
|
||||
if(!tiddler.cache.renditions) {
|
||||
tiddler.cache.renditions = {};
|
||||
}
|
||||
return tiddler.renditions[targetType];
|
||||
if(!tiddler.cache.renditions[targetType]) {
|
||||
tiddler.cache.renditions[targetType] = fn(tiddler,this,utils);
|
||||
}
|
||||
return tiddler.cache.renditions[targetType];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -16,7 +16,7 @@ exports.macro = {
|
||||
},
|
||||
handler: function(type,tiddler,store,params) {
|
||||
var encoder = type === "text/html" ? utils.htmlEncode : function(x) {return x;},
|
||||
parseTree = store.parseTiddler(tiddler.fields.title);
|
||||
parseTree = store.parseTiddler(tiddler.title);
|
||||
if(parseTree) {
|
||||
var r = [];
|
||||
var d = parseTree.dependencies;
|
||||
|
@ -52,8 +52,8 @@ exports.macro = {
|
||||
handler,
|
||||
t;
|
||||
if(template) {
|
||||
templateType = template.fields.type;
|
||||
templateText = template.fields.text;
|
||||
templateType = template.type;
|
||||
templateText = template.text;
|
||||
}
|
||||
handler = handlers[params.type];
|
||||
handler = handler || handlers.all;
|
||||
|
@ -21,13 +21,13 @@ exports.macro = {
|
||||
if(params["with"]) {
|
||||
// Parameterised transclusion
|
||||
var targetTiddler = store.getTiddler(params.target),
|
||||
text = targetTiddler.fields.text;
|
||||
text = targetTiddler.text;
|
||||
var withTokens = [params["with"]];
|
||||
for(var t=0; t<withTokens.length; t++) {
|
||||
var placeholderRegExp = new RegExp("\\$"+(t+1),"mg");
|
||||
text = text.replace(placeholderRegExp,withTokens[t]);
|
||||
}
|
||||
return store.renderText(targetTiddler.fields.type,text,type,tiddler.fields.title);
|
||||
return store.renderText(targetTiddler.type,text,type,tiddler.title);
|
||||
} else {
|
||||
// There's no parameterisation, so we can just render the target tiddler directly
|
||||
return store.renderTiddler(type,params.target);
|
||||
|
@ -18,14 +18,14 @@ exports.macro = {
|
||||
template: {byPos: 2, type: "text", optional: true}
|
||||
},
|
||||
handler: function(type,tiddler,store,params) {
|
||||
var v = tiddler.fields[params.field],
|
||||
var v = tiddler[params.field],
|
||||
encoder = type === "text/html" ? utils.htmlEncode : function(x) {return x;};
|
||||
if(v) {
|
||||
switch(params.format) {
|
||||
case "link":
|
||||
return store.renderMacro("link",type,tiddler,{target: v},encoder(v));
|
||||
case "wikified":
|
||||
return store.renderTiddler(type,tiddler.fields.title);
|
||||
return store.renderTiddler(type,tiddler.title);
|
||||
case "date":
|
||||
var template = params.template || "DD MMM YYYY";
|
||||
return encoder(utils.formatDateString(v,template));
|
||||
|
@ -2,6 +2,6 @@ title: SeventhTiddler
|
||||
|
||||
<<echo {{2+2}}>>
|
||||
|
||||
<<echo {{tiddler.fields.title}}>>
|
||||
<<echo {{tiddler.title}}>>
|
||||
|
||||
<<echo {{"window"}}>>
|
||||
|
@ -135,7 +135,7 @@ var commandLineSwitches = {
|
||||
handler: function(args,callback) {
|
||||
var recipe = [];
|
||||
app.store.forEachTiddler(function(title,tiddler) {
|
||||
var filename = encodeURIComponent(tiddler.fields.title.replace(/ /g,"_")) + ".tid";
|
||||
var filename = encodeURIComponent(tiddler.title.replace(/ /g,"_")) + ".tid";
|
||||
fs.writeFileSync(path.resolve(args[0],filename),tiddlerOutput.outputTiddler(tiddler),"utf8");
|
||||
recipe.push("tiddler: " + filename + "\n");
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user