1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-02 22:39:56 +00:00

Merge branch 'upstream/master'

This commit is contained in:
mrichter 2020-12-07 07:46:56 +01:00
commit 2231256ac2
32 changed files with 553 additions and 89 deletions

View File

@ -18,7 +18,7 @@ Export our filter prefix function
*/
exports.all = function(operationSubFunction) {
return function(results,source,widget) {
Array.prototype.push.apply(results,operationSubFunction(source,widget));
results.push.apply(results, operationSubFunction(source,widget));
};
};

View File

@ -19,9 +19,9 @@ Export our filter prefix function
exports.and = function(operationSubFunction,options) {
return function(results,source,widget) {
// This replaces all the elements of the array, but keeps the actual array so that references to it are preserved
source = options.wiki.makeTiddlerIterator(results);
results.splice(0,results.length);
$tw.utils.pushTop(results,operationSubFunction(source,widget));
source = options.wiki.makeTiddlerIterator(results.toArray());
results.clear();
results.pushTop(operationSubFunction(source,widget));
};
};

View File

@ -19,7 +19,7 @@ exports.else = function(operationSubFunction) {
return function(results,source,widget) {
if(results.length === 0) {
// Main result so far is empty
$tw.utils.pushTop(results,operationSubFunction(source,widget));
results.pushTop(operationSubFunction(source,widget));
}
};
};

View File

@ -18,7 +18,7 @@ Export our filter prefix function
*/
exports.except = function(operationSubFunction) {
return function(results,source,widget) {
$tw.utils.removeArrayEntries(results,operationSubFunction(source,widget));
results.remove(operationSubFunction(source,widget));
};
};

View File

@ -13,17 +13,17 @@ module-type: filterrunprefix
/*
Export our filter function
*/
exports.filter = function(operationSubFunction) {
exports.filter = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var resultsToRemove = [];
$tw.utils.each(results,function(result) {
var filtered = operationSubFunction($tw.wiki.makeTiddlerIterator([result]),widget);
results.each(function(result) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([result]),widget);
if(filtered.length === 0) {
resultsToRemove.push(result);
}
});
$tw.utils.removeArrayEntries(results,resultsToRemove);
results.remove(resultsToRemove);
}
}
};

View File

@ -17,7 +17,8 @@ exports.intersection = function(operationSubFunction) {
return function(results,source,widget) {
if(results.length !== 0) {
var secondRunResults = operationSubFunction(source,widget);
var firstRunResults = results.splice(0);
var firstRunResults = results.toArray();
results.clear();
$tw.utils.each(firstRunResults,function(title) {
if(secondRunResults.indexOf(title) !== -1) {
results.push(title);

View File

@ -17,7 +17,7 @@ Export our filter prefix function
*/
exports.or = function(operationSubFunction) {
return function(results,source,widget) {
$tw.utils.pushTop(results,operationSubFunction(source,widget));
results.pushTop(operationSubFunction(source,widget));
};
};

View File

@ -16,9 +16,9 @@ exports.reduce = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var accumulator = "";
for(var index=0; index<results.length; index++) {
var title = results[index],
list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
var index = 0;
results.each(function(title) {
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name) {
switch(name) {
case "currentTiddler":
@ -39,8 +39,9 @@ exports.reduce = function(operationSubFunction,options) {
if(list.length > 0) {
accumulator = "" + list[0];
}
}
results.splice(0,results.length);
++index;
});
results.clear();
results.push(accumulator);
}
}

View File

@ -311,11 +311,11 @@ exports.compileFilter = function(filterString) {
} else if(typeof source === "object") { // Array or hashmap
source = self.makeTiddlerIterator(source);
}
var results = [];
var results = new $tw.utils.LinkedList();
$tw.utils.each(operationFunctions,function(operationFunction) {
operationFunction(results,source,widget);
});
return results;
return results.toArray();
});
};

View File

@ -217,6 +217,10 @@ Options include:
*/
exports.generateTiddlerFileInfo = function(tiddler,options) {
var fileInfo = {}, metaExt;
// Propagate the isEditableFile flag
if(options.fileInfo) {
fileInfo.isEditableFile = options.fileInfo.isEditableFile || false;
}
// Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace
var hasUnsafeFields = false;
$tw.utils.each(tiddler.getFieldStrings(),function(value,fieldName) {
@ -248,20 +252,22 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
extFilters: options.extFilters,
wiki: options.wiki
});
if(metaExt === ".tid") {
// Overriding to the .tid extension needs special handling
fileInfo.type = "application/x-tiddler";
fileInfo.hasMetaFile = false;
} else if (metaExt === ".json") {
// Overriding to the .json extension needs special handling
fileInfo.type = "application/json";
fileInfo.hasMetaFile = false;
} else if (metaExt) {
//If the new type matches a known extention, use that MIME type's encoding
var extInfo = $tw.utils.getFileExtensionInfo(metaExt);
fileInfo.type = extInfo ? extInfo.type : null;
fileInfo.encoding = $tw.utils.getTypeEncoding(metaExt);
fileInfo.hasMetaFile = true;
if(metaExt){
if(metaExt === ".tid") {
// Overriding to the .tid extension needs special handling
fileInfo.type = "application/x-tiddler";
fileInfo.hasMetaFile = false;
} else if (metaExt === ".json") {
// Overriding to the .json extension needs special handling
fileInfo.type = "application/json";
fileInfo.hasMetaFile = false;
} else {
//If the new type matches a known extention, use that MIME type's encoding
var extInfo = $tw.utils.getFileExtensionInfo(metaExt);
fileInfo.type = extInfo ? extInfo.type : null;
fileInfo.encoding = $tw.utils.getTypeEncoding(metaExt);
fileInfo.hasMetaFile = true;
}
}
}
}
@ -276,10 +282,6 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
fileInfo: options.fileInfo,
originalpath: options.originalpath
});
// Propigate the isEditableFile flag
if(options.fileInfo) {
fileInfo.isEditableFile = options.fileInfo.isEditableFile || false;
}
return fileInfo;
};
@ -380,7 +382,7 @@ exports.generateTiddlerFilepath = function(title,options) {
count++;
} while(fs.existsSync(fullPath));
//If the path does not start with the wikiPath directory or the wikiTiddlersPath directory, or if the last write failed
var encode = !(fullPath.indexOf($tw.boot.wikiPath) == 0 || fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0) || ((options.fileInfo || {writeError: false}).writeError == true);
var encode = !(fullPath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 || fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0) || ((options.fileInfo || {writeError: false}).writeError == true);
if(encode){
//encodeURIComponent() and then resolve to tiddler directory
fullPath = path.resolve(directory, encodeURIComponent(fullPath));

View File

@ -0,0 +1,118 @@
/*\
module-type: utils
title: $:/core/modules/utils/linkedlist.js
type: application/javascript
This is a doubly-linked indexed list intended for manipulation, particularly
pushTop, which it does with significantly better performance than an array.
\*/
(function(){
function LinkedList() {
this.clear();
};
LinkedList.prototype.clear = function() {
this.index = Object.create(null);
// LinkedList performs the duty of both the head and tail node
this.next = this;
this.prev = this;
this.length = 0;
};
LinkedList.prototype.remove = function(value) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
this._removeOne(value[t]);
}
} else {
this._removeOne(value);
}
};
LinkedList.prototype._removeOne = function(value) {
var node = this.index[value];
if(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
this.length -= 1;
// Point index to the next instance of the same value, maybe nothing.
this.index[value] = node.copy;
}
return node;
};
LinkedList.prototype._linkToEnd = function(node) {
// Sticks the given node onto the end of the list.
this.prev.next = node;
node.prev = this.prev;
this.prev = node;
node.next = this;
this.length += 1;
};
LinkedList.prototype.push = function(/* values */) {
for(var i = 0; i < arguments.length; i++) {
var value = arguments[i];
var node = {value: value};
var preexistingNode = this.index[value];
this._linkToEnd(node);
if(preexistingNode) {
// We want to keep pointing to the first instance, but we want
// to have that instance (or chain of instances) point to the
// new one.
while (preexistingNode.copy) {
preexistingNode = preexistingNode.copy;
}
preexistingNode.copy = node;
} else {
this.index[value] = node;
}
}
};
LinkedList.prototype.pushTop = function(value) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
this._removeOne(value[t]);
}
this.push.apply(this, value);
} else {
var node = this._removeOne(value);
if(!node) {
node = {value: value};
this.index[value] = node;
} else {
// Put this node at the end of the copy chain.
var preexistingNode = node;
while(preexistingNode.copy) {
preexistingNode = preexistingNode.copy;
}
// The order of these three statements is important,
// because sometimes preexistingNode == node.
preexistingNode.copy = node;
this.index[value] = node.copy;
node.copy = undefined;
}
this._linkToEnd(node);
}
};
LinkedList.prototype.each = function(callback) {
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
callback(ptr.value);
}
};
LinkedList.prototype.toArray = function() {
var output = [];
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
output.push(ptr.value);
}
return output;
};
exports.LinkedList = LinkedList;
})();

View File

@ -55,9 +55,18 @@ MacroCallWidget.prototype.execute = function() {
// Are we rendering to HTML?
if(this.renderOutput === "text/html") {
// If so we'll return the parsed macro
var parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
parseTreeNodes = parser ? parser.tree : [];
// Check if we've already cached parsing this macro
var parser;
if(variableInfo.srcVariable && variableInfo.srcVariable.parser) {
parser = variableInfo.srcVariable.parser;
} else {
parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
if(variableInfo.isCacheable && variableInfo.srcVariable) {
variableInfo.srcVariable.parser = parser;
}
}
var parseTreeNodes = parser ? parser.tree : [];
// Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
var attributes = {};
$tw.utils.each(variableInfo.params,function(param) {

View File

@ -113,7 +113,8 @@ Widget.prototype.getVariableInfo = function(name,options) {
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
if(parentWidget && name in parentWidget.variables) {
var variable = parentWidget.variables[name],
value = variable.value,
originalValue = variable.value,
value = originalValue,
params = this.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition
$tw.utils.each(params,function(param) {
@ -125,7 +126,9 @@ Widget.prototype.getVariableInfo = function(name,options) {
}
return {
text: value,
params: params
params: params,
srcVariable: variable,
isCacheable: originalValue === value
};
}
// If the variable doesn't exist in the parent widget then look for a macro module

View File

@ -112,8 +112,8 @@ very-muted-foreground: #464646
selection-background: #3F638B
selection-foreground: #ffffff
wikilist-background: <<colour page-background>>
wikilist-button-background: <<colour button-background>>
wikilist-button-foreground: <<colour background>>
wikilist-button-background: #3F638B
wikilist-button-foreground: <<colour foreground>>
wikilist-button-open: #32D74B
wikilist-button-open-hover: #32D74B
wikilist-button-reveal: #0A84FF

View File

@ -97,7 +97,7 @@ tiddler-border: <<colour background>>
tiddler-controls-foreground-hover: #7c6f64
tiddler-controls-foreground-selected: <<colour primary>>
tiddler-controls-foreground: #665c54
tiddler-editor-background: #282828
tiddler-editor-background: #32302f
tiddler-editor-border-image: #282828
tiddler-editor-border: #282828
tiddler-editor-fields-even: #504945
@ -121,7 +121,7 @@ toolbar-done-button:
untagged-background: #504945
very-muted-foreground: #bdae93
wikilist-background: <<colour page-background>>
wikilist-button-background: <<colour button-background>>
wikilist-button-background: #acacac
wikilist-button-foreground: <<colour button-foreground>>
wikilist-item: <<colour background>>
wikilist-toolbar-background: <<colour background>>

View File

@ -99,7 +99,7 @@ tiddler-controls-foreground-selected: #EBCB8B
tiddler-controls-foreground: #4C566A
tiddler-editor-background: #2e3440
tiddler-editor-border-image: #2e3440
tiddler-editor-border: #2e3440
tiddler-editor-border: #3b4252
tiddler-editor-fields-even: #2e3440
tiddler-editor-fields-odd: #2e3440
tiddler-info-background: #2e3440
@ -120,3 +120,14 @@ toolbar-cancel-button:
toolbar-done-button:
untagged-background: #2d3038
very-muted-foreground: #2d3038
wikilist-background: <<colour page-background>>
wikilist-toolbar-background: <<colour background>>
wikilist-item: <<colour background>>
wikilist-title: <<colour foreground>>
wikilist-info: <<colour muted-foreground>>
wikilist-button-open: #A3BE8C
wikilist-button-open-hover: #A3BE8C
wikilist-button-reveal: #81A1C1
wikilist-button-reveal-hover: #81A1C1
wikilist-button-remove: #B48EAD
wikilist-button-remove-hover: #B48EAD

View File

@ -46,12 +46,12 @@ tags: $:/tags/SideBarSegment
<div class="tc-sidebar-lists tc-sidebar-search">
<$vars searchTiddler="$:/temp/search/input" searchListState=<<qualify "$:/state/search-list/selected-item">>>
<$vars editTiddler="$:/temp/search" searchTiddler="$:/temp/search/input" searchListState=<<qualify "$:/state/search-list/selected-item">>>
<div class="tc-search">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-next-input-tab "before">>>
<$keyboard key="((advanced-search-sidebar))" actions=<<advanced-search-actions>>>
<$macrocall $name="keyboard-driven-input" tiddler="$:/temp/search" storeTitle=<<searchTiddler>>
<$macrocall $name="keyboard-driven-input" tiddler=<<editTiddler>> storeTitle=<<searchTiddler>>
selectionStateTitle=<<searchListState>> refreshTitle="$:/temp/search/refresh" type="search"
tag="input" focus={{$:/config/Search/AutoFocus}} focusPopup=<<qualify "$:/state/popup/search-dropdown">>
class="tc-popup-handle" filterMinLength={{$:/config/Search/MinLength}} inputCancelActions=<<cancel-search-actions>>

View File

@ -1,5 +1,6 @@
created: 20140710185051844
modified: 20140710185339032
modified: 20201002010124844
title: wikimethod module type
tags: moduletypes
The startup module [[$:/core/modules/startup/load-modules.js]] in the TiddlyWiki core plug-in loads all modules of type``wikimethod`` and puts their exported functions into the wiki store.

View File

@ -0,0 +1,55 @@
created: 20201002010134640
modified: 20201002012758239
tags: moduletypes dev
title: indexer modules
Indexer modules maintain indexes of tiddlers organized in a manner that's more efficient for different types of access, typically to speed up things like certain filter operators. An example of this would be the tag indexer - it's much faster to maintain a lookup table listing which tiddlers have a given tag than to iterate over //all// of the tiddlers in a wiki and ask each of them if they have the tag you're interested in!
Indexer modules have a `module-type` of `indexer`, and the indexers that are included with TiddlyWiki can be found under `core/modules/indexers`.
! Methods
Indexer modules must export a constructor function, which takes the current wiki object as its only argument. The object built by the construction function must implement the following methods:
!! `init()`
This performs any initial setup required by an indexer.
!! `rebuild()`
This rebuilds an index from scratch, usually after a large number of changes have happened to a wiki.
!! `update(updateDescriptor)`
This is called every time a tiddler is added, changed, or deleted. The `updateDescriptor` value is an object with two fields - `old` and `new` - which represent the pre-update and post-update state of the tiddler, respectively. Each of these has three fields of their own:
* `tiddler` - the state of the tiddler (may be `null`)
* `shadow` - a boolean indicating whether or not the tiddler is a shadow
* `exists` - a boolean indicating whether or not the tiddler exists
For example, let's say you have an indexer `idx` and you create a tiddler T with the text "test" - that would result in your indexer's `update` method being called like this:
```javascript
idx.update({
old: { tiddler: null, shadow: false, exists: false },
new: { tiddler: new $tw.Tiddler({title: 'T', text: 'test'}), shadow: false, exists: true }
});
```
If you then change the text from "test" to "testing", `update` would be called like this:
```javascript
idx.update({
old: { tiddler: new $tw.Tiddler({title: 'T', text: 'test'}), shadow: false, exists: true },
new: { tiddler: new $tw.Tiddler({title: 'T', text: 'testing'}), shadow: false, exists: true }
});
```
And finally, if you delete T, `update` will be called like this:
```javascript
idx.update({
old: { tiddler: new $tw.Tiddler({title: 'T', text: 'testing'}), shadow: false, exists: true },
new: { tiddler: null, shadow: false, exists: false }
});
```

View File

@ -0,0 +1,130 @@
/*\
title: test-linked-list.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests the utils.LinkedList class.
LinkedList was built to behave exactly as $tw.utils.pushTop and
Array.prototype.push would behave with an array.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
describe("LinkedList class tests", function() {
it("can pushTop", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'C');
// singles
list.pushTop('X');
list.pushTop('B');
expect(list.toArray()).toEqual(['A', 'C', 'X', 'B']);
expect(list.length).toBe(4);
//arrays
list.pushTop(['X', 'A', 'G', 'A']);
// If the pushedTopped list has duplicates, they go in unempeded.
expect(list.toArray()).toEqual(['C', 'B', 'X', 'A', 'G', 'A']);
expect(list.length).toBe(6);
});
it("can pushTop with tricky duplicates", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'A', 'C', 'A', 'end');
// If the original list contains duplicates, only one instance is cut
list.pushTop('A');
expect(list.toArray()).toEqual(['B', 'A', 'C', 'A', 'end', 'A']);
expect(list.length).toBe(6);
// And the Llist properly knows the next 'A' to cut if pushed again
list.pushTop(['X', 'A']);
expect(list.toArray()).toEqual(['B', 'C', 'A', 'end', 'A', 'X', 'A']);
expect(list.length).toBe(7);
// One last time, to make sure we maintain the linked chain of copies
list.pushTop('A');
expect(list.toArray()).toEqual(['B', 'C', 'end', 'A', 'X', 'A', 'A']);
expect(list.length).toBe(7);
});
it("can handle particularly nasty pushTop pitfall", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'A', 'C');
list.pushTop('A'); // BACA
list.pushTop('X'); // BACAX
list.remove('A'); // BCAX
list.pushTop('A'); // BCXA
list.remove('A'); // BCX
// But! The way I initially coded the copy chains, a mystery A could
// hang around.
expect(list.toArray()).toEqual(['B', 'C', 'X']);
expect(list.length).toBe(3);
});
it("can push", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'C');
// singles
list.push('B');
expect(list.toArray()).toEqual(['A', 'B', 'C', 'B']);
expect(list.length).toBe(4);
// multiple args
list.push('A', 'B', 'C');
expect(list.toArray()).toEqual(['A', 'B', 'C', 'B', 'A', 'B', 'C']);
expect(list.length).toBe(7);
});
it("can clear", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'C');
list.clear();
expect(list.toArray()).toEqual([]);
expect(list.length).toBe(0);
});
it("can remove", function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'x', 'C', 'x', 'D', 'x', 'E', 'x');
// single
list.remove('x');
expect(list.toArray()).toEqual(['A', 'C', 'x', 'D', 'x', 'E', 'x']);
expect(list.length).toBe(7);
// arrays
list.remove(['x', 'A', 'x']);
expect(list.toArray()).toEqual(['C', 'D', 'E', 'x']);
expect(list.length).toBe(4);
});
it('can ignore removal of nonexistent items', function() {
var list = new $tw.utils.LinkedList();
list.push('A', 'B', 'C', 'D');
// single
list.remove('Z');
expect(list.toArray()).toEqual(['A', 'B', 'C', 'D']);
expect(list.length).toBe(4);
// array
list.remove(['Z', 'B', 'X']);
expect(list.toArray()).toEqual(['A', 'C', 'D']);
expect(list.length).toBe(3);
});
it('can iterate with each', function() {
var list = new $tw.utils.LinkedList();
list.push('0', '1', '2', '3');
var counter = 0;
list.each(function(value) {
expect(value).toBe(counter.toString());
counter = counter + 1;
});
expect(counter).toBe(4);
});
});
})();

View File

@ -0,0 +1,113 @@
/*\
title: test-prefixes-filters.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests the reduce prefix and filter.
\*/
(function(){
/* jslint node: true, browser: true */
/* eslint-env node, browser, jasmine */
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
/* global $tw, require */
"use strict";
describe("'reduce' and 'intersection' filter prefix tests", function() {
var wiki = new $tw.Wiki();
wiki.addTiddler({
title: "Brownies",
text: "//This is a sample shopping list item for the [[Shopping List Example]]//",
tags: ["shopping","food"],
price: "4.99",
quantity: "1"
});
wiki.addTiddler({
title: "Chick Peas",
text: "//This is a sample shopping list item for the [[Shopping List Example]]//",
tags: ["shopping","food"],
price: "1.32",
quantity: "5"
});
wiki.addTiddler({
title: "Milk",
text: "//This is a sample shopping list item for the [[Shopping List Example]]//",
tags: ["shopping", "dairy", "drinks"],
price: "0.46",
quantity: "12"
});
wiki.addTiddler({
title: "Rice Pudding",
price: "2.66",
quantity: "4",
tags: ["shopping", "dairy"],
text: "//This is a sample shopping list item for the [[Shopping List Example]]//"
});
wiki.addTiddler({
title: "Sparkling water",
tags: ["drinks", "mineral water", "textexample"],
text: "This is some text"
});
wiki.addTiddler({
title: "Red wine",
tags: ["drinks", "wine", "textexample"],
text: "This is some more text"
});
wiki.addTiddler({
title: "Cheesecake",
tags: ["cakes", "food", "textexample"],
text: "This is even even even more text"
});
wiki.addTiddler({
title: "Chocolate Cake",
tags: ["cakes", "food", "textexample"],
text: "This is even more text"
});
it("should handle the :reduce filter prefix", function() {
expect(wiki.filterTiddlers("[tag[shopping]] :reduce[get[quantity]add<accumulator>]").join(",")).toBe("22");
expect(wiki.filterTiddlers("[tag[shopping]] :reduce[get[price]multiply{!!quantity}add<accumulator>]").join(",")).toBe("27.75");
expect(wiki.filterTiddlers("[tag[shopping]] :reduce[<index>compare:number:gt[0]then<accumulator>addsuffix[, ]addsuffix<currentTiddler>else<currentTiddler>]").join(",")).toBe("Brownies, Chick Peas, Milk, Rice Pudding");
expect(wiki.filterTiddlers("[tag[non-existent]] :reduce[get[price]multiply{!!quantity}add<accumulator>] :else[[0]]").join(",")).toBe("0");
});
it("should handle the reduce operator", function() {
var widget = require("$:/core/modules/widgets/widget.js");
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
{ wiki:wiki, document:$tw.document});
rootWidget.makeChildWidgets();
var anchorWidget = rootWidget.children[0];
rootWidget.setVariable("add-price","[get[price]multiply{!!quantity}add<accumulator>]");
rootWidget.setVariable("num-items","[get[quantity]add<accumulator>]");
rootWidget.setVariable("join-with-commas","[<index>compare:number:gt[0]then<accumulator>addsuffix[, ]addsuffix<currentTiddler>else<currentTiddler>]");
expect(wiki.filterTiddlers("[tag[shopping]reduce<num-items>]",anchorWidget).join(",")).toBe("22");
expect(wiki.filterTiddlers("[tag[shopping]reduce<add-price>]",anchorWidget).join(",")).toBe("27.75");
expect(wiki.filterTiddlers("[tag[shopping]reduce<join-with-commas>]",anchorWidget).join(",")).toBe("Brownies, Chick Peas, Milk, Rice Pudding");
expect(wiki.filterTiddlers("[tag[non-existent]reduce<add-price>,[0]]",anchorWidget).join(",")).toBe("0");
});
it("should handle the :intersection prefix", function() {
expect(wiki.filterTiddlers("[[Sparkling water]tags[]] :intersection[[Red wine]tags[]]").join(",")).toBe("drinks,textexample");
expect(wiki.filterTiddlers("[[Brownies]tags[]] :intersection[[Chocolate Cake]tags[]]").join(",")).toBe("food");
expect(wiki.filterTiddlers("[tag[shopping]] :intersection[tag[food]]").join(",")).toBe("Brownies,Chick Peas");
expect(wiki.filterTiddlers("[tag[shopping]] :intersection[tag[drinks]]").join(",")).toBe("Milk");
expect(wiki.filterTiddlers("[tag[shopping]] :intersection[tag[wine]]").join(",")).toBe("");
});
it("should handle the :filter prefix and filter operator", function() {
var widget = require("$:/core/modules/widgets/widget.js");
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
{ wiki:wiki, document:$tw.document});
rootWidget.makeChildWidgets();
var anchorWidget = rootWidget.children[0];
rootWidget.setVariable("larger-than-18","[get[text]length[]compare:integer:gteq[18]]");
expect(wiki.filterTiddlers("[tag[textexample]] :filter[get[text]length[]compare:integer:gteq[18]]",anchorWidget).join(",")).toBe("Red wine,Cheesecake,Chocolate Cake");
expect(wiki.filterTiddlers("[tag[textexample]]",anchorWidget).join(",")).toBe("Sparkling water,Red wine,Cheesecake,Chocolate Cake");
expect(wiki.filterTiddlers("[tag[textexample]filter<larger-than-18>]",anchorWidget).join(",")).toBe("Red wine,Cheesecake,Chocolate Cake");
})
});
})();

View File

@ -7,7 +7,7 @@ When you edit a tiddler on https://tiddlywiki.com you will see a small ribbon in
If you are using Node.js, you can replicate this feature for your own TiddlyWiki-based site as follows:
# Make sure the following setting is included in the `tiddlywiki.info` file in your WikiFolder:
# Make sure the following setting is included in the <$link to="tiddlywiki.info Files">`tiddlywiki.info`</$link> file in your [[wiki folder|TiddlyWikiFolders]]:
#> <pre><code> "config": {
"retain-original-tiddler-path": true
}</code></pre>

View File

@ -20,9 +20,13 @@ Keyboard shortcuts are available for common editing operations within the Text E
<<.from-version 5.1.18>> : New ''global'' Keyboard shortcuts:
* Creating a new tiddler (defaults to <kbd>alt-N</kbd> )
* Creating a new journal (defaults to <kbd>alt-J</kbd> )
* Creating a new image (defaults to <kbd>alt-I</kbd> )
|!Action |!Default Shortcut|
|Creating a new tiddler |<kbd>alt-N</kbd> |
|Creating a new journal |<kbd>alt-J</kbd> |
|Creating a new image |<kbd>alt-I</kbd> |
|Focusing sidebar search |<<.from-version 5.1.20>><kbd>ctrl-shift-F</kbd> |
|Toggling the sidebar |<<.from-version 5.1.20>><kbd>shift-alt-S</kbd> |
|Advanced search |<<.from-version 5.1.20>><kbd>ctrl-shift-A</kbd> |
The current shortcuts can be inspected and customised in the "Keyboard Shortcuts" tab of the [[Control Panel|$:/ControlPanel]] <<.icon $:/core/images/options-button>>.

View File

@ -7,9 +7,9 @@ First of all: ''Keep calm!''
{{$:/deprecated}}
For ~TiddlyWiki it means, that you should not use this mechanism for new content anymore! ''AND you should update your existing content''!
For ~TiddlyWiki it means that you should not use this mechanism for new content anymore, ''AND you should update your existing content''!
Deprecated features have a marker. see: [[Custom styles by tag]]
Deprecated features have a marker. See: [[How to apply custom styles by tag]]
''Tiddlers tagged `$:/deprecated`''

View File

@ -8,8 +8,8 @@ type: text/vnd.tiddlywiki
<span class="doc-from-version">{{$:/core/images/warning}} New in: $version$</span>
\end
\define .deprecated-since(version, superseeded:"TODO-Link")
<$button to="Deprecated - What does it mean" class="doc-deprecated-version tc-btn-invisible">{{$:/core/images/warning}} Deprecated since: $version$ </$button> use [[$superseeded$]] instead!
\define .deprecated-since(version, superseded:"TODO-Link")
<$button to="Deprecated - What does it mean" class="doc-deprecated-version tc-btn-invisible">{{$:/core/images/warning}} Deprecated since: $version$ </$button>. Use [[$superseded$]] instead
\end
<pre><$view field="text"/></pre>

View File

@ -5,6 +5,6 @@ tags: [[WebServer Parameters]]
title: WebServer Parameter: tiddler-render-type
type: text/vnd.tiddlywiki
The [[web server configuration parameter|WebServer Parameters]] ''tiddler-render-type'' is used to specify the render type for serving ordinary, non-system tiddlers in the [[read-only single tiddler view|Using the read-only single tiddler view]]. The default value is `text/html`, causing the full HTML of the rendered output to be returned. Alternatively, `text/html` can be used to cause the raw text of rendered system tiddlers to be returned.
The [[web server configuration parameter|WebServer Parameters]] ''tiddler-render-type'' is used to specify the render type for serving ordinary, non-system tiddlers in the [[read-only single tiddler view|Using the read-only single tiddler view]]. The default value is `text/html`, causing the full HTML of the rendered output to be returned. Alternatively, `text/plain` can be used to cause the raw text of rendered system tiddlers to be returned.
<<.tip "This setting may be overwritten by specifying the `_render_type` field of a tiddler.">>

View File

@ -37,5 +37,5 @@ So-called global macros are implemented within the main page template ([[$:/core
<<.from-version "5.1.18">> The `\import` [[pragma|Pragma]] is an alternative syntax for using the ImportVariablesWidget. For example, the previous example could be expressed as:
```
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
```

View File

@ -1,6 +1,6 @@
caption: HTML
created: 20131205160816081
modified: 20161021102422842
modified: 20201125094415933
tags: WikiText
title: HTML in WikiText
type: text/vnd.tiddlywiki
@ -22,6 +22,14 @@ To get the content of an HTML element to be parsed in block mode, the opening ta
Without the two linebreaks, the tag content will be parsed in inline mode which means that block mode formatting such as wikitext tables, lists and headings is not recognised.
! Self closing elements
The following tags are treated as 'void'. This means that `<tag>` is treated as if it were `<tag/>`, and that no terminating `</tag>` is needed (if one is provided it will be ignored and treated as plain text).
* `<area>`, `<base>`, `<br>`, `<col>`, `<command>`, `<embed>`, `<hr>`, `<img>`, `<input>`, `<keygen>`, `<link>`, `<meta>`, `<param>`, `<source>`, `<track>`, `<wbr>`
If you dont close any other tag then it will behave as if the missing closing tag were at the end of the tiddler.
! Attributes
In an extension of conventional HTML syntax, attributes of elements/widgets can be specified in several different ways:

View File

@ -1,5 +1,5 @@
created: 20150330155120127
modified: 20191014091943444
modified: 20201205104857625
tags: [[Working with TiddlyWiki]]
title: Performance
type: text/vnd.tiddlywiki
@ -28,4 +28,6 @@ TiddlyWiki ships with defaults that are designed to get the best out of modern d
** Note that the field indexer currently defaults to indexing field values of less than 128 characters; longer values can still be searched for, but no index will be constructed
** Also note that the “field” operator is also used when the operator name is a fieldname, so, for example, `[all[shadows+tiddlers]caption[x]...` is optimised.
* Use the [[throttling|RefreshThrottling]] feature of the RefreshMechanism judiciously
* Keep in mind that ''transcluding separate tiddlers is more performant than heavy use of macros'' and the difference can be significant in some situations. The result of parsing each tiddler is cached and reused the next time if the tiddler has not changed. The same technique cannot be used for macros and they have to be re-parsed every time, as they are not global but local to the widget tree.
** <<.from-version "5.1.23">> Parse trees are now cached for macros that do ''not'' perform any text substitution either via parameters or variables (i.e. `$parameter$` or `$(variable)$`).
* Where possible ''use the SetWidget or VarsWidget with filters instead of the WikifyWidget'' for declaring variables and string concatenation. The performance of the wikify mechanism is relatively poor as there is no opportunity to cache the parse tree or widget tree.

View File

@ -17,7 +17,10 @@ name: tiddlywiki
\end
\define set-selection-background-css(colour,colourA,colourB)
<$set name="backgroundColour" value=<<contrastcolour target:"""$colour$""" fallbackTarget:"""""" colourA:"""$colourA$""" colourB:"""$colourB$""">>>
.cm-s-tiddlywiki div.CodeMirror-selected, .cm-s-tiddlywiki .CodeMirror-selectedtext, .cm-s-tiddlywiki .CodeMirror-selected, .cm-s-tiddlywiki .CodeMirror-line::selection, .cm-s-tiddlywiki .CodeMirror-line > span::selection, .cm-s-tiddlywiki .CodeMirror-line > span > span::selection, .cm-s-tiddlywiki .CodeMirror-line::-moz-selection, .cm-s-tiddlywiki .CodeMirror-line > span::-moz-selection, .cm-s-tiddlywiki .CodeMirror-line > span > span::-moz-selection { background: <<backgroundColour>> ; }
.cm-s-tiddlywiki div.CodeMirror-selected { background: <<backgroundColour>>; }
.cm-s-tiddlywiki.CodeMirror ::selection { background: <<backgroundColour>>; }
.cm-s-tiddlywiki .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: <<backgroundColour>>; }
.cm-s-tiddlywiki .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: <<backgroundColour>>; }
</$set>
\end
\define set-selection-background-colours(palette)
@ -50,15 +53,14 @@ name: tiddlywiki
}
.cm-s-tiddlywiki.CodeMirror, .cm-s-tiddlywiki .CodeMirror-gutters { background-color: <<colour tiddler-editor-background>>; color: <<colour foreground>>; }
.cm-s-tiddlywiki .CodeMirror-gutters {background: <<colour tiddler-editor-background>>; border-right: 0px;}
.cm-s-tiddlywiki .CodeMirror-gutters {background: <<colour tiddler-editor-background>>; border-right: 1px solid <<colour tiddler-editor-border>>;}
.cm-s-tiddlywiki .CodeMirror-linenumber {color: <<colour foreground>>;}
.cm-s-tiddlywiki .CodeMirror-cursor { border-left: 2px solid <<colour foreground>>; }
.cm-s-tiddlywiki span.cm-comment { color: #586e75; font-style:italic; font-weight:normal; }
.cm-s-tiddlywiki .CodeMirror-activeline-background, .cm-s-tiddlywiki .CodeMirror-activeline-gutter .CodeMirror-linenumber { background: rgba(127,127,127,0.2); }
.cm-s-tiddlywiki span.cm-matchhighlight { color: <<colour background>>; background-color: <<colour primary>>; font-weight: normal;}
.cm-s-tiddlywiki .CodeMirror-widget {
text-shadow: none;
}
.cm-s-tiddlywiki .CodeMirror-widget { text-shadow: none; }
.cm-s-tiddlywiki .CodeMirror-dialog { background: <<colour tiddler-background>>; }
.cm-s-tiddlywiki .cm-header { color: #586e75; }
.cm-s-tiddlywiki .cm-quote { color: #93a1a1; }
.cm-s-tiddlywiki .cm-keyword { color: #cb4b16; }
@ -81,23 +83,12 @@ name: tiddlywiki
.cm-s-tiddlywiki .CodeMirror-nonmatchingbracket { color: #dc322f; }
.cm-s-tiddlywiki .cm-tag { color: #93a1a1; }
.cm-s-tiddlywiki .cm-attribute { color: #2aa198; }
.cm-s-tiddlywiki .cm-hr {
color: transparent;
border-top: 1px solid #586e75;
display: block;
}
.cm-s-tiddlywiki .cm-hr { color: transparent; border-top: 1px solid #586e75; display: block; }
.cm-s-tiddlywiki .cm-link { color: #93a1a1; cursor: pointer; }
.cm-s-tiddlywiki .cm-special { color: #6c71c4; }
.cm-s-tiddlywiki .cm-em {
color: #999;
text-decoration: underline;
text-decoration-style: dotted;
}
.cm-s-tiddlywiki .cm-em { color: #999; text-decoration: underline; text-decoration-style: dotted; }
.cm-s-tiddlywiki .cm-error,
.cm-s-tiddlywiki .cm-invalidchar {
color: #586e75;
border-bottom: 1px dotted #dc322f;
}
.cm-s-tiddlywiki .cm-invalidchar { color: #586e75; border-bottom: 1px dotted #dc322f; }
.cm-s-tiddlywiki .CodeMirror-matchingbracket { color: #859900; }
.cm-s-tiddlywiki .CodeMirror-nonmatchingbracket { color: #dc322f; }
.cm-s-tiddlywiki .cm-searching { background: rgba(243, 155, 53, .3); outline: 1px solid #F39B35; }

View File

@ -53,11 +53,17 @@ It is the responsibility of the filesystem adaptor to update this.boot.files for
*/
FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) {
// Always generate a fileInfo object when this fuction is called
var title = tiddler.fields.title, newInfo;
var title = tiddler.fields.title, newInfo, pathFilters, extFilters;
if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")){
pathFilters = this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n");
}
if(this.wiki.tiddlerExists("$:/config/FileSystemExtensions")){
extFilters = this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n");
}
newInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{
directory: this.boot.wikiTiddlersPath,
pathFilters: this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n"),
extFilters: this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n"),
pathFilters: pathFilters,
extFilters: extFilters,
wiki: this.wiki,
fileInfo: this.boot.files[title],
originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title, "")

View File

@ -14,6 +14,15 @@ The main module of the Jasmine test plugin for TiddlyWiki5
var TEST_TIDDLER_FILTER = "[type[application/javascript]tag[$:/tags/test-spec]]";
// Ensure this startup module is executed in the right order.
// Jasmine calls `process.exit()` with a non-zero exit code if there's
// any failed tests. Because of that, we want to make sure all critical
// startup modules are run before this one.
// * The "commands" module handles the --rendertiddler command-line flag,
// which is typically given to export an HTML file that can be opened with
// a browser to run tests.
exports.after = ["commands"];
/*
Startup function for running tests