mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-25 12:23:42 +00:00
Compare commits
22 Commits
demo-alter
...
fix-list-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dc4b6d173 | ||
|
|
d2d00ffa4d | ||
|
|
c6a72875ba | ||
|
|
aaf7dc355d | ||
|
|
e131dd3761 | ||
|
|
f697f008b1 | ||
|
|
cdfa4b6082 | ||
|
|
8051a3dea2 | ||
|
|
267521ad1b | ||
|
|
e82229210e | ||
|
|
c13c321a61 | ||
|
|
0d2aeb8253 | ||
|
|
9d94459c5d | ||
|
|
5c283f843b | ||
|
|
51862f8128 | ||
|
|
c9be572baf | ||
|
|
b08281a20b | ||
|
|
ad9cb8a0a8 | ||
|
|
1001590326 | ||
|
|
e593f80278 | ||
|
|
8617fa39dc | ||
|
|
f89b52e521 |
@@ -5,7 +5,7 @@
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.3.2
|
||||
TW5_BUILD_VERSION=v5.3.3
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
|
||||
46
boot/boot.js
46
boot/boot.js
@@ -1096,39 +1096,6 @@ $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
|
||||
*/
|
||||
@@ -1165,7 +1132,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 = $tw.Wiki || function(options) {
|
||||
$tw.Wiki = function(options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
tiddlers = Object.create(null), // Hashmap of tiddlers
|
||||
@@ -1527,7 +1494,7 @@ $tw.Wiki.prototype.clearGlobalCache =
|
||||
$tw.Wiki.prototype.enqueueTiddlerEvent = function() {};
|
||||
|
||||
// Add an array of tiddlers
|
||||
$tw.Wiki.prototype.addTiddlers = $tw.Wiki.prototype.addTiddlers || function(tiddlers) {
|
||||
$tw.Wiki.prototype.addTiddlers = function(tiddlers) {
|
||||
for(var t=0; t<tiddlers.length; t++) {
|
||||
this.addTiddler(tiddlers[t]);
|
||||
}
|
||||
@@ -1536,7 +1503,7 @@ $tw.Wiki.prototype.addTiddlers = $tw.Wiki.prototype.addTiddlers || function(tidd
|
||||
/*
|
||||
Define all modules stored in ordinary tiddlers
|
||||
*/
|
||||
$tw.Wiki.prototype.defineTiddlerModules = $tw.Wiki.prototype.defineTiddlerModules || function() {
|
||||
$tw.Wiki.prototype.defineTiddlerModules = function() {
|
||||
this.each(function(tiddler,title) {
|
||||
if(tiddler.hasField("module-type")) {
|
||||
switch (tiddler.fields.type) {
|
||||
@@ -1560,7 +1527,7 @@ $tw.Wiki.prototype.defineTiddlerModules = $tw.Wiki.prototype.defineTiddlerModule
|
||||
/*
|
||||
Register all the module tiddlers that have a module type
|
||||
*/
|
||||
$tw.Wiki.prototype.defineShadowModules = $tw.Wiki.prototype.defineShadowModules || function() {
|
||||
$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
|
||||
@@ -1574,7 +1541,7 @@ $tw.Wiki.prototype.defineShadowModules = $tw.Wiki.prototype.defineShadowModules
|
||||
/*
|
||||
Enable safe mode by deleting any tiddlers that override a shadow tiddler
|
||||
*/
|
||||
$tw.Wiki.prototype.processSafeMode = $tw.Wiki.prototype.processSafeMode || function() {
|
||||
$tw.Wiki.prototype.processSafeMode = function() {
|
||||
var self = this,
|
||||
overrides = [];
|
||||
// Find the overriding tiddlers
|
||||
@@ -1605,7 +1572,7 @@ $tw.Wiki.prototype.processSafeMode = $tw.Wiki.prototype.processSafeMode || funct
|
||||
/*
|
||||
Extracts tiddlers from a typed block of text, specifying default field values
|
||||
*/
|
||||
$tw.Wiki.prototype.deserializeTiddlers = $tw.Wiki.prototype.deserializeTiddlers || function(type,text,srcFields,options) {
|
||||
$tw.Wiki.prototype.deserializeTiddlers = function(type,text,srcFields,options) {
|
||||
srcFields = srcFields || Object.create(null);
|
||||
options = options || {};
|
||||
var deserializer = $tw.Wiki.tiddlerDeserializerModules[options.deserializer],
|
||||
@@ -2471,7 +2438,6 @@ $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,17 +225,13 @@ 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);
|
||||
@@ -245,42 +241,10 @@ 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) {
|
||||
@@ -370,8 +334,8 @@ exports.compileFilterToJavaScript = function(filterParseTree) {
|
||||
}
|
||||
})());
|
||||
});
|
||||
// Make the filter function
|
||||
return function filterFunction(source,widget) {
|
||||
// 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) {
|
||||
if(!source) {
|
||||
source = self.each;
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
@@ -381,12 +345,27 @@ exports.compileFilterToJavaScript = function(filterParseTree) {
|
||||
widget = $tw.rootWidget;
|
||||
}
|
||||
var results = new $tw.utils.LinkedList();
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -635,7 +635,7 @@ SyncFromServerTask.prototype.run = function(callback) {
|
||||
callback(null);
|
||||
};
|
||||
if(this.syncer.syncadaptor.getUpdatedTiddlers) {
|
||||
this.syncer.syncadaptor.getUpdatedTiddlers(self,function(err,updates) {
|
||||
this.syncer.syncadaptor.getUpdatedTiddlers(self.syncer,function(err,updates) {
|
||||
if(err) {
|
||||
self.syncer.displayError($tw.language.getString("Error/RetrievingSkinny"),err);
|
||||
return callback(err);
|
||||
|
||||
@@ -24,6 +24,21 @@ 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
|
||||
*/
|
||||
@@ -36,6 +51,24 @@ 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
|
||||
|
||||
@@ -109,6 +109,7 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
||||
this.explicitJoinTemplate = null;
|
||||
this.hasTemplateInBody = false;
|
||||
var searchChildren = function(childNodes) {
|
||||
var foundInlineTemplate = false;
|
||||
$tw.utils.each(childNodes,function(node) {
|
||||
if(node.type === "list-template") {
|
||||
self.explicitListTemplate = node.children;
|
||||
@@ -118,12 +119,14 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
||||
self.explicitJoinTemplate = node.children;
|
||||
} else if(node.type === "element" && node.tag === "p") {
|
||||
searchChildren(node.children);
|
||||
foundInlineTemplate = true;
|
||||
} else {
|
||||
self.hasTemplateInBody = true;
|
||||
foundInlineTemplate = true;
|
||||
}
|
||||
});
|
||||
return foundInlineTemplate;
|
||||
};
|
||||
searchChildren(this.parseTreeNode.children);
|
||||
this.hasTemplateInBody = searchChildren(this.parseTreeNode.children);
|
||||
}
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/config/OfficialPluginLibrary
|
||||
tags: $:/tags/PluginLibrary
|
||||
url: https://tiddlywiki.com/library/v5.3.2/index.html
|
||||
url: https://tiddlywiki.com/library/v5.3.3/index.html
|
||||
caption: {{$:/language/OfficialPluginLibrary}}
|
||||
|
||||
{{$:/language/OfficialPluginLibrary/Hint}}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: yes
|
||||
text: no
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"description": "Empty edition",
|
||||
"plugins": [
|
||||
"tiddlywiki/sqlite3store"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
||||
58
editions/prerelease/tiddlers/Release 5.3.3.tid
Normal file
58
editions/prerelease/tiddlers/Release 5.3.3.tid
Normal file
@@ -0,0 +1,58 @@
|
||||
caption: 5.3.3
|
||||
created: 20231213080754563
|
||||
modified: 20231213080754563
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.3.3
|
||||
type: text/vnd.tiddlywiki
|
||||
description: Under development
|
||||
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.2...master]]//
|
||||
|
||||
! Translation improvements
|
||||
|
||||
Improvements to the following translations:
|
||||
|
||||
*
|
||||
|
||||
! Plugin Improvements
|
||||
|
||||
*
|
||||
|
||||
! Widget Improvements
|
||||
|
||||
*
|
||||
|
||||
! Usability Improvements
|
||||
|
||||
*
|
||||
|
||||
! Hackability Improvements
|
||||
|
||||
*
|
||||
|
||||
! Bug Fixes
|
||||
|
||||
*
|
||||
|
||||
! Node.js Improvements
|
||||
|
||||
*
|
||||
|
||||
! Performance Improvements
|
||||
|
||||
*
|
||||
|
||||
! Developer Improvements
|
||||
|
||||
*
|
||||
|
||||
! Infrastructure Improvements
|
||||
|
||||
*
|
||||
|
||||
! Acknowledgements
|
||||
|
||||
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
|
||||
|
||||
<<.contributors """
|
||||
""">>
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/config/OfficialPluginLibrary
|
||||
tags: $:/tags/PluginLibrary
|
||||
url: https://tiddlywiki.com/prerelease/library/v5.3.2/index.html
|
||||
url: https://tiddlywiki.com/prerelease/library/v5.3.3/index.html
|
||||
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease)
|
||||
|
||||
The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: yes
|
||||
@@ -1,11 +1,31 @@
|
||||
{
|
||||
"description": "Content for the current prerelease",
|
||||
"plugins": [
|
||||
"tiddlywiki/sqlite3store"
|
||||
"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"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
"tiddlywiki/snowwhite"
|
||||
"tiddlywiki/snowwhite",
|
||||
"tiddlywiki/starlight",
|
||||
"tiddlywiki/seamless",
|
||||
"tiddlywiki/centralised",
|
||||
"tiddlywiki/heavier",
|
||||
"tiddlywiki/tight",
|
||||
"tiddlywiki/tight-heavier",
|
||||
"tiddlywiki/readonly"
|
||||
],
|
||||
"languages": [
|
||||
],
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
title: ListWidget/WithEmptyParagraphTemplate
|
||||
description: List widget with an empty paragraph as inline template
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$list filter="1"><p/></$list>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><p></p></p>
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"description": "TiddlyWiki core tests",
|
||||
"plugins": [
|
||||
"tiddlywiki/jasmine",
|
||||
"tiddlywiki/sqlite3store"
|
||||
"tiddlywiki/jasmine"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
created: 20140211171341271
|
||||
modified: 20230419103154328
|
||||
modified: 20230922094937115
|
||||
tags: Concepts Reference
|
||||
title: Macros
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
!! Important
|
||||
|
||||
<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Functions]] and [[Custom Widgets]] which together provide more robust and flexible ways to encapsulate and re-use code.
|
||||
|
||||
For text substitutions it is now recommended to use: [[Substituted Attribute Values]], [[substitute Operator]] and [[Transclusion and Substitution]]
|
||||
|
||||
!! Introduction
|
||||
|
||||
A <<.def macro>> is a named snippet of text. They are typically defined with the [[Pragma: \define]]:
|
||||
@@ -26,8 +32,6 @@ The parameters that are specified in the macro call are substituted for special
|
||||
* `$parameter-name$` is replaced with the value of the named parameter
|
||||
* `$(variable-name)$` is replaced with the value of the named [[variable|Variables]]).
|
||||
|
||||
<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Custom Widgets]] and [[Functions]] which together provide more robust and flexible ways to encapsulate and re-use code. It is now recommended to only use macros when textual substitution is specifically required.
|
||||
|
||||
!! How Macros Work
|
||||
|
||||
Macros are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20150117152418000
|
||||
modified: 20220523075540462
|
||||
modified: 20231019155036098
|
||||
tags: Concepts
|
||||
title: Title List
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -15,3 +15,7 @@ Title lists are used in various places, including PermaLinks and the ListField.
|
||||
They are in fact the simplest case of a [[filter|Filters]], and are thus a way of expressing a [[selection of titles|Title Selection]].
|
||||
|
||||
<<.warning """The [[Title List]] format cannot reliably represent items that contain certain specific character sequences such as `]] `. Thus it should not be used where there is a possibility of such sequences occurring.""">>
|
||||
|
||||
See also:
|
||||
|
||||
* The [[format Operator]] with the 'titlelist' suffix conditionally wraps double square brackets around a string if it contains whitespace
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
created: 20230922121858167
|
||||
modified: 20230922122333325
|
||||
tags: [[Operator Examples]] [[jsonstringify Operator]]
|
||||
title: jsonstringify Operator (Examples)
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Compare the encoding of quotes and control characters in the first example with the analogue [[example for the stringify operator|stringify Operator (Examples)]].
|
||||
<<.operator-example 1 """[[Backslash \, double quote ", single quote ', tab , line feed
|
||||
]] +[jsonstringify[]]""">>
|
||||
<<.operator-example 2 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 without suffix]] +[jsonstringify[]]""">>
|
||||
<<.operator-example 3 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 with rawunicode suffix]] +[jsonstringify:rawunicode[]]""">>
|
||||
@@ -1,9 +1,11 @@
|
||||
created: 20161017154944352
|
||||
modified: 20230919124059118
|
||||
modified: 20230922122319674
|
||||
tags: [[Operator Examples]] [[stringify Operator]]
|
||||
title: stringify Operator (Examples)
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<<.operator-example 1 """[[Title with "double quotes" and single ' and \backslash]] +[stringify[]]""">>
|
||||
Compare the encoding of quotes and control characters in the first example with the analogue [[example for the jsonstringify operator|jsonstringify Operator (Examples)]].
|
||||
<<.operator-example 1 """[[Backslash \, double quote ", single quote ', tab , line feed
|
||||
]] +[stringify[]]""">>
|
||||
<<.operator-example 2 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 without suffix]] +[stringify[]]""">>
|
||||
<<.operator-example 3 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 with rawunicode suffix]] +[stringify:rawunicode[]]""">>
|
||||
@@ -1,12 +1,35 @@
|
||||
caption: jsonstringify
|
||||
created: 20171029155051467
|
||||
from-version: 5.1.14
|
||||
modified: 20230919124826880
|
||||
modified: 20230922121404577
|
||||
op-input: a [[selection of titles|Title Selection]]
|
||||
op-output: the input with JSON string encodings applied
|
||||
op-parameter:
|
||||
op-parameter-name:
|
||||
op-purpose: deprecated, use <<.olink stringify>> instead
|
||||
op-purpose: apply JSON string encoding to a string, see also the similar <<.olink stringify>>
|
||||
op-suffix: <<.from-version "5.1.23">> optionally, the keyword `rawunicode`
|
||||
op-suffix-name: R
|
||||
tags: [[Filter Operators]] [[String Operators]]
|
||||
title: jsonstringify Operator
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
The following substitutions are made:
|
||||
|
||||
|!Character |!Replacement |!Condition |
|
||||
|`\` |`\\` |Always |
|
||||
|`"` |`\"` |Always |
|
||||
|Carriage return (0x0d) |`\r` |Always |
|
||||
|Line feed (0x0a) |`\n` |Always |
|
||||
|Backspace (0x08) |`\b` |Always |
|
||||
|Form field (0x0c) |`\f` |Always |
|
||||
|Tab (0x09) |`\t` |Always|
|
||||
|Characters from 0x00 to 0x1f, except listed above |`\u####` where #### is four hex digits |Always |
|
||||
|Characters from from 0x80 to 0xffff|`\u####` where #### is four hex digits |If `rawunicode` suffix is not present (default) |
|
||||
|Characters from 0x80 to 0xffff|<<.from-version "5.1.23">> Unchanged |If `rawunicode` suffix is present |
|
||||
|
||||
<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23. Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences.
|
||||
|
||||
<<.note """Mind the differences compared to <<.olink stringify>> in encoding of single quotes and control characters (0x00 to 0x1f).
|
||||
""">>
|
||||
|
||||
<<.operator-examples "jsonstringify">>
|
||||
@@ -1,12 +1,12 @@
|
||||
caption: stringify
|
||||
created: 20161017153038029
|
||||
from-version: 5.1.14
|
||||
modified: 20230919130847809
|
||||
modified: 20230922121406947
|
||||
op-input: a [[selection of titles|Title Selection]]
|
||||
op-output: the input with ~JavaScript string encodings applied
|
||||
op-parameter:
|
||||
op-parameter-name:
|
||||
op-purpose: apply ~JavaScript string encoding to a string
|
||||
op-purpose: apply ~JavaScript string encoding to a string, see also the similar <<.olink jsonstringify>>
|
||||
op-suffix: <<.from-version "5.1.23">> optionally, the keyword `rawunicode`
|
||||
op-suffix-name: R
|
||||
tags: [[Filter Operators]] [[String Operators]]
|
||||
@@ -18,19 +18,16 @@ The following substitutions are made:
|
||||
|!Character |!Replacement |!Condition |
|
||||
|`\` |`\\` |Always |
|
||||
|`"` |`\"` |Always |
|
||||
|Carriage return (0x0d) |`\r` |Always |
|
||||
|`'` |`\'` |Always |
|
||||
|Line feed (0x0a) |`\n` |Always |
|
||||
|Backspace (0x08) |`\b` |Always |
|
||||
|Form field (0x0c) |`\f` |Always |
|
||||
|Tab (0x09) |`\t` |Always |
|
||||
|Characters from 0x00 to 0x1f |`\x##` where ## is two hex digits |Always |
|
||||
|Carriage return (0x0d) |`\r` |Always |
|
||||
|Characters from 0x00 to 0x1f, except listed above |`\x##` where ## is two hex digits |Always |
|
||||
|Characters from 0x80 to 0xffff|`\u####` where #### is four hex digits |If `rawunicode` suffix is not present (default) |
|
||||
|Characters from 0x80 to 0xffff|<<.from-version "5.1.23">> Unchanged |If `rawunicode` suffix is present |
|
||||
|
||||
<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23.
|
||||
<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23. Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences.
|
||||
|
||||
<<.note """Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences.""">>
|
||||
|
||||
<<.olink jsonstringify>> is considered deprecated, as it duplicates the functionality of <<.op stringify>>.
|
||||
<<.note """Mind the differences compared to <<.olink jsonstringify>> in encoding of single quotes and control characters (0x00 to 0x1f).
|
||||
""">>
|
||||
|
||||
<<.operator-examples "stringify">>
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20130822170200000
|
||||
list: [[A Gentle Guide to TiddlyWiki]] [[Discover TiddlyWiki]] [[Some of the things you can do with TiddlyWiki]] [[Ten reasons to switch to TiddlyWiki]] Examples [[What happened to the original TiddlyWiki?]]
|
||||
modified: 20230820112855583
|
||||
modified: 20231213080637781
|
||||
tags: TableOfContents
|
||||
title: HelloThere
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -20,7 +20,7 @@ TiddlyWiki lets you choose where to keep your data, guaranteeing that in the dec
|
||||
</div>
|
||||
<div class="tc-cards tc-small">
|
||||
<$link to="中文社区 - Chinese Community" class="tc-btn-big-green tc-card">
|
||||
中文社区 - Chinese Community
|
||||
中文社区<br/>Chinese Community
|
||||
</$link>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20220427174702859
|
||||
modified: 20230809113620964
|
||||
modified: 20230922122551197
|
||||
tags: [[JSON in TiddlyWiki]] Learning
|
||||
title: Constructing JSON tiddlers
|
||||
|
||||
@@ -13,4 +13,4 @@ At a high level, we have several ways to generate JSON data in TiddlyWiki's own
|
||||
* [[jsontiddler Macro]]
|
||||
* [[jsontiddlers Macro]]
|
||||
|
||||
When constructing JSON data manually, the [[stringify Operator]] is needed to ensure that any special characters are properly escaped.
|
||||
When constructing JSON data manually, the [[jsonstringify Operator]] is needed to ensure that any special characters are properly escaped.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 89 KiB |
@@ -5,4 +5,4 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
The following [[macros|Macros]] are built into ~TiddlyWiki's core:
|
||||
|
||||
<<list-links "[tag[Core Macros]]">>
|
||||
<<list-links "[tag[Core Macros]]" class:"multi-columns">>
|
||||
|
||||
@@ -11,4 +11,6 @@ The `tm-delete-tiddler` message deletes the specified tiddler and removes it fro
|
||||
|param |Title of the tiddler that is to be deleted |
|
||||
|tiddlerTitle |Fallback title that is used if ''param'' isn't specified (automatically set by the ButtonWidget) |
|
||||
|
||||
The delete tiddler message is usually generated with the ButtonWidget and is handled by the NavigatorWidget.
|
||||
The delete tiddler message is usually generated with the ButtonWidget and is handled by the NavigatorWidget.
|
||||
|
||||
Use the [[ActionDeleteTiddlerWidget]] to delete a named tiddler without getting the "Do you wish to delete the tiddler" prompt.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20220917112233317
|
||||
modified: 20230419103154328
|
||||
modified: 20231217185535715
|
||||
tags: Pragmas
|
||||
title: Pragma: \define
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -48,4 +48,6 @@ $caption$
|
||||
<<special-button>>
|
||||
""">>
|
||||
|
||||
<<.warning """If macros are nested, textual substitution will only occur for the outermost macro. Thi is because by the time the inner macros are processed all the substitutions will have already occurred""">>
|
||||
|
||||
A more formal [[presentation|Macro Definition Syntax]] of this syntax is also available.
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
caption: 5.3.2
|
||||
created: 20231016122502955
|
||||
modified: 20231016122502955
|
||||
created: 20231213080637781
|
||||
modified: 20231213080637781
|
||||
released: 20231213080637781
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.3.2
|
||||
type: text/vnd.tiddlywiki
|
||||
description: Under development
|
||||
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.1...master]]//
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.1...v5.3.2]]//
|
||||
|
||||
<<.banner-credits
|
||||
credit:"""Congratulations to [[catter-fly|https://talk.tiddlywiki.org/u/catter-fly]] for their winning design for the banner for this release (here is the [[competition thread|https://talk.tiddlywiki.org/t/banner-image-competition-for-v5-3-2/8569]]).
|
||||
"""
|
||||
url:"https://raw.githubusercontent.com/Jermolene/TiddlyWiki5/51862f812851afda0ed3540f8463f51def0d4f9a/editions/tw5.com/tiddlers/images/New%20Release%20Banner.png"
|
||||
>>
|
||||
! Major Improvements
|
||||
|
||||
!! Conditional Shortcut Syntax
|
||||
@@ -1,2 +0,0 @@
|
||||
title: $:/config/Performance/Instrumentation
|
||||
text: yes
|
||||
@@ -148,6 +148,7 @@ type: text/vnd.tiddlywiki
|
||||
}
|
||||
|
||||
.tc-cards.tc-small {
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: genesis
|
||||
created: 20221101100729587
|
||||
modified: 20230115101800345
|
||||
modified: 20231214093716044
|
||||
tags: Widgets
|
||||
title: GenesisWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -15,6 +15,7 @@ The content of the <<.wid genesis>> widget is used as the content of the dynamic
|
||||
|
||||
|!Attribute |!Description |
|
||||
|$type |The type of widget or element to create (an initial `$` indicates a widget, otherwise an HTML element will be created) |
|
||||
|$remappable |Set to "no" to prevent the generated widget from being affected by any custom widget overrides. Needed when invoking the original widget within a custom widget definition |
|
||||
|$names |An optional filter evaluating to the names of a list of attributes to be applied to the widget |
|
||||
|$values |An optional filter evaluating to the values corresponding to the list of names specified in <<.attr $names>> |
|
||||
|$mode |An optional override of the parsing mode. May be "inline" or "block" |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: set
|
||||
created: 20131115182700000
|
||||
modified: 20220523075522407
|
||||
modified: 20230720174707977
|
||||
tags: Widgets
|
||||
title: SetWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -120,3 +120,19 @@ src='<$set name="myTiddler" value="HelloThere">
|
||||
</$set>'/>
|
||||
|
||||
<<<
|
||||
|
||||
!! Using the Set Widget to Create Global Variables
|
||||
|
||||
There are times when it makes sense to use the features of the [[SetWidget]] rather than procedures or functions to create global variables. This can be accomplished by placing the set variable widget in a tiddler that is tagged [[$:/tags/Global|SystemTag: $:/tags/Global]]. If multiple variables are required, the set variable widget can be nested as shown here:
|
||||
|
||||
<<<
|
||||
<div class="doc-example">
|
||||
|
||||
```
|
||||
<$set name="myGlobalVariable" value="I am global">
|
||||
<$set name="myOtherGlobalVariable" value="I am also a global variable.">
|
||||
</$set>
|
||||
</$set>
|
||||
```
|
||||
</div>
|
||||
<<<
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20141018090608643
|
||||
modified: 20230419103154329
|
||||
modified: 20231030124224424
|
||||
tags: WikiText
|
||||
title: Transclusion and Substitution
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -55,6 +55,6 @@ As described in [[Introduction to filter notation]], you can also transclude a v
|
||||
|
||||
! Textual Substitution
|
||||
|
||||
Textual substitution occurs when the value of a macro/variable is used. It is described in [[Macros]].
|
||||
Textual substitution occurs when the value of a macro/variable is used. It is described in [[Substituted Attribute Values]] and [[substitute Operator]]
|
||||
|
||||
The key difference between substitution and transclusion is that substitution occurs before WikiText parsing. This means that you can use substitution to build WikiText constructions. Transclusions are processed independently, and cannot be combined with adjacent text to define WikiText constructions.
|
||||
The key difference between substitution and transclusion is that substitution occurs before WikiText parsing. This means that you can use substitution to build ~WikiText constructions. Transclusions are processed independently, and cannot be combined with adjacent text to define ~WikiText constructions.
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
{
|
||||
"description": "Documentation from https://tiddlywiki.com",
|
||||
"plugins": [
|
||||
"tiddlywiki/sqlite3store"
|
||||
"tiddlywiki/nodewebkitsaver",
|
||||
"tiddlywiki/browser-sniff",
|
||||
"tiddlywiki/railroad",
|
||||
"tiddlywiki/evernote",
|
||||
"tiddlywiki/internals",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/qrcode"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
"tiddlywiki/snowwhite"
|
||||
"tiddlywiki/snowwhite",
|
||||
"tiddlywiki/starlight",
|
||||
"tiddlywiki/seamless",
|
||||
"tiddlywiki/centralised",
|
||||
"tiddlywiki/tight",
|
||||
"tiddlywiki/heavier",
|
||||
"tiddlywiki/tight-heavier",
|
||||
"tiddlywiki/readonly"
|
||||
],
|
||||
"languages": [
|
||||
],
|
||||
|
||||
@@ -553,3 +553,9 @@ BuckarooBanzay, @BuckarooBanzay, 2023/09/01
|
||||
Timur, @T1mL3arn, 2023/10/04
|
||||
|
||||
Wang Ke, @Gk0Wk, 2023/10/17
|
||||
|
||||
@frittro, 2023/10/27
|
||||
|
||||
@etardiff, 2023/12/10
|
||||
|
||||
John Long, @drevarr, 2023/12/12
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"preferGlobal": "true",
|
||||
"version": "5.3.2-prerelease",
|
||||
"version": "5.3.3-prerelease",
|
||||
"author": "Jeremy Ruston <jeremy@jermolene.com>",
|
||||
"description": "a non-linear personal web notebook",
|
||||
"contributors": [
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"title": "$:/plugins/tiddlywiki/demo-alternate-store",
|
||||
"name": "Demo alternate store",
|
||||
"description": "Developer demo of an alternate wiki store implementation",
|
||||
"list": "readme"
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,6 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/demo-alternate-store/rawmarkup
|
||||
tags: $:/tags/RawMarkupWikified
|
||||
|
||||
`<script>`
|
||||
{{$:/plugins/tiddlywiki/demo-alternate-store/rawmarkup.js}}
|
||||
`</script>`
|
||||
@@ -1,3 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/demo-alternate-store/readme
|
||||
|
||||
Developer demo of an alternate wiki store implementation
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"title": "$:/plugins/tiddlywiki/sqlite3store",
|
||||
"name": "Sqlite3-based store",
|
||||
"description": "Sqlite3-based wiki store implementation",
|
||||
"list": "readme"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/rawmarkup/bottombody
|
||||
tags: $:/tags/RawMarkupWikified/BottomBody
|
||||
|
||||
`<script>`
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/sql-console.js}}
|
||||
{{$:/plugins/tiddlywiki/sqlite3store/init-sqlite3.js}}
|
||||
`</script>`
|
||||
@@ -1,13 +0,0 @@
|
||||
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>`
|
||||
@@ -1,3 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/sqlite3store/readme
|
||||
|
||||
Wiki store implementation based on sqlite3 WASM build.
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,26 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,475 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,397 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,23 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,169 +0,0 @@
|
||||
/*\
|
||||
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
|
||||
@@ -1,7 +1,7 @@
|
||||
<p>Welcome to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a>, a non-linear personal web notebook that anyone can use and keep forever, independently of any corporation.</p><p><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> is a complete interactive wiki in <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/JavaScript.html">JavaScript</a>. It can be used as a single HTML file in the browser or as a powerful Node.js application. It is highly customisable: the entire user interface is itself implemented in hackable <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/WikiText.html">WikiText</a>.</p><p>Learn more and see it in action at <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/</a></p><p>Developer documentation is in progress at <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev/" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/dev/</a></p><h1 class="">Join the Community</h1><p>
|
||||
<h2 class="">Official Forums</h2><p>The new official forum for talking about TiddlyWiki: requests for help, announcements of new releases and plugins, debating new features, or just sharing experiences. You can participate via the associated website, or subscribe via email.</p><p><a class="tc-tiddlylink-external" href="https://talk.tiddlywiki.org/" rel="noopener noreferrer" target="_blank">https://talk.tiddlywiki.org/</a></p><p>Note that talk.tiddlywiki.org is a community run service that we host and maintain ourselves. The modest running costs are covered by community contributions.</p><p>For the convenience of existing users, we also continue to operate the original TiddlyWiki group (hosted on Google Groups since 2005):</p><p><a class="tc-tiddlylink-external" href="https://groups.google.com/group/TiddlyWiki" rel="noopener noreferrer" target="_blank">https://groups.google.com/group/TiddlyWiki</a></p><h2 class="">Developer Forums</h2><p>There are several resources for developers to learn more about <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> and to discuss and contribute to its development.</p><ul><li><a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev" rel="noopener noreferrer" target="_blank">tiddlywiki.com/dev</a> is the official developer documentation</li><li>Get involved in the <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5" rel="noopener noreferrer" target="_blank">development on GitHub</a><ul><li><a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/discussions" rel="noopener noreferrer" target="_blank">Discussions</a> are for Q&A and open-ended discussion</li><li><a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/issues" rel="noopener noreferrer" target="_blank">Issues</a> are for raising bug reports and proposing specific, actionable new ideas</li></ul></li><li>The older TiddlyWikiDev Google Group is now closed in favour of <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/discussions" rel="noopener noreferrer" target="_blank">GitHub Discussions</a> but remains a useful archive: <a class="tc-tiddlylink-external" href="https://groups.google.com/group/TiddlyWikiDev" rel="noopener noreferrer" target="_blank">https://groups.google.com/group/TiddlyWikiDev</a><ul><li>An enhanced group search facility is available on <a class="tc-tiddlylink-external" href="https://www.mail-archive.com/tiddlywikidev@googlegroups.com/" rel="noopener noreferrer" target="_blank">mail-archive.com</a></li></ul></li><li>Follow <a class="tc-tiddlylink-external" href="http://twitter.com/#!/TiddlyWiki" rel="noopener noreferrer" target="_blank">@TiddlyWiki on Twitter</a> for the latest news</li><li>Chat at <a class="tc-tiddlylink-external" href="https://gitter.im/TiddlyWiki/public" rel="noopener noreferrer" target="_blank">https://gitter.im/TiddlyWiki/public</a> (development room coming soon)</li></ul><h2 class="">Other Forums</h2><ul><li><a class="tc-tiddlylink-external" href="https://www.reddit.com/r/TiddlyWiki5/" rel="noopener noreferrer" target="_blank">TiddlyWiki Subreddit</a></li><li>Chat with Gitter at <a class="tc-tiddlylink-external" href="https://gitter.im/TiddlyWiki/public" rel="noopener noreferrer" target="_blank">https://gitter.im/TiddlyWiki/public</a> !</li><li>Chat on Discord at <a class="tc-tiddlylink-external" href="https://discord.gg/HFFZVQ8" rel="noopener noreferrer" target="_blank">https://discord.gg/HFFZVQ8</a></li></ul><h3 class="">Documentation</h3><p>There is also a discussion group specifically for discussing <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> documentation improvement initiatives: <a class="tc-tiddlylink-external" href="https://groups.google.com/group/tiddlywikidocs" rel="noopener noreferrer" target="_blank">https://groups.google.com/group/tiddlywikidocs</a>
|
||||
</p>
|
||||
</p><h1 class="">Installing <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on Node.js</h1><ol><li>Install <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Node.js.html">Node.js</a><ul><li>Linux: <blockquote><div><em>Debian/Ubuntu</em>:<br><code>apt install nodejs</code><br>May need to be followed up by:<br><code>apt install npm</code></div><div><em>Arch Linux</em><br><code>yay -S tiddlywiki</code> <br>(installs node and tiddlywiki)</div></blockquote></li><li>Mac<blockquote><div><code>brew install node</code></div></blockquote></li><li>Android<blockquote><div><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Serving%2520TW5%2520from%2520Android.html">Termux for Android</a></div></blockquote></li><li>Other <blockquote><div>See <a class="tc-tiddlylink-external" href="http://nodejs.org" rel="noopener noreferrer" target="_blank">http://nodejs.org</a></div></blockquote></li></ul></li><li>Open a command line terminal and type:<blockquote><div><code>npm install -g tiddlywiki</code></div><div>If it fails with an error you may need to re-run the command as an administrator:</div><div><code>sudo npm install -g tiddlywiki</code> (Mac/Linux)</div></blockquote></li><li>Ensure TiddlyWiki is installed by typing:<blockquote><div><code>tiddlywiki --version</code></div></blockquote><ul><li>In response, you should see <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> report its current version (eg "5.3.1". You may also see other debugging information reported.)</li></ul></li><li>Try it out:<ol><li><code>tiddlywiki mynewwiki --init server</code> to create a folder for a new wiki that includes server-related components</li><li><code>tiddlywiki mynewwiki --listen</code> to start <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a></li><li>Visit <a class="tc-tiddlylink-external" href="http://127.0.0.1:8080/" rel="noopener noreferrer" target="_blank">http://127.0.0.1:8080/</a> in your browser</li><li>Try editing and creating tiddlers</li></ol></li><li>Optionally, make an offline copy:<ul><li>click the <span class="doc-icon"><svg class="tc-image-save-button-dynamic tc-image-button" height="22pt" viewBox="0 0 128 128" width="22pt">
|
||||
</p><h1 class="">Installing <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on Node.js</h1><ol><li>Install <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Node.js.html">Node.js</a><ul><li>Linux: <blockquote><div><em>Debian/Ubuntu</em>:<br><code>apt install nodejs</code><br>May need to be followed up by:<br><code>apt install npm</code></div><div><em>Arch Linux</em><br><code>yay -S tiddlywiki</code> <br>(installs node and tiddlywiki)</div></blockquote></li><li>Mac<blockquote><div><code>brew install node</code></div></blockquote></li><li>Android<blockquote><div><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Serving%2520TW5%2520from%2520Android.html">Termux for Android</a></div></blockquote></li><li>Other <blockquote><div>See <a class="tc-tiddlylink-external" href="http://nodejs.org" rel="noopener noreferrer" target="_blank">http://nodejs.org</a></div></blockquote></li></ul></li><li>Open a command line terminal and type:<blockquote><div><code>npm install -g tiddlywiki</code></div><div>If it fails with an error you may need to re-run the command as an administrator:</div><div><code>sudo npm install -g tiddlywiki</code> (Mac/Linux)</div></blockquote></li><li>Ensure TiddlyWiki is installed by typing:<blockquote><div><code>tiddlywiki --version</code></div></blockquote><ul><li>In response, you should see <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> report its current version (eg "5.3.2". You may also see other debugging information reported.)</li></ul></li><li>Try it out:<ol><li><code>tiddlywiki mynewwiki --init server</code> to create a folder for a new wiki that includes server-related components</li><li><code>tiddlywiki mynewwiki --listen</code> to start <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a></li><li>Visit <a class="tc-tiddlylink-external" href="http://127.0.0.1:8080/" rel="noopener noreferrer" target="_blank">http://127.0.0.1:8080/</a> in your browser</li><li>Try editing and creating tiddlers</li></ol></li><li>Optionally, make an offline copy:<ul><li>click the <span class="doc-icon"><svg class="tc-image-save-button-dynamic tc-image-button" height="22pt" viewBox="0 0 128 128" width="22pt">
|
||||
<g class="tc-image-save-button-dynamic-clean">
|
||||
<path d="M120.783 34.33c4.641 8.862 7.266 18.948 7.266 29.646 0 35.347-28.653 64-64 64-35.346 0-64-28.653-64-64 0-35.346 28.654-64 64-64 18.808 0 35.72 8.113 47.43 21.03l2.68-2.68c3.13-3.13 8.197-3.132 11.321-.008 3.118 3.118 3.121 8.193-.007 11.32l-4.69 4.691zm-12.058 12.058a47.876 47.876 0 013.324 17.588c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48c14.39 0 27.3 6.332 36.098 16.362L58.941 73.544 41.976 56.578c-3.127-3.127-8.201-3.123-11.32-.005-3.123 3.124-3.119 8.194.006 11.319l22.617 22.617a7.992 7.992 0 005.659 2.347c2.05 0 4.101-.783 5.667-2.349l44.12-44.12z" fill-rule="evenodd"></path>
|
||||
</g>
|
||||
|
||||
Reference in New Issue
Block a user