2015-01-03 20:02:27 +00:00
|
|
|
/*\
|
|
|
|
title: $:/plugins/tiddlywiki/railroad/components.js
|
|
|
|
type: application/javascript
|
|
|
|
module-type: library
|
|
|
|
|
|
|
|
Components of a railroad diagram.
|
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
|
|
|
/*global $tw: false */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var railroad = require("$:/plugins/tiddlywiki/railroad/railroad-diagrams.js");
|
|
|
|
|
|
|
|
/////////////////////////// Base component
|
|
|
|
|
|
|
|
var Component = function() {
|
|
|
|
this.type = "Component";
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set up a leaf component
|
|
|
|
Component.prototype.initialiseLeaf = function(type,text) {
|
|
|
|
this.type = type;
|
|
|
|
this.text = text;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set up a component with a single child
|
|
|
|
Component.prototype.initialiseWithChild = function(type,content) {
|
|
|
|
this.type = type;
|
|
|
|
this.child = toSingleChild(content);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set up a component with an array of children
|
|
|
|
Component.prototype.initialiseWithChildren = function(type,content) {
|
|
|
|
this.type = type;
|
|
|
|
// Force the content to be an array
|
|
|
|
this.children = $tw.utils.isArray(content) ? content : [content];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an array of the SVG strings of an array of children
|
|
|
|
Component.prototype.getSvgOfChildren = function() {
|
|
|
|
return this.children.map(function(child) {
|
|
|
|
return child.toSvg();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Component.prototype.toSvg = function() {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
Component.prototype.debug = function(output,indent) {
|
|
|
|
output.push(indent);
|
|
|
|
output.push(this.type);
|
|
|
|
// Add the text of a leaf component
|
|
|
|
if(this.text && this.text !== "") {
|
|
|
|
output.push(": ");
|
|
|
|
output.push(this.text);
|
|
|
|
}
|
|
|
|
// Flag the normal route
|
|
|
|
if(this.normal !== undefined) {
|
|
|
|
if(this.normal === true) {
|
|
|
|
output.push(" (normal)");
|
|
|
|
} else if(this.normal !== false) {
|
|
|
|
output.push(" (normal: ");
|
|
|
|
output.push(this.normal);
|
|
|
|
output.push(")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output.push("\n");
|
|
|
|
var contentIndent = indent + " ";
|
|
|
|
// Add the one child
|
|
|
|
if(this.child) {
|
|
|
|
this.child.debug(output,contentIndent);
|
|
|
|
}
|
|
|
|
// Add the array of children
|
|
|
|
if(this.children) {
|
|
|
|
this.debugArray(this.children,output,contentIndent);
|
|
|
|
}
|
|
|
|
// Add the separator if there is one
|
|
|
|
if(this.separator) {
|
|
|
|
output.push(indent);
|
|
|
|
output.push("(separator)\n");
|
|
|
|
this.separator.debug(output,contentIndent);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Component.prototype.debugArray = function(array,output,indent) {
|
|
|
|
for(var i=0; i<array.length; i++) {
|
|
|
|
var item = array[i];
|
2015-01-06 01:39:24 +00:00
|
|
|
// Choice content is a special case: we number the branches
|
2015-01-03 20:02:27 +00:00
|
|
|
if(item.isChoiceBranch) {
|
|
|
|
output.push(indent);
|
|
|
|
output.push("(");
|
|
|
|
output.push(i);
|
|
|
|
output.push(")\n");
|
2015-01-06 01:39:24 +00:00
|
|
|
item.debug(output," "+indent);
|
2015-01-03 20:02:27 +00:00
|
|
|
} else {
|
|
|
|
item.debug(output,indent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var toSingleChild = function(content) {
|
|
|
|
if($tw.utils.isArray(content)) {
|
|
|
|
// Reduce an array of one child to just the child
|
|
|
|
if(content.length === 1) {
|
|
|
|
return content[0];
|
|
|
|
} else {
|
|
|
|
// Never allow an empty sequence
|
|
|
|
if(content.length === 0) {
|
|
|
|
content.push(new Dummy());
|
|
|
|
}
|
|
|
|
// Wrap multiple children into a single sequence component
|
|
|
|
return new Sequence(content);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Already single
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////// Leaf components
|
|
|
|
|
|
|
|
var Comment = function(text) {
|
|
|
|
this.initialiseLeaf("Comment",text);
|
|
|
|
};
|
|
|
|
|
|
|
|
Comment.prototype = new Component();
|
|
|
|
|
|
|
|
Comment.prototype.toSvg = function() {
|
|
|
|
return railroad.Comment(this.text);
|
|
|
|
}
|
|
|
|
|
|
|
|
var Dummy = function() {
|
|
|
|
this.initialiseLeaf("Dummy");
|
|
|
|
};
|
|
|
|
|
|
|
|
Dummy.prototype = new Component();
|
|
|
|
|
|
|
|
Dummy.prototype.toSvg = function() {
|
|
|
|
return railroad.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
var Nonterminal = function(text) {
|
|
|
|
this.initialiseLeaf("Nonterminal",text);
|
|
|
|
};
|
|
|
|
|
|
|
|
Nonterminal.prototype = new Component();
|
|
|
|
|
|
|
|
Nonterminal.prototype.toSvg = function() {
|
|
|
|
return railroad.NonTerminal(this.text);
|
|
|
|
}
|
|
|
|
|
|
|
|
var Terminal = function(text) {
|
|
|
|
this.initialiseLeaf("Terminal",text);
|
|
|
|
};
|
|
|
|
|
|
|
|
Terminal.prototype = new Component();
|
|
|
|
|
|
|
|
Terminal.prototype.toSvg = function() {
|
|
|
|
return railroad.Terminal(this.text);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////// Components with one child
|
|
|
|
|
|
|
|
var Optional = function(content,normal) {
|
|
|
|
this.initialiseWithChild("Optional",content);
|
|
|
|
this.normal = normal;
|
|
|
|
};
|
|
|
|
|
|
|
|
Optional.prototype = new Component();
|
|
|
|
|
|
|
|
Optional.prototype.toSvg = function() {
|
|
|
|
// Call Optional(component,"skip")
|
|
|
|
return railroad.Optional(this.child.toSvg(), this.normal ? undefined : "skip");
|
|
|
|
}
|
|
|
|
|
2015-01-12 20:03:37 +00:00
|
|
|
var OptionalRepeated = function(content,separator,normal,wantArrow) {
|
2015-01-03 20:02:27 +00:00
|
|
|
this.initialiseWithChild("OptionalRepeated",content);
|
|
|
|
this.separator = toSingleChild(separator);
|
|
|
|
this.normal = normal;
|
2015-01-12 20:03:37 +00:00
|
|
|
this.wantArrow = wantArrow;
|
2015-01-03 20:02:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
OptionalRepeated.prototype = new Component();
|
|
|
|
|
|
|
|
OptionalRepeated.prototype.toSvg = function() {
|
|
|
|
// Call ZeroOrMore(component,separator,"skip")
|
|
|
|
var separatorSvg = this.separator ? this.separator.toSvg() : null;
|
|
|
|
var skip = this.normal ? undefined : "skip";
|
2015-01-12 20:03:37 +00:00
|
|
|
return railroad.ZeroOrMore(this.child.toSvg(),separatorSvg,skip,this.wantArrow);
|
2015-01-03 20:02:27 +00:00
|
|
|
}
|
|
|
|
|
2015-01-12 20:03:37 +00:00
|
|
|
var Repeated = function(content,separator,wantArrow) {
|
2015-01-03 20:02:27 +00:00
|
|
|
this.initialiseWithChild("Repeated",content);
|
|
|
|
this.separator = toSingleChild(separator);
|
2015-01-12 20:03:37 +00:00
|
|
|
this.wantArrow = wantArrow;
|
2015-01-03 20:02:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Repeated.prototype = new Component();
|
|
|
|
|
|
|
|
Repeated.prototype.toSvg = function() {
|
|
|
|
// Call OneOrMore(component,separator)
|
|
|
|
var separatorSvg = this.separator ? this.separator.toSvg() : null;
|
2015-01-12 20:03:37 +00:00
|
|
|
return railroad.OneOrMore(this.child.toSvg(),separatorSvg,this.wantArrow);
|
2015-01-03 20:02:27 +00:00
|
|
|
}
|
|
|
|
|
2015-01-06 01:39:24 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-01-03 20:02:27 +00:00
|
|
|
/////////////////////////// Components with an array of children
|
|
|
|
|
|
|
|
var Root = function(content) {
|
|
|
|
this.initialiseWithChildren("Root",content);
|
|
|
|
};
|
|
|
|
|
|
|
|
Root.prototype = new Component();
|
|
|
|
|
2015-01-19 22:21:22 +00:00
|
|
|
Root.prototype.toSvg = function(options) {
|
|
|
|
var args = this.getSvgOfChildren();
|
|
|
|
args.unshift(options);
|
|
|
|
// Call Diagram(options,component1,component2,...)
|
|
|
|
return railroad.Diagram.apply(null,args);
|
2015-01-03 20:02:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var Sequence = function(content) {
|
|
|
|
this.initialiseWithChildren("Sequence",content);
|
|
|
|
};
|
|
|
|
|
|
|
|
Sequence.prototype = new Component();
|
|
|
|
|
|
|
|
Sequence.prototype.toSvg = function() {
|
|
|
|
// Call Sequence(component1,component2,...)
|
2015-01-19 22:21:22 +00:00
|
|
|
return railroad.Sequence.apply(null,this.getSvgOfChildren());
|
2015-01-03 20:02:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var Choice = function(content,normal) {
|
|
|
|
this.initialiseWithChildren("Choice",content.map(toSingleChild));
|
|
|
|
for(var i=0; i<this.children.length; i++) {
|
|
|
|
this.children[i].isChoiceBranch = true;
|
|
|
|
}
|
|
|
|
this.normal = normal;
|
|
|
|
};
|
|
|
|
|
|
|
|
Choice.prototype = new Component();
|
|
|
|
|
|
|
|
Choice.prototype.toSvg = function() {
|
|
|
|
// Call Choice(normal,component1,component2,...)
|
|
|
|
var args = this.getSvgOfChildren();
|
|
|
|
args.unshift(this.normal);
|
2015-01-19 22:21:22 +00:00
|
|
|
return railroad.Choice.apply(null,args);
|
2015-01-03 20:02:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////// Exports
|
|
|
|
|
|
|
|
exports.components = {
|
|
|
|
Choice: Choice,
|
|
|
|
Comment: Comment,
|
|
|
|
Dummy: Dummy,
|
2015-01-06 01:39:24 +00:00
|
|
|
Link: Link,
|
2015-01-03 20:02:27 +00:00
|
|
|
Nonterminal: Nonterminal,
|
|
|
|
Optional: Optional,
|
|
|
|
OptionalRepeated: OptionalRepeated,
|
|
|
|
Repeated: Repeated,
|
|
|
|
Root: Root,
|
|
|
|
Sequence: Sequence,
|
2015-01-06 01:39:24 +00:00
|
|
|
Terminal: Terminal,
|
|
|
|
Transclusion: Transclusion
|
2015-01-03 20:02:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
})();
|