1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-26 19:47:20 +00:00

Make shadowTiddlers, pluginTiddlers and pluginInfo be private to the Wiki object constructor

This commit is contained in:
Jermolene 2014-03-17 10:50:18 +00:00
parent 279626a3e3
commit 9de17aa206
6 changed files with 193 additions and 163 deletions

View File

@ -798,39 +798,56 @@ $tw.modules.define("$:/boot/tiddlerfields/list","tiddlerfield",{
/////////////////////////// Barebones wiki store /////////////////////////// Barebones wiki store
/* /*
Construct a wiki store object Wiki constructor. State is stored in private members that only a small number of privileged accessor methods have direct access. Methods added via the prototype have to use these accessors and cannot access the state data directly.
options include:
shadowTiddlers: Array of shadow tiddlers to be added
*/ */
$tw.Wiki = function() { $tw.Wiki = function(options) {
options = options || {};
var self = this, var self = this,
tiddlers = {}; // Hashmap of tiddlers tiddlers = {}, // Hashmap of tiddlers
this.pluginTiddlers = []; // Array of tiddlers containing registered plugins, ordered by priority pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
this.pluginInfo = {}; // Hashmap of parsed plugin content pluginInfo = {}, // Hashmap of parsed plugin content
this.shadowTiddlers = {}; // Hashmap by title of {source:, tiddler:} shadowTiddlers = options.shadowTiddlers || {}; // Hashmap by title of {source:, tiddler:}
// Methods that need access to the private members
// Add a tiddler to the store
this.addTiddler = function(tiddler) { this.addTiddler = function(tiddler) {
if(!(tiddler instanceof $tw.Tiddler)) { if(!(tiddler instanceof $tw.Tiddler)) {
tiddler = new $tw.Tiddler(tiddler); tiddler = new $tw.Tiddler(tiddler);
} }
// Get the title
var title = tiddler.fields.title;
// Save the tiddler // Save the tiddler
if(title) { if(tiddler) {
tiddlers[title] = tiddler; var title = tiddler.fields.title;
this.clearCache(title); if(title) {
this.clearGlobalCache(); tiddlers[title] = tiddler;
this.enqueueTiddlerEvent(title); this.clearCache(title);
this.clearGlobalCache();
this.enqueueTiddlerEvent(title);
}
} }
}; };
// Delete a tiddler
this.deleteTiddler = function(title) {
delete tiddlers[title];
this.clearCache(title);
this.clearGlobalCache();
this.enqueueTiddlerEvent(title,true);
};
// Get a tiddler from the store
this.getTiddler = function(title) { this.getTiddler = function(title) {
var t = tiddlers[title]; var t = tiddlers[title];
if(t instanceof $tw.Tiddler) { if(t instanceof $tw.Tiddler) {
return t; return t;
} else if(title !== undefined && $tw.utils.hop(self.shadowTiddlers,title)) { } else if(title !== undefined && $tw.utils.hop(shadowTiddlers,title)) {
return self.shadowTiddlers[title].tiddler; return shadowTiddlers[title].tiddler;
} else { } else {
return undefined; return undefined;
} }
}; };
// Get a hashmap of all tiddler titles
this.getAllTitles = function() { this.getAllTitles = function() {
var results = {}; var results = {};
for(var title in tiddlers) { for(var title in tiddlers) {
@ -838,20 +855,122 @@ $tw.Wiki = function() {
} }
return results; return results;
}; };
// Iterate through all tiddler titles
this.each = function(callback) { this.each = function(callback) {
for(var title in tiddlers) { for(var title in tiddlers) {
callback(tiddlers[title],title); callback(tiddlers[title],title);
} }
}; };
// Iterate through all shadow tiddler titles
this.eachShadow = function(callback) {
for(var title in shadowTiddlers) {
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
};
// Test for the existence of a tiddler
this.tiddlerExists = function(title) { this.tiddlerExists = function(title) {
return !!$tw.utils.hop(tiddlers,title); return !!$tw.utils.hop(tiddlers,title);
}; };
this.deleteTiddler = function(title) {
delete tiddlers[title]; // Determines if a tiddler is a shadow tiddler, regardless of whether it has been overridden by a real tiddler
this.clearCache(title); this.isShadowTiddler = function(title) {
this.clearGlobalCache(); return $tw.utils.hop(shadowTiddlers,title);
this.enqueueTiddlerEvent(title,true);
}; };
this.getShadowSource = function(title) {
if($tw.utils.hop(shadowTiddlers,title)) {
return shadowTiddlers[title].source;
}
return null;
};
// Read plugin info for all plugins
this.readPluginInfo = function() {
for(var title in tiddlers) {
var tiddler = tiddlers[title];
if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type")) {
pluginInfo[tiddler.fields.title] = JSON.parse(tiddler.fields.text);
}
}
};
// Register the plugin tiddlers of a particular type, optionally restricting registration to an array of tiddler titles. Return the array of titles affected
this.registerPluginTiddlers = function(pluginType,titles) {
var self = this,
registeredTitles = [],
checkTiddler = function(tiddler) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] === pluginType) {
pluginTiddlers.push(tiddler);
registeredTitles.push(tiddler.fields.title);
}
};
if(titles) {
$tw.utils.each(titles,function(title) {
checkTiddler(self.getTiddler(title));
});
} else {
this.each(function(tiddler,title) {
checkTiddler(tiddler);
});
}
return registeredTitles;
};
// Unregister the plugin tiddlers of a particular type, returning an array of the titles affected
this.unregisterPluginTiddlers = function(pluginType) {
var self = this,
titles = [];
// Remove any previous registered plugins of this type
for(var t=pluginTiddlers.length-1; t>=0; t--) {
var tiddler = pluginTiddlers[t];
if(tiddler.fields["plugin-type"] === pluginType) {
titles.push(tiddler.fields.title);
pluginTiddlers.splice(t,1);
}
}
return titles;
};
// Unpack the currently registered plugins, creating shadow tiddlers for their constituent tiddlers
this.unpackPluginTiddlers = function() {
var self = this;
// Sort the plugin titles by the `plugin-priority` field
pluginTiddlers.sort(function(a,b) {
if("plugin-priority" in a.fields && "plugin-priority" in b.fields) {
return a.fields["plugin-priority"] - b.fields["plugin-priority"];
} else if("plugin-priority" in a.fields) {
return -1;
} else if("plugin-priority" in b.fields) {
return +1;
} else if(a.fields.title < b.fields.title) {
return -1;
} else if(a.fields.title === b.fields.title) {
return 0;
} else {
return +1;
}
});
// Now go through the plugins in ascending order and assign the shadows
shadowTiddlers = {};
$tw.utils.each(pluginTiddlers,function(tiddler) {
// Extract the constituent tiddlers
$tw.utils.each(pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) {
// Save the tiddler object
if(constituentTitle) {
shadowTiddlers[constituentTitle] = {
source: tiddler.fields.title,
tiddler: new $tw.Tiddler(constituentTiddler,{title: constituentTitle})
};
}
});
});
};
}; };
// Dummy methods that will be filled in after boot // Dummy methods that will be filled in after boot
@ -859,103 +978,13 @@ $tw.Wiki.prototype.clearCache =
$tw.Wiki.prototype.clearGlobalCache = $tw.Wiki.prototype.clearGlobalCache =
$tw.Wiki.prototype.enqueueTiddlerEvent = function() {}; $tw.Wiki.prototype.enqueueTiddlerEvent = function() {};
// Add an array of tiddlers
$tw.Wiki.prototype.addTiddlers = function(tiddlers) { $tw.Wiki.prototype.addTiddlers = function(tiddlers) {
for(var t=0; t<tiddlers.length; t++) { for(var t=0; t<tiddlers.length; t++) {
this.addTiddler(tiddlers[t]); this.addTiddler(tiddlers[t]);
} }
}; };
/*
Read plugin info for all plugins
*/
$tw.Wiki.prototype.readPluginInfo = function() {
var self = this;
this.each(function(tiddler,title) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type")) {
self.pluginInfo[tiddler.fields.title] = JSON.parse(tiddler.fields.text);
}
});
};
/*
Register the plugin tiddlers of a particular type, optionally restricting registration to an array of tiddler titles. Return the array of titles affected
*/
$tw.Wiki.prototype.registerPluginTiddlers = function(pluginType,titles) {
var self = this,
registeredTitles = [];
// Go through the provided titles, or the entire tiddler list, looking for plugins of this type
var checkTiddler = function(tiddler) {
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] === pluginType) {
self.pluginTiddlers.push(tiddler);
registeredTitles.push(tiddler.fields.title);
}
};
if(titles) {
$tw.utils.each(titles,function(title) {
checkTiddler(self.getTiddler(title));
});
} else {
this.each(function(tiddler,title) {
checkTiddler(tiddler);
});
}
return registeredTitles;
};
/*
Unregister the plugin tiddlers of a particular type, returning an array of the titles affected
*/
$tw.Wiki.prototype.unregisterPluginTiddlers = function(pluginType) {
var self = this,
titles = [];
// Remove any previous registered plugins of this type
for(var t=this.pluginTiddlers.length-1; t>=0; t--) {
var tiddler = this.pluginTiddlers[t];
if(tiddler.fields["plugin-type"] === pluginType) {
titles.push(tiddler.fields.title);
this.pluginTiddlers.splice(t,1);
}
}
return titles;
};
/*
Unpack the currently registered plugins, creating shadow tiddlers for their constituent tiddlers
*/
$tw.Wiki.prototype.unpackPluginTiddlers = function() {
var self = this;
// Sort the plugin titles by the `plugin-priority` field
this.pluginTiddlers.sort(function(a,b) {
if("plugin-priority" in a.fields && "plugin-priority" in b.fields) {
return a.fields["plugin-priority"] - b.fields["plugin-priority"];
} else if("plugin-priority" in a.fields) {
return -1;
} else if("plugin-priority" in b.fields) {
return +1;
} else if(a.fields.title < b.fields.title) {
return -1;
} else if(a.fields.title === b.fields.title) {
return 0;
} else {
return +1;
}
});
// Now go through the plugins in ascending order and assign the shadows
this.shadowTiddlers = {};
$tw.utils.each(this.pluginTiddlers,function(tiddler) {
// Extract the constituent tiddlers
$tw.utils.each(self.pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) {
// Save the tiddler object
if(constituentTitle) {
self.shadowTiddlers[constituentTitle] = {
source: tiddler.fields.title,
tiddler: new $tw.Tiddler(constituentTiddler,{title: constituentTitle})
};
}
});
});
};
/* /*
Define all modules stored in ordinary tiddlers Define all modules stored in ordinary tiddlers
*/ */
@ -985,13 +1014,11 @@ Register all the module tiddlers that have a module type
*/ */
$tw.Wiki.prototype.defineShadowModules = function() { $tw.Wiki.prototype.defineShadowModules = function() {
var self = this; var self = this;
$tw.utils.each(this.shadowTiddlers,function(element,title) { this.eachShadow(function(tiddler,title) {
var tiddler = self.getTiddler(title); // Don't define the module if it is overidden by an ordinary tiddler
if(!self.tiddlerExists(title)) { // Don't define the module if it is overidden by an ordinary tiddler if(!self.tiddlerExists(title) && tiddler.hasField("module-type")) {
if(tiddler.hasField("module-type")) { // Define the module
// Define the module $tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
}
} }
}); });
}; };

View File

@ -34,7 +34,7 @@ exports.shadow = function(source,prefix,options) {
}); });
} else { } else {
if(prefix !== "!") { if(prefix !== "!") {
$tw.utils.each(options.wiki.shadowTiddlers,function(tiddler,title) { options.wiki.eachShadow(function(tiddler,title) {
results.push(title); results.push(title);
}); });
} else { } else {

View File

@ -18,9 +18,9 @@ Export our filter function
exports.shadowsource = function(source,operator,options) { exports.shadowsource = function(source,operator,options) {
var results = [], var results = [],
pushShadowSource = function(title) { pushShadowSource = function(title) {
var shadowInfo = options.wiki.shadowTiddlers[title]; var source = options.wiki.getShadowSource(title);
if(shadowInfo) { if(source) {
$tw.utils.pushTop(results,shadowInfo.source); $tw.utils.pushTop(results,source);
} }
}; };
// Iterate through the source tiddlers // Iterate through the source tiddlers

View File

@ -62,8 +62,9 @@ PluginSwitcher.prototype.switchPlugins = function() {
var unregisteredTiddlers = $tw.wiki.unregisterPluginTiddlers(this.pluginType); var unregisteredTiddlers = $tw.wiki.unregisterPluginTiddlers(this.pluginType);
// Accumulate the titles of shadow tiddlers that have changed as a result of this switch // Accumulate the titles of shadow tiddlers that have changed as a result of this switch
var changedTiddlers = {}; var changedTiddlers = {};
$tw.utils.each(this.wiki.shadowTiddlers,function(shadowInfo,title) { this.wiki.eachShadow(function(tiddler,title) {
if(unregisteredTiddlers.indexOf(shadowInfo.source) !== -1) { var source = self.wiki.getShadowSource(title);
if(unregisteredTiddlers.indexOf(source) !== -1) {
changedTiddlers[title] = true; // isDeleted? changedTiddlers[title] = true; // isDeleted?
} }
}); });
@ -72,8 +73,9 @@ PluginSwitcher.prototype.switchPlugins = function() {
// Unpack the current theme tiddlers // Unpack the current theme tiddlers
$tw.wiki.unpackPluginTiddlers(); $tw.wiki.unpackPluginTiddlers();
// Accumulate the affected shadow tiddlers // Accumulate the affected shadow tiddlers
$tw.utils.each(this.wiki.shadowTiddlers,function(shadowInfo,title) { this.wiki.eachShadow(function(tiddler,title) {
if(registeredTiddlers.indexOf(shadowInfo.source) !== -1) { var source = self.wiki.getShadowSource(title);
if(registeredTiddlers.indexOf(source) !== -1) {
changedTiddlers[title] = false; // isDeleted? changedTiddlers[title] = false; // isDeleted?
} }
}); });

View File

@ -178,13 +178,6 @@ exports.isTemporaryTiddler = function(title) {
return title.indexOf("$:/temp/") === 0; return title.indexOf("$:/temp/") === 0;
}; };
/*
Determines if a tiddler is a shadow tiddler, regardless of whether it has been overridden by a real tiddler
*/
exports.isShadowTiddler = function(title) {
return $tw.utils.hop(this.shadowTiddlers,title);
};
exports.isImageTiddler = function(title) { exports.isImageTiddler = function(title) {
var tiddler = this.getTiddler(title); var tiddler = this.getTiddler(title);
if(tiddler) { if(tiddler) {
@ -453,12 +446,12 @@ exports.getTagMap = function() {
}, },
title, tiddler; title, tiddler;
// Collect up all the tags // Collect up all the tags
for(title in self.shadowTiddlers) { self.eachShadow(function(tiddler,title) {
if(!self.tiddlerExists(title)) { if(!self.tiddlerExists(title)) {
tiddler = self.shadowTiddlers[title].tiddler; tiddler = self.getTiddler(title);
storeTags(tiddler.fields.tags,title); storeTags(tiddler.fields.tags,title);
} }
} });
self.each(function(tiddler,title) { self.each(function(tiddler,title) {
storeTags(tiddler.fields.tags,title); storeTags(tiddler.fields.tags,title);
}); });

View File

@ -15,13 +15,36 @@ Tests the filtering mechanism.
describe("Filter tests", function() { describe("Filter tests", function() {
// Create a wiki // Create a wiki
var wiki = new $tw.Wiki(); var wiki = new $tw.Wiki({
shadowTiddlers: {
// Some helpers "$:/TiddlerFive": {
var addShadowTiddler = function(fields) { tiddler: new $tw.Tiddler({title: "$:/TiddlerFive",
var tiddler = new $tw.Tiddler(fields); text: "Everything in federation",
wiki.shadowTiddlers[tiddler.fields.title] = {tiddler: tiddler}; tags: ["two"]
}; }),
},
"TiddlerSix": {
tiddler: new $tw.Tiddler({title: "TiddlerSix",
text: "Missing inaction from TiddlerOne",
tags: []
}),
},
"TiddlerSeventh": {
tiddler: new $tw.Tiddler({title: "TiddlerSeventh",
text: "",
list: "TiddlerOne [[Tiddler Three]] [[a fourth tiddler]] MissingTiddler",
tags: []
}),
},
"Tiddler8": {
tiddler: new $tw.Tiddler({title: "Tiddler8",
text: "Tidd",
tags: [],
"test-field": "JoeBloggs"
})
}
}
});
// Add a few tiddlers // Add a few tiddlers
wiki.addTiddler({ wiki.addTiddler({
@ -52,28 +75,13 @@ describe("Filter tests", function() {
text: "This is the text of tiddler [[one]]", text: "This is the text of tiddler [[one]]",
list: "[[Tiddler Three]] [[TiddlerOne]]", list: "[[Tiddler Three]] [[TiddlerOne]]",
modifier: "JohnDoe"}); modifier: "JohnDoe"});
// And some shadows
addShadowTiddler({
title: "$:/TiddlerFive",
text: "Everything in federation",
tags: ["two"]});
addShadowTiddler({
title: "TiddlerSix",
text: "Missing inaction from TiddlerOne",
tags: []});
addShadowTiddler({
title: "TiddlerSeventh",
text: "",
list: "TiddlerOne [[Tiddler Three]] [[a fourth tiddler]] MissingTiddler",
tags: []});
addShadowTiddler({
title: "Tiddler8",
text: "Tidd",
tags: [],
"test-field": "JoeBloggs"});
// Our tests // Our tests
it("should retrieve shadow tiddlers", function() {
expect(wiki.getTiddlerText("Tiddler8")).toBe("Tidd");
});
it("should handle the title operator", function() { it("should handle the title operator", function() {
expect(wiki.filterTiddlers("TiddlerOne [title[$:/TiddlerTwo]] [[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,Tiddler Three"); expect(wiki.filterTiddlers("TiddlerOne [title[$:/TiddlerTwo]] [[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,Tiddler Three");
expect(wiki.filterTiddlers("[!title[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,a fourth tiddler,one"); expect(wiki.filterTiddlers("[!title[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,a fourth tiddler,one");