mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-22 23:16:53 +00:00
Merge pull request #1327 from aelocson/railroad-links
Links and transclusions in railroad diagrams
This commit is contained in:
commit
ea07b558a3
@ -27,18 +27,13 @@ exports.init = function(parser) {
|
|||||||
this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg;
|
this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isLinkExternal = function(to) {
|
|
||||||
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s<>{}\[\]`|'"\\^~]+(?:\/|\b)/i;
|
|
||||||
return externalRegExp.test(to);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.parse = function() {
|
exports.parse = function() {
|
||||||
// Move past the match
|
// Move past the match
|
||||||
this.parser.pos = this.matchRegExp.lastIndex;
|
this.parser.pos = this.matchRegExp.lastIndex;
|
||||||
// Process the link
|
// Process the link
|
||||||
var text = this.match[1],
|
var text = this.match[1],
|
||||||
link = this.match[2] || text;
|
link = this.match[2] || text;
|
||||||
if(isLinkExternal(link)) {
|
if($tw.utils.isLinkExternal(link)) {
|
||||||
return [{
|
return [{
|
||||||
type: "element",
|
type: "element",
|
||||||
tag: "a",
|
tag: "a",
|
||||||
|
@ -439,6 +439,12 @@ exports.escapeRegExp = function(s) {
|
|||||||
return s.replace(/[\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}]/g, '\\$&');
|
return s.replace(/[\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}]/g, '\\$&');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Checks whether a link target is external, i.e. not a tiddler title
|
||||||
|
exports.isLinkExternal = function(to) {
|
||||||
|
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s<>{}\[\]`|'"\\^~]+(?:\/|\b)/i;
|
||||||
|
return externalRegExp.test(to);
|
||||||
|
};
|
||||||
|
|
||||||
exports.nextTick = function(fn) {
|
exports.nextTick = function(fn) {
|
||||||
/*global window: false */
|
/*global window: false */
|
||||||
if(typeof process === "undefined") {
|
if(typeof process === "undefined") {
|
||||||
|
@ -106,8 +106,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
|||||||
this.domNodes.push(domNode);
|
this.domNodes.push(domNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
LinkWidget.prototype.handleClickEvent = function (event) {
|
LinkWidget.prototype.handleClickEvent = function(event) {
|
||||||
// Send the click on it's way as a navigate event
|
// Send the click on its way as a navigate event
|
||||||
var bounds = this.domNodes[0].getBoundingClientRect();
|
var bounds = this.domNodes[0].getBoundingClientRect();
|
||||||
this.dispatchEvent({
|
this.dispatchEvent({
|
||||||
type: "tm-navigate",
|
type: "tm-navigate",
|
||||||
|
@ -89,13 +89,13 @@ Component.prototype.debug = function(output,indent) {
|
|||||||
Component.prototype.debugArray = function(array,output,indent) {
|
Component.prototype.debugArray = function(array,output,indent) {
|
||||||
for(var i=0; i<array.length; i++) {
|
for(var i=0; i<array.length; i++) {
|
||||||
var item = array[i];
|
var item = array[i];
|
||||||
// Choice content is a special case: an array of arrays
|
// Choice content is a special case: we number the branches
|
||||||
if(item.isChoiceBranch) {
|
if(item.isChoiceBranch) {
|
||||||
output.push(indent);
|
output.push(indent);
|
||||||
output.push("(");
|
output.push("(");
|
||||||
output.push(i);
|
output.push(i);
|
||||||
output.push(")\n");
|
output.push(")\n");
|
||||||
item.debug(output," " +indent);
|
item.debug(output," "+indent);
|
||||||
} else {
|
} else {
|
||||||
item.debug(output,indent);
|
item.debug(output,indent);
|
||||||
}
|
}
|
||||||
@ -205,6 +205,27 @@ Repeated.prototype.toSvg = function() {
|
|||||||
return railroad.OneOrMore(this.child.toSvg(),separatorSvg);
|
return railroad.OneOrMore(this.child.toSvg(),separatorSvg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Link = function(content,options) {
|
||||||
|
this.initialiseWithChild("Link",content);
|
||||||
|
this.options = options;
|
||||||
|
};
|
||||||
|
|
||||||
|
Link.prototype = new Component();
|
||||||
|
|
||||||
|
Link.prototype.toSvg = function() {
|
||||||
|
return railroad.Link(this.child.toSvg(),this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var Transclusion = function(content) {
|
||||||
|
this.initialiseWithChild("Transclusion",content);
|
||||||
|
};
|
||||||
|
|
||||||
|
Transclusion.prototype = new Component();
|
||||||
|
|
||||||
|
Transclusion.prototype.toSvg = function() {
|
||||||
|
return this.child.toSvg();
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////// Components with an array of children
|
/////////////////////////// Components with an array of children
|
||||||
|
|
||||||
var Root = function(content) {
|
var Root = function(content) {
|
||||||
@ -252,13 +273,15 @@ exports.components = {
|
|||||||
Choice: Choice,
|
Choice: Choice,
|
||||||
Comment: Comment,
|
Comment: Comment,
|
||||||
Dummy: Dummy,
|
Dummy: Dummy,
|
||||||
|
Link: Link,
|
||||||
Nonterminal: Nonterminal,
|
Nonterminal: Nonterminal,
|
||||||
Optional: Optional,
|
Optional: Optional,
|
||||||
OptionalRepeated: OptionalRepeated,
|
OptionalRepeated: OptionalRepeated,
|
||||||
Repeated: Repeated,
|
Repeated: Repeated,
|
||||||
Root: Root,
|
Root: Root,
|
||||||
Sequence: Sequence,
|
Sequence: Sequence,
|
||||||
Terminal: Terminal
|
Terminal: Terminal,
|
||||||
|
Transclusion: Transclusion
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
@ -5,5 +5,5 @@ title: $:/plugins/tiddlywiki/railroad/example-source
|
|||||||
type: text/plain
|
type: text/plain
|
||||||
|
|
||||||
["+"]
|
["+"]
|
||||||
({digit} | "#" <'escape sequence'>)
|
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
|
||||||
[{("@" name-char | :"--" )}]
|
[{("@" name-char | :"--" )}]
|
@ -8,7 +8,7 @@ title: $:/plugins/tiddlywiki/railroad/example
|
|||||||
```
|
```
|
||||||
<$railroad text="""
|
<$railroad text="""
|
||||||
["+"]
|
["+"]
|
||||||
({digit} | "#" <'escape sequence'>)
|
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
|
||||||
[{("@" name-char | :"--" )}]
|
[{("@" name-char | :"--" )}]
|
||||||
"""/>
|
"""/>
|
||||||
```
|
```
|
||||||
|
@ -2,7 +2,7 @@ created: 20150102163222184
|
|||||||
modified: 20150102172016663
|
modified: 20150102172016663
|
||||||
title: $:/plugins/tiddlywiki/railroad/readme
|
title: $:/plugins/tiddlywiki/railroad/readme
|
||||||
|
|
||||||
This plugin provides a `<$railroad>` widget for generating railroad syntax diagrams as SVG images. It is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]].
|
This plugin provides a `<$railroad>` widget for generating railroad syntax diagrams as SVG images. It is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]], and has been extended to allow components of a diagram to function as links.
|
||||||
|
|
||||||
The content of the `<$railroad>` widget is ignored.
|
The content of the `<$railroad>` widget is ignored.
|
||||||
|
|
||||||
@ -10,8 +10,10 @@ The content of the `<$railroad>` widget is ignored.
|
|||||||
|text |Text in a special syntax that defines the diagram's layout |
|
|text |Text in a special syntax that defines the diagram's layout |
|
||||||
|mode |If set to `debug`, the diagram will display its internal tree structure. The default mode is `svg` |
|
|mode |If set to `debug`, the diagram will display its internal tree structure. The default mode is `svg` |
|
||||||
|
|
||||||
The `text` can be transcluded from another tiddler:
|
The entire `text` can be transcluded from another tiddler:
|
||||||
|
|
||||||
```
|
```
|
||||||
<$railroad tiddler={{diagram}}>
|
<$railroad tiddler={{diagram}}>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively, the diagram syntax allows specific parts of the `text` to be transcluded from other tiddlers.
|
@ -2,10 +2,12 @@ created: 20150103184022184
|
|||||||
modified: 20150103184022184
|
modified: 20150103184022184
|
||||||
title: $:/plugins/tiddlywiki/railroad/syntax
|
title: $:/plugins/tiddlywiki/railroad/syntax
|
||||||
|
|
||||||
The railroad widget constructs a diagram from the components defined below.
|
The railroad widget uses a special ''diagram syntax'' to construct the components defined below.
|
||||||
|
|
||||||
`x` and `y` here stand for any component.
|
`x` and `y` here stand for any component.
|
||||||
|
|
||||||
|
Names (as opposed to quoted strings) are available when a value starts with a letter and contains only letters, digits, underscores, dots and hyphens.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
; sequence
|
; sequence
|
||||||
@ -52,7 +54,6 @@ The railroad widget constructs a diagram from the components defined below.
|
|||||||
; nonterminal
|
; nonterminal
|
||||||
: <$railroad text=""" (name | "<" string ">") """/>
|
: <$railroad text=""" (name | "<" string ">") """/>
|
||||||
* A nonterminal component, i.e. the name of another diagram
|
* A nonterminal component, i.e. the name of another diagram
|
||||||
* The simple `name` option is available when the text starts with a letter and contains only letters, digits, underscores, dots and hyphens
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -64,4 +65,16 @@ The railroad widget constructs a diagram from the components defined below.
|
|||||||
|
|
||||||
; dummy
|
; dummy
|
||||||
: <$railroad text=""" "-" """/>
|
: <$railroad text=""" "-" """/>
|
||||||
* The absence of a component
|
* The absence of a component
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
; link
|
||||||
|
: <$railroad text=""" "[[" x "|" (name|string) "]]" """/>
|
||||||
|
* A link to the tiddler title or URI given by the string or name
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
; transclusion
|
||||||
|
: <$railroad text=""" "{{" (name|string) "}}" """/>
|
||||||
|
* Treats the content of another tiddler as diagram syntax and transcludes it into the current diagram
|
||||||
|
@ -467,15 +467,16 @@ var temp = (function(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TiddlyWiki: added linking ability */
|
/* TiddlyWiki: added linking ability */
|
||||||
function Link(target, item) {
|
function Link(item,options) {
|
||||||
if(!(this instanceof Link)) return new Link(target, item);
|
if(!(this instanceof Link)) return new Link(item,options);
|
||||||
FakeSVG.call(this, 'a', {'xlink:href': target});
|
FakeSVG.call(this,'a',options);
|
||||||
this.item = item;
|
this.item = item;
|
||||||
this.width = item.width;
|
this.width = item.width;
|
||||||
this.up = item.up;
|
this.up = item.up;
|
||||||
this.down = item.down;
|
this.down = item.down;
|
||||||
}
|
}
|
||||||
subclassOf(Link, FakeSVG);
|
subclassOf(Link, FakeSVG);
|
||||||
|
Link.prototype.needsSpace = true;
|
||||||
Link.prototype.format = function(x, y, width) {
|
Link.prototype.format = function(x, y, width) {
|
||||||
this.item.format(x,y,width).addTo(this);
|
this.item.format(x,y,width).addTo(this);
|
||||||
return this;
|
return this;
|
||||||
|
@ -21,13 +21,11 @@ x y z sequence
|
|||||||
<"x"> nonterminal
|
<"x"> nonterminal
|
||||||
/"blah"/ comment
|
/"blah"/ comment
|
||||||
- dummy
|
- dummy
|
||||||
|
[[x|"tiddler"]] link
|
||||||
|
{{"tiddler"}} transclusion
|
||||||
|
|
||||||
"x" can also be written 'x' or """x"""
|
"x" can also be written 'x' or """x"""
|
||||||
|
|
||||||
Future extensions:
|
|
||||||
[[x|tiddler]] link
|
|
||||||
{{tiddler}} transclusion
|
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
@ -37,12 +35,14 @@ Future extensions:
|
|||||||
|
|
||||||
var components = require("$:/plugins/tiddlywiki/railroad/components.js").components;
|
var components = require("$:/plugins/tiddlywiki/railroad/components.js").components;
|
||||||
|
|
||||||
var Parser = function(source) {
|
var Parser = function(widget,source) {
|
||||||
|
this.widget = widget;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.tokens = this.tokenise(source);
|
this.tokens = this.tokenise(source);
|
||||||
this.tokenPos = 0;
|
this.tokenPos = 0;
|
||||||
this.advance();
|
this.advance();
|
||||||
this.root = new components.Root(this.parseContent());
|
this.content = this.parseContent();
|
||||||
|
this.root = new components.Root(this.content);
|
||||||
this.checkFinished();
|
this.checkFinished();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ Parser.prototype.parseComponent = function() {
|
|||||||
if(this.token) {
|
if(this.token) {
|
||||||
if(this.at("string")) {
|
if(this.at("string")) {
|
||||||
component = this.parseTerminal();
|
component = this.parseTerminal();
|
||||||
} else if(this.at("identifier")) {
|
} else if(this.at("name")) {
|
||||||
component = this.parseIdentifier();
|
component = this.parseName();
|
||||||
} else {
|
} else {
|
||||||
switch(this.token.value) {
|
switch(this.token.value) {
|
||||||
case "[":
|
case "[":
|
||||||
@ -85,6 +85,12 @@ Parser.prototype.parseComponent = function() {
|
|||||||
case "/":
|
case "/":
|
||||||
component = this.parseComment();
|
component = this.parseComment();
|
||||||
break;
|
break;
|
||||||
|
case "[[":
|
||||||
|
component = this.parseLink();
|
||||||
|
break;
|
||||||
|
case "{{":
|
||||||
|
component = this.parseTransclusion();
|
||||||
|
break;
|
||||||
case "<-":
|
case "<-":
|
||||||
component = this.parseSequence();
|
component = this.parseSequence();
|
||||||
break;
|
break;
|
||||||
@ -112,25 +118,21 @@ Parser.prototype.parseChoice = function() {
|
|||||||
// Parse the next branch
|
// Parse the next branch
|
||||||
content.push(this.parseContent());
|
content.push(this.parseContent());
|
||||||
} while(this.eat("|"));
|
} while(this.eat("|"));
|
||||||
// Create a component
|
|
||||||
var component = new components.Choice(content,colon === -1 ? 0 : colon);
|
|
||||||
// Consume the closing bracket
|
// Consume the closing bracket
|
||||||
this.close(")");
|
this.close(")");
|
||||||
return component;
|
// Create a component
|
||||||
|
return new components.Choice(content,colon === -1 ? 0 : colon);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseComment = function() {
|
Parser.prototype.parseComment = function() {
|
||||||
// Consume the /
|
// Consume the /
|
||||||
this.advance();
|
this.advance();
|
||||||
// The comment's content should be in a string literal
|
// The comment's content should be in a string literal
|
||||||
this.expectStringLiteral("/");
|
var content = this.expectString("after /");
|
||||||
// Create a component
|
|
||||||
var component = new components.Comment(this.token.value);
|
|
||||||
// Consume the string literal
|
|
||||||
this.advance();
|
|
||||||
// Consume the closing /
|
// Consume the closing /
|
||||||
this.close("/");
|
this.close("/");
|
||||||
return component;
|
// Create a component
|
||||||
|
return new components.Comment(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseDummy = function() {
|
Parser.prototype.parseDummy = function() {
|
||||||
@ -140,27 +142,43 @@ Parser.prototype.parseDummy = function() {
|
|||||||
return new components.Dummy();
|
return new components.Dummy();
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseIdentifier = function() {
|
Parser.prototype.parseLink = function() {
|
||||||
|
// Consume the [[
|
||||||
|
this.advance();
|
||||||
|
// Parse the content
|
||||||
|
var content = this.parseContent();
|
||||||
|
// Consume the |
|
||||||
|
this.expect("|");
|
||||||
|
// Consume the target
|
||||||
|
var target = this.expectNameOrString("as link target");
|
||||||
|
// Prepare some attributes for the SVG "a" element to carry
|
||||||
|
var options = {"data-tw-target": target};
|
||||||
|
if($tw.utils.isLinkExternal(target)) {
|
||||||
|
options["data-tw-external"] = true;
|
||||||
|
}
|
||||||
|
// Consume the closing ]]
|
||||||
|
this.close("]]");
|
||||||
|
// Create a component
|
||||||
|
return new components.Link(content,options);
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseName = function() {
|
||||||
// Create a component
|
// Create a component
|
||||||
var component = new components.Nonterminal(this.token.value);
|
var component = new components.Nonterminal(this.token.value);
|
||||||
// Consume the identifier
|
// Consume the name
|
||||||
this.advance();
|
this.advance();
|
||||||
return component;
|
return component;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Parser.prototype.parseNonterminal = function() {
|
Parser.prototype.parseNonterminal = function() {
|
||||||
// Consume the <
|
// Consume the <
|
||||||
this.advance();
|
this.advance();
|
||||||
// The nonterminal's name should be in a string literal
|
// The nonterminal's name should be in a string literal
|
||||||
this.expectStringLiteral("<");
|
var content = this.expectString("after <");
|
||||||
// Create a component
|
|
||||||
var component = new components.Nonterminal(this.token.value);
|
|
||||||
// Consume the string literal
|
|
||||||
this.advance();
|
|
||||||
// Consume the closing bracket
|
// Consume the closing bracket
|
||||||
this.close(">");
|
this.close(">");
|
||||||
return component;
|
// Create a component
|
||||||
|
return new components.Nonterminal(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseOptional = function() {
|
Parser.prototype.parseOptional = function() {
|
||||||
@ -177,14 +195,13 @@ Parser.prototype.parseOptional = function() {
|
|||||||
if(repeated && this.eat("+")) {
|
if(repeated && this.eat("+")) {
|
||||||
separator = this.parseContent();
|
separator = this.parseContent();
|
||||||
}
|
}
|
||||||
// Create a component
|
|
||||||
var component = repeated ? new components.OptionalRepeated(content,separator,normal) : new components.Optional(content,normal);
|
|
||||||
// Consume the closing brackets
|
// Consume the closing brackets
|
||||||
if(repeated) {
|
if(repeated) {
|
||||||
this.close("}");
|
this.close("}");
|
||||||
}
|
}
|
||||||
this.close("]");
|
this.close("]");
|
||||||
return component;
|
// Create a component
|
||||||
|
return repeated ? new components.OptionalRepeated(content,separator,normal) : new components.Optional(content,normal);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseRepeated = function() {
|
Parser.prototype.parseRepeated = function() {
|
||||||
@ -197,23 +214,21 @@ Parser.prototype.parseRepeated = function() {
|
|||||||
if(this.eat("+")) {
|
if(this.eat("+")) {
|
||||||
separator = this.parseContent();
|
separator = this.parseContent();
|
||||||
}
|
}
|
||||||
// Create a component
|
|
||||||
var component = new components.Repeated(content,separator);
|
|
||||||
// Consume the closing bracket
|
// Consume the closing bracket
|
||||||
this.close("}");
|
this.close("}");
|
||||||
return component;
|
// Create a component
|
||||||
|
return new components.Repeated(content,separator);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseSequence = function() {
|
Parser.prototype.parseSequence = function() {
|
||||||
// Consume the ~
|
// Consume the <-
|
||||||
this.advance();
|
this.advance();
|
||||||
// Parse the content
|
// Parse the content
|
||||||
var content = this.parseContent();
|
var content = this.parseContent();
|
||||||
// Create a component
|
// Consume the closing ->
|
||||||
var component = new components.Sequence(content);
|
|
||||||
// Consume the closing ~
|
|
||||||
this.close("->");
|
this.close("->");
|
||||||
return component;
|
// Create a component
|
||||||
|
return new components.Sequence(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseTerminal = function() {
|
Parser.prototype.parseTerminal = function() {
|
||||||
@ -223,6 +238,21 @@ Parser.prototype.parseTerminal = function() {
|
|||||||
return component;
|
return component;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseTransclusion = function() {
|
||||||
|
// Consume the {{
|
||||||
|
this.advance();
|
||||||
|
// Consume the text reference
|
||||||
|
var textRef = this.expectNameOrString("as transclusion source");
|
||||||
|
// Consume the closing }}
|
||||||
|
this.close("}}");
|
||||||
|
// Retrieve the content of the text reference
|
||||||
|
var source = this.widget.wiki.getTextReference(textRef,"",this.widget.getVariable("currentTiddler"));
|
||||||
|
// Parse the content
|
||||||
|
var content = new Parser(this.widget,source).content;
|
||||||
|
// Create a component
|
||||||
|
return new components.Transclusion(content);
|
||||||
|
};
|
||||||
|
|
||||||
/////////////////////////// Token manipulation
|
/////////////////////////// Token manipulation
|
||||||
|
|
||||||
Parser.prototype.advance = function() {
|
Parser.prototype.advance = function() {
|
||||||
@ -244,10 +274,10 @@ Parser.prototype.eat = function(token) {
|
|||||||
return at;
|
return at;
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.expectStringLiteral = function(preamble) {
|
Parser.prototype.tokenValue = function() {
|
||||||
if(!this.at("string")) {
|
var output = this.token.value;
|
||||||
throw "String expected after " + preamble;
|
this.advance();
|
||||||
}
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.close = function(token) {
|
Parser.prototype.close = function(token) {
|
||||||
@ -262,6 +292,27 @@ Parser.prototype.checkFinished = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Parser.prototype.expect = function(token) {
|
||||||
|
if(!this.eat(token)) {
|
||||||
|
throw token + " expected";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.expectString = function(context,token) {
|
||||||
|
if(!this.at("string")) {
|
||||||
|
token = token || "String";
|
||||||
|
throw token + " expected " + context;
|
||||||
|
}
|
||||||
|
return this.tokenValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.expectNameOrString = function(context) {
|
||||||
|
if(this.at("name")) {
|
||||||
|
return this.tokenValue();
|
||||||
|
}
|
||||||
|
return this.expectString(context,"Name or string");
|
||||||
|
};
|
||||||
|
|
||||||
/////////////////////////// Tokenisation
|
/////////////////////////// Tokenisation
|
||||||
|
|
||||||
Parser.prototype.tokenise = function(source) {
|
Parser.prototype.tokenise = function(source) {
|
||||||
@ -294,12 +345,12 @@ Parser.prototype.tokenise = function(source) {
|
|||||||
} else if(c === "-") {
|
} else if(c === "-") {
|
||||||
// - or ->
|
// - or ->
|
||||||
s = source.charAt(pos+1) === ">" ? "->" : "-";
|
s = source.charAt(pos+1) === ">" ? "->" : "-";
|
||||||
} else if("()>+|/:".indexOf(c) !== -1) {
|
} else if("()>+/:|".indexOf(c) !== -1) {
|
||||||
// Single character
|
// Single character
|
||||||
s = c;
|
s = c;
|
||||||
} else if(c.match(/[a-zA-Z]/)) {
|
} else if(c.match(/[a-zA-Z]/)) {
|
||||||
// Identifier
|
// Name
|
||||||
token = this.readIdentifier(source,pos);
|
token = this.readName(source,pos);
|
||||||
} else {
|
} else {
|
||||||
throw "Syntax error at " + c;
|
throw "Syntax error at " + c;
|
||||||
}
|
}
|
||||||
@ -316,14 +367,14 @@ Parser.prototype.tokenise = function(source) {
|
|||||||
return tokens;
|
return tokens;
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.readIdentifier = function(source,pos) {
|
Parser.prototype.readName = function(source,pos) {
|
||||||
var re = /([a-zA-Z0-9_.-]+)/g;
|
var re = /([a-zA-Z0-9_.-]+)/g;
|
||||||
re.lastIndex = pos;
|
re.lastIndex = pos;
|
||||||
var match = re.exec(source);
|
var match = re.exec(source);
|
||||||
if(match && match.index === pos) {
|
if(match && match.index === pos) {
|
||||||
return {type: "identifier", value: match[1], start: pos, end: pos + match[1].length};
|
return {type: "name", value: match[1], start: pos, end: pos+match[1].length};
|
||||||
} else {
|
} else {
|
||||||
throw "Invalid identifier";
|
throw "Invalid name";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,24 +38,73 @@ RailroadWidget.prototype.render = function(parent,nextSibling) {
|
|||||||
var div = this.document.createElement("div");
|
var div = this.document.createElement("div");
|
||||||
try {
|
try {
|
||||||
// Parse the source
|
// Parse the source
|
||||||
var parser = new Parser(source);
|
var parser = new Parser(this,source);
|
||||||
|
// Generate content into the div
|
||||||
if(this.getAttribute("mode","svg") === "debug") {
|
if(this.getAttribute("mode","svg") === "debug") {
|
||||||
var output = ["<pre>"];
|
this.renderDebug(parser,div);
|
||||||
parser.root.debug(output, "");
|
|
||||||
output.push("</pre>");
|
|
||||||
div.innerHTML = output.join("");
|
|
||||||
} else {
|
} else {
|
||||||
div.innerHTML = parser.root.toSvg();
|
this.renderSvg(parser,div);
|
||||||
}
|
}
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
div.className = "tc-error";
|
div.className = "tc-error";
|
||||||
div.textContent = ex;
|
div.textContent = ex;
|
||||||
}
|
}
|
||||||
// Insert it into the DOM
|
// Insert the div into the DOM
|
||||||
parent.insertBefore(div,nextSibling);
|
parent.insertBefore(div,nextSibling);
|
||||||
this.domNodes.push(div);
|
this.domNodes.push(div);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RailroadWidget.prototype.renderDebug = function(parser,div) {
|
||||||
|
var output = ["<pre>"];
|
||||||
|
parser.root.debug(output, "");
|
||||||
|
output.push("</pre>");
|
||||||
|
div.innerHTML = output.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
RailroadWidget.prototype.renderSvg = function(parser,div) {
|
||||||
|
// Generate a model of the diagram
|
||||||
|
var fakeSvg = parser.root.toSvg();
|
||||||
|
// Render the model into a tree of SVG DOM nodes
|
||||||
|
var svg = fakeSvg.toSVG();
|
||||||
|
// Fill in the remaining attributes of any link nodes
|
||||||
|
this.patchLinks(svg);
|
||||||
|
// Insert the SVG tree into the div
|
||||||
|
div.appendChild(svg);
|
||||||
|
};
|
||||||
|
|
||||||
|
RailroadWidget.prototype.patchLinks = function(node) {
|
||||||
|
var self = this;
|
||||||
|
if(node.hasChildNodes()) {
|
||||||
|
var children = node.childNodes;
|
||||||
|
for(var i=0; i<children.length; i++) {
|
||||||
|
var child = children[i];
|
||||||
|
var attributes = child.attributes;
|
||||||
|
if(attributes) {
|
||||||
|
// Find each element that has a data-tw-target attribute
|
||||||
|
var target = child.attributes["data-tw-target"];
|
||||||
|
if(target !== undefined) {
|
||||||
|
target = target.value;
|
||||||
|
if(child.attributes["data-tw-external"]) {
|
||||||
|
// External links are straightforward
|
||||||
|
child.setAttribute("target","_blank");
|
||||||
|
} else {
|
||||||
|
// Each internal link gets its own onclick handler, capturing its own copy of target
|
||||||
|
(function(myTarget) {
|
||||||
|
child.onclick = function(event) {
|
||||||
|
self.dispatchLink(myTarget,event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})(target);
|
||||||
|
target = "#" + target;
|
||||||
|
}
|
||||||
|
child.setAttributeNS("http://www.w3.org/1999/xlink","href",target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.patchLinks(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
RailroadWidget.prototype.refresh = function(changedTiddlers) {
|
RailroadWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
var changedAttributes = this.computeAttributes();
|
var changedAttributes = this.computeAttributes();
|
||||||
if(changedAttributes.text) {
|
if(changedAttributes.text) {
|
||||||
@ -65,6 +114,23 @@ RailroadWidget.prototype.refresh = function(changedTiddlers) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RailroadWidget.prototype.dispatchLink = function(to,event) {
|
||||||
|
// Send the click on its way as a navigate event
|
||||||
|
var bounds = this.domNodes[0].getBoundingClientRect();
|
||||||
|
this.dispatchEvent({
|
||||||
|
type: "tm-navigate",
|
||||||
|
navigateTo: to,
|
||||||
|
navigateFromTitle: this.getVariable("storyTiddler"),
|
||||||
|
navigateFromNode: this,
|
||||||
|
navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
|
||||||
|
},
|
||||||
|
navigateSuppressNavigation: event.metaKey || event.ctrlKey || (event.button === 1)
|
||||||
|
});
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
exports.railroad = RailroadWidget;
|
exports.railroad = RailroadWidget;
|
||||||
|
|
||||||
})();
|
})();
|
Loading…
Reference in New Issue
Block a user