xcraft-jsonviz
Version:
A Node.js module that converts JSON to DOT language
365 lines (304 loc) • 8.66 kB
JavaScript
;
// ------------ Requirements ------------ //
var fs = require("fs"),
util = require("./util.js");
// ------------------------------------------- //
// ------------ CLASS JSONGraph ------------ //
function JSONGraph(opts)
{
this.strict = opts && opts.strict ? true : false;
this.type = opts && opts.type || "digraph";
this.name = opts && opts.name || "";
this.graph = opts && opts.graph || {};
this.node = opts && opts.node || {};
this.edge = opts && opts.edge || {};
this.styles = opts && opts.styles || {};
this.css = opts && opts.css || [];
this.statements = opts && (opts.statements || opts.stmts) || [];
this._computedDot = null;
}
// ------------ JSONGraph Public Static Methods ------------ //
JSONGraph.import = function(json)
{
return new JSONGraph(__getJSON(json));
};
JSONGraph.extends = function(modName)
{
// Requires
var path = require.resolve(modName.indexOf("/") > -1 ? modName : "./modules/" + modName + ".js"),
extension = require.cache[path];
if(!extension)
{
extension = require(path);
extension(JSONGraph);
}
return JSONGraph;
};
// ------------ JSONGraph Public Methods ------------ //
// ---> Generates
JSONGraph.prototype.reset = function()
{
this._computedDot = null;
return this;
};
JSONGraph.prototype.dot = function()
{
if(this._computedDot === null)
{
this._computedDot = this._dot("");
}
return this._computedDot;
};
JSONGraph.prototype.clone = function(name)
{
return new JSONGraph({
strict: this.strict,
type: this.type,
name: name || this.name,
graph: util.clone(this.graph),
node: util.clone(this.node),
edge: util.clone(this.edge),
styles: util.clone(this.styles),
css: util.clone(this.css),
statements: util.clone(this.statements)
});
};
// ---> Converts
JSONGraph.prototype.toJSON = function(replace, space)
{
return JSON.stringify({
strict: this.strict,
type: this.type,
name: this.name,
graph: this.graph,
node: this.node,
edge: this.edge,
styles: this.styles,
css: this.css,
statements: this.statements
}, replace, space);
};
// ---> Statements
JSONGraph.prototype.addRaw = function(stmt)
{
var n = this.statements.length;
this.statements.push(new JSONGraph.Raw(stmt));
return n;
};
JSONGraph.prototype.addNode = function(node, attrs)
{
var n = this.statements.length;
this.statements.push({stmt: __nodeName(node), attrs: attrs});
return n;
};
JSONGraph.prototype.addEdge =
JSONGraph.prototype.linkNodes = function(nodes, attrs)
{
var n = this.statements.length;
this.statements.push({stmt: nodes.map(__nodeDecl).join("->"), attrs: attrs});
return n;
};
JSONGraph.prototype.addStruct = function(name, nodes, attrs)
{
var n = this.statements.length;
if(!attrs) attrs = {};
attrs.label = new JSONGraph.Raw(__struct(nodes));
attrs.shape = attrs.shape ? "record," + attrs.shape : "record";
this.statements.push({stmt: __nodeName(name), attrs: attrs});
return n;
};
JSONGraph.prototype.addSubgraph = function(sub)
{
var n = this.statements.length;
if(!(sub instanceof JSONGraph))
{
sub = new JSONGraph(sub);
}
sub.type = "subgraph";
this.statements.push(sub);
return n;
};
// ------------ JSONGraph Protected Methods ------------ //
Object.defineProperties(JSONGraph.prototype,
{
_dot: {value: function(tab)
{
return tab + (this.strict ? "strict " : "") + this.type + (this.name ? " " + util.ID(this.name) : "") +
"\n" + tab + "{\n" +
this._statements(tab + "\t") +
tab + "}";
}},
_statements: {value: function(tab)
{
var i = 0, str = "", stmts = this.statements;
str += this._eltStyle("graph", tab);
str += this._eltStyle("node", tab);
str += this._eltStyle("edge", tab);
for(;i < stmts.length; i++)
{
str += this._statement(stmts[i], tab);
}
return str;
}},
_statement: {value: function(stmt, tab)
{
// The statement is empty -> returns an empty string
if(!stmt)
{
return "";
}
// The statement is a none empty string -> returns iut directly
if(typeof(stmt) == "string")
{
return tab + stmt + ";\n";
}
if(stmt && stmt instanceof JSONGraph)
{
return stmt._dot(tab) + "\n";
}
if(stmt && stmt instanceof JSONGraph.Raw)
{
return tab + stmt.content + ";\n";
}
var s = stmt.statement || stmt.stmt,
a = stmt.attributes || stmt.attrs;
var str = s || "";
if(a) str += this._attributes(a, s);
return str ? tab + str + ";\n" : "";
}},
_eltStyle: {value: function(n, tab)
{
var s = this[n], a = this._attributes(s);
return a ? tab + n + " " + a + ";\n" : "";
}},
_array: {value: function(a)
{
return a instanceof Array ? a : a.split(/,\s*/g);
}},
_styles: {value: function(n, lb)
{
n = this._array(n);
var str = "", s, i = 0, attrs;
for(; i < n.length; i++)
{
s = this.styles[n[i]];
if(s)
{
attrs = this._nAttributes(s, lb);
if(attrs)
{
if(str) str += ", ";
str += attrs;
}
}
}
return str;
}},
_textStyle: {value: function(n, lb)
{
var a = n = this._array(n), i = 0, st, s = "", e = "";
for(; i < a.length; i++)
{
st = __textStyles[a[i]];
if(st)
{
s += "<" + st + ">";
e = "</" + st + ">" + e;
}
}
if(lb[0] == '"')
{
lb = lb.replace(/^\"/, "").replace(/\"\s*$/, "");
}
return s ? "label=<" + s + util.escapeHTML(lb) + e + ">" : "";
}},
_attributes: {value: function(attrs, lb)
{
var str = "";
// Attributes is a string ref
if(typeof(attrs) == "string" || attrs instanceof Array)
{
str = this._styles(attrs, lb);
}
else
{
str = this._nAttributes(attrs, lb);
}
return str ? " [" + str + "]" : "";
}},
_nAttributes: {value: function(attrs, lb)
{
var n, str = "";
for(n in attrs)
{
n = n == "ref" ? this._styles(attrs[n], lb) :
n == "textstyle" ? this._textStyle(attrs[n], lb) :
util.ID(n) + "=" + util.ID(attrs[n]);
if(n)
{
if(str) str += ", ";
str += n;
}
}
return str;
}}
});
// ---------------------------------------------- //
// -------------- CLASS JSONGraph.HTML -------------- //
Object.defineProperty(JSONGraph, "HTML",
{
value: require("./html.class")
});
// ---------------------------------------------- //
// -------------- CLASS JSONGraph.Raw -------------- //
Object.defineProperty(JSONGraph, "Raw",
{
value: require("./raw.class")
});
// -------------- PRIVATE -------------- //
var __textStyles = {italic: "i", bold: "b", underline: "u", stoke: "s",
i: "i", b: "b", u: "u", s: "s"};
// --> JSON
function __getJSON(json)
{
// JSON string
if(/^\s*\{/.test(json))
{
return JSON.parse(json);
}
// Path
return JSON.parse(fs.readFIleSync(json, "utf8"));
}
// --> Node
function __nodeDecl(n)
{
if(typeof(n) == "object")
{
var s = Object.keys(n);
return "\"" + util.escapeString(s) + "\":\"" + util.escapeString(n[s]) + "\"";
}
return "\"" + util.escapeString(n) + "\"";
}
function __nodeName(n)
{
return "\"" + util.escapeString(n) + "\"";
}
function __structElt(e)
{
if(e instanceof Array)
{
return "{" + __struct(e) + "}";
}
if(/^\s*</.test(e))
{
return e;
}
e = util.escapeHTML(e);
return "<" + e + "> " + e;
}
function __struct(a)
{
return a.map(__structElt).join(" | ");
}
// -------------- EXPORTS THE MODULE -------------- //
exports = module.exports = JSONGraph;