mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-22 10:54:46 +00:00
Compare commits
76 Commits
survey-edi
...
demo-alter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6983564194 | ||
|
|
4897248809 | ||
|
|
5d20e983b6 | ||
|
|
6997c61bc1 | ||
|
|
d7f0c5cb6b | ||
|
|
02f3065e4f | ||
|
|
9493084f95 | ||
|
|
8c1f7a6928 | ||
|
|
d98265868e | ||
|
|
e4af21a169 | ||
|
|
a58f119c50 | ||
|
|
01e1882083 | ||
|
|
e9d640b61b | ||
|
|
3a4f5b8cfc | ||
|
|
dc94ed8be8 | ||
|
|
12c6cb35a0 | ||
|
|
f49b9faab0 | ||
|
|
d4dec0ca65 | ||
|
|
863066d41f | ||
|
|
fd3d8aef36 | ||
|
|
c52014c66f | ||
|
|
1754be279f | ||
|
|
25138ecd04 | ||
|
|
66cba18e0f | ||
|
|
e6309e95c9 | ||
|
|
bb41ae0c9b | ||
|
|
7eeaa20e7e | ||
|
|
39d04517dd | ||
|
|
b9245da91f | ||
|
|
7fd2dd5a3e | ||
|
|
2d3027faab | ||
|
|
2cd2a057f8 | ||
|
|
09b0e2815c | ||
|
|
b4fe89657b | ||
|
|
88c8c2c9f3 | ||
|
|
979a1f746d | ||
|
|
83e7d32c8e | ||
|
|
cc2cd20e32 | ||
|
|
709669b714 | ||
|
|
f48bddb167 | ||
|
|
e3255a4d2a | ||
|
|
b557deac79 | ||
|
|
64ffa52da9 | ||
|
|
d2e21ddd3c | ||
|
|
f3bc32a2e9 | ||
|
|
21ef2d7646 | ||
|
|
9e190a46db | ||
|
|
2d229e2159 | ||
|
|
71c02e57c0 | ||
|
|
687b1df0c3 | ||
|
|
1f4be3e92f | ||
|
|
b29af447e5 | ||
|
|
6ded5e68bf | ||
|
|
87213f2c65 | ||
|
|
12a19bb9cf | ||
|
|
2099c4f9a8 | ||
|
|
8399538814 | ||
|
|
ede5f1e9ad | ||
|
|
9cb8721343 | ||
|
|
7e6072611e | ||
|
|
9ac21f167f | ||
|
|
9427cf7ac6 | ||
|
|
8690936805 | ||
|
|
831fb3996d | ||
|
|
c43bc8f92a | ||
|
|
6f24f33a3d | ||
|
|
449e2274e2 | ||
|
|
146a22b894 | ||
|
|
0546a14201 | ||
|
|
544e079033 | ||
|
|
1d0b9280d8 | ||
|
|
3561318515 | ||
|
|
b6bc197f76 | ||
|
|
62337101c3 | ||
|
|
7fdd8a5164 | ||
|
|
fdec12f43b |
46
boot/boot.js
46
boot/boot.js
@@ -1096,6 +1096,39 @@ $tw.Tiddler.prototype.isEqual = function(tiddler,excludeFields) {
|
||||
return differences.length === 0;
|
||||
};
|
||||
|
||||
$tw.Tiddler.prototype.getFieldString = function(field,defaultValue) {
|
||||
var value = this.fields[field];
|
||||
// Check for a missing field
|
||||
if(value === undefined || value === null) {
|
||||
return defaultValue || "";
|
||||
}
|
||||
// Stringify the field with the associated tiddler field module (if any)
|
||||
var fieldModule = $tw.Tiddler.fieldModules[field];
|
||||
if(fieldModule && fieldModule.stringify) {
|
||||
return fieldModule.stringify.call(this,value);
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get all the fields as a hashmap of strings. Options:
|
||||
exclude: an array of field names to exclude
|
||||
*/
|
||||
$tw.Tiddler.prototype.getFieldStrings = function(options) {
|
||||
options = options || {};
|
||||
var exclude = options.exclude || [];
|
||||
var fields = {};
|
||||
for(var field in this.fields) {
|
||||
if($tw.utils.hop(this.fields,field)) {
|
||||
if(exclude.indexOf(field) === -1) {
|
||||
fields[field] = this.getFieldString(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
};
|
||||
|
||||
/*
|
||||
Register and install the built in tiddler field modules
|
||||
*/
|
||||
@@ -1132,7 +1165,7 @@ Wiki constructor. State is stored in private members that only a small number of
|
||||
options include:
|
||||
enableIndexers - Array of indexer names to enable, or null to use all available indexers
|
||||
*/
|
||||
$tw.Wiki = function(options) {
|
||||
$tw.Wiki = $tw.Wiki || function(options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
tiddlers = Object.create(null), // Hashmap of tiddlers
|
||||
@@ -1494,7 +1527,7 @@ $tw.Wiki.prototype.clearGlobalCache =
|
||||
$tw.Wiki.prototype.enqueueTiddlerEvent = function() {};
|
||||
|
||||
// Add an array of tiddlers
|
||||
$tw.Wiki.prototype.addTiddlers = function(tiddlers) {
|
||||
$tw.Wiki.prototype.addTiddlers = $tw.Wiki.prototype.addTiddlers || function(tiddlers) {
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
this.addTiddler(tiddlers[t]);
|
||||
}
|
||||
@@ -1503,7 +1536,7 @@ $tw.Wiki.prototype.addTiddlers = function(tiddlers) {
|
||||
/*
|
||||
Define all modules stored in ordinary tiddlers
|
||||
*/
|
||||
$tw.Wiki.prototype.defineTiddlerModules = function() {
|
||||
$tw.Wiki.prototype.defineTiddlerModules = $tw.Wiki.prototype.defineTiddlerModules || function() {
|
||||
this.each(function(tiddler,title) {
|
||||
if(tiddler.hasField("module-type")) {
|
||||
switch (tiddler.fields.type) {
|
||||
@@ -1527,7 +1560,7 @@ $tw.Wiki.prototype.defineTiddlerModules = function() {
|
||||
/*
|
||||
Register all the module tiddlers that have a module type
|
||||
*/
|
||||
$tw.Wiki.prototype.defineShadowModules = function() {
|
||||
$tw.Wiki.prototype.defineShadowModules = $tw.Wiki.prototype.defineShadowModules || function() {
|
||||
var self = this;
|
||||
this.eachShadow(function(tiddler,title) {
|
||||
// Don't define the module if it is overidden by an ordinary tiddler
|
||||
@@ -1541,7 +1574,7 @@ $tw.Wiki.prototype.defineShadowModules = function() {
|
||||
/*
|
||||
Enable safe mode by deleting any tiddlers that override a shadow tiddler
|
||||
*/
|
||||
$tw.Wiki.prototype.processSafeMode = function() {
|
||||
$tw.Wiki.prototype.processSafeMode = $tw.Wiki.prototype.processSafeMode || function() {
|
||||
var self = this,
|
||||
overrides = [];
|
||||
// Find the overriding tiddlers
|
||||
@@ -1572,7 +1605,7 @@ $tw.Wiki.prototype.processSafeMode = function() {
|
||||
/*
|
||||
Extracts tiddlers from a typed block of text, specifying default field values
|
||||
*/
|
||||
$tw.Wiki.prototype.deserializeTiddlers = function(type,text,srcFields,options) {
|
||||
$tw.Wiki.prototype.deserializeTiddlers = $tw.Wiki.prototype.deserializeTiddlers || function(type,text,srcFields,options) {
|
||||
srcFields = srcFields || Object.create(null);
|
||||
options = options || {};
|
||||
var deserializer = $tw.Wiki.tiddlerDeserializerModules[options.deserializer],
|
||||
@@ -2438,6 +2471,7 @@ $tw.boot.initStartup = function(options) {
|
||||
$tw.utils.registerFileType("image/svg+xml","utf8",".svg",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/vnd.microsoft.icon","base64",".ico",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]});
|
||||
$tw.utils.registerFileType("application/wasm","base64",".wasm");
|
||||
$tw.utils.registerFileType("application/font-woff","base64",".woff");
|
||||
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
|
||||
$tw.utils.registerFileType("application/font-woff2","base64",".woff2");
|
||||
|
||||
@@ -171,7 +171,7 @@ exports.parseFilter = function(filterString) {
|
||||
}
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
@@ -179,7 +179,7 @@ exports.parseFilter = function(filterString) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
if(match[4]) { // Opening square bracket
|
||||
@@ -225,13 +225,17 @@ source: an iterator function for the source tiddlers, called source(iterator), w
|
||||
widget: an optional widget node for retrieving the current tiddler etc.
|
||||
*/
|
||||
exports.compileFilter = function(filterString) {
|
||||
var self = this;
|
||||
// Set up the filter function cache
|
||||
if(!this.filterCache) {
|
||||
this.filterCache = Object.create(null);
|
||||
this.filterCacheCount = 0;
|
||||
}
|
||||
// Use the cached version of this filter function if it exists
|
||||
if(this.filterCache[filterString] !== undefined) {
|
||||
return this.filterCache[filterString];
|
||||
}
|
||||
// Parse the filter string
|
||||
var filterParseTree;
|
||||
try {
|
||||
filterParseTree = this.parseFilter(filterString);
|
||||
@@ -241,10 +245,42 @@ exports.compileFilter = function(filterString) {
|
||||
return [$tw.language.getString("Error/Filter") + ": " + e];
|
||||
};
|
||||
}
|
||||
// Get the filter function
|
||||
var fnFilter = this.optimiseFilter && this.optimiseFilter(filterString,filterParseTree);
|
||||
if(!fnFilter) {
|
||||
fnFilter = this.compileFilterToJavaScript(filterParseTree);
|
||||
}
|
||||
// Add recursion detection
|
||||
var fnGuardedFilter = function guardedFilterFunction(source,widget) {
|
||||
var results;
|
||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
results = fnFilter(source,widget);
|
||||
} else {
|
||||
results = ["/**-- Excessive filter recursion --**/"];
|
||||
}
|
||||
self.filterRecursionCount = self.filterRecursionCount - 1;
|
||||
return results;
|
||||
}
|
||||
// Add performance measurement
|
||||
var fnMeasured = $tw.perf.measure("filter: " + filterString,fnGuardedFilter);
|
||||
// Cache the final filter function
|
||||
if(this.filterCacheCount >= 2000) {
|
||||
// To prevent memory leak, we maintain an upper limit for cache size.
|
||||
// Reset if exceeded. This should give us 95% of the benefit
|
||||
// that no cache limit would give us.
|
||||
this.filterCache = Object.create(null);
|
||||
this.filterCacheCount = 0;
|
||||
}
|
||||
this.filterCache[filterString] = fnMeasured;
|
||||
this.filterCacheCount++;
|
||||
return fnMeasured;
|
||||
};
|
||||
|
||||
exports.compileFilterToJavaScript = function(filterParseTree) {
|
||||
var operationFunctions = [];
|
||||
// Get the hashmap of filter operator functions
|
||||
var filterOperators = this.getFilterOperators();
|
||||
// Assemble array of functions, one for each operation
|
||||
var operationFunctions = [];
|
||||
// Step through the operations
|
||||
var self = this;
|
||||
$tw.utils.each(filterParseTree,function(operation) {
|
||||
@@ -334,8 +370,8 @@ exports.compileFilter = function(filterString) {
|
||||
}
|
||||
})());
|
||||
});
|
||||
// Return a function that applies the operations to a source iterator of tiddler titles
|
||||
var fnMeasured = $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
|
||||
// Make the filter function
|
||||
return function filterFunction(source,widget) {
|
||||
if(!source) {
|
||||
source = self.each;
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
@@ -345,27 +381,12 @@ exports.compileFilter = function(filterString) {
|
||||
widget = $tw.rootWidget;
|
||||
}
|
||||
var results = new $tw.utils.LinkedList();
|
||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
} else {
|
||||
results.push("/**-- Excessive filter recursion --**/");
|
||||
}
|
||||
self.filterRecursionCount = self.filterRecursionCount - 1;
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
return results.toArray();
|
||||
});
|
||||
if(this.filterCacheCount >= 2000) {
|
||||
// To prevent memory leak, we maintain an upper limit for cache size.
|
||||
// Reset if exceeded. This should give us 95% of the benefit
|
||||
// that no cache limit would give us.
|
||||
this.filterCache = Object.create(null);
|
||||
this.filterCacheCount = 0;
|
||||
}
|
||||
this.filterCache[filterString] = fnMeasured;
|
||||
this.filterCacheCount++;
|
||||
return fnMeasured;
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -24,21 +24,6 @@ exports.isDraft = function() {
|
||||
return this.hasField("draft.of");
|
||||
};
|
||||
|
||||
exports.getFieldString = function(field,defaultValue) {
|
||||
var value = this.fields[field];
|
||||
// Check for a missing field
|
||||
if(value === undefined || value === null) {
|
||||
return defaultValue || "";
|
||||
}
|
||||
// Stringify the field with the associated tiddler field module (if any)
|
||||
var fieldModule = $tw.Tiddler.fieldModules[field];
|
||||
if(fieldModule && fieldModule.stringify) {
|
||||
return fieldModule.stringify.call(this,value);
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get the value of a field as a list
|
||||
*/
|
||||
@@ -51,24 +36,6 @@ exports.getFieldList = function(field) {
|
||||
return $tw.utils.parseStringArray(value);
|
||||
};
|
||||
|
||||
/*
|
||||
Get all the fields as a hashmap of strings. Options:
|
||||
exclude: an array of field names to exclude
|
||||
*/
|
||||
exports.getFieldStrings = function(options) {
|
||||
options = options || {};
|
||||
var exclude = options.exclude || [];
|
||||
var fields = {};
|
||||
for(var field in this.fields) {
|
||||
if($tw.utils.hop(this.fields,field)) {
|
||||
if(exclude.indexOf(field) === -1) {
|
||||
fields[field] = this.getFieldString(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
};
|
||||
|
||||
/*
|
||||
Get all the fields as a name:value block. Options:
|
||||
exclude: an array of field names to exclude
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: no
|
||||
text: yes
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"description": "Empty edition",
|
||||
"plugins": [
|
||||
"tiddlywiki/sqlite3store"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: yes
|
||||
@@ -1,31 +1,11 @@
|
||||
{
|
||||
"description": "Content for the current prerelease",
|
||||
"plugins": [
|
||||
"tiddlywiki/browser-sniff",
|
||||
"tiddlywiki/help",
|
||||
"tiddlywiki/stacked-view",
|
||||
"tiddlywiki/powered-by-tiddlywiki",
|
||||
"tiddlywiki/internals",
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/bibtex",
|
||||
"tiddlywiki/savetrail",
|
||||
"tiddlywiki/external-attachments",
|
||||
"tiddlywiki/dynaview",
|
||||
"tiddlywiki/dynannotate",
|
||||
"tiddlywiki/codemirror",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/jszip"
|
||||
"tiddlywiki/sqlite3store"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
"tiddlywiki/snowwhite",
|
||||
"tiddlywiki/starlight",
|
||||
"tiddlywiki/seamless",
|
||||
"tiddlywiki/centralised",
|
||||
"tiddlywiki/heavier",
|
||||
"tiddlywiki/tight",
|
||||
"tiddlywiki/tight-heavier",
|
||||
"tiddlywiki/readonly"
|
||||
"tiddlywiki/snowwhite"
|
||||
],
|
||||
"languages": [
|
||||
],
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"description": "TiddlyWiki core tests",
|
||||
"plugins": [
|
||||
"tiddlywiki/jasmine"
|
||||
"tiddlywiki/jasmine",
|
||||
"tiddlywiki/sqlite3store"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: yes
|
||||
@@ -1,24 +1,11 @@
|
||||
{
|
||||
"description": "Documentation from https://tiddlywiki.com",
|
||||
"plugins": [
|
||||
"tiddlywiki/nodewebkitsaver",
|
||||
"tiddlywiki/browser-sniff",
|
||||
"tiddlywiki/railroad",
|
||||
"tiddlywiki/evernote",
|
||||
"tiddlywiki/internals",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/qrcode"
|
||||
"tiddlywiki/sqlite3store"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
"tiddlywiki/snowwhite",
|
||||
"tiddlywiki/starlight",
|
||||
"tiddlywiki/seamless",
|
||||
"tiddlywiki/centralised",
|
||||
"tiddlywiki/tight",
|
||||
"tiddlywiki/heavier",
|
||||
"tiddlywiki/tight-heavier",
|
||||
"tiddlywiki/readonly"
|
||||
"tiddlywiki/snowwhite"
|
||||
],
|
||||
"languages": [
|
||||
],
|
||||
|
||||
6
plugins/tiddlywiki/demo-alternate-store/plugin.info
Normal file
6
plugins/tiddlywiki/demo-alternate-store/plugin.info
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"title": "$:/plugins/tiddlywiki/demo-alternate-store",
|
||||
"name": "Demo alternate store",
|
||||
"description": "Developer demo of an alternate wiki store implementation",
|
||||
"list": "readme"
|
||||
}
|
||||
357
plugins/tiddlywiki/demo-alternate-store/rawmarkup.js
Normal file
357
plugins/tiddlywiki/demo-alternate-store/rawmarkup.js
Normal file
@@ -0,0 +1,357 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/demo-alternate-store/rawmarkup.js
|
||||
type: text/plain
|
||||
tags: $:/tags/AlternateStoreArea
|
||||
|
||||
Startup code injected as raw markup
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
// Need to initialise these because we run before bootprefix.js and boot.js
|
||||
$tw = window.$tw || Object.create(null);
|
||||
$tw.hooks = $tw.hooks || { names: {}};
|
||||
$tw.boot = $tw.boot || {};
|
||||
$tw.boot.preloadDirty = $tw.boot.preloadDirty || [];
|
||||
|
||||
$tw.Wiki = function(options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
tiddlers = Object.create(null), // Hashmap of tiddlers
|
||||
getTiddlerTitles = function() {
|
||||
return Object.keys(tiddlers).sort(function(a,b) {return a.localeCompare(b);});
|
||||
},
|
||||
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
|
||||
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
|
||||
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
|
||||
getShadowTiddlerTitles = function() {
|
||||
return Object.keys(shadowTiddlers);
|
||||
};
|
||||
//$tw.utils replacements
|
||||
var eachObj = function(object,callback) {
|
||||
var next,f,length;
|
||||
if(object) {
|
||||
if(Object.prototype.toString.call(object) == "[object Array]") {
|
||||
for (f=0, length=object.length; f<length; f++) {
|
||||
next = callback(object[f],f,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var keys = Object.keys(object);
|
||||
for (f=0, length=keys.length; f<length; f++) {
|
||||
var key = keys[f];
|
||||
next = callback(object[key],key,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hop = function(object,property) {
|
||||
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
|
||||
},
|
||||
insertSortedArray = function(array,value) {
|
||||
var low = 0, high = array.length - 1, mid, cmp;
|
||||
while(low <= high) {
|
||||
mid = (low + high) >> 1;
|
||||
cmp = value.localeCompare(array[mid]);
|
||||
if(cmp > 0) {
|
||||
low = mid + 1;
|
||||
} else if(cmp < 0) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
return array;
|
||||
}
|
||||
}
|
||||
array.splice(low,0,value);
|
||||
return array;
|
||||
},
|
||||
parseJSONSafe = function(text,defaultJSON) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch(e) {
|
||||
if(typeof defaultJSON === "function") {
|
||||
return defaultJSON(e);
|
||||
} else {
|
||||
return defaultJSON || {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.addIndexer = function(indexer,name) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.getIndexer = function(name) {
|
||||
return null;
|
||||
};
|
||||
|
||||
// Add a tiddler to the store
|
||||
this.addTiddler = function(tiddler) {
|
||||
if(!(tiddler instanceof $tw.Tiddler)) {
|
||||
tiddler = new $tw.Tiddler(tiddler);
|
||||
}
|
||||
// Save the tiddler
|
||||
if(tiddler) {
|
||||
var title = tiddler.fields.title;
|
||||
if(title) {
|
||||
// Save the new tiddler
|
||||
tiddlers[title] = tiddler;
|
||||
// Update caches
|
||||
this.clearCache(title);
|
||||
this.clearGlobalCache();
|
||||
// Queue a change event
|
||||
this.enqueueTiddlerEvent(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a tiddler
|
||||
this.deleteTiddler = function(title) {
|
||||
// Uncomment the following line for detailed logs of all tiddler deletions
|
||||
// console.log("Deleting",title)
|
||||
if(hop(tiddlers,title)) {
|
||||
// Delete the tiddler
|
||||
delete tiddlers[title];
|
||||
// Update caches
|
||||
this.clearCache(title);
|
||||
this.clearGlobalCache();
|
||||
// Queue a change event
|
||||
this.enqueueTiddlerEvent(title,true);
|
||||
}
|
||||
};
|
||||
|
||||
// Get a tiddler from the store
|
||||
this.getTiddler = function(title) {
|
||||
if(title) {
|
||||
var t = tiddlers[title];
|
||||
if(t !== undefined) {
|
||||
return t;
|
||||
} else {
|
||||
var s = shadowTiddlers[title];
|
||||
if(s !== undefined) {
|
||||
return s.tiddler;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Get an array of all tiddler titles
|
||||
this.allTitles = function() {
|
||||
return getTiddlerTitles();
|
||||
};
|
||||
|
||||
// Iterate through all tiddler titles
|
||||
this.each = function(callback) {
|
||||
var titles = getTiddlerTitles(),
|
||||
index,titlesLength,title;
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
callback(self.getTiddler(title),title);
|
||||
}
|
||||
};
|
||||
|
||||
// Get an array of all shadow tiddler titles
|
||||
this.allShadowTitles = function() {
|
||||
return getShadowTiddlerTitles();
|
||||
};
|
||||
|
||||
// Iterate through all shadow tiddler titles
|
||||
this.eachShadow = function(callback) {
|
||||
var titles = getShadowTiddlerTitles(),
|
||||
index,titlesLength,title;
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(self.tiddlerExists(title)) {
|
||||
callback(self.getTiddler(title),title);
|
||||
} else {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate through all tiddlers and then the shadows
|
||||
this.eachTiddlerPlusShadows = function(callback) {
|
||||
var index,titlesLength,title,
|
||||
titles = getTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
callback(self.getTiddler(title),title);
|
||||
}
|
||||
titles = getShadowTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(!self.tiddlerExists(title)) {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate through all the shadows and then the tiddlers
|
||||
this.eachShadowPlusTiddlers = function(callback) {
|
||||
var index,titlesLength,title,
|
||||
titles = getShadowTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(self.tiddlerExists(title)) {
|
||||
callback(self.getTiddler(title),title);
|
||||
} else {
|
||||
var shadowInfo = shadowTiddlers[title];
|
||||
callback(shadowInfo.tiddler,title);
|
||||
}
|
||||
}
|
||||
titles = getTiddlerTitles();
|
||||
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
|
||||
title = titles[index];
|
||||
if(!shadowTiddlers[title]) {
|
||||
callback(self.getTiddler(title),title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.tiddlerExists = function(title) {
|
||||
return !!hop(tiddlers,title);
|
||||
};
|
||||
|
||||
this.isShadowTiddler = function(title) {
|
||||
return hop(shadowTiddlers,title);
|
||||
};
|
||||
|
||||
this.getShadowSource = function(title) {
|
||||
if(hop(shadowTiddlers,title)) {
|
||||
return shadowTiddlers[title].source;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Get an array of all the currently recognised plugin types
|
||||
this.getPluginTypes = function() {
|
||||
var types = [];
|
||||
eachObj(pluginTiddlers,function(pluginTiddler) {
|
||||
var pluginType = pluginTiddler.fields["plugin-type"];
|
||||
if(pluginType && types.indexOf(pluginType) === -1) {
|
||||
types.push(pluginType);
|
||||
}
|
||||
});
|
||||
return types;
|
||||
};
|
||||
|
||||
// Read plugin info for all plugins, or just an array of titles. Returns the number of plugins updated or deleted
|
||||
this.readPluginInfo = function(titles) {
|
||||
var results = {
|
||||
modifiedPlugins: [],
|
||||
deletedPlugins: []
|
||||
};
|
||||
eachObj(titles || getTiddlerTitles(),function(title) {
|
||||
var tiddler = self.getTiddler(title);
|
||||
if(tiddler) {
|
||||
if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type") && tiddler.fields.text) {
|
||||
pluginInfo[tiddler.fields.title] = parseJSONSafe(tiddler.fields.text);
|
||||
results.modifiedPlugins.push(tiddler.fields.title);
|
||||
}
|
||||
} else {
|
||||
if(pluginInfo[title]) {
|
||||
delete pluginInfo[title];
|
||||
results.deletedPlugins.push(title);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Get plugin info for a plugin
|
||||
this.getPluginInfo = function(title) {
|
||||
return pluginInfo[title];
|
||||
};
|
||||
|
||||
// Register the plugin tiddlers of a particular type, or null/undefined for any 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,title) {
|
||||
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] && (!pluginType || tiddler.fields["plugin-type"] === pluginType)) {
|
||||
var disablingTiddler = self.getTiddler("$:/config/Plugins/Disabled/" + title);
|
||||
if(title === "$:/core" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
|
||||
self.unregisterPluginTiddlers(null,[title]); // Unregister the plugin if it's already registered
|
||||
pluginTiddlers.push(tiddler);
|
||||
registeredTitles.push(tiddler.fields.title);
|
||||
}
|
||||
}
|
||||
};
|
||||
if(titles) {
|
||||
eachObj(titles,function(title) {
|
||||
checkTiddler(self.getTiddler(title),title);
|
||||
});
|
||||
} else {
|
||||
this.each(function(tiddler,title) {
|
||||
checkTiddler(tiddler,title);
|
||||
});
|
||||
}
|
||||
return registeredTitles;
|
||||
};
|
||||
|
||||
// Unregister the plugin tiddlers of a particular type, or null/undefined for any type, optionally restricting unregistering to an array of tiddler titles. Returns an array of the titles affected
|
||||
this.unregisterPluginTiddlers = function(pluginType,titles) {
|
||||
var self = this,
|
||||
unregisteredTitles = [];
|
||||
// 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 || tiddler.fields["plugin-type"] === pluginType) && (!titles || titles.indexOf(tiddler.fields.title) !== -1)) {
|
||||
unregisteredTitles.push(tiddler.fields.title);
|
||||
pluginTiddlers.splice(t,1);
|
||||
}
|
||||
}
|
||||
return unregisteredTitles;
|
||||
};
|
||||
|
||||
// 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 = Object.create(null);
|
||||
eachObj(pluginTiddlers,function(tiddler) {
|
||||
// Extract the constituent tiddlers
|
||||
if(hop(pluginInfo,tiddler.fields.title)) {
|
||||
eachObj(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})
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
shadowTiddlerTitles = null;
|
||||
this.clearCache(null);
|
||||
this.clearGlobalCache();
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/demo-alternate-store/rawmarkup.js
|
||||
6
plugins/tiddlywiki/demo-alternate-store/rawmarkup.tid
Normal file
6
plugins/tiddlywiki/demo-alternate-store/rawmarkup.tid
Normal file
@@ -0,0 +1,6 @@
|
||||
title: $:/plugins/tiddlywiki/demo-alternate-store/rawmarkup
|
||||
tags: $:/tags/RawMarkupWikified
|
||||
|
||||
`<script>`
|
||||
{{$:/plugins/tiddlywiki/demo-alternate-store/rawmarkup.js}}
|
||||
`</script>`
|
||||
3
plugins/tiddlywiki/demo-alternate-store/readme.tid
Normal file
3
plugins/tiddlywiki/demo-alternate-store/readme.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/demo-alternate-store/readme
|
||||
|
||||
Developer demo of an alternate wiki store implementation
|
||||
11806
plugins/tiddlywiki/sqlite3store/files/sqlite3.js
Normal file
11806
plugins/tiddlywiki/sqlite3store/files/sqlite3.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/tiddlywiki/sqlite3store/files/sqlite3.wasm
Normal file
BIN
plugins/tiddlywiki/sqlite3store/files/sqlite3.wasm
Normal file
Binary file not shown.
19
plugins/tiddlywiki/sqlite3store/files/tiddlywiki.files
Executable file
19
plugins/tiddlywiki/sqlite3store/files/tiddlywiki.files
Executable file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"tiddlers": [
|
||||
{
|
||||
"file": "sqlite3.js",
|
||||
"fields": {
|
||||
"type": "text/plain",
|
||||
"title": "$:/plugins/tiddlywiki/sqlite3store/sqlite3.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "sqlite3.wasm",
|
||||
"encoding": "base64",
|
||||
"fields": {
|
||||
"type": "application/wasm",
|
||||
"title": "$:/plugins/tiddlywiki/sqlite3store/sqlite3.wasm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
66
plugins/tiddlywiki/sqlite3store/init-sqlite3.js
Normal file
66
plugins/tiddlywiki/sqlite3store/init-sqlite3.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/init-sqlite3.js
|
||||
type: application/javascript
|
||||
|
||||
Initialise sqlite3 and then boot TiddlyWiki
|
||||
|
||||
This file is spliced into the HTML file to be executed after the boot kernel has been loaded.
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
// Get the main tiddler store out of the HTML file
|
||||
var storeEl = document.querySelector("script.tiddlywiki-tiddler-store"),
|
||||
tiddlerStore = JSON.parse(storeEl.textContent);
|
||||
|
||||
// Helper to get a tiddler from the store by title
|
||||
function getTiddler(title) {
|
||||
for(var t=0; t<tiddlerStore.length; t++) {
|
||||
var tiddler = tiddlerStore[t];
|
||||
if(tiddler.title === title) {
|
||||
return tiddler;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Get the shadow tiddlers of this plugin
|
||||
var thisPlugin = getTiddler("$:/plugins/tiddlywiki/sqlite3store"),
|
||||
thisPluginTiddlers = JSON.parse(thisPlugin.text).tiddlers;
|
||||
// Execute the sqlite3 module
|
||||
var sqlite3js = thisPluginTiddlers["$:/plugins/tiddlywiki/sqlite3store/sqlite3.js"].text,
|
||||
context = {
|
||||
exports: {}
|
||||
};
|
||||
$tw.utils.evalSandboxed(sqlite3js,context,"$:/plugins/tiddlywiki/sqlite3store/sqlite3.js",true);
|
||||
// Create a Blob URL for the wasm data
|
||||
var sqlite3wasm = thisPluginTiddlers["$:/plugins/tiddlywiki/sqlite3store/sqlite3.wasm"].text;
|
||||
var decodedData = window.atob(sqlite3wasm),
|
||||
uInt8Array = new Uint8Array(decodedData.length);
|
||||
for (var i = 0; i < decodedData.length; ++i) {
|
||||
uInt8Array[i] = decodedData.charCodeAt(i);
|
||||
}
|
||||
var blobUrl = URL.createObjectURL(new Blob([uInt8Array],{type: "application/wasm"}));
|
||||
// Pass sqlite an URLSearchParams object containing the Blob URL of our wasm data
|
||||
self.sqlite3InitModuleState.urlParams = new URLSearchParams();
|
||||
self.sqlite3InitModuleState.urlParams.set("sqlite3.wasm",blobUrl);
|
||||
// Initialise sqlite
|
||||
self.sqlite3InitModule().then((sqlite3)=>{
|
||||
// Save a reference to the sqlite3 object
|
||||
$tw.sqlite3 = sqlite3;
|
||||
var capi = $tw.sqlite3.capi, // C-style API
|
||||
oo = $tw.sqlite3.oo1; // High-level OO API
|
||||
// Boot the console
|
||||
$tw.sqlConsole = new $tw.SqlConsole();
|
||||
// Get version numbers
|
||||
console.log("sqlite3 version",capi.sqlite3_libversion());
|
||||
// Run tests
|
||||
if($tw.testSqlFunctions) {
|
||||
$tw.testSqlFunctions();
|
||||
}
|
||||
// Boot TiddlyWiki
|
||||
$tw.boot.boot();
|
||||
});
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/init-sqlite3.js
|
||||
6
plugins/tiddlywiki/sqlite3store/plugin.info
Normal file
6
plugins/tiddlywiki/sqlite3store/plugin.info
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"title": "$:/plugins/tiddlywiki/sqlite3store",
|
||||
"name": "Sqlite3-based store",
|
||||
"description": "Sqlite3-based wiki store implementation",
|
||||
"list": "readme"
|
||||
}
|
||||
7
plugins/tiddlywiki/sqlite3store/rawmarkup-bottombody.tid
Normal file
7
plugins/tiddlywiki/sqlite3store/rawmarkup-bottombody.tid
Normal file
@@ -0,0 +1,7 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/rawmarkup/bottombody
|
||||
tags: $:/tags/RawMarkupWikified/BottomBody
|
||||
|
||||
`<script>`
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-console.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/init-sqlite3.js}}
|
||||
`</script>`
|
||||
13
plugins/tiddlywiki/sqlite3store/rawmarkup-bottomhead.tid
Normal file
13
plugins/tiddlywiki/sqlite3store/rawmarkup-bottomhead.tid
Normal file
@@ -0,0 +1,13 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/rawmarkup-bottomhead
|
||||
tags: $:/tags/RawMarkupWikified
|
||||
|
||||
`<style>`
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-console/styles}}
|
||||
`</style>
|
||||
<script>`
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/suppress-boot.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-functions.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-wiki-store.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-filter-compiler.js}}
|
||||
`</script>`
|
||||
3
plugins/tiddlywiki/sqlite3store/readme.tid
Normal file
3
plugins/tiddlywiki/sqlite3store/readme.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/readme
|
||||
|
||||
Wiki store implementation based on sqlite3 WASM build.
|
||||
20
plugins/tiddlywiki/sqlite3store/sql-console-styles.tid
Normal file
20
plugins/tiddlywiki/sqlite3store/sql-console-styles.tid
Normal file
@@ -0,0 +1,20 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/sql-console/styles
|
||||
code-body: yes
|
||||
|
||||
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
|
||||
|
||||
.sql-console * {
|
||||
}
|
||||
|
||||
.sql-console-input,
|
||||
.sql-console-output-input {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background: #222222;
|
||||
color: #00ff00;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
94
plugins/tiddlywiki/sqlite3store/sql-console.js
Normal file
94
plugins/tiddlywiki/sqlite3store/sql-console.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/sql-console.js
|
||||
type: application/javascript
|
||||
|
||||
SQL console for debugging
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
$tw.SqlConsole = function SqlConsole() {
|
||||
var self = this,
|
||||
dm = $tw.utils.domMaker;
|
||||
// Input box
|
||||
this.consoleInput = dm("textarea",{
|
||||
"class": "sql-console-input",
|
||||
attributes: {
|
||||
"rows": "10"
|
||||
}
|
||||
});
|
||||
// Run button
|
||||
this.consoleRunButton = dm("button",{
|
||||
text: "run sql"
|
||||
});
|
||||
this.consoleRunButton.addEventListener("click",this.runQuery.bind(this));
|
||||
// Clear output button
|
||||
this.consoleClearButton = dm("button",{
|
||||
text: "clear output"
|
||||
});
|
||||
this.consoleClearButton.addEventListener("click",this.clearOutput.bind(this));
|
||||
// Output
|
||||
this.consoleOutput = dm("div",{
|
||||
"class": "sql-console-output-container"
|
||||
});
|
||||
// Container
|
||||
this.consoleContainer = dm("div",{
|
||||
"class": "sql-console",
|
||||
children: [
|
||||
document.createTextNode("console for sqlite3"),
|
||||
this.consoleInput,
|
||||
this.consoleRunButton,
|
||||
this.consoleClearButton,
|
||||
this.consoleOutput
|
||||
]
|
||||
});
|
||||
// Insert into DOM
|
||||
document.body.insertBefore(this.consoleContainer,document.body.firstChild);
|
||||
};
|
||||
|
||||
$tw.SqlConsole.prototype.clearOutput = function() {
|
||||
while(this.consoleOutput.firstChild) {
|
||||
this.consoleOutput.removeChild(this.consoleOutput.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
$tw.SqlConsole.prototype.runQuery = function() {
|
||||
var self = this,
|
||||
dm = $tw.utils.domMaker,
|
||||
sql = this.consoleInput.value,
|
||||
resultRows = [],
|
||||
exception;
|
||||
// Execute the query
|
||||
try {
|
||||
$tw.wiki.sqlFunctions.db.exec({
|
||||
sql: sql,
|
||||
rowMode: "object",
|
||||
resultRows: resultRows
|
||||
});
|
||||
} catch(e) {
|
||||
exception = e.toString();
|
||||
}
|
||||
// Display the result
|
||||
var output = dm("div",{
|
||||
"class": "sql-console-output",
|
||||
children: [
|
||||
dm("div",{
|
||||
"class": "sql-console-output-input",
|
||||
text: sql
|
||||
}),
|
||||
dm("div",{
|
||||
"class": "sql-console-output-count",
|
||||
text: "" + resultRows.length
|
||||
}),
|
||||
dm("div",{
|
||||
"class": "sql-console-output-output",
|
||||
text: exception || JSON.stringify(resultRows)
|
||||
})
|
||||
]
|
||||
});
|
||||
this.consoleOutput.insertBefore(output,this.consoleOutput.firstChild);
|
||||
};
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/sql-console.js
|
||||
26
plugins/tiddlywiki/sqlite3store/sql-filter-compiler.js
Normal file
26
plugins/tiddlywiki/sqlite3store/sql-filter-compiler.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/sql-filter-compiler.js
|
||||
type: application/javascript
|
||||
|
||||
A sqlite3 implementation of a wiki store object
|
||||
|
||||
This file is spliced into the HTML file to be executed before the boot kernel has been loaded.
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
/*
|
||||
If possible, return a filter evaluation function with the signature (source,widget) that executes the provided filter parse tree
|
||||
*/
|
||||
$tw.Wiki.prototype.optimiseFilter = function(filterString,filterParseTree) {
|
||||
// switch($tw.utils.trim(filterString)) {
|
||||
// case "[all[shadows+tiddlers]prefix[$:/language/Docs/Types/]get[name]length[]maxall[]]":
|
||||
// return [this.sqlFunctions.sqlQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength()];
|
||||
// break;
|
||||
// }
|
||||
return undefined;
|
||||
};
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/sql-filter-compiler.js
|
||||
475
plugins/tiddlywiki/sqlite3store/sql-functions.js
Normal file
475
plugins/tiddlywiki/sqlite3store/sql-functions.js
Normal file
@@ -0,0 +1,475 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/sql-functions.js
|
||||
type: application/javascript
|
||||
|
||||
Functions to perform basic tiddler operations with a sqlite3 database
|
||||
|
||||
This file is spliced into the HTML file to be executed before the boot kernel has been loaded.
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
$tw.SqlFunctions = function(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
// Setting useCustomCollation to true allows the tests to pass (run `tiddlywiki editions/test/ --build index`)
|
||||
// - but it takes 6 times longer to boot the prerelease than with useCustomCollation set to false
|
||||
var useCustomCollation = false;
|
||||
var COLLATION_CLAUSE = useCustomCollation ? "COLLATE custom_collation" : "";
|
||||
// Create anonymous database
|
||||
this.db = new $tw.sqlite3.oo1.DB("","c");
|
||||
// Setup custom collation to precisely match existing sort orders
|
||||
// Create field with `title TEXT NOT NULL COLLATE custom_collation`
|
||||
// Use it like `... order by shadow collate custom_collation`
|
||||
if(useCustomCollation) {
|
||||
function customCollation(ptr,lenA,a,lenB,b) {
|
||||
// There may be a problem here: lenA and lenB are the lengths of the two UTF8 strings in bytes,
|
||||
// and yet we're using them with JS slice() method which counts in characters
|
||||
var jsA = $tw.sqlite3.wasm.cstrToJs(a).slice(0,lenA),
|
||||
jsB = $tw.sqlite3.wasm.cstrToJs(b).slice(0,lenB);
|
||||
return jsA.localeCompare(jsB);
|
||||
}
|
||||
var SQLITE_UTF8 = 1; /* IMP: R-37514-35566 */
|
||||
var SQLITE_UTF16LE = 2; /* IMP: R-03371-37637 */
|
||||
var SQLITE_UTF16BE = 3; /* IMP: R-51971-34154 */
|
||||
var SQLITE_UTF16 = 4; /* Use native byte order */
|
||||
var SQLITE_ANY = 5; /* Deprecated */
|
||||
var SQLITE_UTF16_ALIGNED = 8; /* sqlite3_create_collation only */
|
||||
var collationResult = $tw.sqlite3.capi.sqlite3_create_collation_v2(this.db.pointer,"custom_collation",SQLITE_UTF8,this,customCollation,0);
|
||||
}
|
||||
/*
|
||||
Create tables and indexes
|
||||
*/
|
||||
self.db.exec({
|
||||
sql: `
|
||||
DROP TABLE IF EXISTS plugins;
|
||||
CREATE TABLE plugins (
|
||||
plugintitle TEXT NOT NULL, -- Empty string shoud be the highest priority
|
||||
priority INTEGER NOT NULL,
|
||||
PRIMARY KEY(plugintitle)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS plugins_plugintitle_index ON plugins(plugintitle);
|
||||
DROP TABLE IF EXISTS tiddlers;
|
||||
CREATE TABLE tiddlers (
|
||||
title TEXT NOT NULL ${COLLATION_CLAUSE},
|
||||
plugintitle TEXT NOT NULL, -- Empty string for tiddlers that are not part of a plugin
|
||||
meta TEXT NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
PRIMARY KEY(title,plugintitle)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tiddlers_title_index ON tiddlers(title);
|
||||
CREATE INDEX IF NOT EXISTS tiddlers_plugintitle_index ON tiddlers(plugintitle);
|
||||
DROP TABLE IF EXISTS titles;
|
||||
CREATE TABLE titles (
|
||||
title TEXT NOT NULL ${COLLATION_CLAUSE},
|
||||
plugintitle TEXT NOT NULL, -- Empty string for tiddlers that are not part of a plugin
|
||||
PRIMARY KEY(title)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS titles_title_index ON titles(title);
|
||||
CREATE INDEX IF NOT EXISTS titles_plugintitle_index ON titles(plugintitle);
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
tag_id INTEGER PRIMARY KEY,
|
||||
tag TEXT NOT NULL
|
||||
);
|
||||
DROP TABLE IF EXISTS tiddler_tags;
|
||||
CREATE TABLE tiddler_tags (
|
||||
title TEXT NOT NULL,
|
||||
plugintitle INTEGER NOT NULL,
|
||||
tag_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (title, plugintitle) REFERENCES tiddlers (title, plugintitle) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (tag_id) REFERENCES tags (tag_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY (title, plugintitle, tag_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tiddler_tags_title_index ON tiddler_tags(title);
|
||||
CREATE INDEX IF NOT EXISTS tiddler_tags_plugintitle_index ON tiddler_tags(plugintitle);
|
||||
CREATE INDEX IF NOT EXISTS tiddler_tags_tag_id_index ON tiddler_tags(tag_id);
|
||||
`
|
||||
});
|
||||
/*
|
||||
Debugging
|
||||
*/
|
||||
var statementLogTiddlersTable = self.db.prepare("select title, plugintitle, meta, text from tiddlers order by title, plugintitle;"),
|
||||
statementLogPluginsTable = self.db.prepare("select plugintitle, priority from plugins order by plugintitle;"),
|
||||
statementLogTitlesTable = self.db.prepare("select title, plugintitle from titles order by title;"),
|
||||
statementLogTagsTable = self.db.prepare("select tag_id, tag from tags order by tag_id;"),
|
||||
statementLogTiddlerTagsTable = self.db.prepare("select title, plugintitle, tag_id from tiddler_tags order by title, plugintitle;");
|
||||
function sqlLogTable(statement) {
|
||||
let resultRows = [];
|
||||
while(statement.step()) {
|
||||
var row = statement.get({});
|
||||
resultRows.push(row);
|
||||
}
|
||||
statement.reset();
|
||||
return resultRows;
|
||||
}
|
||||
this.sqlLogTables = function() {
|
||||
console.log("tiddlers",sqlLogTable(statementLogTiddlersTable));
|
||||
console.log("plugins",sqlLogTable(statementLogPluginsTable));
|
||||
console.log("titles",sqlLogTable(statementLogTitlesTable));
|
||||
console.log("tags",sqlLogTable(statementLogTagsTable));
|
||||
console.log("tiddlertags",sqlLogTable(statementLogTiddlerTagsTable));
|
||||
};
|
||||
/*
|
||||
Set the plugin priorities
|
||||
*/
|
||||
this.sqlSetPluginPriorities = function(prioritisedPluginTitles) {
|
||||
const plugintitles = prioritisedPluginTitles.concat([""]);
|
||||
self.db.exec({
|
||||
sql: "DELETE FROM plugins"
|
||||
});
|
||||
let priority = 1;
|
||||
for(const plugintitle of plugintitles) {
|
||||
self.db.exec({
|
||||
sql: "insert or replace into plugins (plugintitle, priority) values ($plugintitle, $priority)",
|
||||
bind: {
|
||||
$plugintitle: plugintitle,
|
||||
$priority: priority++
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
/*
|
||||
Save a tiddler
|
||||
*/
|
||||
var querySaveTiddlerTableTiddlers = self.db.prepare(`
|
||||
-- Insert the new tiddler into the tiddlers table
|
||||
INSERT OR REPLACE INTO tiddlers (title, plugintitle, meta, text)
|
||||
VALUES ($title, $plugintitle, $meta, $text);
|
||||
`);
|
||||
var querySaveTiddlerTableTitles = self.db.prepare(`
|
||||
-- Insert the new title into the titles table
|
||||
INSERT OR REPLACE INTO titles (title, plugintitle)
|
||||
SELECT
|
||||
t.title,
|
||||
(SELECT t2.plugintitle
|
||||
FROM tiddlers AS t2
|
||||
JOIN plugins AS p ON t2.plugintitle = p.plugintitle
|
||||
WHERE t2.title = t.title
|
||||
ORDER BY p.priority DESC
|
||||
LIMIT 1
|
||||
) AS plugintitle
|
||||
FROM tiddlers AS t
|
||||
WHERE t.title = $title;
|
||||
`);
|
||||
var querySaveTiddlerTableTags = self.db.prepare(`
|
||||
-- Parse and insert tags from the $tags JSON array
|
||||
WITH tag_values AS (
|
||||
SELECT json_each.value AS tag
|
||||
FROM json_each($tags)
|
||||
)
|
||||
INSERT INTO tags (tag)
|
||||
SELECT DISTINCT tag
|
||||
FROM tag_values
|
||||
WHERE tag NOT IN (
|
||||
SELECT tag
|
||||
FROM tags
|
||||
);
|
||||
`);
|
||||
var querySaveTiddlerTableTiddlerTags = self.db.prepare(`
|
||||
-- Associate the new tiddler with the tags in the tiddler_tags table
|
||||
WITH tag_values AS (
|
||||
SELECT json_each.value AS tag
|
||||
FROM json_each($tags)
|
||||
)
|
||||
INSERT OR IGNORE INTO tiddler_tags (title, plugintitle, tag_id)
|
||||
SELECT $title, $plugintitle, tags.tag_id
|
||||
FROM tag_values
|
||||
JOIN tags ON tag_values.tag = tags.tag;
|
||||
`);
|
||||
this.sqlSaveTiddler = function(tiddlerFields,plugintitle) {
|
||||
plugintitle = plugintitle || "";
|
||||
// Normalise the tags by removing any double square brackets
|
||||
let tags = tiddlerFields.tags;
|
||||
if(typeof tags === "string") {
|
||||
tags = $tw.utils.parseStringArray(tags);
|
||||
}
|
||||
const normalisedTags = (tags || []).map(tag => {
|
||||
const match = /^[^\S\xA0]*\[\[(.*)\]\][^\S\xA0]*$/mg.exec(tag);
|
||||
if(match) {
|
||||
return match[1];
|
||||
} else {
|
||||
return tag;
|
||||
}
|
||||
});
|
||||
const jsonNormalisedTags = JSON.stringify(normalisedTags);
|
||||
querySaveTiddlerTableTiddlers.bind({
|
||||
$title: tiddlerFields.title,
|
||||
$plugintitle: plugintitle,
|
||||
$meta: JSON.stringify(Object.assign({},tiddlerFields,{title: undefined, text: undefined})),
|
||||
$text: tiddlerFields.text || ""
|
||||
});
|
||||
querySaveTiddlerTableTiddlers.step();
|
||||
querySaveTiddlerTableTiddlers.reset();
|
||||
querySaveTiddlerTableTitles.bind({
|
||||
$title: tiddlerFields.title,
|
||||
});
|
||||
querySaveTiddlerTableTitles.step();
|
||||
querySaveTiddlerTableTitles.reset();
|
||||
querySaveTiddlerTableTags.bind({
|
||||
$tags: jsonNormalisedTags
|
||||
});
|
||||
querySaveTiddlerTableTags.step();
|
||||
querySaveTiddlerTableTags.reset();
|
||||
querySaveTiddlerTableTiddlerTags.bind({
|
||||
$title: tiddlerFields.title,
|
||||
$plugintitle: plugintitle || "",
|
||||
$tags: jsonNormalisedTags
|
||||
});
|
||||
querySaveTiddlerTableTiddlerTags.step();
|
||||
querySaveTiddlerTableTiddlerTags.reset();
|
||||
};
|
||||
/*
|
||||
Delete a tiddler
|
||||
*/
|
||||
var statementDeleteTiddlerDeleteFromTiddlers = self.db.prepare(`
|
||||
DELETE FROM tiddlers
|
||||
WHERE title = $title AND plugintitle = $plugintitle;
|
||||
`);
|
||||
var statementDeleteTiddlerFindShadow = self.db.prepare(`
|
||||
SELECT t.title, t.plugintitle
|
||||
FROM tiddlers AS t
|
||||
JOIN plugins AS p ON t.plugintitle = p.plugintitle
|
||||
WHERE t.title = $title
|
||||
ORDER BY p.priority DESC
|
||||
LIMIT 1;
|
||||
`);
|
||||
this.sqlDeleteTiddler = function(title,plugintitle) {
|
||||
plugintitle = plugintitle || "";
|
||||
// Delete the tiddler from the tiddlers table
|
||||
statementDeleteTiddlerDeleteFromTiddlers.bind({
|
||||
$title: title,
|
||||
$plugintitle: plugintitle
|
||||
});
|
||||
statementDeleteTiddlerDeleteFromTiddlers.step();
|
||||
statementDeleteTiddlerDeleteFromTiddlers.reset();
|
||||
// Find any corresponding shadow tiddler
|
||||
statementDeleteTiddlerFindShadow.bind({
|
||||
$title: title
|
||||
});
|
||||
if(statementDeleteTiddlerFindShadow.step()) {
|
||||
var row = statementDeleteTiddlerFindShadow.get({});
|
||||
statementDeleteTiddlerFindShadow.reset();
|
||||
// Replace the tiddler with the shadow
|
||||
self.db.exec({
|
||||
sql: "insert or replace into titles (title, plugintitle) values ($title, $plugintitle)",
|
||||
bind: {
|
||||
$title: title,
|
||||
$plugintitle: row.plugintitle
|
||||
}
|
||||
});
|
||||
} else {
|
||||
statementDeleteTiddlerFindShadow.reset();
|
||||
// There is no shadow tiddler, so just delete the tiddler
|
||||
self.db.exec({
|
||||
sql: "delete from titles where title = $title",
|
||||
bind: {
|
||||
$title: title
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
/*
|
||||
Remove all shadow tiddlers
|
||||
*/
|
||||
this.sqlClearShadows = function() {
|
||||
self.db.exec({
|
||||
sql: "delete from tiddlers where plugintitle != '';"
|
||||
});
|
||||
self.db.exec({
|
||||
sql: "delete from titles where plugintitle != '';"
|
||||
});
|
||||
};
|
||||
/*
|
||||
Check whether a tiddler exists
|
||||
*/
|
||||
var statementTiddlerExists = self.db.prepare(`select title from titles where title = $title and plugintitle = '';`)
|
||||
this.sqlTiddlerExists = function(title) {
|
||||
statementTiddlerExists.bind({
|
||||
$title: title
|
||||
});
|
||||
if(statementTiddlerExists.step()) {
|
||||
statementTiddlerExists.reset();
|
||||
return true;
|
||||
} else {
|
||||
statementTiddlerExists.reset();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
/*
|
||||
Get the value of a tiddler
|
||||
*/
|
||||
var statementGetTiddler = self.db.prepare(`
|
||||
select t.title, ti.meta, ti.text
|
||||
FROM titles AS t
|
||||
JOIN tiddlers AS ti
|
||||
ON t.title = ti.title AND t.plugintitle = ti.plugintitle
|
||||
WHERE t.title = $title;
|
||||
`);
|
||||
this.sqlGetTiddler = function(title) {
|
||||
statementGetTiddler.bind({
|
||||
$title: title
|
||||
});
|
||||
if(statementGetTiddler.step()) {
|
||||
var row = statementGetTiddler.get({});
|
||||
statementGetTiddler.reset();
|
||||
return Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
|
||||
} else {
|
||||
statementGetTiddler.reset();
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
/*
|
||||
Get the plugin from which a tiddler came
|
||||
*/
|
||||
var statementGetShadowSource = self.db.prepare(`
|
||||
SELECT t.title, t.plugintitle
|
||||
FROM tiddlers AS t
|
||||
JOIN plugins AS p ON t.plugintitle = p.plugintitle
|
||||
WHERE t.title = $title AND t.plugintitle <> ''
|
||||
ORDER BY p.priority DESC
|
||||
LIMIT 1;
|
||||
`);
|
||||
this.sqlGetShadowSource = function(title) {
|
||||
statementGetShadowSource.bind({
|
||||
$title: title
|
||||
});
|
||||
if(statementGetShadowSource.step()) {
|
||||
var row = statementGetShadowSource.get({});
|
||||
statementGetShadowSource.reset();
|
||||
return row.plugintitle;
|
||||
} else {
|
||||
statementGetShadowSource.reset();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
/*
|
||||
Get all titles
|
||||
*/
|
||||
var statementAllTitles = self.db.prepare(`select title from titles where plugintitle = '' order by title ${COLLATION_CLAUSE}`);
|
||||
this.sqlAllTitles = function() {
|
||||
let resultRows = [];
|
||||
while(statementAllTitles.step()) {
|
||||
var row = statementAllTitles.get({});
|
||||
resultRows.push(row.title);
|
||||
}
|
||||
statementAllTitles.reset();
|
||||
return resultRows;
|
||||
};
|
||||
/*
|
||||
All shadow titles
|
||||
*/
|
||||
var statementAllShadowTitles = self.db.prepare(`
|
||||
SELECT title
|
||||
FROM tiddlers
|
||||
WHERE plugintitle != ''
|
||||
ORDER BY title ${COLLATION_CLAUSE}
|
||||
`);
|
||||
this.sqlAllShadowTitles = function() {
|
||||
let resultRows = [];
|
||||
while(statementAllShadowTitles.step()) {
|
||||
var row = statementAllShadowTitles.get({});
|
||||
resultRows.push(row.title);
|
||||
}
|
||||
statementAllShadowTitles.reset();
|
||||
return resultRows;
|
||||
};
|
||||
/*
|
||||
Iterate through each tiddler
|
||||
*/
|
||||
var statementEachTiddler = self.db.prepare(`
|
||||
SELECT t.title, ti.meta, ti.text
|
||||
FROM titles AS t
|
||||
JOIN tiddlers AS ti ON t.title = ti.title AND t.plugintitle = ti.plugintitle
|
||||
WHERE t.plugintitle == ''
|
||||
ORDER BY t.title ${COLLATION_CLAUSE}
|
||||
`);
|
||||
this.sqlEachTiddler = function(callback) {
|
||||
while(statementEachTiddler.step()) {
|
||||
var row = statementEachTiddler.get({}),
|
||||
tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
|
||||
callback(tiddlerFields,row.title);
|
||||
}
|
||||
statementEachTiddler.reset();
|
||||
};
|
||||
/*
|
||||
Iterate through each tiddler that is a shadow (including overridden shadows)
|
||||
*/
|
||||
var statementEachShadowTiddler = self.db.prepare(`
|
||||
SELECT DISTINCT t.title, td.meta, td.text
|
||||
FROM titles AS t
|
||||
JOIN tiddlers AS td ON t.title = td.title
|
||||
WHERE td.plugintitle != ''
|
||||
ORDER BY t.title ${COLLATION_CLAUSE};
|
||||
`);
|
||||
this.sqlEachShadowTiddler = function(callback) {
|
||||
while(statementEachShadowTiddler.step()) {
|
||||
var row = statementEachShadowTiddler.get({});
|
||||
var tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
|
||||
callback(tiddlerFields,row.title);
|
||||
}
|
||||
statementEachShadowTiddler.reset();
|
||||
};
|
||||
/*
|
||||
Iterate all tiddlers, and then the shadows
|
||||
*/
|
||||
this.sqlEachTiddlerPlusShadows = function(callback) {
|
||||
const titles = Object.create(null);
|
||||
self.sqlEachTiddler(function(fields,title) {
|
||||
titles[title] = true;
|
||||
callback(fields,title);
|
||||
});
|
||||
self.sqlEachShadowTiddler(function(fields,title) {
|
||||
if(!titles[title]) {
|
||||
callback(fields,title);
|
||||
}
|
||||
});
|
||||
};
|
||||
/*
|
||||
Iterate all shadows, and then the tiddlers
|
||||
*/
|
||||
this.sqlEachShadowPlusTiddlers = function(callback) {
|
||||
const titles = Object.create(null);
|
||||
self.sqlEachShadowTiddler(function(fields,title) {
|
||||
titles[title] = true;
|
||||
callback(fields,title);
|
||||
});
|
||||
self.sqlEachTiddler(function(fields,title) {
|
||||
if(!titles[title]) {
|
||||
callback(fields,title);
|
||||
}
|
||||
});
|
||||
};
|
||||
/*
|
||||
Return all tiddlers with a given tag
|
||||
*/
|
||||
var statementGetTiddlersWithTag = self.db.prepare(`
|
||||
SELECT titles.title
|
||||
FROM titles
|
||||
JOIN tiddlers ON titles.title = tiddlers.title AND titles.plugintitle = tiddlers.plugintitle
|
||||
JOIN plugins ON titles.plugintitle = plugins.plugintitle
|
||||
JOIN tiddler_tags ON tiddlers.title = tiddler_tags.title AND tiddlers.plugintitle = tiddler_tags.plugintitle
|
||||
JOIN tags ON tiddler_tags.tag_id = tags.tag_id
|
||||
WHERE tags.tag = $tag
|
||||
ORDER BY CASE
|
||||
WHEN titles.plugintitle <> '' THEN 1
|
||||
ELSE 2
|
||||
END, titles.title ${COLLATION_CLAUSE} ASC;
|
||||
`);
|
||||
this.sqlGetTiddlersWithTag = function(tag,method) {
|
||||
statementGetTiddlersWithTag.bind({
|
||||
$tag: tag
|
||||
});
|
||||
var resultRows = [];
|
||||
while(statementGetTiddlersWithTag.step()) {
|
||||
var row = statementGetTiddlersWithTag.get({});
|
||||
resultRows.push(row.title);
|
||||
}
|
||||
statementGetTiddlersWithTag.reset();
|
||||
return resultRows;
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/sql-functions.js
|
||||
397
plugins/tiddlywiki/sqlite3store/sql-wiki-store.js
Normal file
397
plugins/tiddlywiki/sqlite3store/sql-wiki-store.js
Normal file
@@ -0,0 +1,397 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/sql-wiki-store.js
|
||||
type: application/javascript
|
||||
|
||||
A sqlite3 implementation of a wiki store object
|
||||
|
||||
This file is spliced into the HTML file to be executed before the boot kernel has been loaded.
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
$tw.Wiki = function(options) {
|
||||
options = options || {};
|
||||
this.sqlFunctions = new $tw.SqlFunctions();
|
||||
this.sqlFunctions.sqlSetPluginPriorities([]);
|
||||
// Adapted version of the boot.js wiki store implementation follows
|
||||
var self = this,
|
||||
cachedTiddlerTitles = null;
|
||||
getTiddlerTitles = function() {
|
||||
if(!cachedTiddlerTitles) {
|
||||
cachedTiddlerTitles = self.sqlFunctions.sqlAllTitles();
|
||||
}
|
||||
return cachedTiddlerTitles;
|
||||
},
|
||||
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
|
||||
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
|
||||
cachedShadowTiddlerTitles = null;
|
||||
getShadowTiddlerTitles = function() {
|
||||
if(!cachedShadowTiddlerTitles) {
|
||||
cachedShadowTiddlerTitles = self.sqlFunctions.sqlAllShadowTitles();
|
||||
}
|
||||
return cachedShadowTiddlerTitles;
|
||||
},
|
||||
enableIndexers = options.enableIndexers || null,
|
||||
indexers = [],
|
||||
indexersByName = Object.create(null);
|
||||
|
||||
this.clearAllCaches = function(title) {
|
||||
cachedTiddlerTitles = null;
|
||||
cachedShadowTiddlerTitles = null;
|
||||
if(title !== undefined) {
|
||||
this.clearCache(title);
|
||||
}
|
||||
this.clearGlobalCache();
|
||||
$tw.utils.each(indexers,function(indexer) {
|
||||
indexer.update();
|
||||
});
|
||||
};
|
||||
|
||||
this.addIndexer = function(indexer,name) {
|
||||
// We stub out this method because this store doesn't support external indexers
|
||||
};
|
||||
|
||||
this.addInternalIndexer = function(indexer,name) {
|
||||
// Bail if this indexer is not enabled
|
||||
if(enableIndexers && enableIndexers.indexOf(name) === -1) {
|
||||
return;
|
||||
}
|
||||
console.log("Added indexer",name)
|
||||
indexers.push(indexer);
|
||||
indexersByName[name] = indexer;
|
||||
indexer.init();
|
||||
};
|
||||
|
||||
this.getIndexer = function(name) {
|
||||
return indexersByName[name] || null;
|
||||
};
|
||||
|
||||
// $tw.utils replacements
|
||||
var eachObj = function(object,callback) {
|
||||
var next,f,length;
|
||||
if(object) {
|
||||
if(Object.prototype.toString.call(object) == "[object Array]") {
|
||||
for (f=0, length=object.length; f<length; f++) {
|
||||
next = callback(object[f],f,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var keys = Object.keys(object);
|
||||
for (f=0, length=keys.length; f<length; f++) {
|
||||
var key = keys[f];
|
||||
next = callback(object[key],key,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hop = function(object,property) {
|
||||
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
|
||||
},
|
||||
insertSortedArray = function(array,value) {
|
||||
var low = 0, high = array.length - 1, mid, cmp;
|
||||
while(low <= high) {
|
||||
mid = (low + high) >> 1;
|
||||
cmp = value.localeCompare(array[mid]);
|
||||
if(cmp > 0) {
|
||||
low = mid + 1;
|
||||
} else if(cmp < 0) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
return array;
|
||||
}
|
||||
}
|
||||
array.splice(low,0,value);
|
||||
return array;
|
||||
},
|
||||
parseJSONSafe = function(text,defaultJSON) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch(e) {
|
||||
if(typeof defaultJSON === "function") {
|
||||
return defaultJSON(e);
|
||||
} else {
|
||||
return defaultJSON || {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.logTables = function() {
|
||||
self.sqlFunctions.sqlLogTables();
|
||||
}
|
||||
|
||||
// Add a tiddler to the store
|
||||
this.addTiddler = function(tiddler) {
|
||||
if(!(tiddler instanceof $tw.Tiddler)) {
|
||||
tiddler = new $tw.Tiddler(tiddler);
|
||||
}
|
||||
// Save the tiddler
|
||||
if(tiddler) {
|
||||
var title = tiddler.fields.title;
|
||||
if(title) {
|
||||
// Save the new tiddler
|
||||
self.sqlFunctions.sqlSaveTiddler(tiddler.fields);
|
||||
// Update caches
|
||||
this.clearAllCaches(title);
|
||||
// Queue a change event
|
||||
this.enqueueTiddlerEvent(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a tiddler
|
||||
this.deleteTiddler = function(title) {
|
||||
// Uncomment the following line for detailed logs of all tiddler deletions
|
||||
// console.log("Deleting",title)
|
||||
if(self.tiddlerExists(title)) {
|
||||
// Delete the tiddler
|
||||
self.sqlFunctions.sqlDeleteTiddler(title);
|
||||
// Update caches
|
||||
this.clearAllCaches(title);
|
||||
// Queue a change event
|
||||
this.enqueueTiddlerEvent(title,true);
|
||||
}
|
||||
};
|
||||
|
||||
// Get a tiddler from the store
|
||||
this.getTiddler = function(title) {
|
||||
if(title) {
|
||||
var t = self.sqlFunctions.sqlGetTiddler(title);
|
||||
if(t !== undefined) {
|
||||
return new $tw.Tiddler(t);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Get an array of all tiddler titles
|
||||
this.allTitles = function() {
|
||||
return getTiddlerTitles();
|
||||
};
|
||||
|
||||
// Iterate through all tiddler titles
|
||||
this.each = function(callback) {
|
||||
self.sqlFunctions.sqlEachTiddler(function(tiddlerFields,title) {
|
||||
callback(new $tw.Tiddler(tiddlerFields),title);
|
||||
});
|
||||
};
|
||||
|
||||
// Get an array of all shadow tiddler titles
|
||||
this.allShadowTitles = function() {
|
||||
return getShadowTiddlerTitles();
|
||||
};
|
||||
|
||||
// Iterate through all shadow tiddler titles
|
||||
this.eachShadow = function(callback) {
|
||||
self.sqlFunctions.sqlEachShadowTiddler(function(tiddlerFields,title) {
|
||||
callback(new $tw.Tiddler(tiddlerFields),title);
|
||||
});
|
||||
};
|
||||
|
||||
// Iterate through all tiddlers and then the shadows
|
||||
this.eachTiddlerPlusShadows = function(callback) {
|
||||
self.sqlFunctions.sqlEachTiddlerPlusShadows(function(tiddlerFields,title) {
|
||||
callback(new $tw.Tiddler(tiddlerFields),title);
|
||||
});
|
||||
};
|
||||
|
||||
// Iterate through all the shadows and then the tiddlers
|
||||
this.eachShadowPlusTiddlers = function(callback) {
|
||||
self.sqlFunctions.sqlEachShadowPlusTiddlers(function(tiddlerFields,title) {
|
||||
callback(new $tw.Tiddler(tiddlerFields),title);
|
||||
});
|
||||
};
|
||||
|
||||
this.tiddlerExists = function(title) {
|
||||
return self.sqlFunctions.sqlTiddlerExists(title);
|
||||
};
|
||||
|
||||
this.isShadowTiddler = function(title) {
|
||||
return !!self.sqlFunctions.sqlGetShadowSource(title);
|
||||
};
|
||||
|
||||
this.getShadowSource = function(title) {
|
||||
return self.sqlFunctions.sqlGetShadowSource(title);
|
||||
};
|
||||
|
||||
// Get an array of all the currently recognised plugin types
|
||||
this.getPluginTypes = function() {
|
||||
var types = [];
|
||||
eachObj(pluginTiddlers,function(pluginTiddler) {
|
||||
var pluginType = pluginTiddler.fields["plugin-type"];
|
||||
if(pluginType && types.indexOf(pluginType) === -1) {
|
||||
types.push(pluginType);
|
||||
}
|
||||
});
|
||||
return types;
|
||||
};
|
||||
|
||||
// Read plugin info for all plugins, or just an array of titles. Returns the number of plugins updated or deleted
|
||||
this.readPluginInfo = function(titles) {
|
||||
var results = {
|
||||
modifiedPlugins: [],
|
||||
deletedPlugins: []
|
||||
};
|
||||
eachObj(titles || getTiddlerTitles(),function(title) {
|
||||
var tiddler = self.getTiddler(title);
|
||||
if(tiddler) {
|
||||
if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type") && tiddler.fields.text) {
|
||||
pluginInfo[tiddler.fields.title] = parseJSONSafe(tiddler.fields.text);
|
||||
results.modifiedPlugins.push(tiddler.fields.title);
|
||||
}
|
||||
} else {
|
||||
if(pluginInfo[title]) {
|
||||
delete pluginInfo[title];
|
||||
results.deletedPlugins.push(title);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Get plugin info for a plugin
|
||||
this.getPluginInfo = function(title) {
|
||||
return pluginInfo[title];
|
||||
};
|
||||
|
||||
// Register the plugin tiddlers of a particular type, or null/undefined for any 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,title) {
|
||||
if(tiddler && tiddler.fields.type === "application/json" && tiddler.fields["plugin-type"] && (!pluginType || tiddler.fields["plugin-type"] === pluginType)) {
|
||||
var disablingTiddler = self.getTiddler("$:/config/Plugins/Disabled/" + title);
|
||||
if(title === "$:/core" || !disablingTiddler || (disablingTiddler.fields.text || "").trim() !== "yes") {
|
||||
self.unregisterPluginTiddlers(null,[title]); // Unregister the plugin if it's already registered
|
||||
pluginTiddlers.push(tiddler);
|
||||
registeredTitles.push(tiddler.fields.title);
|
||||
}
|
||||
}
|
||||
};
|
||||
if(titles) {
|
||||
eachObj(titles,function(title) {
|
||||
checkTiddler(self.getTiddler(title),title);
|
||||
});
|
||||
} else {
|
||||
this.each(function(tiddler,title) {
|
||||
checkTiddler(tiddler,title);
|
||||
});
|
||||
}
|
||||
return registeredTitles;
|
||||
};
|
||||
|
||||
// Unregister the plugin tiddlers of a particular type, or null/undefined for any type, optionally restricting unregistering to an array of tiddler titles. Returns an array of the titles affected
|
||||
this.unregisterPluginTiddlers = function(pluginType,titles) {
|
||||
var self = this,
|
||||
unregisteredTitles = [];
|
||||
// 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 || tiddler.fields["plugin-type"] === pluginType) && (!titles || titles.indexOf(tiddler.fields.title) !== -1)) {
|
||||
unregisteredTitles.push(tiddler.fields.title);
|
||||
pluginTiddlers.splice(t,1);
|
||||
}
|
||||
}
|
||||
return unregisteredTitles;
|
||||
};
|
||||
|
||||
// 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
|
||||
self.sqlFunctions.sqlClearShadows();
|
||||
self.sqlFunctions.sqlSetPluginPriorities(pluginTiddlers.map(tiddler => tiddler.fields.title));
|
||||
eachObj(pluginTiddlers,function(tiddler) {
|
||||
// Extract the constituent tiddlers
|
||||
if(hop(pluginInfo,tiddler.fields.title)) {
|
||||
eachObj(pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) {
|
||||
// Save the tiddler object
|
||||
if(constituentTitle) {
|
||||
var shadowTiddler = Object.assign({},constituentTiddler,{title: constituentTitle})
|
||||
self.sqlFunctions.sqlSaveTiddler(shadowTiddler,tiddler.fields.title);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this.clearAllCaches();
|
||||
};
|
||||
|
||||
function TagSubIndexer(indexer,iteratorMethod) {
|
||||
this.indexer = indexer;
|
||||
this.iteratorMethod = iteratorMethod;
|
||||
this.cache = Object.create(null); // Hashmap by title containing arrays of titles
|
||||
}
|
||||
|
||||
TagSubIndexer.prototype.addIndexMethod = function() {
|
||||
var self = this;
|
||||
this.indexer.wiki[this.iteratorMethod].byTag = function(tag) {
|
||||
return self.lookup(tag).slice(0);
|
||||
};
|
||||
};
|
||||
|
||||
TagSubIndexer.prototype.update = function() {
|
||||
this.cache = Object.create(null);
|
||||
};
|
||||
|
||||
TagSubIndexer.prototype.lookup = function(tag) {
|
||||
var cachedResult = this.cache[tag];
|
||||
if(cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
var listing = self.sqlFunctions.sqlGetTiddlersWithTag(tag,this.iteratorMethod);
|
||||
if(this.indexer.wiki.sortByList) {
|
||||
listing = this.indexer.wiki.sortByList(listing,tag);
|
||||
}
|
||||
this.cache[tag] = listing;
|
||||
return listing;
|
||||
};
|
||||
|
||||
function TagIndexer(wiki) {
|
||||
this.wiki = wiki;
|
||||
this.subIndexers = [
|
||||
new TagSubIndexer(this,"each"),
|
||||
new TagSubIndexer(this,"eachShadow"),
|
||||
new TagSubIndexer(this,"eachTiddlerPlusShadows"),
|
||||
new TagSubIndexer(this,"eachShadowPlusTiddlers")
|
||||
];
|
||||
$tw.utils.each(this.subIndexers,function(subIndexer) {
|
||||
subIndexer.addIndexMethod();
|
||||
});
|
||||
}
|
||||
|
||||
TagIndexer.prototype.init = function() {
|
||||
};
|
||||
|
||||
TagIndexer.prototype.update = function() {
|
||||
$tw.utils.each(this.subIndexers,function(subIndexer) {
|
||||
subIndexer.update();
|
||||
});
|
||||
};
|
||||
|
||||
this.addInternalIndexer(new TagIndexer(this),"TagIndexer");
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/sql-wiki-store.js
|
||||
23
plugins/tiddlywiki/sqlite3store/suppress-boot.js
Normal file
23
plugins/tiddlywiki/sqlite3store/suppress-boot.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/suppress-boot.js
|
||||
type: application/javascript
|
||||
|
||||
Suppress the usual synchronous startup process so that it can instead be done within the callback from sqlite3 initialisation.
|
||||
|
||||
This file is spliced into the HTML file to be executed before the boot kernel has been loaded.
|
||||
|
||||
\*/
|
||||
|
||||
(function() {
|
||||
|
||||
// Initialse skeleton TiddlyWiki global because we run before bootprefix.js and boot.js
|
||||
window.$tw = window.$tw || Object.create(null);
|
||||
$tw.hooks = $tw.hooks || { names: {}};
|
||||
$tw.boot = $tw.boot || {};
|
||||
$tw.boot.preloadDirty = $tw.boot.preloadDirty || [];
|
||||
|
||||
// Tell TiddlyWiki not to boot itself
|
||||
$tw.boot.suppressBoot = true;
|
||||
|
||||
})();
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/suppress-boot.js
|
||||
169
plugins/tiddlywiki/sqlite3store/test-sql-functions.js
Normal file
169
plugins/tiddlywiki/sqlite3store/test-sql-functions.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js
|
||||
type: application/javascript
|
||||
|
||||
Test harness for the functions in sql-functions.js
|
||||
|
||||
\*/
|
||||
|
||||
$tw.testSqlFunctions = function() {
|
||||
|
||||
// Deep equal
|
||||
|
||||
function deepEqual(obj1, obj2) {
|
||||
if (obj1 === undefined && obj2 === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (obj1 === undefined || obj2 === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (obj1 === obj2) {
|
||||
return true;
|
||||
}
|
||||
if (isPrimitive(obj1) && isPrimitive(obj2)) {
|
||||
return obj1 === obj2;
|
||||
}
|
||||
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
|
||||
return false;
|
||||
}
|
||||
for (let key in obj1) {
|
||||
if (!(key in obj2)) {
|
||||
return false;
|
||||
}
|
||||
if (!deepEqual(obj1[key], obj2[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isPrimitive(obj) {
|
||||
return (obj !== Object(obj));
|
||||
}
|
||||
|
||||
let tests = [];
|
||||
|
||||
// Define a test
|
||||
function test(name, fn) {
|
||||
tests.push({
|
||||
name: name,
|
||||
fn: fn
|
||||
});
|
||||
}
|
||||
|
||||
// Run all the tests
|
||||
function run() {
|
||||
while(tests.length > 0) {
|
||||
const test = tests.shift();
|
||||
try {
|
||||
test.fn();
|
||||
console.log("✅", test.name);
|
||||
} catch (e) {
|
||||
console.log("❌", test.name);
|
||||
console.log(e.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let assert = {
|
||||
equal: function(obj1,obj2,message) {
|
||||
if(!deepEqual(obj1,obj2)) {
|
||||
throw new Error("" + (message || "assert.equal failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define the tests
|
||||
|
||||
test("Instantiate the database", function () {
|
||||
const sqlFunctions = new $tw.SqlFunctions();
|
||||
test("Write a tiddler and retrieve it", function() {
|
||||
// Utilities
|
||||
function checkExists(title,result,message) {
|
||||
const exists = sqlFunctions.sqlTiddlerExists(title);
|
||||
assert.equal(exists,result,message);
|
||||
}
|
||||
function checkTiddler(title,result,message) {
|
||||
const tiddler = sqlFunctions.sqlGetTiddler(title);
|
||||
assert.equal(tiddler,result,message);
|
||||
}
|
||||
function checkShadowSource(title,result,message) {
|
||||
const tiddler = sqlFunctions.sqlGetShadowSource(title);
|
||||
assert.equal(tiddler,result,message);
|
||||
}
|
||||
function checkAllTitles(result,message) {
|
||||
const titles = sqlFunctions.sqlAllTitles();
|
||||
assert.equal(titles,result,message + " (sqlAllTitles)");
|
||||
const accumulator = [];
|
||||
sqlFunctions.sqlEachTiddler(function(tiddlerFields,title) {
|
||||
accumulator.push(title);
|
||||
});
|
||||
assert.equal(accumulator,result,message + " (sqlEachTiddler)");
|
||||
}
|
||||
function checkAllShadowTitles(result,message) {
|
||||
const titles = sqlFunctions.sqlAllShadowTitles();
|
||||
assert.equal(titles,result,message);
|
||||
}
|
||||
// Set priorities for the plugins we'll use
|
||||
sqlFunctions.sqlSetPluginPriorities([]);
|
||||
// Save and verify an ordinary tiddler
|
||||
sqlFunctions.sqlSaveTiddler({
|
||||
title: "HelloThere",
|
||||
text: "This is a tiddler"
|
||||
});
|
||||
checkExists("HelloThere",true,"Check the tiddler exists");
|
||||
checkTiddler("HelloThere",{
|
||||
title: "HelloThere",
|
||||
text: "This is a tiddler"
|
||||
},"Retrieve the tiddler");
|
||||
checkShadowSource("HelloThere",null,"Check that the shadow source is correct");
|
||||
// Delete the tiddler and check it no longer exists
|
||||
sqlFunctions.sqlDeleteTiddler("HelloThere");
|
||||
checkTiddler("HelloThere",undefined,"Try to retrieve the deleted tiddler");
|
||||
checkExists("HelloThere",false,"Check the tiddler doesn't exist");
|
||||
checkAllTitles([],"Check that the title list is correct");
|
||||
checkAllShadowTitles([],"Check that the shadow title list is correct");
|
||||
// Save and verify a shadow tiddler
|
||||
sqlFunctions.sqlSetPluginPriorities(["myplugin"]);
|
||||
sqlFunctions.sqlSaveTiddler({
|
||||
title: "HelloThere",
|
||||
text: "This is a shadow tiddler"
|
||||
},"myplugin");
|
||||
// Check that the shadow tiddler exists and has the expected value
|
||||
checkExists("HelloThere",false,"Check the shadow tiddler does not exist");
|
||||
checkTiddler("HelloThere",{
|
||||
title: "HelloThere",
|
||||
text: "This is a shadow tiddler"
|
||||
},"Retrieve the tiddler");
|
||||
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
|
||||
sqlFunctions.sqlLogTables();
|
||||
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
|
||||
// Save an ordinary tiddler over the top and check it can be retrieved
|
||||
sqlFunctions.sqlSaveTiddler({
|
||||
title: "HelloThere",
|
||||
text: "This is a tiddler"
|
||||
});
|
||||
checkExists("HelloThere",true,"Check the tiddler exists");
|
||||
checkTiddler("HelloThere",{
|
||||
title: "HelloThere",
|
||||
text: "This is a tiddler"
|
||||
},"Retrieve the tiddler");
|
||||
checkAllTitles(["HelloThere"],"Check that the title list is correct");
|
||||
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
|
||||
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
|
||||
// Delete the ordinary tiddler and check that the shadow tiddler is still available
|
||||
sqlFunctions.sqlDeleteTiddler("HelloThere");
|
||||
checkTiddler("HelloThere",{
|
||||
title: "HelloThere",
|
||||
text: "This is a shadow tiddler"
|
||||
},"Try to retrieve the shadow tiddler exposed by the deleted tiddler");
|
||||
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
|
||||
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
|
||||
});
|
||||
});
|
||||
|
||||
// Run the tests
|
||||
run();
|
||||
|
||||
};
|
||||
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js
|
||||
Reference in New Issue
Block a user