mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 03:57:21 +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) {
|
exports.parseMacroInvocation = function(source,pos) {
|
||||||
var node = {
|
var node = {
|
||||||
type: "macro-invocation",
|
type: "macrocall",
|
||||||
start: pos,
|
start: pos,
|
||||||
parameters: []
|
params: []
|
||||||
}
|
}
|
||||||
// Define our regexps
|
// Define our regexps
|
||||||
var reMacroName = /([^\s>"'=]+)/g;
|
var reMacroName = /([^\s>"'=]+)/g;
|
||||||
@ -218,7 +218,7 @@ exports.parseMacroInvocation = function(source,pos) {
|
|||||||
// Process parameters
|
// Process parameters
|
||||||
var parameter = this.parseMacroParameter(source,pos);
|
var parameter = this.parseMacroParameter(source,pos);
|
||||||
while(parameter) {
|
while(parameter) {
|
||||||
node.parameters.push(parameter);
|
node.params.push(parameter);
|
||||||
pos = parameter.end;
|
pos = parameter.end;
|
||||||
// Get the next parameter
|
// Get the next parameter
|
||||||
parameter = this.parseMacroParameter(source,pos);
|
parameter = this.parseMacroParameter(source,pos);
|
||||||
|
@ -95,20 +95,30 @@ var ElementRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ElementRenderer.prototype.computeAttributes = function() {
|
ElementRenderer.prototype.computeAttributes = function() {
|
||||||
var changedAttributes = {};
|
var changedAttributes = {},
|
||||||
var self = this;
|
self = this,
|
||||||
|
value;
|
||||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||||
if(attribute.type === "indirect") {
|
if(attribute.type === "indirect") {
|
||||||
var value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
||||||
if(self.attributes[name] !== value) {
|
} else if(attribute.type === "macro") {
|
||||||
self.attributes[name] = value;
|
// Get the macro definition
|
||||||
changedAttributes[name] = true;
|
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
|
} else { // String attribute
|
||||||
if(self.attributes[name] !== attribute.value) {
|
value = attribute.value;
|
||||||
self.attributes[name] = attribute.value;
|
}
|
||||||
changedAttributes[name] = true;
|
// Check whether the attribute has changed
|
||||||
}
|
if(self.attributes[name] !== value) {
|
||||||
|
self.attributes[name] = value;
|
||||||
|
changedAttributes[name] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return changedAttributes;
|
return changedAttributes;
|
||||||
|
@ -28,7 +28,7 @@ var MacroCallRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
|||||||
childTree = [{type: "text", text: "<<Undefined macro: " + this.parseTreeNode.name + ">>"}];
|
childTree = [{type: "text", text: "<<Undefined macro: " + this.parseTreeNode.name + ">>"}];
|
||||||
} else {
|
} else {
|
||||||
// Substitute the macro parameters
|
// Substitute the macro parameters
|
||||||
var text = this.substituteParameters(macro.text,this.parseTreeNode,macro);
|
var text = this.renderTree.substituteParameters(macro,this.parseTreeNode);
|
||||||
// Parse the text
|
// Parse the text
|
||||||
childTree = this.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.parseTreeNode.isBlock}).tree;
|
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);
|
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() {
|
MacroCallRenderer.prototype.renderInDom = function() {
|
||||||
// Create the element
|
// Create the element
|
||||||
this.domNode = this.renderTree.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
|
this.domNode = this.renderTree.document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
|
||||||
|
@ -155,6 +155,40 @@ WikiRenderTree.prototype.findMacroDefinition = function(renderer,name) {
|
|||||||
return undefined;
|
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;
|
exports.WikiRenderTree = WikiRenderTree;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -99,22 +99,22 @@ describe("HTML tag new parser tests", function() {
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
expect(parser.parseMacroInvocation("<<mymacro>>",0)).toEqual(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
null
|
||||||
);
|
);
|
||||||
expect(parser.parseTag("<$mytag attrib3=<<myMacro one:two three:'four and five'>>>",0)).toEqual(
|
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(
|
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: "TiddlerOne", text: "The quick brown fox"});
|
||||||
wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"});
|
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: "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");
|
expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
|
||||||
});
|
});
|
||||||
it("should preserve single new lines", function() {
|
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() {
|
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>");
|
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>
|
</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
|
||||||
|
|
||||||
Widgets provide rich functionality within WikiText. For example, the `<$video>` widget can be used to embed videos from YouTube, Vimeo or the Internet Archive:
|
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