TiddlyWiki5/plugins/tiddlywiki/railroad/components.js

291 lines
6.8 KiB
JavaScript

/*\
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];
// Choice content is a special case: we number the branches
if(item.isChoiceBranch) {
output.push(indent);
output.push("(");
output.push(i);
output.push(")\n");
item.debug(output," "+indent);
} 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");
}
var OptionalRepeated = function(content,separator,normal,wantArrow) {
this.initialiseWithChild("OptionalRepeated",content);
this.separator = toSingleChild(separator);
this.normal = normal;
this.wantArrow = wantArrow;
};
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";
return railroad.ZeroOrMore(this.child.toSvg(),separatorSvg,skip,this.wantArrow);
}
var Repeated = function(content,separator,wantArrow) {
this.initialiseWithChild("Repeated",content);
this.separator = toSingleChild(separator);
this.wantArrow = wantArrow;
};
Repeated.prototype = new Component();
Repeated.prototype.toSvg = function() {
// Call OneOrMore(component,separator)
var separatorSvg = this.separator ? this.separator.toSvg() : null;
return railroad.OneOrMore(this.child.toSvg(),separatorSvg,this.wantArrow);
}
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
var Root = function(content) {
this.initialiseWithChildren("Root",content);
};
Root.prototype = new Component();
Root.prototype.toSvg = function(options) {
var args = this.getSvgOfChildren();
args.unshift(options);
// Call Diagram(options,component1,component2,...)
return railroad.Diagram.apply(null,args);
}
var Sequence = function(content) {
this.initialiseWithChildren("Sequence",content);
};
Sequence.prototype = new Component();
Sequence.prototype.toSvg = function() {
// Call Sequence(component1,component2,...)
return railroad.Sequence.apply(null,this.getSvgOfChildren());
}
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);
return railroad.Choice.apply(null,args);
}
/////////////////////////// Exports
exports.components = {
Choice: Choice,
Comment: Comment,
Dummy: Dummy,
Link: Link,
Nonterminal: Nonterminal,
Optional: Optional,
OptionalRepeated: OptionalRepeated,
Repeated: Repeated,
Root: Root,
Sequence: Sequence,
Terminal: Terminal,
Transclusion: Transclusion
};
})();