1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-25 09:30:28 +00:00

Merge branch 'master' into confetti-plugin

This commit is contained in:
jeremy@jermolene.com 2023-05-04 09:45:37 +01:00
commit 55a52c9d8d
147 changed files with 4030 additions and 655 deletions

View File

@ -25,20 +25,10 @@ exports.cascade = function(operationSubFunction,options) {
if(!filterFnList[index]) { if(!filterFnList[index]) {
filterFnList[index] = options.wiki.compileFilter(filter); filterFnList[index] = options.wiki.compileFilter(filter);
} }
var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),{ var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": widget.getVariable("currentTiddler","")
opts.variables = { }));
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
if(output.length !== 0) { if(output.length !== 0) {
result = output[0]; result = output[0];
return false; return false;

View File

@ -19,23 +19,13 @@ exports.filter = function(operationSubFunction,options) {
var resultsToRemove = [], var resultsToRemove = [],
index = 0; index = 0;
results.each(function(title) { results.each(function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": widget.getVariable("currentTiddler",""),
opts.variables = { "index": "" + index,
"currentTiddler": "" + title, "revIndex": "" + (results.length - 1 - index),
"..currentTiddler": widget.getVariable("currentTiddler"), "length": "" + results.length
"index": "" + index, }));
"revIndex": "" + (results.length - 1 - index),
"length": "" + results.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
if(filtered.length === 0) { if(filtered.length === 0) {
resultsToRemove.push(title); resultsToRemove.push(title);
} }

View File

@ -21,23 +21,13 @@ exports.map = function(operationSubFunction,options) {
flatten = (suffixes[0] && suffixes[0][0] === "flat") ? true : false; flatten = (suffixes[0] && suffixes[0][0] === "flat") ? true : false;
results.clear(); results.clear();
$tw.utils.each(inputTitles,function(title) { $tw.utils.each(inputTitles,function(title) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": widget.getVariable("currentTiddler",""),
opts.variables = { "index": "" + index,
"currentTiddler": "" + title, "revIndex": "" + (inputTitles.length - 1 - index),
"..currentTiddler": widget.getVariable("currentTiddler"), "length": "" + inputTitles.length
"index": "" + index, }));
"revIndex": "" + (inputTitles.length - 1 - index),
"length": "" + inputTitles.length
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
if(filtered.length && flatten) { if(filtered.length && flatten) {
$tw.utils.each(filtered,function(value) { $tw.utils.each(filtered,function(value) {
results.push(value); results.push(value);

View File

@ -18,24 +18,14 @@ exports.reduce = function(operationSubFunction,options) {
var accumulator = "", var accumulator = "",
index = 0; index = 0;
results.each(function(title) { results.each(function(title) {
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": widget.getVariable("currentTiddler"),
opts.variables = { "index": "" + index,
"currentTiddler": "" + title, "revIndex": "" + (results.length - 1 - index),
"..currentTiddler": widget.getVariable("currentTiddler"), "length": "" + results.length,
"index": "" + index, "accumulator": "" + accumulator
"revIndex": "" + (results.length - 1 - index), }));
"length": "" + results.length,
"accumulator": "" + accumulator
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
if(list.length > 0) { if(list.length > 0) {
accumulator = "" + list[0]; accumulator = "" + list[0];
} }

View File

@ -25,20 +25,10 @@ exports.sort = function(operationSubFunction,options) {
indexes = new Array(inputTitles.length), indexes = new Array(inputTitles.length),
compareFn; compareFn;
results.each(function(title) { results.each(function(title) {
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": widget.getVariable("currentTiddler")
opts.variables = { }));
"currentTiddler": "" + title,
"..currentTiddler": widget.getVariable("currentTiddler")
};
if(name in opts.variables) {
return opts.variables[name];
} else {
return widget.getVariable(name,opts);
}
}
});
sortKeys.push(key[0] || ""); sortKeys.push(key[0] || "");
}); });
results.clear(); results.clear();

View File

@ -255,19 +255,21 @@ exports.compileFilter = function(filterString) {
var operands = [], var operands = [],
operatorFunction; operatorFunction;
if(!operator.operator) { if(!operator.operator) {
// Use the "title" operator if no operator is specified
operatorFunction = filterOperators.title; operatorFunction = filterOperators.title;
} else if(!filterOperators[operator.operator]) { } else if(!filterOperators[operator.operator]) {
operatorFunction = filterOperators.field; // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
operatorFunction = filterOperators["[unknown]"];
} else { } else {
// Use the operator function
operatorFunction = filterOperators[operator.operator]; operatorFunction = filterOperators[operator.operator];
} }
$tw.utils.each(operator.operands,function(operand) { $tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) { if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) { } else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text); var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""}); operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || "";
} else { } else {
operand.value = operand.text; operand.value = operand.text;
} }

View File

@ -20,19 +20,10 @@ exports.filter = function(source,operator,options) {
results = [], results = [],
target = operator.prefix !== "!"; target = operator.prefix !== "!";
source(function(tiddler,title) { source(function(tiddler,title) {
var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{ var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),options.widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": options.widget.getVariable("currentTiddler","")
switch(name) { }));
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return options.widget.getVariable("currentTiddler");
default:
return options.widget.getVariable(name,opts);
}
}
});
if((list.length > 0) === target) { if((list.length > 0) === target) {
results.push(title); results.push(title);
} }

View File

@ -0,0 +1,32 @@
/*\
title: $:/core/modules/filters/function.js
type: application/javascript
module-type: filteroperator
Filter operator returning those input titles that are returned from a function
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source});
}
// Return the input list if the function wasn't found
var results = [];
source(function(tiddler,title) {
results.push(title);
});
return results;
};
})();

View File

@ -26,27 +26,14 @@ exports.reduce = function(source,operator,options) {
accumulator = operator.operands[1] || ""; accumulator = operator.operands[1] || "";
for(var index=0; index<results.length; index++) { for(var index=0; index<results.length; index++) {
var title = results[index], var title = results[index],
list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{ list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),options.widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": options.widget.getVariable("currentTiddler"),
switch(name) { "accumulator": "" + accumulator,
case "currentTiddler": "index": "" + index,
return "" + title; "revIndex": "" + (results.length - 1 - index),
case "..currentTiddler": "length": "" + results.length
return options.widget.getVariable("currentTiddler"); }));
case "accumulator":
return "" + accumulator;
case "index":
return "" + index;
case "revIndex":
return "" + (results.length - 1 - index);
case "length":
return "" + results.length;
default:
return options.widget.getVariable(name,opts);
}
}
});
if(list.length > 0) { if(list.length > 0) {
accumulator = "" + list[0]; accumulator = "" + list[0];
} }

View File

@ -25,19 +25,10 @@ exports.sortsub = function(source,operator,options) {
inputTitles.push(title); inputTitles.push(title);
var r = filterFn.call(options.wiki,function(iterator) { var r = filterFn.call(options.wiki,function(iterator) {
iterator(options.wiki.getTiddler(title),title); iterator(options.wiki.getTiddler(title),title);
},{ },options.widget.makeFakeWidgetWithVariables({
getVariable: function(name,opts) { "currentTiddler": "" + title,
opts = opts || {}; "..currentTiddler": options.widget.getVariable("currentTiddler")
switch(name) { }));
case "currentTiddler":
return "" + title;
case "..currentTiddler":
return options.widget.getVariable("currentTiddler");
default:
return options.widget.getVariable(name,opts);
}
}
});
sortKeys.push(r[0] || ""); sortKeys.push(r[0] || "");
}); });
// Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays // Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays

View File

@ -0,0 +1,45 @@
/*\
title: $:/core/modules/filters/unknown.js
type: application/javascript
module-type: filteroperator
Filter operator for handling unknown filter operators.
Not intended to be used directly by end users, hence the square brackets around the name.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var fieldFilterOperatorFn = require("$:/core/modules/filters/field.js").field;
/*
Export our filter function
*/
exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator
if(operator.operator.charAt(0) === ".") {
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source});
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {
if(list.indexOf(title) === -1) {
results.push(title);
}
});
return results;
} else {
return list;
}
}
}
// Otherwise, use the "field" operator
return fieldFilterOperatorFn(source,operator,options);
};
})();

View File

@ -28,6 +28,8 @@ var AudioParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text}; element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
} }
this.tree = [element]; this.tree = [element];
this.source = text;
this.type = type;
}; };
exports["audio/ogg"] = AudioParser; exports["audio/ogg"] = AudioParser;

View File

@ -23,7 +23,7 @@ var BinaryParser = function(type,text,options) {
children: [{ children: [{
type: "transclude", type: "transclude",
attributes: { attributes: {
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE} "$tiddler": {type: "string", value: BINARY_WARNING_MESSAGE}
} }
}] }]
}; };
@ -38,7 +38,7 @@ var BinaryParser = function(type,text,options) {
children: [{ children: [{
type: "transclude", type: "transclude",
attributes: { attributes: {
tiddler: {type: "string", value: EXPORT_BUTTON_IMAGE} "$tiddler": {type: "string", value: EXPORT_BUTTON_IMAGE}
} }
}] }]
}; };
@ -64,6 +64,8 @@ var BinaryParser = function(type,text,options) {
children: [warn, link] children: [warn, link]
} }
this.tree = [element]; this.tree = [element];
this.source = text;
this.type = type;
}; };
exports["application/octet-stream"] = BinaryParser; exports["application/octet-stream"] = BinaryParser;

View File

@ -52,6 +52,8 @@ var CsvParser = function(type,text,options) {
tag = "td"; tag = "td";
this.tree[0].children[0].children[0].children.push(row); this.tree[0].children[0].children[0].children.push(row);
} }
this.source = text;
this.type = type;
}; };
exports["text/csv"] = CsvParser; exports["text/csv"] = CsvParser;

View File

@ -29,6 +29,8 @@ var HtmlParser = function(type,text,options) {
if($tw.wiki.getTiddlerText("$:/config/HtmlParser/DisableSandbox","no") !== "yes") { if($tw.wiki.getTiddlerText("$:/config/HtmlParser/DisableSandbox","no") !== "yes") {
this.tree[0].attributes.sandbox = {type: "string", value: $tw.wiki.getTiddlerText("$:/config/HtmlParser/SandboxTokens","")}; this.tree[0].attributes.sandbox = {type: "string", value: $tw.wiki.getTiddlerText("$:/config/HtmlParser/SandboxTokens","")};
} }
this.source = text;
this.type = type;
}; };
exports["text/html"] = HtmlParser; exports["text/html"] = HtmlParser;

View File

@ -28,6 +28,8 @@ var ImageParser = function(type,text,options) {
} }
} }
this.tree = [element]; this.tree = [element];
this.source = text;
this.type = type;
}; };
exports["image/svg+xml"] = ImageParser; exports["image/svg+xml"] = ImageParser;

View File

@ -123,6 +123,36 @@ exports.parseStringLiteral = function(source,pos) {
} }
}; };
/*
Returns an array of {name:} with an optional "default" property. Options include:
requireParenthesis: require the parameter definition to be wrapped in parenthesis
*/
exports.parseParameterDefinition = function(paramString,options) {
options = options || {};
if(options.requireParenthesis) {
var parenMatch = /^\s*\((.*)\)\s*$/g.exec(paramString);
if(!parenMatch) {
return [];
}
paramString = parenMatch[1];
}
var params = [],
reParam = /\s*([^:),\s]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|([^,"'\s]+)))?/mg,
paramMatch = reParam.exec(paramString);
while(paramMatch) {
// Save the parameter details
var paramInfo = {name: paramMatch[1]},
defaultValue = paramMatch[2] || paramMatch[3] || paramMatch[4] || paramMatch[5];
if(defaultValue !== undefined) {
paramInfo["default"] = defaultValue;
}
params.push(paramInfo);
// Look for the next parameter
paramMatch = reParam.exec(paramString);
}
return params;
};
exports.parseMacroParameters = function(node,source,pos) { exports.parseMacroParameters = function(node,source,pos) {
// Process parameters // Process parameters
var parameter = $tw.utils.parseMacroParameter(source,pos); var parameter = $tw.utils.parseMacroParameter(source,pos);
@ -175,7 +205,36 @@ exports.parseMacroParameter = function(source,pos) {
}; };
/* /*
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:} Look for a macro invocation. Returns null if not found, or {type: "transclude", attributes:, start:, end:}
*/
exports.parseMacroInvocationAsTransclusion = function(source,pos) {
var node = $tw.utils.parseMacroInvocation(source,pos);
if(node) {
var positionalName = 0,
transclusion = {
type: "transclude",
start: node.start,
end: node.end
};
$tw.utils.addAttributeToParseTreeNode(transclusion,"$variable",node.name);
$tw.utils.each(node.params,function(param) {
var name = param.name;
if(name) {
if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(transclusion,{name: name,type: "string", value: param.value, start: param.start, end: param.end});
} else {
$tw.utils.addAttributeToParseTreeNode(transclusion,{name: (positionalName++) + "",type: "string", value: param.value, start: param.start, end: param.end});
}
});
return transclusion;
}
return node;
};
/*
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, params:, start:, end:}
*/ */
exports.parseMacroInvocation = function(source,pos) { exports.parseMacroInvocation = function(source,pos) {
var node = { var node = {

View File

@ -25,6 +25,8 @@ var ImageParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:application/pdf;base64," + text}; element.attributes.src = {type: "string", value: "data:application/pdf;base64," + text};
} }
this.tree = [element]; this.tree = [element];
this.source = text;
this.type = type;
}; };
exports["application/pdf"] = ImageParser; exports["application/pdf"] = ImageParser;

View File

@ -20,6 +20,8 @@ var TextParser = function(type,text,options) {
language: {type: "string", value: type} language: {type: "string", value: type}
} }
}]; }];
this.source = text;
this.type = type;
}; };
exports["text/plain"] = TextParser; exports["text/plain"] = TextParser;

View File

@ -28,6 +28,8 @@ var VideoParser = function(type,text,options) {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text}; element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
} }
this.tree = [element]; this.tree = [element];
this.source = text;
this.type = type;
}; };
exports["video/ogg"] = VideoParser; exports["video/ogg"] = VideoParser;

View File

@ -0,0 +1,97 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/fnprocdef.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for function, procedure and widget definitions
```
\function name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\procedure name(param:defaultvalue,param2:defaultvalue)
definition text
\end
\widget $mywidget(param:defaultvalue,param2:defaultvalue)
definition text
\end
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "fnprocdef";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg;
};
/*
Parse the most recent match
*/
exports.parse = function() {
// Move past the macro name and parameters
this.parser.pos = this.matchRegExp.lastIndex;
// Parse the parameters
var params = [];
if(this.match[3]) {
params = $tw.utils.parseParameterDefinition(this.match[4]);
}
// Is this a multiline definition?
var reEnd;
if(this.match[5]) {
// If so, the end of the body is marked with \end
reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
reEnd = /($|\r?\n)/mg;
// Move past any whitespace
this.parser.pos = $tw.utils.skipWhiteSpace(this.parser.source,this.parser.pos);
}
// Find the end of the definition
reEnd.lastIndex = this.parser.pos;
var text,
endMatch = reEnd.exec(this.parser.source);
if(endMatch) {
text = this.parser.source.substring(this.parser.pos,endMatch.index);
this.parser.pos = endMatch.index + endMatch[0].length;
} else {
// We didn't find the end of the definition, so we'll make it blank
text = "";
}
// Save the macro definition
var parseTreeNodes = [{
type: "set",
attributes: {},
children: [],
params: params
}];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[2]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
if(this.match[1] === "function") {
parseTreeNodes[0].isFunctionDefinition = true;
} else if(this.match[1] === "procedure") {
parseTreeNodes[0].isProcedureDefinition = true;
} else if(this.match[1] === "widget") {
parseTreeNodes[0].isWidgetDefinition = true;
}
if(this.parser.configTrimWhiteSpace) {
parseTreeNodes[0].configTrimWhiteSpace = true;
}
return parseTreeNodes;
};
})();

View File

@ -93,9 +93,6 @@ exports.parseTag = function(source,pos,options) {
return null; return null;
} }
node.tag = token.match[1]; node.tag = token.match[1];
if(node.tag.slice(1).indexOf("$") !== -1) {
return null;
}
if(node.tag.charAt(0) === "$") { if(node.tag.charAt(0) === "$") {
node.type = node.tag.substr(1); node.type = node.tag.substr(1);
} }

View File

@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos; var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match // Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) { while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
var nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart); var nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(nextCall) { if(nextCall) {
var c = this.parser.source.charAt(nextCall.end); var c = this.parser.source.charAt(nextCall.end);
// Ensure EOL after parsed macro // Ensure EOL after parsed macro

View File

@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) {
var nextStart = startPos; var nextStart = startPos;
// Try parsing at all possible macrocall openers until we match // Try parsing at all possible macrocall openers until we match
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) { while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
this.nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart); this.nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart);
if(this.nextCall) { if(this.nextCall) {
return nextStart; return nextStart;
} }

View File

@ -77,16 +77,16 @@ exports.parse = function() {
text = ""; text = "";
} }
// Save the macro definition // Save the macro definition
return [{ var parseTreeNodes = [{
type: "set", type: "set",
attributes: { attributes: {},
name: {type: "string", value: this.match[1]},
value: {type: "string", value: text}
},
children: [], children: [],
params: params, params: params,
isMacroDefinition: true isMacroDefinition: true
}]; }];
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]);
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text);
return parseTreeNodes;
}; };
})(); })();

View File

@ -0,0 +1,60 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/parameters.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for parameter definitions
```
\parameters(param:defaultvalue,param2:defaultvalue)
definition text
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "parameters";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\parameters\s*\(([^)]*)\)\s*\r?\n/mg;
};
/*
Parse the most recent match
*/
exports.parse = function() {
// Move past the macro name and parameters
this.parser.pos = this.matchRegExp.lastIndex;
// Parse the parameters
var params = $tw.utils.parseParameterDefinition(this.match[1]);
var attributes = Object.create(null),
orderedAttributes = [];
$tw.utils.each(params,function(param) {
var name = param.name;
// Parameter names starting with dollar must be escaped to double dollars for the parameters widget
if(name.charAt(0) === "$") {
name = "$" + name;
}
var attribute = {name: name, type: "string", value: param["default"] || ""};
attributes[name] = attribute;
orderedAttributes.push(attribute);
});
// Save the macro definition
return [{
type: "parameters",
attributes: attributes,
orderedAttributes: orderedAttributes
}];
};
})();

View File

@ -23,7 +23,7 @@ exports.types = {block: true};
exports.init = function(parser) { exports.init = function(parser) {
this.parser = parser; this.parser = parser;
// Regexp to match // Regexp to match
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}(?:\r?\n|$)/mg; this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg;
}; };
exports.parse = function() { exports.parse = function() {
@ -31,13 +31,22 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details // Get the match details
var template = $tw.utils.trim(this.match[2]), var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]); textRef = $tw.utils.trim(this.match[1]),
params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget // Prepare the transclude widget
var transcludeNode = { var transcludeNode = {
type: "transclude", type: "transclude",
attributes: {}, attributes: {},
isBlock: true isBlock: true
}; };
$tw.utils.each(params,function(paramValue,index) {
var name = "" + index;
transcludeNode.attributes[name] = {
name: name,
type: "string",
value: paramValue
}
});
// Prepare the tiddler widget // Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode; var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) { if(textRef) {
@ -48,14 +57,14 @@ exports.parse = function() {
tiddlerNode = { tiddlerNode = {
type: "tiddler", type: "tiddler",
attributes: { attributes: {
tiddler: {type: "string", value: targetTitle} tiddler: {name: "tiddler", type: "string", value: targetTitle}
}, },
isBlock: true, isBlock: true,
children: [transcludeNode] children: [transcludeNode]
}; };
} }
if(template) { if(template) {
transcludeNode.attributes.tiddler = {type: "string", value: template}; transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) { if(textRef) {
return [tiddlerNode]; return [tiddlerNode];
} else { } else {
@ -63,12 +72,12 @@ exports.parse = function() {
} }
} else { } else {
if(textRef) { if(textRef) {
transcludeNode.attributes.tiddler = {type: "string", value: targetTitle}; transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) { if(targetField) {
transcludeNode.attributes.field = {type: "string", value: targetField}; transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
} }
if(targetIndex) { if(targetIndex) {
transcludeNode.attributes.index = {type: "string", value: targetIndex}; transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
} }
return [tiddlerNode]; return [tiddlerNode];
} else { } else {

View File

@ -23,7 +23,7 @@ exports.types = {inline: true};
exports.init = function(parser) { exports.init = function(parser) {
this.parser = parser; this.parser = parser;
// Regexp to match // Regexp to match
this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}/mg; this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg;
}; };
exports.parse = function() { exports.parse = function() {
@ -31,12 +31,21 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Get the match details // Get the match details
var template = $tw.utils.trim(this.match[2]), var template = $tw.utils.trim(this.match[2]),
textRef = $tw.utils.trim(this.match[1]); textRef = $tw.utils.trim(this.match[1]),
params = this.match[3] ? this.match[3].split("|") : [];
// Prepare the transclude widget // Prepare the transclude widget
var transcludeNode = { var transcludeNode = {
type: "transclude", type: "transclude",
attributes: {} attributes: {}
}; };
$tw.utils.each(params,function(paramValue,index) {
var name = "" + index;
transcludeNode.attributes[name] = {
name: name,
type: "string",
value: paramValue
}
});
// Prepare the tiddler widget // Prepare the tiddler widget
var tr, targetTitle, targetField, targetIndex, tiddlerNode; var tr, targetTitle, targetField, targetIndex, tiddlerNode;
if(textRef) { if(textRef) {
@ -47,13 +56,13 @@ exports.parse = function() {
tiddlerNode = { tiddlerNode = {
type: "tiddler", type: "tiddler",
attributes: { attributes: {
tiddler: {type: "string", value: targetTitle} tiddler: {name: "tiddler", type: "string", value: targetTitle}
}, },
children: [transcludeNode] children: [transcludeNode]
}; };
} }
if(template) { if(template) {
transcludeNode.attributes.tiddler = {type: "string", value: template}; transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template};
if(textRef) { if(textRef) {
return [tiddlerNode]; return [tiddlerNode];
} else { } else {
@ -61,12 +70,12 @@ exports.parse = function() {
} }
} else { } else {
if(textRef) { if(textRef) {
transcludeNode.attributes.tiddler = {type: "string", value: targetTitle}; transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle};
if(targetField) { if(targetField) {
transcludeNode.attributes.field = {type: "string", value: targetField}; transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField};
} }
if(targetIndex) { if(targetIndex) {
transcludeNode.attributes.index = {type: "string", value: targetIndex}; transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex};
} }
return [tiddlerNode]; return [tiddlerNode];
} else { } else {

View File

@ -32,6 +32,7 @@ options: see below:
parseAsInline: true to parse text as inline instead of block parseAsInline: true to parse text as inline instead of block
wiki: reference to wiki to use wiki: reference to wiki to use
_canonical_uri: optional URI of content if text is missing or empty _canonical_uri: optional URI of content if text is missing or empty
configTrimWhiteSpace: true to trim whitespace
*/ */
var WikiParser = function(type,text,options) { var WikiParser = function(type,text,options) {
this.wiki = options.wiki; this.wiki = options.wiki;
@ -46,7 +47,7 @@ var WikiParser = function(type,text,options) {
this.source = text || ""; this.source = text || "";
this.sourceLength = this.source.length; this.sourceLength = this.source.length;
// Flag for ignoring whitespace // Flag for ignoring whitespace
this.configTrimWhiteSpace = false; this.configTrimWhiteSpace = options.configTrimWhiteSpace !== undefined ? options.configTrimWhiteSpace : false;
// Parser mode // Parser mode
this.parseAsInline = options.parseAsInline; this.parseAsInline = options.parseAsInline;
// Set current parse position // Set current parse position

View File

@ -0,0 +1,30 @@
/*\
title: $:/core/modules/widgets/fill.js
type: application/javascript
module-type: widget
Sub-widget used by the transclude widget for specifying values for slots within transcluded content. It doesn't do anything by itself because the transclude widget only ever deals with the parse tree nodes, and doesn't instantiate the widget itself
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var FillWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
FillWidget.prototype = new Widget();
exports.fill = FillWidget;
})();

View File

@ -52,38 +52,44 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true}); var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true});
if(parser) { if(parser) {
var parseTreeNode = parser.tree[0]; var parseTreeNode = parser.tree[0];
while(parseTreeNode && parseTreeNode.type === "set") { while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) {
var node = { var node = {
type: "set", type: "set",
attributes: parseTreeNode.attributes, attributes: parseTreeNode.attributes,
params: parseTreeNode.params, params: parseTreeNode.params,
isMacroDefinition: parseTreeNode.isMacroDefinition isMacroDefinition: parseTreeNode.isMacroDefinition,
isFunctionDefinition: parseTreeNode.isFunctionDefinition,
isProcedureDefinition: parseTreeNode.isProcedureDefinition,
isWidgetDefinition: parseTreeNode.isWidgetDefinition,
configTrimWhiteSpace: parseTreeNode.configTrimWhiteSpace
}; };
if (parseTreeNode.isMacroDefinition) { if(parseTreeNode.type === "set" || parseTreeNode.type === "setvariable") {
// Macro definitions can be folded into if(parseTreeNode.isMacroDefinition || parseTreeNode.isProcedureDefinition || parseTreeNode.isWidgetDefinition || parseTreeNode.isFunctionDefinition) {
// current widget instead of adding // Macro definitions can be folded into
// another link to the chain. // current widget instead of adding
var widget = widgetPointer.makeChildWidget(node); // another link to the chain.
widget.computeAttributes(); var widget = widgetPointer.makeChildWidget(node);
widget.execute(); widget.computeAttributes();
// We SHALLOW copy over all variables widget.execute();
// in widget. We can't use // We SHALLOW copy over all variables
// $tw.utils.assign, because that copies // in widget. We can't use
// up the prototype chain, which we // $tw.utils.assign, because that copies
// don't want. // up the prototype chain, which we
$tw.utils.each(Object.keys(widget.variables), function(key) { // don't want.
widgetPointer.variables[key] = widget.variables[key]; $tw.utils.each(Object.keys(widget.variables), function(key) {
}); widgetPointer.variables[key] = widget.variables[key];
} else { });
widgetPointer.children = [widgetPointer.makeChildWidget(node)]; } else {
// No more regenerating children for widgetPointer.children = [widgetPointer.makeChildWidget(node)];
// this widget. If it needs to refresh, // No more regenerating children for
// it'll do so along with the the whole // this widget. If it needs to refresh,
// importvariable tree. // it'll do so along with the the whole
if (widgetPointer != this) { // importvariable tree.
widgetPointer.makeChildWidgets = function(){}; if (widgetPointer != this) {
widgetPointer.makeChildWidgets = function(){};
}
widgetPointer = widgetPointer.children[0];
} }
widgetPointer = widgetPointer.children[0];
} }
parseTreeNode = parseTreeNode.children && parseTreeNode.children[0]; parseTreeNode = parseTreeNode.children && parseTreeNode.children[0];
} }

View File

@ -37,7 +37,7 @@ MacroCallWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget Compute the internal state of the widget
*/ */
MacroCallWidget.prototype.execute = function() { MacroCallWidget.prototype.execute = function() {
// Get the parse type if specified this.macroName = this.parseTreeNode.name || this.getAttribute("$name"),
this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki"); this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki");
this.renderOutput = this.getAttribute("$output","text/html"); this.renderOutput = this.getAttribute("$output","text/html");
// Merge together the parameters specified in the parse tree with the specified attributes // Merge together the parameters specified in the parse tree with the specified attributes
@ -47,49 +47,26 @@ MacroCallWidget.prototype.execute = function() {
params.push({name: name, value: attribute}); params.push({name: name, value: attribute});
} }
}); });
// Get the macro value // Make a transclude widget
var macroName = this.parseTreeNode.name || this.getAttribute("$name"), var positionalName = 0,
variableInfo = this.getVariableInfo(macroName,{params: params}),
text = variableInfo.text,
parseTreeNodes;
// Are we rendering to HTML?
if(this.renderOutput === "text/html") {
// If so we'll return the parsed macro
// Check if we've already cached parsing this macro
var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser",
parser;
if(variableInfo.srcVariable && variableInfo.srcVariable[mode]) {
parser = variableInfo.srcVariable[mode];
} else {
parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
if(variableInfo.isCacheable && variableInfo.srcVariable) {
variableInfo.srcVariable[mode] = 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) {
var name = "__" + param.name + "__";
attributes[name] = {
name: name,
type: "string",
value: param.value
};
});
parseTreeNodes = [{ parseTreeNodes = [{
type: "vars", type: "transclude",
attributes: attributes, isBlock: this.parseTreeNode.isBlock
children: parseTreeNodes
}]; }];
} else if(this.renderOutput === "text/raw") { $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$variable",this.macroName);
parseTreeNodes = [{type: "text", text: text}]; $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$type",this.parseType);
} else { $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$output",this.renderOutput);
// Otherwise, we'll render the text $tw.utils.each(params,function(param) {
var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this}); var name = param.name;
parseTreeNodes = [{type: "text", text: plainText}]; if(name) {
} if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],name,param.value);
} else {
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],(positionalName++) + "",param.value);
}
});
// Construct the child widgets // Construct the child widgets
this.makeChildWidgets(parseTreeNodes); this.makeChildWidgets(parseTreeNodes);
}; };

View File

@ -0,0 +1,96 @@
/*\
title: $:/core/modules/widgets/parameters.js
type: application/javascript
module-type: widget
Widget for definition of transclusion parameters
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude;
var ParametersWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
ParametersWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
ParametersWidget.prototype.render = function(parent,nextSibling) {
// Call the constructor
Widget.call(this);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
ParametersWidget.prototype.execute = function() {
var self = this;
this.parametersDepth = Math.max(parseInt(this.getAttribute("$depth","1"),10) || 1,1);
// Find the parent transclusions
var pointer = this.parentWidget,
depth = this.parametersDepth;
while(pointer) {
if(pointer instanceof TranscludeWidget) {
depth--;
if(depth <= 0) {
break;
}
}
pointer = pointer.parentWidget;
}
// Process each parameter
if(pointer instanceof TranscludeWidget) {
// Get the value for each defined parameter
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(self.parseTreeNode),function(attr,index) {
var name = attr.name;
// If the attribute name starts with $$ then reduce to a single dollar
if(name.substr(0,2) === "$$") {
name = name.substr(1);
}
var value = pointer.getTransclusionParameter(name,index,self.getAttribute(attr.name,""));
self.setVariable(name,value);
});
// Assign any metaparameters
$tw.utils.each(pointer.getTransclusionMetaParameters(),function(getValue,name) {
var variableName = self.getAttribute("$" + name);
if(variableName) {
self.setVariable(variableName,getValue(name));
}
});
}
// Construct the child widgets
this.makeChildWidgets();
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
ParametersWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(Object.keys(changedAttributes).length) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports.parameters = ParametersWidget;
})();

View File

@ -48,7 +48,17 @@ SetWidget.prototype.execute = function() {
this.setValue = this.getAttribute("value"); this.setValue = this.getAttribute("value");
this.setEmptyValue = this.getAttribute("emptyValue"); this.setEmptyValue = this.getAttribute("emptyValue");
// Set context variable // Set context variable
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,!!this.parseTreeNode.isMacroDefinition); if(this.parseTreeNode.isMacroDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,true);
} else if(this.parseTreeNode.isFunctionDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isFunctionDefinition: true});
} else if(this.parseTreeNode.isProcedureDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isProcedureDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace});
} else if(this.parseTreeNode.isWidgetDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isWidgetDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace});
} else {
this.setVariable(this.setName,this.getValue());
}
// Construct the child widgets // Construct the child widgets
this.makeChildWidgets(); this.makeChildWidgets();
}; };

View File

@ -0,0 +1,82 @@
/*\
title: $:/core/modules/widgets/slot.js
type: application/javascript
module-type: widget
Widget for definition of slots within transcluded content. The values provided by the translusion are passed to the slot.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude;
var SlotWidget = function(parseTreeNode,options) {
// Initialise
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
SlotWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
SlotWidget.prototype.render = function(parent,nextSibling) {
// Call the constructor
Widget.call(this);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
SlotWidget.prototype.execute = function() {
var self = this;
this.slotName = this.getAttribute("$name");
this.slotDepth = parseInt(this.getAttribute("$depth","1"),10) || 1;
// Find the parent transclusions
var pointer = this.parentWidget,
depth = this.slotDepth;
while(pointer) {
if(pointer instanceof TranscludeWidget) {
depth--;
if(depth <= 0) {
break;
}
}
pointer = pointer.parentWidget;
}
var parseTreeNodes = [{type: "text", attributes: {text: {type: "string", value: "Missing slot reference!"}}}];
if(pointer instanceof TranscludeWidget) {
// Get the parse tree nodes comprising the slot contents
parseTreeNodes = pointer.getTransclusionSlotFill(this.slotName,this.parseTreeNode.children);
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
SlotWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes["$name"] || changedAttributes["$depth"]) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports.slot = SlotWidget;
})();

View File

@ -37,46 +37,347 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget Compute the internal state of the widget
*/ */
TranscludeWidget.prototype.execute = function() { TranscludeWidget.prototype.execute = function() {
// Get our parameters // Get our attributes, string parameters, and slot values into properties of the widget object
this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); this.collectAttributes();
this.transcludeSubTiddler = this.getAttribute("subtiddler"); this.collectStringParameters();
this.transcludeField = this.getAttribute("field"); this.collectSlotFillParameters();
this.transcludeIndex = this.getAttribute("index"); // Get the parse tree nodes that we are transcluding
this.transcludeMode = this.getAttribute("mode"); var target = this.getTransclusionTarget(),
this.recursionMarker = this.getAttribute("recursionMarker","yes"); parseTreeNodes = target.parseTreeNodes;
// Parse the text reference this.sourceText = target.text;
this.sourceType = target.type;
this.parseAsInline = target.parseAsInline;
// Process the transclusion according to the output type
switch(this.transcludeOutput || "text/html") {
case "text/html":
// No further processing required
break;
case "text/raw":
// Just return the raw text
parseTreeNodes = [{type: "text", text: this.sourceText}];
break;
default:
// text/plain
var plainText = this.wiki.renderText("text/plain",this.sourceType,this.sourceText,{parentWidget: this});
parseTreeNodes = [{type: "text", text: plainText}];
break;
}
// Set the legacy transclusion context variables only if we're not transcluding a variable
if(!this.transcludeVariable) {
var recursionMarker = this.makeRecursionMarker();
this.setVariable("transclusion",recursionMarker);
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};
/*
Collect the attributes we need, in the process determining whether we're being used in legacy mode
*/
TranscludeWidget.prototype.collectAttributes = function() {
var self = this;
// Detect legacy mode
this.legacyMode = true;
$tw.utils.each(this.attributes,function(value,name) {
if(name.charAt(0) === "$") {
self.legacyMode = false;
}
});
// Get the attributes for the appropriate mode
if(this.legacyMode) {
this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.transcludeSubTiddler = this.getAttribute("subtiddler");
this.transcludeField = this.getAttribute("field");
this.transcludeIndex = this.getAttribute("index");
this.transcludeMode = this.getAttribute("mode");
this.recursionMarker = this.getAttribute("recursionMarker","yes");
} else {
this.transcludeVariable = this.getAttribute("$variable");
this.transcludeType = this.getAttribute("$type");
this.transcludeOutput = this.getAttribute("$output","text/html");
this.transcludeTitle = this.getAttribute("$tiddler",this.getVariable("currentTiddler"));
this.transcludeSubTiddler = this.getAttribute("$subtiddler");
this.transcludeField = this.getAttribute("$field");
this.transcludeIndex = this.getAttribute("$index");
this.transcludeMode = this.getAttribute("$mode");
this.recursionMarker = this.getAttribute("$recursionMarker","yes");
}
};
/*
Collect string parameters
*/
TranscludeWidget.prototype.collectStringParameters = function() {
var self = this;
this.stringParametersByName = Object.create(null);
if(!this.legacyMode) {
$tw.utils.each(this.attributes,function(value,name) {
if(name.charAt(0) === "$") {
if(name.charAt(1) === "$") {
// Attributes starting $$ represent parameters starting with a single $
name = name.slice(1);
} else {
// Attributes starting with a single $ are reserved for the widget
return;
}
}
self.stringParametersByName[name] = value;
});
}
};
/*
Collect slot value parameters
*/
TranscludeWidget.prototype.collectSlotFillParameters = function() {
var self = this;
this.slotFillParseTrees = Object.create(null);
if(this.legacyMode) {
this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children;
} else {
this.slotFillParseTrees["ts-raw"] = this.parseTreeNode.children;
var noFillWidgetsFound = true,
searchParseTreeNodes = function(nodes) {
$tw.utils.each(nodes,function(node) {
if(node.type === "fill") {
if(node.attributes["$name"] && node.attributes["$name"].type === "string") {
var slotValueName = node.attributes["$name"].value;
self.slotFillParseTrees[slotValueName] = node.children || [];
}
noFillWidgetsFound = false;
} else {
searchParseTreeNodes(node.children);
}
});
};
searchParseTreeNodes(this.parseTreeNode.children);
if(noFillWidgetsFound) {
this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children;
}
}
};
/*
Get transcluded parse tree nodes as an object {parser:,text:,type:}
*/
TranscludeWidget.prototype.getTransclusionTarget = function() {
var self = this;
// Determine whether we're being used in inline or block mode
var parseAsInline = !this.parseTreeNode.isBlock; var parseAsInline = !this.parseTreeNode.isBlock;
if(this.transcludeMode === "inline") { if(this.transcludeMode === "inline") {
parseAsInline = true; parseAsInline = true;
} else if(this.transcludeMode === "block") { } else if(this.transcludeMode === "block") {
parseAsInline = false; parseAsInline = false;
} }
var parser = this.wiki.parseTextReference( var parser;
// Get the parse tree
if(this.transcludeVariable) {
// Transcluding a variable
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
srcVariable = variableInfo && variableInfo.srcVariable;
if(srcVariable) {
if(srcVariable.isFunctionDefinition) {
// Function to return parameters by name or position
var fnGetParam = function(name,index) {
// Parameter names starting with dollar must be escaped to double dollars
if(name.charAt(0) === "$") {
name = "$" + name;
}
// Look for the parameter by name
if(self.hasAttribute(name)) {
return self.getAttribute(name);
// Look for the parameter by index
} else if(self.hasAttribute(index + "")) {
return self.getAttribute(index + "");
} else {
return undefined;
}
},
result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || "";
parser = {
tree: [{
type: "text",
text: result
}],
source: result,
type: "text/vnd.tiddlywiki"
};
if(parseAsInline) {
parser.tree[0] = {
type: "text",
text: result
};
} else {
parser.tree[0] = {
type: "element",
tag: "p",
children: [{
type: "text",
text: result
}]
}
}
} else {
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
parser = srcVariable[cacheKey];
} else {
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
if(variableInfo.isCacheable) {
srcVariable[cacheKey] = parser;
}
}
}
if(parser) {
// Add parameters widget for procedures and custom widgets
if(srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition) {
parser = {
tree: [
{
type: "parameters",
children: parser.tree
}
],
source: parser.source,
type: parser.type
}
$tw.utils.each(srcVariable.params,function(param) {
var name = param.name;
// Parameter names starting with dollar must be escaped to double dollars
if(name.charAt(0) === "$") {
name = "$" + name;
}
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
});
} else {
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
parser = {
tree: [
{
type: "vars",
children: parser.tree
}
],
source: parser.source,
type: parser.type
}
$tw.utils.each(variableInfo.params,function(param) {
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],"__" + param.name + "__",param.value)
});
}
}
}
} else {
// Transcluding a text reference
parser = this.wiki.parseTextReference(
this.transcludeTitle, this.transcludeTitle,
this.transcludeField, this.transcludeField,
this.transcludeIndex, this.transcludeIndex,
{ {
parseAsInline: parseAsInline, parseAsInline: parseAsInline,
subTiddler: this.transcludeSubTiddler subTiddler: this.transcludeSubTiddler,
}), defaultType: this.transcludeType
parseTreeNodes = parser ? parser.tree : this.parseTreeNode.children; });
this.sourceText = parser ? parser.source : null;
this.parserType = parser? parser.type : null;
// Set context variables for recursion detection
var recursionMarker = this.makeRecursionMarker();
if(this.recursionMarker === "yes") {
this.setVariable("transclusion",recursionMarker);
} }
// Check for recursion // Return the parse tree
if(parser) { if(parser) {
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) { return {
parseTreeNodes = [{type: "error", attributes: { parser: parser,
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")} parseTreeNodes: parser.tree,
}}]; parseAsInline: parseAsInline,
text: parser.source,
type: parser.type
};
} else {
// If there's no parse tree then return the missing slot value
return {
parser: null,
parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []),
parseAsInline: parseAsInline,
text: null,
type: null
};
}
};
/*
Fetch all the string parameters as an ordered array of {name:, value:} where the name is optional
*/
TranscludeWidget.prototype.getOrderedTransclusionParameters = function() {
var result = [];
// Collect the parameters
for(var name in this.stringParametersByName) {
var value = this.stringParametersByName[name];
result.push({name: name, value: value});
}
// Sort numerical parameter names first
result.sort(function(a,b) {
var aIsNumeric = !isNaN(a.name),
bIsNumeric = !isNaN(b.name);
if(aIsNumeric && bIsNumeric) {
return a.name - b.name;
} else if(aIsNumeric) {
return -1;
} else if(bIsNumeric) {
return 1;
} else {
return a.name === b.name ? 0 : (a.name < b.name ? -1 : 1);
}
});
// Remove names from numerical parameters
$tw.utils.each(result,function(param,index) {
if(!isNaN(param.name)) {
delete param.name;
}
});
return result;
};
/*
Fetch the value of a parameter
*/
TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaultValue) {
if(name in this.stringParametersByName) {
return this.stringParametersByName[name];
} else {
var name = "" + index;
if(name in this.stringParametersByName) {
return this.stringParametersByName[name];
} }
} }
// Construct the child widgets return defaultValue;
this.makeChildWidgets(parseTreeNodes); };
/*
Get one of the special parameters to be provided by the parameters widget
*/
TranscludeWidget.prototype.getTransclusionMetaParameters = function() {
var self = this;
return {
"parseMode": function() {
return self.parseAsInline ? "inline" : "block";
},
"parseTreeNodes": function() {
return JSON.stringify(self.parseTreeNode.children || []);
},
"slotFillParseTreeNodes": function() {
return JSON.stringify(self.slotFillParseTrees);
},
"params": function() {
return JSON.stringify(self.stringParametersByName);
}
};
};
/*
Fetch the value of a slot
*/
TranscludeWidget.prototype.getTransclusionSlotFill = function(name,defaultParseTreeNodes) {
if(name && this.slotFillParseTrees[name] && this.slotFillParseTrees[name].length > 0) {
return this.slotFillParseTrees[name];
} else {
return defaultParseTreeNodes || [];
}
}; };
/* /*
@ -99,6 +400,7 @@ TranscludeWidget.prototype.makeRecursionMarker = function() {
}; };
TranscludeWidget.prototype.parserNeedsRefresh = function() { TranscludeWidget.prototype.parserNeedsRefresh = function() {
// Doesn't need to consider transcluded variables because a parent variable can't change once a widget has been created
var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler}); var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler});
return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType) return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType)
}; };
@ -108,7 +410,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/ */
TranscludeWidget.prototype.refresh = function(changedTiddlers) { TranscludeWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(); var changedAttributes = this.computeAttributes();
if(($tw.utils.count(changedAttributes) > 0) || (changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) { if(($tw.utils.count(changedAttributes) > 0) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
} else { } else {

View File

@ -41,10 +41,7 @@ Widget.prototype.initialise = function(parseTreeNode,options) {
this.parseTreeNode = parseTreeNode; this.parseTreeNode = parseTreeNode;
this.wiki = options.wiki; this.wiki = options.wiki;
this.parentWidget = options.parentWidget; this.parentWidget = options.parentWidget;
this.variables = Object.create(null); this.variables = Object.create(this.parentWidget ? this.parentWidget.variables : null);
if(this.parentWidget) {
Object.setPrototypeOf(this.variables,this.parentWidget.variables);
}
this.document = options.document; this.document = options.document;
this.attributes = {}; this.attributes = {};
this.children = []; this.children = [];
@ -92,9 +89,22 @@ name: name of the variable
value: value of the variable value: value of the variable
params: array of {name:, default:} for each parameter params: array of {name:, default:} for each parameter
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed) isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
options includes:
isProcedureDefinition: true if the variable is set via a \procedure pragma (and hence should not have variable substitution performed)
isFunctionDefinition: true if the variable is set via a \function pragma (and hence should not have variable substitution performed)
isWidgetDefinition: true if the variable is set via a \widget pragma (and hence should not have variable substitution performed)
*/ */
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition) { Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
this.variables[name] = {value: value, params: params, isMacroDefinition: !!isMacroDefinition}; options = options || {};
this.variables[name] = {
value: value,
params: params,
isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition,
isProcedureDefinition: !!options.isProcedureDefinition,
isWidgetDefinition: !!options.isWidgetDefinition,
configTrimWhiteSpace: !!options.configTrimWhiteSpace
};
}; };
/* /*
@ -104,6 +114,7 @@ options: see below
Options include Options include
params: array of {name:, value:} for each parameter params: array of {name:, value:} for each parameter
defaultValue: default value if the variable is not defined defaultValue: default value if the variable is not defined
allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent
Returns an object with the following fields: Returns an object with the following fields:
@ -112,21 +123,27 @@ text: text of variable, with parameters properly substituted
*/ */
Widget.prototype.getVariableInfo = function(name,options) { Widget.prototype.getVariableInfo = function(name,options) {
options = options || {}; options = options || {};
var actualParams = options.params || [], var self = this,
parentWidget = this.parentWidget; actualParams = options.params || [],
variable;
if(options.allowSelfAssigned) {
variable = this.variables[name];
} else {
variable = this.parentWidget && this.parentWidget.variables[name];
}
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain) // Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
if(parentWidget && name in parentWidget.variables) { if(variable) {
var variable = parentWidget.variables[name], var originalValue = variable.value,
originalValue = variable.value,
value = originalValue, value = originalValue,
params = this.resolveVariableParameters(variable.params,actualParams); params = [];
// Substitute any parameters specified in the definition // Only substitute parameter and variable references if this variable was defined with the \define pragma
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
// Only substitute variable references if this variable was defined with the \define pragma
if(variable.isMacroDefinition) { if(variable.isMacroDefinition) {
value = this.substituteVariableReferences(value,options); params = self.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
value = self.substituteVariableReferences(value,options);
} }
return { return {
text: value, text: value,
@ -136,8 +153,13 @@ Widget.prototype.getVariableInfo = function(name,options) {
}; };
} }
// If the variable doesn't exist in the parent widget then look for a macro module // If the variable doesn't exist in the parent widget then look for a macro module
var text = this.evaluateMacroModule(name,actualParams);
if(text === undefined) {
text = options.defaultValue;
}
return { return {
text: this.evaluateMacroModule(name,actualParams,options.defaultValue) text: text,
srcVariable: {}
}; };
}; };
@ -148,6 +170,11 @@ Widget.prototype.getVariable = function(name,options) {
return this.getVariableInfo(name,options).text; return this.getVariableInfo(name,options).text;
}; };
/*
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
formalParams - Array of {name:,default:} (default value is optional)
actualParams - Array of string values or {name:,value:} (name is optional)
*/
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) { Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
formalParams = formalParams || []; formalParams = formalParams || [];
actualParams = actualParams || []; actualParams = actualParams || [];
@ -160,7 +187,7 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
paramInfo = formalParams[p]; paramInfo = formalParams[p];
paramValue = undefined; paramValue = undefined;
for(var m=0; m<actualParams.length; m++) { for(var m=0; m<actualParams.length; m++) {
if(actualParams[m].name === paramInfo.name) { if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
paramValue = actualParams[m].value; paramValue = actualParams[m].value;
} }
} }
@ -169,7 +196,8 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
nextAnonParameter++; nextAnonParameter++;
} }
if(paramValue === undefined && nextAnonParameter < actualParams.length) { if(paramValue === undefined && nextAnonParameter < actualParams.length) {
paramValue = actualParams[nextAnonParameter++].value; var param = actualParams[nextAnonParameter++];
paramValue = typeof param === "string" ? param : param.value;
} }
// If we've still not got a value, use the default, if any // If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || ""; paramValue = paramValue || paramInfo["default"] || "";
@ -263,12 +291,103 @@ Widget.prototype.getStateQualifier = function(name) {
}; };
/* /*
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed Make a fake widget with specified variables, suitable for variable lookup in filters
*/ */
Widget.prototype.computeAttributes = function() { Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
var self = this;
return {
getVariable: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return variables[name];
} else {
opts = opts || {};
opts.variables = variables;
return self.getVariable(name,opts);
};
},
getVariableInfo: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return {
text: variables[name]
};
} else {
opts = opts || {};
opts.variables = variables;
return self.getVariableInfo(name,opts);
};
},
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables,
evaluateVariable: self.evaluateVariable,
resolveVariableParameters: self.resolveVariableParameters,
wiki: self.wiki
};
};
/*
Evaluate a variable and associated actual parameters and result the resulting array.
The way that the variable is evaluated depends upon its type:
* Functions are evaluated as parameterised filter strings
* Macros are returned as plain text with substitution of parameters
* Procedures and widgets are returned as plain text
Options are:
params - the actual parameters may be one of:
* an array of values that may be an anonymous string value, or a {name:, value:} pair
* a hashmap of {name: value} pairs
* a function invoked with parameters (name,index) that returns a parameter value by name or position
source - iterator for source tiddlers
*/
Widget.prototype.evaluateVariable = function(name,options) {
options = options || {};
var params = options.params || [];
// Get the details of the variable (includes processing text substitution for macros
var variableInfo = this.getVariableInfo(name,{params: params,defaultValue: ""});
// Process function parameters
var variables = Object.create(null);
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
// Apply default parameter values
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
if(param["default"]) {
variables[param.name] = param["default"];
}
});
if($tw.utils.isArray(params)) {
// Parameters are an array of values or {name:, value:} pairs
$tw.utils.each(this.resolveVariableParameters(variableInfo.srcVariable.params,params),function(param) {
variables[param.name] = param.value;
});
} else if(typeof params === "function") {
// Parameters are passed via a function
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
variables[param.name] = params(param.name,index) || param["default"] || "";
});
} else {
// Parameters are a hashmap
$tw.utils.each(params,function(value,name) {
variables[name] = value;
});
}
return this.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables),options.source);
} else {
return [variableInfo.text];
}
};
/*
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed.
Options include:
filterFn: only include attributes where filterFn(name) returns true
*/
Widget.prototype.computeAttributes = function(options) {
options = options || {};
var changedAttributes = {}, var changedAttributes = {},
self = this; self = this;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) { $tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(options.filterFn) {
if(!options.filterFn(name)) {
return;
}
}
var value = self.computeAttribute(attribute); var value = self.computeAttribute(attribute);
if(self.attributes[name] !== value) { if(self.attributes[name] !== value) {
self.attributes[name] = value; self.attributes[name] = value;
@ -279,13 +398,21 @@ Widget.prototype.computeAttributes = function() {
}; };
Widget.prototype.computeAttribute = function(attribute) { Widget.prototype.computeAttribute = function(attribute) {
var value; var self = this,
value;
if(attribute.type === "filtered") { if(attribute.type === "filtered") {
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || ""; value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
} else if(attribute.type === "indirect") { } else if(attribute.type === "indirect") {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")); value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
} else if(attribute.type === "macro") { } else if(attribute.type === "macro") {
value = this.getVariable(attribute.value.name,{params: attribute.value.params}); var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
// It is a function definition. Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter
var paramArray = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params);
value = this.evaluateVariable(attribute.value.name,{params: paramArray})[0] || "";
} else {
value = variableInfo.text;
}
} else { // String attribute } else { // String attribute
value = attribute.value; value = attribute.value;
} }
@ -413,7 +540,34 @@ options include:
variables: optional hashmap of variables to wrap around the widget variables: optional hashmap of variables to wrap around the widget
*/ */
Widget.prototype.makeChildWidget = function(parseTreeNode,options) { Widget.prototype.makeChildWidget = function(parseTreeNode,options) {
var self = this;
options = options || {}; options = options || {};
// Check whether this node type is defined by a custom widget definition
var variableDefinitionName = "$" + parseTreeNode.type;
if(this.variables[variableDefinitionName]) {
var isOverrideable = function() {
// Widget is overrideable if it has a double dollar user defined name, or if it is an existing JS widget and we're not in safe mode
return parseTreeNode.type.charAt(0) === "$" || (!!self.widgetClasses[parseTreeNode.type] && !$tw.safeMode);
};
if(!parseTreeNode.isNotRemappable && isOverrideable()) {
var variableInfo = this.getVariableInfo(variableDefinitionName,{allowSelfAssigned: true});
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.value && variableInfo.srcVariable.isWidgetDefinition) {
var newParseTreeNode = {
type: "transclude",
children: parseTreeNode.children,
isBlock: parseTreeNode.isBlock
};
$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,"$variable",variableDefinitionName);
$tw.utils.each(parseTreeNode.attributes,function(attr,name) {
// If the attribute starts with a dollar then add an extra dollar so that it doesn't clash with the $xxx attributes of transclude
name = name.charAt(0) === "$" ? "$" + name : name;
$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,$tw.utils.extend({},attr,{name: name}));
});
parseTreeNode = newParseTreeNode;
}
}
}
// Get the widget class for this node type
var WidgetClass = this.widgetClasses[parseTreeNode.type]; var WidgetClass = this.widgetClasses[parseTreeNode.type];
if(!WidgetClass) { if(!WidgetClass) {
WidgetClass = this.widgetClasses.text; WidgetClass = this.widgetClasses.text;

View File

@ -988,7 +988,8 @@ exports.parseText = function(type,text,options) {
return new Parser(type,text,{ return new Parser(type,text,{
parseAsInline: options.parseAsInline, parseAsInline: options.parseAsInline,
wiki: this, wiki: this,
_canonical_uri: options._canonical_uri _canonical_uri: options._canonical_uri,
configTrimWhiteSpace: options.configTrimWhiteSpace
}); });
}; };
@ -1028,10 +1029,11 @@ exports.parseTextReference = function(title,field,index,options) {
}; };
exports.getTextReferenceParserInfo = function(title,field,index,options) { exports.getTextReferenceParserInfo = function(title,field,index,options) {
var tiddler, var defaultType = options.defaultType || "text/vnd.tiddlywiki",
tiddler,
parserInfo = { parserInfo = {
sourceText : null, sourceText : null,
parserType : "text/vnd.tiddlywiki" parserType : defaultType
}; };
if(options.subTiddler) { if(options.subTiddler) {
tiddler = this.getSubTiddler(title,options.subTiddler); tiddler = this.getSubTiddler(title,options.subTiddler);
@ -1077,19 +1079,20 @@ exports.makeWidget = function(parser,options) {
children: [] children: []
}, },
currWidgetNode = widgetNode; currWidgetNode = widgetNode;
// Create set variable widgets for each variable // Create let variable widget for variables
$tw.utils.each(options.variables,function(value,name) { if($tw.utils.count(options.variables) > 0) {
var setVariableWidget = { var letVariableWidget = {
type: "set", type: "let",
attributes: { attributes: {
name: {type: "string", value: name},
value: {type: "string", value: value}
}, },
children: [] children: []
}; };
currWidgetNode.children = [setVariableWidget]; $tw.utils.each(options.variables,function(value,name) {
currWidgetNode = setVariableWidget; $tw.utils.addAttributeToParseTreeNode(letVariableWidget,name,"" + value);
}); });
currWidgetNode.children = [letVariableWidget];
currWidgetNode = letVariableWidget;
}
// Add in the supplied parse tree nodes // Add in the supplied parse tree nodes
currWidgetNode.children = parser ? parser.tree : []; currWidgetNode.children = parser ? parser.tree : [];
// Create the widget // Create the widget

View File

@ -0,0 +1,48 @@
title: $:/core/ui/VisibleTransclude
<!--
Import this component to make all the child transclusions visible.
Block transclusions are shown in red, and inline transclusions are shown in green.
-->
\widget $transclude()
<!-- Use a parameters widget so that we can access the `$params` data -->
<$parameters tiddler="" $$tiddler="" mode="" $$mode="" $parseMode="@parseMode" $params="@params">
<!-- Replicate the logic of the transclude widget to determine the output mode, and hence the tag and colour to use for output -->
<$let
mode={{{ [[$mode]is[variable]then<$mode>!is[blank]] :else[[mode]is[variable]then<mode>!is[blank]] :else[<@parseMode>] }}}
outputTag={{{ [<mode>match[inline]then[span]else[div]] }}}
outputColour={{{ [<mode>match[inline]then[green]else[red]] }}}
>
<!-- Use divs or spans according to the mode -->
<$genesis $type=<<outputTag>> style="color:white;padding:4px;" style.background=<<outputColour>>>
<$genesis $type=<<outputTag>> style="display: inline-block;">
<div style="background:white;color:black;font-size: 12px;line-height:1.2;text-align:left;font-weight:normal;padding:4px;margin:4px;">
<!-- Render the parameters to the transclusion -->
<$list filter="[<@params>jsonindexes[]]" emptyMessage="(none)">
<div>
<$text text=<<currentTiddler>>/><$text text=": "/><$text text={{{ [<@params>jsonget<currentTiddler>] }}}/>
</div>
</$list>
</div>
</$genesis>
<$genesis $type=<<outputTag>> style="background:white;color:black;padding:4px;">
<!-- Look for a parameter starting with $ to determine if we are in legacy mode -->
<$list filter="[<@params>jsonindexes[]] :filter[<currentTiddler>prefix[$]] +[limit[1]]" variable="ignore" emptyMessage="""
<!-- Legacy mode: we render the transclusion without a dollar sign for recursionMarker and mode -->
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" recursionMarker="no" mode=<<mode>>>
<!-- Reach back up to the grandparent transclusion to get the correct slot value -->
<$slot $name="ts-raw" $depth="2"/>
</$genesis>
""">
<!-- Non-legacy mode: we use dollar signs for the recursionMarker and mode -->
<$genesis $type="$transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>]" $$recursionMarker="no" $$mode=<<mode>>>
<!-- Reach back up to the grandparent transclusion to get the correct slot fill value -->
<$slot $name="ts-raw" $depth="2"/>
</$genesis>
</$list>
</$genesis>
</$genesis>
</$let>
</$parameters>
\end

View File

@ -60,4 +60,4 @@ code-body: yes
</div> </div>
</$let> </$let>
</$qualify> </$qualify>
\end \end

View File

@ -1,60 +0,0 @@
caption: 5.2.8
created: 20230326093239710
modified: 20230326093239710
tags: ReleaseNotes
title: Release 5.2.8
type: text/vnd.tiddlywiki
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.7...master]]//
! Major Improvements
! Translation Improvements
Improvements to the following translations:
*
! Plugin Improvements
*
! Accessibility Improvements
*
! Usability Improvements
*
! Widget Improvements
*
! Filter improvements
*
! Hackability Improvements
*
! Bug Fixes
*
! Node.js Improvements
*
! Performance 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 """
""">>

View File

@ -0,0 +1,83 @@
caption: 5.3.0
created: 20230419103154368
modified: 20230419103154368
tags: ReleaseNotes
title: Release 5.3.0
type: text/vnd.tiddlywiki
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/master...parameterised-transclusions]]//
! About v5.3.0
This pre-release introduces a number of significant improvements and new features related to some of TiddlyWiki's most fundamental components: macros, widgets, operators and transclusion.
! Introduction to v5.3.0
The motivation of these changes is to fix one of ~TiddlyWiki 5's early design flaws: the reliance on macros using textual substitution as the primary way to modularise and reuse wikitext and filters.
Experience has shown that while macros are a good match for a small number of tasks, they are brittle and error prone for many common operations. See [[Macro Pitfalls]] for a discussion of the problems that accompany this approach. Over the years we have introduced mitigations for the worst problems but these have come at a cost of increased complexity.
The changes in this release provide powerful new ways to achieve common tasks, and unlock completely new capabilities that were previously impossible in wikitext.
* [[Procedures]], which are essentially what macros should have been; they work in exactly the same way except that parameters are exposed as simple variables (without the double underscores) and no textual substitution takes place
* [[Custom Widgets]], allowing the creation of widgets in wikitext, and the redefinition of built-in widgets
* [[Functions]], a new way to encapsulate filter expressions with named parameters, including the ability to make custom filter operators
* Parameterised [[Transclusions|Transclusion]], allowing strings and wikitext trees to be passed to transclusions
The approach taken by this release is to add new functionality by extending and augmenting the system without disturbing existing functionality. All of these changes are thus intended to be backwards compatible. While they represent a new field of opportunities for wikitext authors, it is possible for authors to ignore all these new features and continue to use ~TiddlyWiki 5 in the way that they have always done.
These changes lay the groundwork for macros and related features to be deprecated (which is the point at which users are advised not to use old features, and instead given clear pointers to the equivalent modern functionality).
The new transclusion architecture is not by itself sufficient to enable us to fully deprecate macros yet. To handle the remaining use cases we propose a new backtick quoted attribute format that allows for the substitution of variable values. See https://github.com/Jermolene/TiddlyWiki5/issues/6663 for details.
! Plugin Improvements
*
! Translation improvement
Improvements to the following translations:
*
! Accessibility Improvements
*
! Usability Improvements
*
! Widget Improvements
*
! Filter improvements
*
! Hackability Improvements
*
! Bug Fixes
*
! Developer Improvements
*
! Node.js Improvements
*
! Performance 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 """
""">>

View File

@ -0,0 +1,24 @@
title: CustomOperators/NestedParameterised
description: Nested parameterised custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(first:ignored,factor:0.5)
[divide[2]multiply<factor>]
\end
\function .multiplebysomething(first:ignored,factor:2)
[multiply[2].dividebysomething[],<factor>]
\end
<$text text={{{ [[123].multiplebysomething[]] }}}/>
-
<$text text={{{ [[123].multiplebysomething[x],[4]] }}}/>
+
title: ExpectedResult
<p>246-492</p>

View File

@ -0,0 +1,24 @@
title: CustomOperators/Parameterised
description: Parameterised custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .multiplybysomething(first:ignored,factor:2)
[multiply[2]multiply<factor>]
\end
<$text text={{{ [[123].multiplybysomething[]] }}}/>
-
<$text text={{{ [[123].multiplybysomething[x],[4]] }}}/>
|
<$text text={{{ [[123]function[.multiplybysomething]] }}}/>
-
<$text text={{{ [[123]function[.multiplybysomething],[x],[4]] }}}/>
+
title: ExpectedResult
<p>492-984|492-984</p>

View File

@ -0,0 +1,21 @@
title: CustomOperators/Simple
description: Simple custom operator usage
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .multiplybytwo()
[multiply[2]]
\end
<$text text={{{ [[123].multiplybytwo[]] }}}/>
|
<$text text={{{ [[123]function[.multiplybytwo]] }}}/>
+
title: ExpectedResult
<p>246|246</p>

View File

@ -0,0 +1,24 @@
title: Functions/FunctionAttributes
description: Attributes specified as function invocations
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(factor:0.5)
[divide<factor>]
\end
\function multiplebysomething(first:ignored,factor:2)
[<factor>multiply[2].dividebysomething[0.25]]
\end
<$text text=<<multiplebysomething>>/>
|
<$text text=<<multiplebysomething "nothing" "4">>/>
+
title: ExpectedResult
<p>16|32</p>

View File

@ -0,0 +1,24 @@
title: Functions/FunctionOperator
description: Calling a function via the function operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .dividebysomething(factor:0.5)
[divide<factor>]
\end
\function multiplebysomething(first:ignored,factor:2)
[multiply<factor>multiply[2].dividebysomething[0.25]]
\end
<$text text={{{ [[4]function[multiplebysomething]] }}}/>
|
<$text text={{{ [[6]function[multiplebysomething],[ignored],[4]] }}}/>
+
title: ExpectedResult
<p>64|192</p>

View File

@ -0,0 +1,15 @@
title: Functions/MissingFunction
description: Calling a missing function via the function operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$text text={{{ [[23]function[missing]] }}}/>
+
title: ExpectedResult
23

View File

@ -0,0 +1,18 @@
title: Functions/RunawayRecursiveFunctions
description: Runaway recursive functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function .buffalo(p)
[.buffalo<p>]
\end
<$text text=<<.buffalo 8>>/>
+
title: ExpectedResult
/**-- Excessive filter recursion --**/

View File

@ -0,0 +1,22 @@
title: Functions/UndefinedParameters
description: Undefined function parameters
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function greet(who)
[[hello ]addsuffix<who>]
\end
<$text text={{{[function[greet],[world]]}}}/>
<<greet world>>
<$text text={{{[function[greet]]}}}/>
<<greet>>
+
title: ExpectedResult
hello world<p>hello world</p>hello <p>hello </p>

View File

@ -0,0 +1,36 @@
title: Functions/WikifiedFunctions
description: Wikified functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function fn-buffalo(param)
[<param>addsuffix[ with a ''buffalo'']]
\end
\procedure proc-buffalo(param)
<<param>> with a ''buffalo''
\end
\define macro-buffalo(param)
$param$ with a ''buffalo''
\end
<<fn-buffalo "Going to lunch">>
<<proc-buffalo "Going to breakfast">>
<<macro-buffalo "Going to dinner">>
<$transclude $variable="fn-buffalo" param="Going to lunch" $output="text/plain"/>
<$transclude $variable="proc-buffalo" param="Going to breakfast" $output="text/plain"/>
<$transclude $variable="macro-buffalo" param="Going to dinner" $output="text/plain"/>
+
title: ExpectedResult
<p>Going to lunch with a ''buffalo''</p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo

View File

@ -0,0 +1,31 @@
title: Genesis/RedefineLet
description: Using the genesis widget to override the let widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $let()
\whitespace trim
<$parameters $params="@params">
<$setmultiplevariables $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget<currentTiddler>addprefix[--]addsuffix[--]]">
<$slot $name="ts-raw"/>
</$setmultiplevariables>
</$parameters>
\end
<$let
one="Elephant"
$two="Kangaroo"
$$three="Giraffe"
>
(<$text text=<<one>>/>)
(<$text text=<<$two>>/>)
(<$text text=<<$$three>>/>)
</$let>
+
title: ExpectedResult
<p>(--Elephant--)
(--Kangaroo--)
(--Giraffe--)</p>

View File

@ -0,0 +1,20 @@
title: Procedures/Nested
description: Nested Procedures
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure alpha(x)
\procedure beta(y)
<$text text=<<y>>/>
\end beta
<$transclude $variable="beta" y={{{ [<x>addprefix<x>] }}}/>
\end alpha
<<alpha "Elephant">>
+
title: ExpectedResult
<p>ElephantElephant</p>

View File

@ -0,0 +1,27 @@
title: Transclude/CustomWidget/ActionWidget
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='Result'>
</$transclude>
+
title: Actions
\whitespace trim
<!-- Define the <$$action-mywidget> widget by defining a transcludable variable with that name -->
\widget $$action-mywidget(one:'Jaguar')
\whitespace trim
<$action-setfield $tiddler="Result" $field="text" $value=<<one>>/>
\end
<$$action-mywidget one="Dingo">
Crocodile
</$$action-mywidget>
+
title: ExpectedResult
<p>Dingo</p>

View File

@ -0,0 +1,26 @@
title: Transclude/CustomWidget/Fail
description: Custom widget failed definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<!-- Attempt to define the <$non-existent-widget> widget by defining a transcludable variable with that name -->
\widget $non-existent-widget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$non-existent-widget one="Dingo">
Crocodile
</$non-existent-widget>
<$non-existent-widget one="BumbleBee">
Squirrel
</$non-existent-widget>
+
title: ExpectedResult
<p>Undefined widget 'non-existent-widget'Undefined widget 'non-existent-widget'</p>

View File

@ -0,0 +1,29 @@
title: CustomWidget-Override-Codeblock
description: Usage of genesis widget with attributes starting with dollar signs
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\import Definition
<$codeblock code="Kangaroo"/>
<$codeblock code={{Subject}}/>
<$let test="Tiger">
<$codeblock code=<<test>>/>
</$let>
+
title: Definition
\whitespace trim
\widget $codeblock(code)
<$genesis $type="$codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/>
\end
+
title: Subject
Python
+
title: ExpectedResult
<p><pre><code>£Kangaroo@</code></pre><pre><code>£Python@</code></pre><pre><code>£Tiger@</code></pre></p>

View File

@ -0,0 +1,33 @@
title: Transclude/CustomWidget/OverrideTransclude
description: Custom widget definition attempting to override transclude
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerZero
Antelope
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$transclude> widget by defining a transcludable variable with that name -->
\widget $transclude(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="body">
Whale
</$slot>
\end
<$genesis $type="$transclude" $remappable="no" $$tiddler="TiddlerZero">
Crocodile
</$genesis>
+
title: ExpectedResult
<p>Antelope</p>

View File

@ -0,0 +1,33 @@
title: Transclude/CustomWidget/Simple
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Define the <$$mywidget> widget by defining a transcludable variable with that name -->
\widget $$mywidget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$$mywidget one="Dingo">
Crocodile
</$$mywidget>
<$$mywidget one="BumbleBee">
Squirrel
</$$mywidget>
<$$mywidget/>
+
title: ExpectedResult
<p>DingoCrocodileBumbleBeeSquirrelJaguarWhale</p>

View File

@ -0,0 +1,20 @@
title: CustomWidget/Slotted/Empty
description: Custom widget with empty slotted values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $$mywidget()
<$slot $name=ts-raw>the body is empty</$slot>
\end
#<$$mywidget/>
#<$$mywidget></$$mywidget>
#<$$mywidget>the body is not empty</$$mywidget>
+
title: ExpectedResult
<ol><li>the body is empty</li><li>the body is empty</li><li>the body is not empty</li></ol>

View File

@ -0,0 +1,27 @@
title: Transclude/CustomWidget/Slotted
description: Custom widget definition
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\widget $$mywidget(one:'Jaguar')
\whitespace trim
<$text text=<<one>>/>
<$slot $name="ts-stuff">
Whale
</$slot>
\end
<$$mywidget one="Dingo">
<$fill $name="ts-stuff">
Crocodile
</$fill>
</$$mywidget>
<$$mywidget one="BumbleBee">
Squirrel
</$$mywidget>
+
title: ExpectedResult
<p>DingoCrocodileBumbleBeeWhale</p>

View File

@ -0,0 +1,27 @@
title: Transclude/CustomWidget/TextWidgetOverride
description: Custom widget definition redefining the text widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefining the text widget only works when it is explicitly invoked with the <$text> syntax, and not implicitly via typed text -->
\widget $text(text:'Jaguar')
\whitespace trim
<$genesis $type="$text" $remappable="no" text={{{ [<text>addprefix[≤]addsuffix[≥]] }}}/>
\end
<$text text="Dingo"/>
Crocodile
+
title: ExpectedResult
<p>≤Dingo≥≤Jaguar≥</p>

View File

@ -0,0 +1,31 @@
title: Transclude/CustomWidget/TextWidgetOverrideWithSlot
description: Custom widget definition redefining the text widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$text> widget by defining a transcludable variable with that name -->
\widget $text(text:'Jaguar')
\whitespace trim
<$genesis $type="$text" $remappable="no" text=<<text>>/>
<$set name="$text" value="">
<$slot $name="ts-raw">
Whale
</$slot>
</$set>
\end
<$text text="Dingo">
Crocodile
</$text>
+
title: ExpectedResult
<p>DingoCrocodile</p>

View File

@ -0,0 +1,31 @@
title: CustomWidget-Unoverride-Codeblock
description: Usage of genesis widget with attributes starting with dollar signs, and unoverriding a core widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\import Definition
<$let $codeblock="">
<$codeblock code="Kangaroo"/>
<$codeblock code={{Subject}}/>
<$let test="Tiger">
<$codeblock code=<<test>>/>
</$let>
</$let>
+
title: Definition
\whitespace trim
\widget $codeblock(code)
<$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/>
\end
+
title: Subject
Python
+
title: ExpectedResult
<p><pre><code>Kangaroo</code></pre><pre><code>Python</code></pre><pre><code>Tiger</code></pre></p>

View File

@ -0,0 +1,29 @@
title: Transclude/CustomWidget/VariableAttribute
description: Custom widget definition using an attribute called $variable
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<!-- Redefine the <$$mywidget> widget by defining a transcludable variable with that name -->
\widget $$mywidget($variable:'Jaguar')
\whitespace trim
<$text text=<<$variable>>/>
<$slot $name="ts-raw">
Whale
</$slot>
\end
<$$mywidget $variable="Dingo">
Crocodile
</$$mywidget>
+
title: ExpectedResult
<p>DingoCrocodile</p>

View File

@ -0,0 +1,17 @@
title: Transclude/Macro/JavaScript
description: Transcluding a javascript macro
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<<makedatauri text:"Wildebeest" type:"text/plain">>
<$macrocall $name="makedatauri" text="Wildebeest" type="text/plain"/>
+
title: ExpectedResult
<p><a class="tc-tiddlylink-external" href="data:text/plain,Wildebeest" rel="noopener noreferrer" target="_blank">data:text/plain,Wildebeest</a></p><p><a class="tc-tiddlylink-external" href="data:text/plain,Wildebeest" rel="noopener noreferrer" target="_blank">data:text/plain,Wildebeest</a></p>

View File

@ -0,0 +1,17 @@
title: Transclude/Macro/Plain
description: Transcluding a macro as plain text
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$let currentTab="Jeremy">
<$macrocall $name="currentTab" $type="text/plain" $output="text/plain"/>
|
<$transclude $variable="currentTab" $type="text/plain" $output="text/plain"/>
</$let>
+
title: ExpectedResult
<p>Jeremy|Jeremy</p>

View File

@ -0,0 +1,26 @@
title: Transclude/Macro/Simple
description: Transcluding a macro
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\define mamacro(one:"red",two:"green")
It is $one$ and $two$ or <<__one__>> and <<__two__>>.
\end
<$macrocall $name="mamacro"/>
<$transclude $variable="mamacro"/>
<$transclude $variable="mamacro" one="orange"/>
<$transclude $variable="mamacro" 0="pink"/>
<$transclude $variable="mamacro" one="purple" 1="pink"/>
+
title: ExpectedResult
<p>It is red and green or red and green.</p><p>It is red and green or red and green.</p><p>It is orange and green or orange and green.</p><p>It is pink and green or pink and green.</p><p>It is purple and pink or purple and pink.</p>

View File

@ -0,0 +1,48 @@
title: Transclude/MissingTarget
description: Transcluding a missing target
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$transclude>
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="ts-missing">
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$fill>
</$transclude>
<$transclude $tiddler='MissingTiddler' one='Ferret'>
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$transclude>
<$transclude $tiddler='MissingTiddler' one='Ferret'>
<$fill $name="ts-missing">
<$parameters one='Ferret'>
Badger
<$text text=<<one>>/>
</$parameters>
</$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Kangaroo'>
Piranha
<$text text=<<one>>/>
</$parameters>
+
title: ExpectedResult
<p>PiranhaFerretPiranhaFerretBadgerFerretBadgerFerret</p>

View File

@ -0,0 +1,34 @@
title: Transclude/Parameterised/Depth
description: Parameterised transclusion using the $depth attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
|
<$transclude $tiddler='TiddlerOne'/>
|
<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/>
|
<$transclude $tiddler='TiddlerOne' $$two="Falcon"/>
+
title: TiddlerOne
\whitespace trim
{{TiddlerTwo}}
+
title: TiddlerTwo
\whitespace trim
<$parameters one='Jaguar' $$two='Piranha' $depth="2">
<$text text=<<one>>/>:<$text text=<<$two>>/>
</$parameters>
<$parameters one='Leopard' $$two='Coelacanth'>
(<$text text=<<one>>/>|<$text text=<<$two>>/>)
</$parameters>
+
title: ExpectedResult
<p>Ferret:Piranha(Leopard|Coelacanth)|Jaguar:Piranha(Leopard|Coelacanth)|Ferret:Osprey(Leopard|Coelacanth)|Jaguar:Falcon(Leopard|Coelacanth)</p>

View File

@ -0,0 +1,29 @@
title: Transclude/Parameterised/Mode
description: Parameterised transclusion using the $parseMode attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
This is a block
</$transclude>
<$transclude $tiddler='TiddlerOne'>
This is inline
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $parseMode="@parseMode">
<$text text=<<@parseMode>>/>
</$parameters>
+
title: ExpectedResult
<p>block</p><p>inline</p>

View File

@ -0,0 +1,34 @@
title: Transclude/Parameterised/Name/Values
description: Parameterised transclusion accessing parameters as name/value pairs
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler="TiddlerOne" 0="" 1="" 2=""/>
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar' $$one='Lizard' two='Mole' $params="@params">
<$list filter="[<@params>jsonindexes[]]">
{<$text text=<<currentTiddler>>/>: <$text text={{{ [<@params>jsonget<currentTiddler>] }}}/>}
</$list>
</$parameters>
+
title: TiddlerTwo
\whitespace trim
\parameters(zero:'Mouse',$one:'Horse',two:'Owl')
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> $$one=<<$one>> two=<<two>>/>)
+
title: ExpectedResult
<p>{0:}{1:}{2:}</p><p></p><p>{0:Ferret}</p><p>{0:Butterfly}{1:Moth}</p><p>{0:Beetle}{1:Scorpion}{2:Snake}</p><p>({$one:Scorpion}{two:Snake}{zero:Beetle})</p>

View File

@ -0,0 +1,29 @@
title: Transclude/Parameterised/ParseTreeNodes
description: Parameterised transclusion using the $parseTreeNodes attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
This is a block
</$transclude>
<$transclude $tiddler='TiddlerOne'>
This is inline
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $parseTreeNodes="@parseTreeNodes">
<$text text=<<@parseTreeNodes>>/>
</$parameters>
+
title: ExpectedResult
<p>[{"type":"element","tag":"p","children":[{"type":"text","text":"This is a block","start":68,"end":83}],"start":68,"end":83}]</p><p>[{"type":"text","text":"This is inline","start":136,"end":152}]</p>

View File

@ -0,0 +1,29 @@
title: Transclude/Parameterised/Positional/Shortcut/Parameters
description: Positional parameterised transclusion using shortcut syntax and parameters pragma
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
\parameters(zero:Jaguar,one:'Lizard',two:'Mole')
[{<$text text=<<zero>>/>}{<$text text=<<one>>/>}{<$text text=<<two>>/>}]
+
title: TiddlerTwo
\whitespace trim
\parameters(zero:'Mouse',one:Horse,two:'Owl')
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> one=<<one>> two=<<two>>/>)
+
title: ExpectedResult
<p>[{Jaguar}{Lizard}{Mole}]</p><p>[{Ferret}{Lizard}{Mole}]</p><p>[{Butterfly}{Moth}{Mole}]</p><p>[{Beetle}{Scorpion}{Snake}]</p><p>([{Beetle}{Scorpion}{Snake}])</p>

View File

@ -0,0 +1,29 @@
title: Transclude/Parameterised/Positional/Shortcut
description: Positional parameterised transclusion using shortcut syntax
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
{{TiddlerOne}}
{{TiddlerOne|Ferret}}
{{TiddlerOne|Butterfly|Moth}}
{{TiddlerOne|Beetle|Scorpion|Snake}}
{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}}
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar' one='Lizard' two='Mole'>[{<$text text=<<zero>>/>}{<$text text=<<one>>/>}{<$text text=<<two>>/>}]</$parameters>
+
title: TiddlerTwo
\whitespace trim
<$parameters zero='Mouse' one='Horse' two='Owl'>
(<$transclude $tiddler=<<currentTiddler>> zero=<<zero>> one=<<one>> two=<<two>>/>)
</$parameters>
+
title: ExpectedResult
<p>[{Jaguar}{Lizard}{Mole}]</p><p>[{Ferret}{Lizard}{Mole}]</p><p>[{Butterfly}{Moth}{Mole}]</p><p>[{Beetle}{Scorpion}{Snake}]</p><p>([{Beetle}{Scorpion}{Snake}])</p>

View File

@ -0,0 +1,30 @@
title: Transclude/Parameterised/Positional/Variables
description: Positional parameterised transclusion of variables
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function myfunction(alpha:"apple",beta:"banana",gamma:"grenadine") [<alpha>]
\define mymacro(alpha:"apple",beta:"banana",gamma:"grenadine") $beta$
\function f(a) [<a>]
(Functions:
<$text text={{{ [<myfunction gamma:"unused" f1>] }}}/>
,
<$text text=<<myfunction gamma:"unused" f1>>/>
,
<<myfunction gamma:"unused" f1>>
)(Macros:
<$text text={{{ [<mymacro gamma:"unused" f1>] }}}/>
,
<$text text=<<mymacro gamma:"unused" f1>>/>
,
<<mymacro gamma:"unused" f1>>
)
+
title: ExpectedResult
<p>(Functions:f1,f1,f1)(Macros:banana,banana,banana)</p>

View File

@ -0,0 +1,26 @@
title: Transclude/Parameterised/Positional
description: Positional parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' zero='Ferret'/>
<$transclude zero='Ferret' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne' 0='Pigeon'/>
<$transclude 0='Pigeon' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne' zero='Ferret' 0='Pigeon'/>
<$transclude zero='Ferret' 0='Pigeon' $tiddler='TiddlerOne'/>
<$transclude $tiddler='TiddlerOne'/>
+
title: TiddlerOne
\whitespace trim
<$parameters zero='Jaguar'>
<$text text=<<zero>>/>
</$parameters>
+
title: ExpectedResult
<p>FerretFerretPigeonPigeonFerretFerretJaguar</p>

View File

@ -0,0 +1,20 @@
title: Transclude/Parameterised/Shortcut/Parameters
description: Simple parameterised transclusion using the parameters pragma
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
<$transclude $tiddler='TiddlerOne'/>
+
title: TiddlerOne
\whitespace trim
\parameters(one:'Jaguar')
<$text text=<<one>>/>
+
title: ExpectedResult
<p>FerretJaguar</p>

View File

@ -0,0 +1,21 @@
title: Transclude/Parameterised/Shortcut
description: Simple parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure test(one:'Jaguar')
{<$text text=<<one>>/>}
\end
<$transclude $variable='test' one='Ferret'/>
<$transclude $variable='test'/>
<<test "Rat">>
<<test one:"Mouse">>
+
title: ExpectedResult
<p>{Ferret}{Jaguar}{Rat}{Mouse}</p>

View File

@ -0,0 +1,26 @@
title: Transclude/Parameterised/Simple
description: Simple parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'/>
|
<$transclude $tiddler='TiddlerOne'/>
|
<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/>
|
<$transclude $tiddler='TiddlerOne' $$two="Falcon"/>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar' $$two='Piranha'>
<$text text=<<one>>/>:<$text text=<<$two>>/>
</$parameters>
+
title: ExpectedResult
<p>Ferret:Piranha|Jaguar:Piranha|Ferret:Osprey|Jaguar:Falcon</p>

View File

@ -0,0 +1,29 @@
title: Transclude/Parameterised/SlotFillParseTreeNodes
description: Parameterised transclusion using the $slotFillParseTreeNodes attribute
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="one">This is first</$fill>
<$fill $name="two">But this is second</$fill>
</$transclude>
<$transclude $tiddler='TiddlerOne'>
<$fill $name="one">This is first
<$fill $name="two">But this is second</$fill></$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters $slotFillParseTreeNodes="@slotFillParseTreeNodes">
<$text text={{{ [<@slotFillParseTreeNodes>jsonindexes[]join[,]] }}}/>
</$parameters>
+
title: ExpectedResult
<p>one,ts-raw,two</p><p>one,ts-raw</p>

View File

@ -0,0 +1,24 @@
title: Transclude/Parameterised/Slotted/Missing
description: Parameterised transclusion with slotted missing values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar'>
<$text text=<<one>>/>
<$slot $name="content">
Whale
</$slot>
</$parameters>
+
title: ExpectedResult
<p>FerretWhale</p>

View File

@ -0,0 +1,27 @@
title: Transclude/Parameterised/Slotted
description: Parameterised transclusion with slotted values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$transclude $tiddler='TiddlerOne' one='Ferret'>
<$fill $name="content">
Hippopotamus
</$fill>
</$transclude>
+
title: TiddlerOne
\whitespace trim
<$parameters one='Jaguar'>
<$text text=<<one>>/>
<$slot $name="content">
Whale
</$slot>
</$parameters>
+
title: ExpectedResult
<p>FerretHippopotamus</p>

View File

@ -0,0 +1,25 @@
title: Transclude/Procedures/Whitespace
description: Procedures should inherit whitespace settings from definition site
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure testproc()
This is a sentence
\end
\define testmacro()
This is a sentence
\end
This is a sentence
[<<testproc>>]
[<<testmacro>>]
+
title: ExpectedResult
<p>This is a sentence
[This is a sentence]
[This is a sentence ]</p>

View File

@ -0,0 +1,38 @@
title: Transclude/Typed
description: Typed transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\procedure testproc()
This is ''wikitext''
\end
<$transclude $variable="testproc"/>
-
<$transclude $variable="testproc" $type="text/plain"/>
<$transclude $tiddler="Data" $index="testindex"/>
-
<$transclude $tiddler="Data" $index="testindex" $type="text/plain"/>
<$transclude $tiddler="Data" $field="custom"/>
-
<$transclude $tiddler="Data" $field="custom" $type="text/plain"/>
+
title: Data
type: application/x-tiddler-dictionary
custom: This is ''wikitext''
testindex: This is ''wikitext''
+
title: ExpectedResult
<p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p><p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p><p>This is <strong>wikitext</strong>
-
<pre><code>This is ''wikitext''</code></pre></p>

View File

@ -422,7 +422,7 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,Tiddler8,TiddlerOne,TiddlerSeventh"); expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,Tiddler8,TiddlerOne,TiddlerSeventh");
expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8");
expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three"); expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three");
var fakeWidget = {getVariable: function() {return "one";}}; var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "one": undefined;}};
expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8");
}); });
@ -625,7 +625,7 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("[{!!title}]").join(",")).toBe(""); expect(wiki.filterTiddlers("[{!!title}]").join(",")).toBe("");
expect(wiki.filterTiddlers("[prefix{Tiddler8}] +[sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); expect(wiki.filterTiddlers("[prefix{Tiddler8}] +[sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne");
expect(wiki.filterTiddlers("[modifier{Tiddler8!!test-field}] +[sort[title]]").join(",")).toBe("TiddlerOne"); expect(wiki.filterTiddlers("[modifier{Tiddler8!!test-field}] +[sort[title]]").join(",")).toBe("TiddlerOne");
var fakeWidget = {wiki: wiki, getVariable: function() {return "Tiddler Three";}}; var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "Tiddler Three": undefined;}};
expect(wiki.filterTiddlers("[modifier{!!modifier}] +[sort[title]]",fakeWidget).join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three"); expect(wiki.filterTiddlers("[modifier{!!modifier}] +[sort[title]]",fakeWidget).join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three");
}); });

View File

@ -124,7 +124,7 @@ describe("Wiki.parseTextReference tests", function() {
// Non-existent subtiddler of a plugin // Non-existent subtiddler of a plugin
expect(parseAndGetSource("$:/ShadowPlugin","text",null,"MyMissingTiddler")).toEqual(null); expect(parseAndGetSource("$:/ShadowPlugin","text",null,"MyMissingTiddler")).toEqual(null);
// Plain text tiddler // Plain text tiddler
expect(parseAndGetSource("TiddlerNine")).toEqual(undefined); expect(parseAndGetSource("TiddlerNine")).toEqual("this is plain text");
}); });
}); });

View File

@ -188,4 +188,4 @@ describe("Utility tests", function() {
}); });
})(); })();

View File

@ -143,7 +143,7 @@ describe("Widget module", function() {
var wiki = new $tw.Wiki(); var wiki = new $tw.Wiki();
// Add a tiddler // Add a tiddler
wiki.addTiddlers([ wiki.addTiddlers([
{title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>\n"}, {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>"},
{title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"} {title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"}
]); ]);
// Test parse tree // Test parse tree
@ -157,7 +157,7 @@ describe("Widget module", function() {
// Render the widget node to the DOM // Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode); var wrapper = renderWidgetNode(widgetNode);
// Test the rendering // Test the rendering
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>\n"); expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
}); });
it("should deal with SVG elements", function() { it("should deal with SVG elements", function() {
@ -683,7 +683,7 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("<p>New value</p>"); expect(wrapper.innerHTML).toBe("<p>New value</p>");
}); });
it("should can mix setWidgets and macros when importing", function() { it("should support mixed setWidgets and macros when importing", function() {
var wiki = new $tw.Wiki(); var wiki = new $tw.Wiki();
// Add some tiddlers // Add some tiddlers
wiki.addTiddlers([ wiki.addTiddlers([
@ -699,6 +699,20 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("<p>Aval Bval Cval</p>"); expect(wrapper.innerHTML).toBe("<p>Aval Bval Cval</p>");
}); });
it("should skip parameters widgets when importing", function() {
var wiki = new $tw.Wiki();
// Add some tiddlers
wiki.addTiddlers([
{title: "B", text: "<$parameters bee=nothing><$set name='B' value='Bval'>\n\ndummy text</$set></$parameters>"},
]);
var text = "\\import B\n<<B>>";
var widgetNode = createWidgetNode(parseText(text,wiki),wiki);
// Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode);
// Test the rendering
expect(wrapper.innerHTML).toBe("<p>Bval</p>");
});
it("can have more than one macroDef variable imported", function() { it("can have more than one macroDef variable imported", function() {
var wiki = new $tw.Wiki(); var wiki = new $tw.Wiki();
wiki.addTiddlers([ wiki.addTiddlers([

View File

@ -19,7 +19,8 @@ describe("WikiText parser tests", function() {
// Define a parsing shortcut // Define a parsing shortcut
var parse = function(text) { var parse = function(text) {
return wiki.parseText("text/vnd.tiddlywiki",text).tree; var tree = wiki.parseText("text/vnd.tiddlywiki",text).tree;
return tree;
}; };
it("should parse tags", function() { it("should parse tags", function() {
@ -114,7 +115,70 @@ describe("WikiText parser tests", function() {
it("should parse macro definitions", function() { it("should parse macro definitions", function() {
expect(parse("\\define myMacro()\nnothing\n\\end\n")).toEqual( expect(parse("\\define myMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ] [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}]}]
);
});
it("should parse procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro()\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse single line procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro() nothing\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse procedure definitions with parameters", function() {
expect(parse("\\procedure myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}]
);
});
it("should parse procedure definitions", function() {
expect(parse("\\procedure myMacro(one:'Jaguar')\n<$text text=<<one>>/>\n\\end\n\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<<one>>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<<one>>/>"}],"isProcedureDefinition":true}]
);
}); it("should parse function definitions with no parameters", function() {
expect(parse("\\function myMacro()\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse single line function definitions with no parameters", function() {
expect(parse("\\function myMacro() nothing\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse function definitions with parameters", function() {
expect(parse("\\function myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}]
);
});
it("should parse function definitions", function() {
expect(parse("\\function myMacro(one:'Jaguar')\n<$text text=<<one>>/>\n\\end\n\n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<<one>>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<<one>>/>"}],"isFunctionDefinition":true}]
); );
}); });
@ -122,7 +186,7 @@ describe("WikiText parser tests", function() {
it("should parse comment in pragma area. Comment will be invisible", function() { it("should parse comment in pragma area. Comment will be invisible", function() {
expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual( expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'aMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ] [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"aMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"aMacro"},{"name":"value","type":"string","value":"nothing"}]}]
); );
}); });
@ -143,38 +207,38 @@ describe("WikiText parser tests", function() {
it("should parse inline macro calls", function() { it("should parse inline macro calls", function() {
expect(parse("<<john>><<paul>><<george>><<ringo>>")).toEqual( expect(parse("<<john>><<paul>><<george>><<ringo>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 35, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'macrocall', start: 8, params: [ ], name: 'paul', end: 16 }, { type: 'macrocall', start: 16, params: [ ], name: 'george', end: 26 }, { type: 'macrocall', start: 26, params: [ ], name: 'ringo', end: 35 } ] } ] [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"transclude","start":8,"end":16,"attributes":{"$variable":{"name":"$variable","type":"string","value":"paul"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"paul"}]},{"type":"transclude","start":16,"end":26,"attributes":{"$variable":{"name":"$variable","type":"string","value":"george"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"george"}]},{"type":"transclude","start":26,"end":35,"attributes":{"$variable":{"name":"$variable","type":"string","value":"ringo"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"ringo"}]}],"start":0,"end":35}]
); );
expect(parse("text <<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual( expect(parse("text <<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[{ type: 'element', tag: 'p', start: 0, end: 92, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: 'john', start: 5, params: [ { type: 'macro-parameter', start: 11, value: 'val1', name: 'one', end: 20 }, { type: 'macro-parameter', start: 20, value: 'val "2"', name: 'two', end: 35 }, { type: 'macro-parameter', start: 35, value: 'val \'3\'', name: 'three', end: 52 }, { type: 'macro-parameter', start: 52, value: 'val 4"5\'', name: 'four', end: 73 }, { type: 'macro-parameter', start: 73, value: 'val 5', name: 'five', end: 89 } ], end: 92 } ] } ] [{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":92,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":11,"end":20},"two":{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},"three":{"name":"three","type":"string","value":"val '3'","start":35,"end":52},"four":{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},"five":{"name":"five","type":"string","value":"val 5","start":73,"end":89}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":11,"end":20},{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},{"name":"three","type":"string","value":"val '3'","start":35,"end":52},{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},{"name":"five","type":"string","value":"val 5","start":73,"end":89}]}],"start":0,"end":92}]
); );
expect(parse("ignored << carrots <<john>>")).toEqual( expect(parse("ignored << carrots <<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 27, children: [ { type: 'text', text: 'ignored << carrots ', start: 0, end: 19 }, { type: 'macrocall', name: 'john', start: 19, params: [ ], end: 27 } ] } ] [{"type":"element","tag":"p","children":[{"type":"text","text":"ignored << carrots ","start":0,"end":19},{"type":"transclude","start":19,"end":27,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":27}]
); );
expect(parse("text <<<john>>")).toEqual( expect(parse("text <<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 14, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: '<john', start: 5, params: [ ], end: 14 } ] } ] [{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":14,"attributes":{"$variable":{"name":"$variable","type":"string","value":"<john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"<john"}]}],"start":0,"end":14}]
); );
expect(parse("before\n<<john>>")).toEqual( expect(parse("before\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'text', text: 'before\n', start: 0, end: 7 }, { type: 'macrocall', start: 7, params: [ ], name: 'john', end: 15 } ] } ] [{"type":"element","tag":"p","children":[{"type":"text","text":"before\n","start":0,"end":7},{"type":"transclude","start":7,"end":15,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":15}]
); );
// A single space will cause it to be inline // A single space will cause it to be inline
expect(parse("<<john>> ")).toEqual( expect(parse("<<john>> ")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 9, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'text', text: ' ', start: 8, end: 9 } ] } ] [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"text","text":" ","start":8,"end":9}],"start":0,"end":9}]
); );
expect(parse("text <<outie one:'my <<innie>>' >>")).toEqual( expect(parse("text <<outie one:'my <<innie>>' >>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 34, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', start: 5, params: [ { type: 'macro-parameter', start: 12, value: 'my <<innie>>', name: 'one', end: 31 } ], name: 'outie', end: 34 } ] } ] [{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":34,"attributes":{"$variable":{"name":"$variable","type":"string","value":"outie"},"one":{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}]}],"start":0,"end":34}]
); );
@ -183,37 +247,37 @@ describe("WikiText parser tests", function() {
it("should parse block macro calls", function() { it("should parse block macro calls", function() {
expect(parse("<<john>>\n<<paul>>\r\n<<george>>\n<<ringo>>")).toEqual( expect(parse("<<john>>\n<<paul>>\r\n<<george>>\n<<ringo>>")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'macrocall', start: 9, name: 'paul', params: [ ], end: 17, isBlock: true }, { type: 'macrocall', start: 19, name: 'george', params: [ ], end: 29, isBlock: true }, { type: 'macrocall', start: 30, name: 'ringo', params: [ ], end: 39, isBlock: true } ] [ { type: 'transclude', start: 0, attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: 'transclude', start: 9, attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: 'transclude', start: 19, attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: 'transclude', start: 30, attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ]
); );
expect(parse("<<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual( expect(parse("<<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ { type: 'macro-parameter', start: 6, value: 'val1', name: 'one', end: 15 }, { type: 'macro-parameter', start: 15, value: 'val "2"', name: 'two', end: 30 }, { type: 'macro-parameter', start: 30, value: 'val \'3\'', name: 'three', end: 47 }, { type: 'macro-parameter', start: 47, value: 'val 4"5\'', name: 'four', end: 68 }, { type: 'macro-parameter', start: 68, value: 'val 5', name: 'five', end: 84 }], end: 87, isBlock: true } ] [{"type":"transclude","start":0,"end":87,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":6,"end":15},"two":{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},"three":{"name":"three","type":"string","value":"val '3'","start":30,"end":47},"four":{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},"five":{"name":"five","type":"string","value":"val 5","start":68,"end":84}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":6,"end":15},{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},{"name":"three","type":"string","value":"val '3'","start":30,"end":47},{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},{"name":"five","type":"string","value":"val 5","start":68,"end":84}],"isBlock":true}]
); );
expect(parse("<< carrots\n\n<<john>>")).toEqual( expect(parse("<< carrots\n\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'macrocall', start: 12, params: [ ], name: 'john', end: 20, isBlock: true } ] [ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'transclude', start: 12, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ]
); );
expect(parse("before\n\n<<john>>")).toEqual( expect(parse("before\n\n<<john>>")).toEqual(
[ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'macrocall', start: 8, name: 'john', params: [ ], end: 16, isBlock: true } ] [ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'transclude', start: 8, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ]
); );
expect(parse("<<john>>\nafter")).toEqual( expect(parse("<<john>>\nafter")).toEqual(
[ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ] [ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ]
); );
expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual( expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 11, value: '\n\nwikitext\n', name: 'arg', end: 33 } ], name: 'multiline', end: 36, isBlock: true }] [{"type":"transclude","start":0,"end":36,"attributes":{"$variable":{"name":"$variable","type":"string","value":"multiline"},"arg":{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}},"orderedAttributes":[{"name":"$variable","type":"string","value":"multiline"},{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}],"isBlock":true}]
); );
expect(parse("<<outie one:'my <<innie>>' >>")).toEqual( expect(parse("<<outie one:'my <<innie>>' >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 7, value: 'my <<innie>>', name: 'one', end: 26 } ], name: 'outie', end: 29, isBlock: true } ] [ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} ], end: 29, isBlock: true } ]
); );
}); });
@ -221,23 +285,23 @@ describe("WikiText parser tests", function() {
it("should parse tricky macrocall parameters", function() { it("should parse tricky macrocall parameters", function() {
expect(parse("<<john pa>am>>")).toEqual( expect(parse("<<john pa>am>>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'pa>am', end: 12 } ], name: 'john', end: 14, isBlock: true } ] [{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"pa>am","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"pa>am","start":6,"end":12}],"isBlock":true}]
); );
expect(parse("<<john param> >>")).toEqual( expect(parse("<<john param> >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param>', end: 13 } ], name: 'john', end: 16, isBlock: true } ] [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"param>","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param>","start":6,"end":13}],"isBlock":true}]
); );
expect(parse("<<john param>>>")).toEqual( expect(parse("<<john param>>>")).toEqual(
[ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param', end: 12 } ], name: 'john', end: 14 }, { type: 'text', text: '>', start: 14, end: 15 } ] } ] [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"param","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param","start":6,"end":12}]},{"type":"text","text":">","start":14,"end":15}],"start":0,"end":15}]
); );
// equals signs should be allowed // equals signs should be allowed
expect(parse("<<john var>=4 >>")).toEqual( expect(parse("<<john var>=4 >>")).toEqual(
[ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'var>=4', end: 13 } ], name: 'john', end: 16, isBlock: true } ] [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"var>=4","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"var>=4","start":6,"end":13}],"isBlock":true}]
); );

View File

@ -1,7 +1,7 @@
/*\ /*\
title: test-wikitext-tabs-macro.js title: test-wikitext-tabs-macro.js
type: application/javascript type: application/javascript
tags: [[$:/tags/test-spec]] tags: [[$:/tags/test-spec-disabled]]
Tests the core tabs macro by comparing the HTML output with a stored template. Tests the core tabs macro by comparing the HTML output with a stored template.
Intended to permit future readability improvements. Intended to permit future readability improvements.
@ -74,7 +74,7 @@ describe("Tabs-macro HTML tests", function() {
expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal")).toBe(expected.fields.text.replace(/\n/g,"")); expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal")).toBe(expected.fields.text.replace(/\n/g,""));
}); });
it("should render 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() { it("should render all 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() {
expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal-all")).toBe(expectedAll.fields.text.replace(/\n/g,"")); expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal-all")).toBe(expectedAll.fields.text.replace(/\n/g,""));
}); });

View File

@ -10,5 +10,5 @@ WikiText syntax uses a number of different types of brackets. Their names are sh
|`()` |Round brackets |Parenthesis |Not used in WikiText | |`()` |Round brackets |Parenthesis |Not used in WikiText |
|`[]` |Square brackets |Brackets |[[Links|Linking in WikiText]], [[Filters|Filters]] | |`[]` |Square brackets |Brackets |[[Links|Linking in WikiText]], [[Filters|Filters]] |
|`{}` |Curly brackets |Braces |[[Text references|TextReference]], [[Filtered attributes|HTML in WikiText]] | |`{}` |Curly brackets |Braces |[[Text references|TextReference]], [[Filtered attributes|HTML in WikiText]] |
|`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros|Macros in WikiText]] | |`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros]] |

View File

@ -1,31 +1,42 @@
created: 20140211171341271 created: 20140211171341271
modified: 20220505082754270 modified: 20230419103154328
tags: Concepts Reference tags: Concepts Reference
title: Macros title: Macros
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
A <<.def macro>> is a named snippet of text. WikiText can use the name as a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters that get substituted for special placeholders within the snippet. !! Introduction
For the syntax, see [[Macros in WikiText]]. A <<.def macro>> is a named snippet of text. They are typically defined with the [[Pragma: \define]]:
Most macros are in fact just parameterised [[variables|Variables]]. ```
\define my-macro(parameter:"Default value")
This is the macro, and the parameter is $parameter$.
\end
```
They are created using the `\define` [[pragma|Pragma]]. (Behind the scenes, this is transformed into a <<.wlink SetWidget>>, i.e. macros and variables are two sides of the same coin.) The name wrapped in double angled [[brackets|Brackets]] is used a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters:
The snippet and its incoming parameter values are treated as simple strings of characters with no WikiText meaning, at least until the placeholders have been filled in and the macro call has returned. This means that a macro can assemble and return the complete syntax of a ~WikiText component, such as a [[link|Linking in WikiText]]. (See [[Transclusion and Substitution]] for further discussion of this.) ```
<<my-macro>>
<<my-macro "The parameter">>
```
Within a snippet itself, the only markup detected is `$name$` (a placeholder for a macro parameter) and `$(name)$` (a placeholder for a [[variable|Variables]]). The parameters that are specified in the macro call are substituted for special placeholders within the snippet:
The <<.mlink dumpvariables>> macro lists all variables (including macros) that are available at that position in the widget tree. * `$parameter-name$` is replaced with the value of the named parameter
* `$(variable-name)$` is replaced with the value of the named [[variable|Variables]]).
An <<.wlink ImportVariablesWidget>> widget can be used to copy macro definitions to another branch of the [[widget tree|Widgets]]. ~TiddlyWiki uses this technique internally to implement global macros -- namely any macros defined in tiddlers with the <<.tag $:/tags/Macro>> tag. <<.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.
The tag <<.tag $:/tags/Macro/View>> is used to define macros that should only be available within the main view template and the preview panel. !! How Macros Work
The tag <<.tag $:/tags/Macro/View/Body>> is used to define macros that should only be available within the main view template body and the preview panel. 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.
For maximum flexibility, macros can also be <<.js-macro-link "written as JavaScript modules">>. !! Using Macros
A similar effect to a parameterised macro call can be produced by setting [[variables|Variables]] around a [[transclusion|Transclusion]]. * [[Macro Definitions]] describes how to create macros
* [[Macro Calls]] describes how to use macros
* [[Macro Parameter Handling]] describes how macro parameters work
* [[Macro Pitfalls]] describes some of the pitfalls of using macros
* [[Core Macros]] lists the built-in core macros
~TiddlyWiki's core has [[several macros|Core Macros]] built in.

View File

@ -1,24 +1,8 @@
created: 20150219175930000 created: 20150219175930000
modified: 20230117112239663 modified: 20220122182842041
tags: Concepts [[WikiText Parser Modes]] tags:
title: Pragma title: Pragma
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
A <<.def pragma>> is a special component of WikiText that provides control over the way the remaining text is parsed. See [[Pragmas]].
Pragmas occupy lines that start with `\`. They can only appear at the start of the text, but blank lines are allowed between them. If a pragma line appears in the main body of the text, it is treated as if it was ordinary text.
<<.from-version "5.2.6">> Pragmas can have preceding optional whitespace characters.
The following pragmas are available:
;`\define`
: for defining a [[macro|Macros]]
;`\rules`
: for adjusting the set of rules used to parse the text
;`\whitespace trim` or `\whitespace notrim`
: <<.from-version "5.1.15">> Control whether whitespace is trimmed from the start and end of text runs (the default is ''notrim''). This setting can be useful when the whitespace generated by linebreaks disturbs formatting
;`\import <filter-expression>`
: <<.from-version "5.1.18">> Import macro definitions from tiddlers identified by a filter expression
;`\parsermode block` or `\parsermode inline`
: <<.from-version "5.2.4">> Adjust whether the remaining text is parsed in block mode or inline mode.

View File

@ -1,5 +1,5 @@
title: Tiddlyhost title: Tiddlyhost
tags: definition tags: Definitions
created: 20230410105035569 created: 20230410105035569
modified: 20230410105035569 modified: 20230410105035569

View File

@ -5,7 +5,7 @@ modified: 20230410105035569
<span style="float:right;">[img width=340 [Xememex Logo]]</span> <span style="float:right;">[img width=340 [Xememex Logo]]</span>
Xememex is a multiuser TiddlyWiki from [[Federatial]]. It allows large groups of people to work together on intertwingled wikis that can share content. Xememex is a multiuser TiddlyWiki from [[Federatial]]. It allows large groups of people to work together on intertwingled wikis that can share content. It is implemented as a serverless application on Amazon Web Services.
The largest customer implementation has hundreds of online wikis with thousands of users. See https://manuals.annafreud.org/ The largest customer implementation has hundreds of online wikis with thousands of users. See https://manuals.annafreud.org/

View File

@ -33,7 +33,7 @@ The initial startup actions are useful for customising TiddlyWiki according to e
<$action-setfield $tiddler="$:/language" text={{{ [[$:/languages/en-GB]] [plugin-type[language]sort[description]removeprefix[$:/languages/]] +[prefix{$:/info/browser/language}] ~[[en-GB]] +[addprefix[$:/languages/]] }}}/> <$action-setfield $tiddler="$:/language" text={{{ [[$:/languages/en-GB]] [plugin-type[language]sort[description]removeprefix[$:/languages/]] +[prefix{$:/info/browser/language}] ~[[en-GB]] +[addprefix[$:/languages/]] }}}/>
``` ```
Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an ''import'' [[pragma|Pragma]] at the top of the tiddler: Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an [[Pragma: \import]] at the top of the tiddler:
``` ```
\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

@ -0,0 +1,21 @@
caption: function
created: 20220909111836951
modified: 20230419103154328
op-input: a [[selection of titles|Title Selection]] passed as input to the function <<.place F>>
op-output: the [[selection of titles|Title Selection]] returned from the function <<.place F>>
op-parameter: first parameter is the [[function name|Functions]], subsequent parameters are passed to the function by position
op-parameter-name: F
op-purpose: apply a [[function|Functions]] to the input list, and return the result
tags: [[Filter Operators]]
title: function Operator
type: text/vnd.tiddlywiki
<<.from-version "5.3.0">> The <<.op function>> operator applies a named [[function|Functions]] to the input titles, and returns the results from the function. The function is invoked once with all of the input titles (in contrast, the [[filter Operator]] invokes its function separately for each input title).
The first parameter of the <<.op function>> operator specifies the name of the function to be called. Subsequent parameters are passed to the function.
The mapping between the parameters is //positional//, with each consecutive parameter specified in the function call mapped to the corresponding parameter in the function definition. Any parameters that are not provided are given their default values.
<<.tip "Compare with the similar [[filter|filter Operator]] and [[subfilter|subfilter Operator]] operators which take a filter strings as their parameter instead of a named function, and does not permit parameters to be passed">>
<<.operator-examples "function">>

View File

@ -0,0 +1,28 @@
created: 20221009124003601
modified: 20230419103154328
tags: Concepts Reference
title: Functions
type: text/vnd.tiddlywiki
!! Introduction
<<.from-version "5.3.0">> A <<.def function>> is a named snippet of text containing a [[Filter Expression]]. Functions can have named parameters which are available within the function as variables.
Functions are usually defined with the [[Pragma: \function]]:
```
\function my-function(parameter:"2")
[<parameter>multiply[1.5]]
\end
```
Functions can be invoked in several ways:
* Directly transclude functions with the syntax `<<myfn param:"value">>`
* Assign functions to widget attributes with the syntax `<div class=<<myfn param:"value">>>`
* Invoke functions via the [[function Operator]] with the syntax `[function[myfn],[value],...]`
* Directly invoke functions whose names start with a period as custom filter operators with the syntax `[.myfn[value]]`
!! How Functions Work
Functions 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.

View File

@ -51,6 +51,9 @@ TiddlyWiki lets you choose where to keep your data, guaranteeing that in the dec
<a href="https://www.reddit.com/r/TiddlyWiki5/" class="tc-btn-big-green" style="border-radius:4px;background-color:#FF4500;" target="_blank" rel="noopener noreferrer"> <a href="https://www.reddit.com/r/TiddlyWiki5/" class="tc-btn-big-green" style="border-radius:4px;background-color:#FF4500;" target="_blank" rel="noopener noreferrer">
{{Reddit Logo}} Reddit {{Reddit Logo}} Reddit
</a> </a>
<a href="https://opencollective.com/tiddlywiki" class="tc-btn-big-green" style="border-radius:4px;background-color:#0c2c66;" target="_blank" rel="noopener noreferrer">
{{Open Collective Logo}} Open Collective
</a>
</div> </div>
!! ''Testimonials & Reviews'' !! ''Testimonials & Reviews''

View File

@ -0,0 +1,14 @@
created: 20220909111836951
modified: 20230419103154329
tags: Learning
title: Visible Transclusions
type: text/vnd.tiddlywiki
!! Visible Transclusions
Block transclusions are shown in red, and inline transclusions are shown in green.
<$button>
<$action-setfield $tiddler="$:/temp/VisibleTransclusions" tags="$:/tags/Macro/View/Body" text={{$:/core/ui/VisibleTransclude}}/>
Click here to make transclusions visible within story river tiddlers
</$button>

Some files were not shown because too many files have changed in this diff Show More