mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-14 13:54:50 +00:00
Initial Commit
This commit is contained in:
parent
fa9bfa07a0
commit
c1a9215677
108
core/modules/parsers/wikiparser/rules/conditional.js
Normal file
108
core/modules/parsers/wikiparser/rules/conditional.js
Normal file
@ -0,0 +1,108 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/conditional.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Conditional shortcut syntax
|
||||
|
||||
```
|
||||
This is a {% if [{something}] %}Elephant{% elseif [{else}] %}Pelican{% else %}Crocodile{% endif %}
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "conditional";
|
||||
exports.types = {inline: true, block: false};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /\{\%\s*if\s+/mg;
|
||||
this.terminateIfRegExp = /\%\}/mg;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
// Look for the next {% if shortcut
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
// If not found then return no match
|
||||
if(!this.match) {
|
||||
return undefined;
|
||||
}
|
||||
// Check for the next %}
|
||||
this.terminateIfRegExp.lastIndex = this.match.index;
|
||||
this.terminateIfMatch = this.terminateIfRegExp.exec(this.parser.source);
|
||||
// If not found then return no match
|
||||
if(!this.terminateIfMatch) {
|
||||
return undefined;
|
||||
}
|
||||
// Return the position at which the construction was found
|
||||
return this.match.index;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get the filter condition
|
||||
var filterCondition = this.parser.source.substring(this.match.index + this.match[0].length,this.terminateIfMatch.index);
|
||||
// Advance the parser position to past the %}
|
||||
this.parser.pos = this.terminateIfMatch.index + this.terminateIfMatch[0].length;
|
||||
// Create the list widget
|
||||
var listWidget = {
|
||||
type: "list",
|
||||
tag: "$list",
|
||||
isBlock: this.is.block,
|
||||
children: [
|
||||
{
|
||||
type: "list-template",
|
||||
tag: "$list-template"
|
||||
},
|
||||
{
|
||||
type: "list-empty",
|
||||
tag: "$list-empty"
|
||||
}
|
||||
]
|
||||
};
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"filter",filterCondition);
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"variable","condition");
|
||||
$tw.utils.addAttributeToParseTreeNode(listWidget,"limit","1");
|
||||
// Parse the body looking for else or endif
|
||||
var reEndString = "\\{\\%\\s*(endif)\\s*\\%\\}|\\{\\%\\s*(else)\\s*\\%\\}",
|
||||
ex;
|
||||
if(this.is.block) {
|
||||
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
|
||||
} else {
|
||||
var reEnd = new RegExp(reEndString,"mg");
|
||||
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
|
||||
}
|
||||
// Put the body into the list template
|
||||
listWidget.children[0].children = ex.tree;
|
||||
// Check for an else or elseif
|
||||
if(ex.match) {
|
||||
if(ex.match[1] === "endif") {
|
||||
// Nothing to do if we just found an endif
|
||||
} else if(ex.match[2] === "else") {
|
||||
// If we found an else then we need to parse the body looking for the endif
|
||||
var reEndString = "\\{\\%\\s*(endif)\\s*\\%\\}",
|
||||
ex;
|
||||
if(this.is.block) {
|
||||
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
|
||||
} else {
|
||||
var reEnd = new RegExp(reEndString,"mg");
|
||||
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
|
||||
}
|
||||
// Put the parsed content inside the list empty template
|
||||
listWidget.children[1].children = ex.tree;
|
||||
}
|
||||
}
|
||||
// Return the parse tree node
|
||||
return [listWidget];
|
||||
};
|
||||
|
||||
})();
|
@ -264,11 +264,21 @@ WikiParser.prototype.parseBlocksUnterminated = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Parse blocks of text until a terminating regexp is encountered
|
||||
Parse blocks of text until a terminating regexp is encountered. Wrapper for parseBlocksTerminatedExtended that just returns the parse tree
|
||||
*/
|
||||
WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
|
||||
var ex = this.parseBlocksTerminatedExtended(terminatorRegExpString);
|
||||
return ex.tree;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse blocks of text until a terminating regexp is encountered
|
||||
*/
|
||||
WikiParser.prototype.parseBlocksTerminatedExtended = function(terminatorRegExpString) {
|
||||
var terminatorRegExp = new RegExp("(" + terminatorRegExpString + ")","mg"),
|
||||
tree = [];
|
||||
result = {
|
||||
tree: []
|
||||
};
|
||||
// Skip any whitespace
|
||||
this.skipWhitespace();
|
||||
// Check if we've got the end marker
|
||||
@ -277,7 +287,7 @@ WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
|
||||
// Parse the text into blocks
|
||||
while(this.pos < this.sourceLength && !(match && match.index === this.pos)) {
|
||||
var blocks = this.parseBlock(terminatorRegExpString);
|
||||
tree.push.apply(tree,blocks);
|
||||
result.tree.push.apply(result.tree,blocks);
|
||||
// Skip any whitespace
|
||||
this.skipWhitespace();
|
||||
// Check if we've got the end marker
|
||||
@ -286,8 +296,9 @@ WikiParser.prototype.parseBlocksTerminated = function(terminatorRegExpString) {
|
||||
}
|
||||
if(match && match.index === this.pos) {
|
||||
this.pos = match.index + match[0].length;
|
||||
result.match = match;
|
||||
}
|
||||
return tree;
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -330,6 +341,11 @@ WikiParser.prototype.parseInlineRunUnterminated = function(options) {
|
||||
};
|
||||
|
||||
WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,options) {
|
||||
var ex = this.parseInlineRunTerminatedExtended(terminatorRegExp,options);
|
||||
return ex.tree;
|
||||
};
|
||||
|
||||
WikiParser.prototype.parseInlineRunTerminatedExtended = function(terminatorRegExp,options) {
|
||||
options = options || {};
|
||||
var tree = [];
|
||||
// Find the next occurrence of the terminator
|
||||
@ -349,7 +365,10 @@ WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
|
||||
if(options.eatTerminator) {
|
||||
this.pos += terminatorMatch[0].length;
|
||||
}
|
||||
return tree;
|
||||
return {
|
||||
match: terminatorMatch,
|
||||
tree: tree
|
||||
};
|
||||
}
|
||||
}
|
||||
// Process any inline rule, along with the text preceding it
|
||||
@ -373,7 +392,9 @@ WikiParser.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
|
||||
this.pushTextWidget(tree,this.source.substr(this.pos),this.pos,this.sourceLength);
|
||||
}
|
||||
this.pos = this.sourceLength;
|
||||
return tree;
|
||||
return {
|
||||
tree: tree
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -60,6 +60,7 @@ ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ListWidget.prototype.execute = function() {
|
||||
var self = this;
|
||||
// Get our attributes
|
||||
this.template = this.getAttribute("template");
|
||||
this.editTemplate = this.getAttribute("editTemplate");
|
||||
@ -67,6 +68,16 @@ ListWidget.prototype.execute = function() {
|
||||
this.counterName = this.getAttribute("counter");
|
||||
this.storyViewName = this.getAttribute("storyview");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
||||
this.explicitListTemplate = null;
|
||||
this.explicitEmptyTemplate = null;
|
||||
$tw.utils.each(this.parseTreeNode.children,function(node) {
|
||||
if(node.type === "list-template") {
|
||||
self.explicitListTemplate = node.children;
|
||||
} else if(node.type === "list-empty") {
|
||||
self.explicitEmptyTemplate = node.children;
|
||||
}
|
||||
});
|
||||
// Compose the list elements
|
||||
this.list = this.getTiddlerList();
|
||||
var members = [],
|
||||
@ -86,17 +97,29 @@ ListWidget.prototype.execute = function() {
|
||||
};
|
||||
|
||||
ListWidget.prototype.getTiddlerList = function() {
|
||||
var limit = $tw.utils.getInt(this.getAttribute("limit",""),undefined);
|
||||
var defaultFilter = "[!is[system]sort[title]]";
|
||||
return this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
|
||||
var results = this.wiki.filterTiddlers(this.getAttribute("filter",defaultFilter),this);
|
||||
if(limit !== undefined) {
|
||||
if(limit >= 0) {
|
||||
results = results.slice(0,limit);
|
||||
} else {
|
||||
results = results.slice(limit);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
var parser,
|
||||
emptyMessage = this.getAttribute("emptyMessage","");
|
||||
// this.wiki.parseText() calls
|
||||
// new Parser(..), which should only be done, if needed, because it's heavy!
|
||||
if (emptyMessage === "") {
|
||||
return [];
|
||||
emptyMessage = this.getAttribute("emptyMessage");
|
||||
// If emptyMessage attribute is not present or empty then look for an explicit empty template
|
||||
if(!emptyMessage) {
|
||||
if(this.explicitEmptyTemplate) {
|
||||
return this.explicitEmptyTemplate;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
parser = this.wiki.parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
if(parser) {
|
||||
@ -122,8 +145,14 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
|
||||
if(template) {
|
||||
templateTree = [{type: "transclude", attributes: {tiddler: {type: "string", value: template}}}];
|
||||
} else {
|
||||
// Check for child nodes of the list widget
|
||||
if(this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
// Check for a <$list-item> widget
|
||||
if(this.explicitListTemplate) {
|
||||
templateTree = this.explicitListTemplate;
|
||||
} else {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
}
|
||||
} else {
|
||||
// Default template is a link to the title
|
||||
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||
|
26
editions/test/tiddlers/tests/data/conditionals/Basic.tid
Normal file
26
editions/test/tiddlers/tests/data/conditionals/Basic.tid
Normal file
@ -0,0 +1,26 @@
|
||||
title: Conditionals/Basic
|
||||
description: Basic conditional shortcut syntax
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Text
|
||||
|
||||
This is a {% if [<something>match[one]] %}Elephant{% endif %}, I think.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$let something="one">
|
||||
{{Text}}
|
||||
</$let>
|
||||
|
||||
<$let something="two">
|
||||
{{Text}}
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
This is a Elephant, I think.
|
||||
</p><p>
|
||||
This is a , I think.
|
||||
</p>
|
26
editions/test/tiddlers/tests/data/conditionals/Else.tid
Normal file
26
editions/test/tiddlers/tests/data/conditionals/Else.tid
Normal file
@ -0,0 +1,26 @@
|
||||
title: Conditionals/Else
|
||||
description: Else conditional shortcut syntax
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Text
|
||||
|
||||
This is a {% if [<something>match[one]] %}Elephant{% else %}Crocodile{% endif %}, I think.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$let something="one">
|
||||
{{Text}}
|
||||
</$let>
|
||||
|
||||
<$let something="two">
|
||||
{{Text}}
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
This is a Elephant, I think.
|
||||
</p><p>
|
||||
This is a Crocodile, I think.
|
||||
</p>
|
@ -0,0 +1,26 @@
|
||||
title: Conditionals/MissingEndif
|
||||
description: Conditional shortcut syntax with missing endif
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Text
|
||||
|
||||
This is a {% if [<something>match[one]] %}Elephant
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$let something="one">
|
||||
{{Text}}
|
||||
</$let>
|
||||
|
||||
<$let something="two">
|
||||
{{Text}}
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
This is a Elephant
|
||||
</p><p>
|
||||
This is a
|
||||
</p>
|
@ -0,0 +1,12 @@
|
||||
title: Conditionals/MultipleResults
|
||||
description: Check that multiple results from the filter are ignored
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
This is a {% if 1 2 3 4 5 6 %}Elephant{% endif %}, I think.
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>This is a Elephant, I think.</p>
|
39
editions/test/tiddlers/tests/data/conditionals/Nested.tid
Normal file
39
editions/test/tiddlers/tests/data/conditionals/Nested.tid
Normal file
@ -0,0 +1,39 @@
|
||||
title: Conditionals/Nested
|
||||
description: Nested conditional shortcut syntax
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\procedure test(animal)
|
||||
{% if [<animal>match[Elephant]] %}
|
||||
It is an elephant
|
||||
{% else %}
|
||||
{% if [<animal>match[Giraffe]] %}
|
||||
It is a giraffe
|
||||
{% else %}
|
||||
It is completely unknown
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
\end
|
||||
|
||||
<<test "Giraffe">>
|
||||
|
||||
<<test "Elephant">>
|
||||
|
||||
<<test "Antelope">>
|
||||
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
|
||||
It is a giraffe
|
||||
|
||||
</p><p>
|
||||
It is an elephant
|
||||
</p><p>
|
||||
|
||||
It is completely unknown
|
||||
|
||||
</p>
|
@ -0,0 +1,29 @@
|
||||
title: ListWidget/WithExplicitTemplates
|
||||
description: List widget with explicit templates
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
\procedure test(filter)
|
||||
<$list filter=<<filter>>>
|
||||
<$list-template>
|
||||
<$text text=<<currentTiddler>>/>
|
||||
</$list-template>
|
||||
<$list-empty>
|
||||
None!
|
||||
</$list-empty>
|
||||
</$list>
|
||||
\end
|
||||
|
||||
<<test "1 2 3">>
|
||||
|
||||
<<test "">>
|
||||
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>123</p><p>None!</p>
|
@ -0,0 +1,33 @@
|
||||
title: ListWidget/WithExplicitTemplatesOverriddenByAttributes
|
||||
description: List widget with explicit templates
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
\procedure test(filter)
|
||||
<$list filter=<<filter>> emptyMessage="Zero" template="Template">
|
||||
<$list-template>
|
||||
<$text text=<<currentTiddler>>/>
|
||||
</$list-template>
|
||||
<$list-empty>
|
||||
None!
|
||||
</$list-empty>
|
||||
</$list>
|
||||
\end
|
||||
|
||||
<<test "1 2 3">>
|
||||
|
||||
<<test "">>
|
||||
|
||||
+
|
||||
title: Template
|
||||
|
||||
<$text text=<<currentTiddler>>/><$text text=<<currentTiddler>>/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>112233</p><p>Zero</p>
|
25
editions/test/tiddlers/tests/data/list-widget/WithLimit.tid
Normal file
25
editions/test/tiddlers/tests/data/list-widget/WithLimit.tid
Normal file
@ -0,0 +1,25 @@
|
||||
title: ListWidget/WithLimit
|
||||
description: List widget with limit
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
+
|
||||
title: Output
|
||||
|
||||
Zero: <$list filter="1 2 3 4" limit="0" template="Template"/>
|
||||
|
||||
One: <$list filter="1 2 3 4" limit="1" template="Template"/>
|
||||
|
||||
Two: <$list filter="1 2 3 4" limit="2" template="Template"/>
|
||||
|
||||
Minus Two: <$list filter="1 2 3 4" limit="-2" template="Template"/>
|
||||
|
||||
+
|
||||
title: Template
|
||||
|
||||
<$text text=<<currentTiddler>>/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>Zero: </p><p>One: 1</p><p>Two: 12</p><p>Minus Two: 34
|
||||
</p>
|
@ -1,6 +1,6 @@
|
||||
caption: list
|
||||
created: 20131024141900000
|
||||
modified: 20230725203601441
|
||||
modified: 20230831182949930
|
||||
tags: Widgets Lists
|
||||
title: ListWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@ -70,6 +70,8 @@ See GroupedLists for how to generate nested and grouped lists using the ListWidg
|
||||
|
||||
The content of the `<$list>` widget is an optional template to use for rendering each tiddler in the list.
|
||||
|
||||
<<.from-version "5.3.2">> If the widgets `<$list-template>` or `<$list-empty>` are found as immediate children of the <<.wid "ListWidget">> widget then the content of those widgets are used as the list item template and/or the empty template. Note that the <<.attr "emptyMessage">> and <<.attr "template">> attributes take precedence if they are present.
|
||||
|
||||
The action of the list widget depends on the results of the filter combined with several options for specifying the template:
|
||||
|
||||
* If the filter evaluates to an empty list, the text of the ''emptyMessage'' attribute is rendered, and all other templates are ignored
|
||||
@ -79,6 +81,7 @@ The action of the list widget depends on the results of the filter combined with
|
||||
|
||||
|!Attribute |!Description |
|
||||
|filter |The [[tiddler filter|Filters]] to display |
|
||||
|limit |<<.from-version "5.3.2">> Optional numeric limit for the number of results that are returned. Negative values will return the results from the end of the list |
|
||||
|template |The title of a template tiddler for transcluding each tiddler in the list. When no template is specified, the body of the ListWidget serves as the item template. With no body, a simple link to the tiddler is returned. |
|
||||
|editTemplate |An alternative template to use for [[DraftTiddlers|DraftMechanism]] in edit mode |
|
||||
|variable |The name for a [[variable|Variables]] in which the title of each listed tiddler is stored. Defaults to ''currentTiddler'' |
|
||||
|
@ -0,0 +1,30 @@
|
||||
created: 20230901122740573
|
||||
modified: 20230901123102263
|
||||
tags: WikiText
|
||||
title: Conditional Shortcut Syntax
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
The conditional shortcut syntax provides a convenient way to express if-then-else logic within WikiText. For example:
|
||||
|
||||
<$macrocall $name='wikitext-example-without-html'
|
||||
src='
|
||||
\procedure test(animal)
|
||||
{% if [<animal>match[Elephant]] %}
|
||||
It is an elephant
|
||||
{% else %}
|
||||
{% if [<animal>match[Giraffe]] %}
|
||||
It is a giraffe
|
||||
{% else %}
|
||||
It is completely unknown
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
\end
|
||||
|
||||
<<test "Giraffe">>
|
||||
|
||||
<<test "Elephant">>
|
||||
|
||||
<<test "Antelope">>
|
||||
'/>
|
||||
|
||||
Note that widgets and HTML elements must be within a single conditional block; it is not possible to start an element in one conditional block and end it in another.
|
Loading…
Reference in New Issue
Block a user