mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-27 18:40:28 +00:00
Extend the HTML rendering mechanism to support attributes specified as macro invocations
This commit is contained in:
parent
18f8b7266e
commit
12b471b8fb
@ -190,13 +190,13 @@ exports.parseMacroParameter = function(source,pos) {
|
||||
};
|
||||
|
||||
/*
|
||||
Look for a macro invocation. Returns null if not found, or {type: "macro-invocation", name:, parameters:, start:, end:}
|
||||
Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:}
|
||||
*/
|
||||
exports.parseMacroInvocation = function(source,pos) {
|
||||
var node = {
|
||||
type: "macro-invocation",
|
||||
type: "macrocall",
|
||||
start: pos,
|
||||
parameters: []
|
||||
params: []
|
||||
}
|
||||
// Define our regexps
|
||||
var reMacroName = /([^\s>"'=]+)/g;
|
||||
@ -218,7 +218,7 @@ exports.parseMacroInvocation = function(source,pos) {
|
||||
// Process parameters
|
||||
var parameter = this.parseMacroParameter(source,pos);
|
||||
while(parameter) {
|
||||
node.parameters.push(parameter);
|
||||
node.params.push(parameter);
|
||||
pos = parameter.end;
|
||||
// Get the next parameter
|
||||
parameter = this.parseMacroParameter(source,pos);
|
||||
|
@ -95,20 +95,30 @@ var ElementRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
};
|
||||
|
||||
ElementRenderer.prototype.computeAttributes = function() {
|
||||
var changedAttributes = {};
|
||||
var self = this;
|
||||
var changedAttributes = {},
|
||||
self = this,
|
||||
value;
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(attribute.type === "indirect") {
|
||||
var value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
||||
if(self.attributes[name] !== value) {
|
||||
self.attributes[name] = value;
|
||||
changedAttributes[name] = true;
|
||||
value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
||||
} else if(attribute.type === "macro") {
|
||||
// Get the macro definition
|
||||
var macro = self.renderTree.findMacroDefinition(self.parentRenderer,attribute.value.name);
|
||||
if(!macro) {
|
||||
value = "";
|
||||
} else {
|
||||
// Substitute the macro parameters
|
||||
value = self.renderTree.substituteParameters(macro,attribute.value);
|
||||
// Parse the text and render it as text
|
||||
value = self.renderTree.wiki.renderText("text/plain","text/vnd.tiddlywiki",value,self.context);
|
||||
}
|
||||
} else { // String attribute
|
||||
if(self.attributes[name] !== attribute.value) {
|
||||
self.attributes[name] = attribute.value;
|
||||
changedAttributes[name] = true;
|
||||
}
|
||||
value = attribute.value;
|
||||
}
|
||||
// Check whether the attribute has changed
|
||||
if(self.attributes[name] !== value) {
|
||||
self.attributes[name] = value;
|
||||
changedAttributes[name] = true;
|
||||
}
|
||||
});
|
||||
return changedAttributes;
|
||||
|
@ -28,7 +28,7 @@ var MacroCallRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
childTree = [{type: "text", text: "<<Undefined macro: " + this.parseTreeNode.name + ">>"}];
|
||||
} else {
|
||||
// Substitute the macro parameters
|
||||
var text = this.substituteParameters(macro.text,this.parseTreeNode,macro);
|
||||
var text = this.renderTree.substituteParameters(macro,this.parseTreeNode);
|
||||
// Parse the text
|
||||
childTree = this.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.parseTreeNode.isBlock}).tree;
|
||||
}
|
||||
@ -36,39 +36,6 @@ var MacroCallRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
||||
this.children = this.renderTree.createRenderers(this,childTree);
|
||||
};
|
||||
|
||||
/*
|
||||
Expand the parameters in a block of text
|
||||
*/
|
||||
MacroCallRenderer.prototype.substituteParameters = function(text,macroCallParseTreeNode,macroDefinition) {
|
||||
var nextAnonParameter = 0; // Next candidate anonymous parameter in macro call
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<macroDefinition.params.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
var paramInfo = macroDefinition.params[p],
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<macroCallParseTreeNode.params.length; m++) {
|
||||
if(macroCallParseTreeNode.params[m].name === paramInfo.name) {
|
||||
paramValue = macroCallParseTreeNode.params[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
if(!paramValue && macroCallParseTreeNode.params.length > 0) {
|
||||
while(macroCallParseTreeNode.params[nextAnonParameter].name && nextAnonParameter < macroCallParseTreeNode.params.length-1) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(!macroCallParseTreeNode.params[nextAnonParameter].name) {
|
||||
paramValue = macroCallParseTreeNode.params[nextAnonParameter].value;
|
||||
nextAnonParameter++;
|
||||
}
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Replace any instances of this parameter
|
||||
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
MacroCallRenderer.prototype.renderInDom = function() {
|
||||
// Create the element
|
||||
this.domNode = this.renderTree.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
|
||||
|
@ -155,6 +155,40 @@ WikiRenderTree.prototype.findMacroDefinition = function(renderer,name) {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Expand the parameters of a macro
|
||||
*/
|
||||
WikiRenderTree.prototype.substituteParameters = function(macroDefinition,macroCallParseTreeNode) {
|
||||
var text = macroDefinition.text,
|
||||
nextAnonParameter = 0; // Next candidate anonymous parameter in macro call
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<macroDefinition.params.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
var paramInfo = macroDefinition.params[p],
|
||||
paramValue = undefined;
|
||||
for(var m=0; m<macroCallParseTreeNode.params.length; m++) {
|
||||
if(macroCallParseTreeNode.params[m].name === paramInfo.name) {
|
||||
paramValue = macroCallParseTreeNode.params[m].value;
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
if(!paramValue && macroCallParseTreeNode.params.length > 0) {
|
||||
while(macroCallParseTreeNode.params[nextAnonParameter].name && nextAnonParameter < macroCallParseTreeNode.params.length-1) {
|
||||
nextAnonParameter++;
|
||||
}
|
||||
if(!macroCallParseTreeNode.params[nextAnonParameter].name) {
|
||||
paramValue = macroCallParseTreeNode.params[nextAnonParameter].value;
|
||||
nextAnonParameter++;
|
||||
}
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
// Replace any instances of this parameter
|
||||
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
exports.WikiRenderTree = WikiRenderTree;
|
||||
|
||||
})();
|
||||
|
@ -99,22 +99,22 @@ describe("HTML tag new parser tests", function() {
|
||||
null
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<mymacro>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ ], name : 'mymacro', end : 11 }
|
||||
{ type : 'macrocall', start : 0, params : [ ], name : 'mymacro', end : 11 }
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<mymacro one two three>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ { type : 'macro-parameter', start : 9, value : 'one', end : 13 }, { type : 'macro-parameter', start : 13, value : 'two', end : 17 }, { type : 'macro-parameter', start : 17, value : 'three', end : 23 } ], name : 'mymacro', end : 25 }
|
||||
{ type : 'macrocall', start : 0, params : [ { type : 'macro-parameter', start : 9, value : 'one', end : 13 }, { type : 'macro-parameter', start : 13, value : 'two', end : 17 }, { type : 'macro-parameter', start : 17, value : 'three', end : 23 } ], name : 'mymacro', end : 25 }
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<mymacro p:one q:two three>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ { type : 'macro-parameter', start : 9, value : 'one', name : 'p', end : 15 }, { type : 'macro-parameter', start : 15, value : 'two', name : 'q', end : 21 }, { type : 'macro-parameter', start : 21, value : 'three', end : 27 } ], name : 'mymacro', end : 29 }
|
||||
{ type : 'macrocall', start : 0, params : [ { type : 'macro-parameter', start : 9, value : 'one', name : 'p', end : 15 }, { type : 'macro-parameter', start : 15, value : 'two', name : 'q', end : 21 }, { type : 'macro-parameter', start : 21, value : 'three', end : 27 } ], name : 'mymacro', end : 29 }
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<mymacro 'one two three'>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ { type : 'macro-parameter', start : 9, value : 'one two three', end : 25 } ], name : 'mymacro', end : 27 }
|
||||
{ type : 'macrocall', start : 0, params : [ { type : 'macro-parameter', start : 9, value : 'one two three', end : 25 } ], name : 'mymacro', end : 27 }
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<mymacro r:'one two three'>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ { type : 'macro-parameter', start : 9, value : 'one two three', name : 'r', end : 27 } ], name : 'mymacro', end : 29 }
|
||||
{ type : 'macrocall', start : 0, params : [ { type : 'macro-parameter', start : 9, value : 'one two three', name : 'r', end : 27 } ], name : 'mymacro', end : 29 }
|
||||
);
|
||||
expect(parser.parseMacroInvocation("<<myMacro one:two three:'four and five'>>",0)).toEqual(
|
||||
{ type : 'macro-invocation', start : 0, parameters : [ { type : 'macro-parameter', start : 9, value : 'two', name : 'one', end : 17 }, { type : 'macro-parameter', start : 17, value : 'four and five', name : 'three', end : 39 } ], name : 'myMacro', end : 41 }
|
||||
{ type : 'macrocall', start : 0, params : [ { type : 'macro-parameter', start : 9, value : 'two', name : 'one', end : 17 }, { type : 'macro-parameter', start : 17, value : 'four and five', name : 'three', end : 39 } ], name : 'myMacro', end : 41 }
|
||||
);
|
||||
});
|
||||
|
||||
@ -177,10 +177,10 @@ describe("HTML tag new parser tests", function() {
|
||||
null
|
||||
);
|
||||
expect(parser.parseTag("<$mytag attrib3=<<myMacro one:two three:'four and five'>>>",0)).toEqual(
|
||||
{ type : 'element', start : 0, attributes : { attrib3 : { type : 'macro', start : 7, name : 'attrib3', value : { type : 'macro-invocation', start : 16, parameters : [ { type : 'macro-parameter', start : 25, value : 'two', name : 'one', end : 33 }, { type : 'macro-parameter', start : 33, value : 'four and five', name : 'three', end : 55 } ], name : 'myMacro', end : 57 }, end : 57 } }, tag : '$mytag', end : 58 }
|
||||
{ type : 'element', start : 0, attributes : { attrib3 : { type : 'macro', start : 7, name : 'attrib3', value : { type : 'macrocall', start : 16, params : [ { type : 'macro-parameter', start : 25, value : 'two', name : 'one', end : 33 }, { type : 'macro-parameter', start : 33, value : 'four and five', name : 'three', end : 55 } ], name : 'myMacro', end : 57 }, end : 57 } }, tag : '$mytag', end : 58 }
|
||||
);
|
||||
expect(parser.parseTag("<$mytag attrib1='something' attrib2=else thing attrib3=<<myMacro one:two three:'four and five'>>>",0)).toEqual(
|
||||
{ type : 'element', start : 0, attributes : { attrib1 : { type : 'string', start : 7, name : 'attrib1', value : 'something', end : 27 }, attrib2 : { type : 'string', start : 27, name : 'attrib2', value : 'else', end : 40 }, thing : { type : 'string', start : 40, name : 'thing', value : 'true', end : 47 }, attrib3 : { type : 'macro', start : 47, name : 'attrib3', value : { type : 'macro-invocation', start : 55, parameters : [ { type : 'macro-parameter', start : 64, value : 'two', name : 'one', end : 72 }, { type : 'macro-parameter', start : 72, value : 'four and five', name : 'three', end : 94 } ], name : 'myMacro', end : 96 }, end : 96 } }, tag : '$mytag', end : 97 }
|
||||
{ type : 'element', start : 0, attributes : { attrib1 : { type : 'string', start : 7, name : 'attrib1', value : 'something', end : 27 }, attrib2 : { type : 'string', start : 27, name : 'attrib2', value : 'else', end : 40 }, thing : { type : 'string', start : 40, name : 'thing', value : 'true', end : 47 }, attrib3 : { type : 'macro', start : 47, name : 'attrib3', value : { type : 'macrocall', start : 55, params : [ { type : 'macro-parameter', start : 64, value : 'two', name : 'one', end : 72 }, { type : 'macro-parameter', start : 72, value : 'four and five', name : 'three', end : 94 } ], name : 'myMacro', end : 96 }, end : 96 } }, tag : '$mytag', end : 97 }
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -20,8 +20,9 @@ describe("WikiText tests", function() {
|
||||
wiki.addTiddler({title: "TiddlerOne", text: "The quick brown fox"});
|
||||
wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"});
|
||||
wiki.addTiddler({title: "TiddlerThree", text: "The speed of sound\n\nThe light of speed"});
|
||||
wiki.addTiddler({title: "TiddlerFour", text: "\\define my-macro(adjective:'groovy')\nThis is my ''amazingly'' $adjective$ macro!\n\\end\n\n<$link to=<<my-macro>>>This is a link</$link>"});
|
||||
|
||||
it("should render tiddlers with no special markup render as-is", function() {
|
||||
it("should render tiddlers with no special markup as-is", function() {
|
||||
expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
|
||||
});
|
||||
it("should preserve single new lines", function() {
|
||||
@ -41,6 +42,10 @@ describe("WikiText tests", function() {
|
||||
it("should use double new lines to create paragraphs", function() {
|
||||
expect(wiki.renderTiddler("text/html","TiddlerThree")).toBe("<p>\nThe speed of sound</p><p>\nThe light of speed</p>");
|
||||
});
|
||||
it("should support attributes specified as macro invocations", function() {
|
||||
expect(wiki.renderTiddler("text/html","TiddlerFour")).toBe("<p>\n<a class='tw-tiddlylink tw-tiddlylink-internal tw-tiddlylink-missing' href='This%20is%20my%20amazingly%20groovy%20macro!'>\nThis is a link</a></p>");
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
@ -230,6 +230,20 @@ This is my nice and simple block of text. HelloThere
|
||||
</article>
|
||||
```
|
||||
|
||||
Attributes in HTML tags can be specified as a transclusion or a macro invocation. For example, here the value of the `href` attribute will be set to the value of the tiddler MyLinkDestination:
|
||||
|
||||
```
|
||||
<a href={{MyLinkDestination}}>link</a>
|
||||
```
|
||||
|
||||
Here an attribute is specified as a macro invocation:
|
||||
|
||||
```
|
||||
<a href=<<MyMacro "Brian">>>link</a>
|
||||
```
|
||||
|
||||
* As a macro invocation
|
||||
|
||||
! Widgets
|
||||
|
||||
Widgets provide rich functionality within WikiText. For example, the `<$video>` widget can be used to embed videos from YouTube, Vimeo or the Internet Archive:
|
||||
|
Loading…
Reference in New Issue
Block a user