2011-12-13 12:30:09 +00:00
|
|
|
/*\
|
|
|
|
title: js/WikiStore.js
|
|
|
|
|
2012-01-17 13:01:55 +00:00
|
|
|
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)
|
|
|
|
|
2011-12-13 12:30:09 +00:00
|
|
|
\*/
|
2011-12-12 10:52:04 +00:00
|
|
|
(function(){
|
|
|
|
|
2011-12-09 16:34:02 +00:00
|
|
|
/*jslint node: true */
|
2011-11-30 17:27:00 +00:00
|
|
|
"use strict";
|
|
|
|
|
2011-12-05 16:50:25 +00:00
|
|
|
var Tiddler = require("./Tiddler.js").Tiddler,
|
2012-02-16 20:38:10 +00:00
|
|
|
Renderer = require("./Renderer.js").Renderer,
|
2012-02-20 18:04:50 +00:00
|
|
|
Dependencies = require("./Dependencies.js").Dependencies,
|
2012-01-06 17:53:37 +00:00
|
|
|
utils = require("./Utils.js");
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2011-12-28 17:16:56 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
2011-12-11 18:28:09 +00:00
|
|
|
var WikiStore = function WikiStore(options) {
|
2011-12-28 17:16:56 +00:00
|
|
|
options = options || {};
|
2012-01-17 13:31:06 +00:00
|
|
|
this.tiddlers = {}; // Hashmap of tiddlers by title
|
|
|
|
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
|
2012-04-05 11:21:49 +00:00
|
|
|
this.changeCount = {}; // Hashmap of integer changecount (>1) for each tiddler; persistent across deletions
|
2012-01-17 13:31:06 +00:00
|
|
|
this.tiddlerSerializers = {}; // Hashmap of serializers by target MIME type
|
|
|
|
this.tiddlerDeserializers = {}; // Hashmap of deserializers by accepted MIME type
|
2012-01-14 15:49:12 +00:00
|
|
|
this.eventListeners = []; // Array of {filter:,listener:}
|
|
|
|
this.eventsTriggered = false;
|
|
|
|
this.changedTiddlers = {}; // Hashmap of {title: "created|modified|deleted"}
|
2011-12-11 18:28:09 +00:00
|
|
|
this.shadows = options.shadowStore !== undefined ? options.shadowStore : new WikiStore({
|
2011-12-28 17:16:56 +00:00
|
|
|
shadowStore: null
|
2011-12-11 18:28:09 +00:00
|
|
|
});
|
2011-11-22 14:29:29 +00:00
|
|
|
};
|
|
|
|
|
2012-04-05 11:21:49 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-05 11:08:05 +00:00
|
|
|
WikiStore.prototype.registerParser = function(type,parser) {
|
2012-03-01 23:59:46 +00:00
|
|
|
if(type instanceof Array) {
|
|
|
|
for(var t=0; t<type.length; t++) {
|
|
|
|
this.parsers[type[t]] = parser;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.parsers[type] = parser;
|
|
|
|
}
|
2011-12-28 17:16:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2011-12-01 10:19:21 +00:00
|
|
|
};
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2012-01-14 15:49:12 +00:00
|
|
|
WikiStore.prototype.addEventListener = function(filter,listener) {
|
|
|
|
this.eventListeners.push({
|
|
|
|
filter: filter,
|
|
|
|
listener: listener
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiStore.prototype.removeEventListener = function(listener) {
|
|
|
|
for(var c=this.eventListeners.length-1; c>=0; c--) {
|
|
|
|
var l = this.eventListeners[c];
|
|
|
|
if(l.listener === listener) {
|
|
|
|
this.eventListeners.splice(c,1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Causes a tiddler to be marked as changed, so that event listeners are triggered for it
|
|
|
|
type: Type of change to be registered for the tiddler "created", "modified" or "deleted"
|
|
|
|
If the tiddler is already touched, the resultant touch type is as follows:
|
|
|
|
|
|
|
|
If the tiddler is already marked "created",
|
|
|
|
... attempts to mark it "modified" leave it "created"
|
|
|
|
... attempts to mark it "deleted" succeed
|
|
|
|
|
|
|
|
If the tiddler is already marked "modified",
|
|
|
|
... attempts to mark it "deleted" succeed
|
|
|
|
|
|
|
|
If the tiddler is already marked "deleted",
|
|
|
|
... attempts to mark it "created" succeed
|
|
|
|
... attempts to mark it "modified" fail
|
|
|
|
|
|
|
|
*/
|
|
|
|
WikiStore.prototype.touchTiddler = function(type,title) {
|
|
|
|
this.changedTiddlers[title] = type;
|
|
|
|
this.triggerEvents();
|
|
|
|
};
|
|
|
|
|
2012-04-05 11:21:49 +00:00
|
|
|
WikiStore.prototype.clearEvents = function() {
|
|
|
|
this.changedTiddlers = {};
|
|
|
|
};
|
|
|
|
|
2012-01-14 15:49:12 +00:00
|
|
|
/*
|
|
|
|
Trigger the execution of the event dispatcher at the next tick, if it is not already triggered
|
|
|
|
*/
|
|
|
|
WikiStore.prototype.triggerEvents = function() {
|
|
|
|
if(!this.eventsTriggered) {
|
|
|
|
var me = this;
|
|
|
|
utils.nextTick(function() {
|
|
|
|
var changes = me.changedTiddlers;
|
|
|
|
me.changedTiddlers = {};
|
|
|
|
me.eventsTriggered = false;
|
|
|
|
for(var e=0; e<me.eventListeners.length; e++) {
|
|
|
|
var listener = me.eventListeners[e];
|
|
|
|
listener.listener(changes);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.eventsTriggered = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-06 18:29:53 +00:00
|
|
|
WikiStore.prototype.getTiddler = function(title) {
|
2011-11-22 14:29:29 +00:00
|
|
|
var t = this.tiddlers[title];
|
2011-12-03 17:02:34 +00:00
|
|
|
if(t instanceof Tiddler) {
|
|
|
|
return t;
|
|
|
|
} else if(this.shadows) {
|
|
|
|
return this.shadows.getTiddler(title);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2011-12-01 10:19:21 +00:00
|
|
|
};
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2012-02-19 17:19:12 +00:00
|
|
|
WikiStore.prototype.getTiddlerText = function(title,defaultText) {
|
|
|
|
defaultText = typeof defaultText === "string" ? defaultText : null;
|
2011-12-03 17:02:34 +00:00
|
|
|
var t = this.getTiddler(title);
|
2012-02-19 17:19:12 +00:00
|
|
|
return t instanceof Tiddler ? t.text : defaultText;
|
2011-12-01 15:07:10 +00:00
|
|
|
};
|
|
|
|
|
2011-12-06 18:29:53 +00:00
|
|
|
WikiStore.prototype.deleteTiddler = function(title) {
|
2012-04-05 11:21:49 +00:00
|
|
|
this.incChangeCount(title);
|
2011-11-22 14:29:29 +00:00
|
|
|
delete this.tiddlers[title];
|
2012-02-17 12:35:21 +00:00
|
|
|
this.clearCache(title);
|
|
|
|
this.touchTiddler("deleted",title);
|
2011-12-01 10:19:21 +00:00
|
|
|
};
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2011-12-06 18:29:53 +00:00
|
|
|
WikiStore.prototype.tiddlerExists = function(title) {
|
2011-12-16 10:48:36 +00:00
|
|
|
var exists = this.tiddlers[title] instanceof Tiddler;
|
|
|
|
if(exists) {
|
|
|
|
return true;
|
|
|
|
} else if (this.shadows) {
|
|
|
|
return this.shadows.tiddlerExists(title);
|
|
|
|
}
|
|
|
|
return ;
|
2011-12-02 14:40:18 +00:00
|
|
|
};
|
2011-12-01 15:07:10 +00:00
|
|
|
|
2011-12-06 18:29:53 +00:00
|
|
|
WikiStore.prototype.addTiddler = function(tiddler) {
|
2012-03-14 18:57:04 +00:00
|
|
|
// Check if we're passed a fields hashmap instead of a tiddler
|
|
|
|
if(!(tiddler instanceof Tiddler)) {
|
|
|
|
tiddler = new Tiddler(tiddler);
|
|
|
|
}
|
2012-04-05 11:21:49 +00:00
|
|
|
this.incChangeCount(tiddler.title);
|
2012-01-25 10:51:04 +00:00
|
|
|
var status = tiddler.title in this.tiddlers ? "modified" : "created";
|
|
|
|
this.clearCache(tiddler.title);
|
2012-01-17 13:01:55 +00:00
|
|
|
this.tiddlers[tiddler.title] = tiddler;
|
2012-01-25 10:51:04 +00:00
|
|
|
this.touchTiddler(status,tiddler.title);
|
2011-12-01 10:19:21 +00:00
|
|
|
};
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2012-03-14 18:57:04 +00:00
|
|
|
WikiStore.prototype.addTiddlers = function(tiddlers) {
|
|
|
|
for(var t=0; t<tiddlers.length; t++) {
|
|
|
|
this.addTiddler(tiddlers[t]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-12 08:59:28 +00:00
|
|
|
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++],
|
2012-01-05 21:31:47 +00:00
|
|
|
t,
|
|
|
|
tiddlers = [],
|
|
|
|
tiddler;
|
2011-12-12 08:59:28 +00:00
|
|
|
if(sortField) {
|
|
|
|
for(t in this.tiddlers) {
|
|
|
|
tiddlers.push(this.tiddlers[t]);
|
|
|
|
}
|
2012-01-17 13:01:55 +00:00
|
|
|
tiddlers.sort(function (a,b) {return Tiddler.compareTiddlerFields(a,b,sortField);});
|
2011-12-12 08:59:28 +00:00
|
|
|
for(t=0; t<tiddlers.length; t++) {
|
|
|
|
if(!tiddlers[t].hasTag(excludeTag)) {
|
2012-01-17 13:01:55 +00:00
|
|
|
callback.call(this,tiddlers[t].title,tiddlers[t]);
|
2011-12-12 08:59:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for(t in this.tiddlers) {
|
|
|
|
tiddler = this.tiddlers[t];
|
|
|
|
if(tiddler instanceof Tiddler && !tiddler.hasTag(excludeTag))
|
|
|
|
callback.call(this,t,tiddler);
|
|
|
|
}
|
2011-11-22 14:29:29 +00:00
|
|
|
}
|
2011-12-01 10:19:21 +00:00
|
|
|
};
|
2011-11-22 14:29:29 +00:00
|
|
|
|
2011-12-14 18:25:40 +00:00
|
|
|
WikiStore.prototype.getTitles = function(sortField,excludeTag) {
|
2011-12-28 16:10:30 +00:00
|
|
|
sortField = sortField || "title";
|
2011-12-14 18:25:40 +00:00
|
|
|
var tiddlers = [];
|
|
|
|
this.forEachTiddler(sortField,excludeTag,function(title,tiddler) {
|
|
|
|
tiddlers.push(title);
|
|
|
|
});
|
|
|
|
return tiddlers;
|
|
|
|
};
|
|
|
|
|
2011-12-28 16:10:30 +00:00
|
|
|
WikiStore.prototype.getMissingTitles = function() {
|
|
|
|
return [];
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiStore.prototype.getOrphanTitles = function() {
|
|
|
|
return [];
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiStore.prototype.getShadowTitles = function() {
|
|
|
|
return this.shadows ? this.shadows.getTitles() : [];
|
|
|
|
};
|
|
|
|
|
2012-04-05 11:21:49 +00:00
|
|
|
WikiStore.prototype.serializeTiddlers = function(tiddlers,type) {
|
|
|
|
type = type || "application/x-tiddler";
|
2011-12-28 17:16:56 +00:00
|
|
|
var serializer = this.tiddlerSerializers[type];
|
|
|
|
if(serializer) {
|
2012-04-05 11:21:49 +00:00
|
|
|
return serializer(tiddlers);
|
2011-12-28 17:16:56 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiStore.prototype.deserializeTiddlers = function(type,text,srcFields) {
|
|
|
|
var fields = {},
|
|
|
|
deserializer = this.tiddlerDeserializers[type],
|
|
|
|
t;
|
|
|
|
if(srcFields) {
|
|
|
|
for(t in srcFields) {
|
|
|
|
fields[t] = srcFields[t];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(deserializer) {
|
|
|
|
return deserializer(text,fields);
|
|
|
|
} else {
|
|
|
|
// Return a raw tiddler for unknown types
|
|
|
|
fields.text = text;
|
|
|
|
return [fields];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-17 13:31:06 +00:00
|
|
|
// Return the named cache object for a tiddler. If the cache doesn't exist then the initializer function is invoked to create it
|
|
|
|
WikiStore.prototype.getCacheForTiddler = function(title,cacheName,initializer) {
|
|
|
|
var caches = this.caches[title];
|
|
|
|
if(caches && caches[cacheName]) {
|
|
|
|
return caches[cacheName];
|
|
|
|
} else {
|
|
|
|
if(!caches) {
|
|
|
|
caches = {};
|
|
|
|
this.caches[title] = caches;
|
|
|
|
}
|
|
|
|
caches[cacheName] = initializer();
|
|
|
|
return caches[cacheName];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-25 10:51:04 +00:00
|
|
|
// Clear all caches associated with a particular tiddler
|
|
|
|
WikiStore.prototype.clearCache = function(title) {
|
2012-02-01 16:12:49 +00:00
|
|
|
if(this.caches.hasOwnProperty(title)) {
|
2012-01-25 10:51:04 +00:00
|
|
|
delete this.caches[title];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-03-03 18:07:33 +00:00
|
|
|
/*
|
|
|
|
Parse a block of text of a specified MIME type
|
|
|
|
|
|
|
|
Options are:
|
|
|
|
defaultType: Default MIME type to use if the specified one is unknown
|
|
|
|
*/
|
|
|
|
WikiStore.prototype.parseText = function(type,text,options) {
|
|
|
|
options = options || {};
|
2012-01-05 11:08:05 +00:00
|
|
|
var parser = this.parsers[type];
|
|
|
|
if(!parser) {
|
2012-03-03 18:07:33 +00:00
|
|
|
parser = this.parsers[options.defaultType || "text/x-tiddlywiki"];
|
2011-12-28 17:16:56 +00:00
|
|
|
}
|
2012-01-05 11:08:05 +00:00
|
|
|
if(parser) {
|
2012-01-24 18:10:27 +00:00
|
|
|
return parser.parse(type,text);
|
2011-12-28 17:16:56 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-11 18:28:09 +00:00
|
|
|
WikiStore.prototype.parseTiddler = function(title) {
|
2012-01-17 13:31:06 +00:00
|
|
|
var me = this,
|
2012-02-17 12:35:21 +00:00
|
|
|
tiddler = this.getTiddler(title);
|
|
|
|
return tiddler ? this.getCacheForTiddler(title,"parseTree",function() {
|
2012-01-17 13:31:06 +00:00
|
|
|
return me.parseText(tiddler.type,tiddler.text);
|
2012-02-17 12:35:21 +00:00
|
|
|
}) : null;
|
2011-12-12 10:52:04 +00:00
|
|
|
};
|
2011-12-11 18:28:09 +00:00
|
|
|
|
2011-12-14 14:11:11 +00:00
|
|
|
/*
|
2012-02-01 16:12:49 +00:00
|
|
|
Render a tiddler to a particular MIME type
|
|
|
|
targetType: target MIME type
|
|
|
|
title: title of the tiddler to render
|
|
|
|
template: optional title of the tiddler to use as a template
|
2012-02-02 17:48:09 +00:00
|
|
|
*/
|
2012-02-20 18:04:50 +00:00
|
|
|
WikiStore.prototype.renderTiddler = function(targetType,tiddlerTitle,templateTitle) {
|
|
|
|
// Construct the tiddler macro
|
|
|
|
var macro = Renderer.MacroNode(
|
|
|
|
"tiddler",
|
|
|
|
{target: tiddlerTitle, template: templateTitle},
|
|
|
|
null,
|
|
|
|
this);
|
2012-03-07 17:42:41 +00:00
|
|
|
// Execute the macro
|
2012-03-29 13:30:22 +00:00
|
|
|
macro.execute([],tiddlerTitle);
|
2012-03-07 17:42:41 +00:00
|
|
|
// Render it
|
2012-02-20 18:04:50 +00:00
|
|
|
return macro.render(targetType);
|
2012-02-02 17:48:09 +00:00
|
|
|
};
|
|
|
|
|
2012-01-30 20:04:47 +00:00
|
|
|
WikiStore.prototype.installMacro = function(macro) {
|
|
|
|
this.macros[macro.name] = macro;
|
|
|
|
};
|
|
|
|
|
2012-03-07 17:42:41 +00:00
|
|
|
WikiStore.prototype.renderMacro = function(macroName,params,children,tiddlerTitle) {
|
2012-02-20 20:52:54 +00:00
|
|
|
var macro = Renderer.MacroNode(macroName,params,children,this);
|
2012-03-29 13:30:22 +00:00
|
|
|
macro.execute([],tiddlerTitle);
|
2012-02-20 20:52:54 +00:00
|
|
|
return macro;
|
|
|
|
};
|
|
|
|
|
2011-12-06 18:29:53 +00:00
|
|
|
exports.WikiStore = WikiStore;
|
2011-12-12 10:52:04 +00:00
|
|
|
|
|
|
|
})();
|