0&&(r+=o.selectorText+" { "+o.style.cssText+" }\n")}}}var c="",l="";for(var h in t)t.hasOwnProperty(h)&&"undefined"!=typeof h&&("default"===h?(t.default.styles instanceof Array&&(c+="#"+e.id.trim()+" .node { "+t[h].styles.join("; ")+"; }\n"),t.default.nodeLabelStyles instanceof Array&&(c+="#"+e.id.trim()+" .node text { "+t[h].nodeLabelStyles.join("; ")+"; }\n"),t.default.edgeLabelStyles instanceof Array&&(c+="#"+e.id.trim()+" .edgeLabel text { "+t[h].edgeLabelStyles.join("; ")+"; }\n")):t[h].styles instanceof Array&&(l+="#"+e.id.trim()+" ."+h+" { "+t[h].styles.join("; ")+"; }\n"));if(""!==r||""!==c||""!==l){var d=document.createElement("style");d.setAttribute("type","text/css"),d.setAttribute("title","mermaid-svg-internal-css"),d.innerHTML="/* */\n",e.insertBefore(d,e.firstChild)}}},{}]},{},[112]);
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index c81be08596..9b35dc3ced 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -71,11 +71,23 @@ gulp.task('less', function () {
var browserify = require('gulp-browserify');
+//var slim_ext_libs = [
+// 'dagre-d3',
+// 'd3'
+//];
+
// Basic usage
gulp.task('slimDist', function() {
// Single entry point to browserify
return gulp.src('src/main.js')
.pipe(browserify())
+ /*.pipe(browserify({standalone: 'mermaid'}))
+ .on('prebundle', function(bundle) {
+ // Keep these external for the slim version.
+ slim_ext_libs.forEach(function(lib) {
+ bundle.external(lib);
+ });
+ })*/
.pipe(rename('mermaid.slim.js'))
.pipe(gulp.dest('./dist/'))
.pipe(uglify())
diff --git a/src/diagrams/flowchart/dagre-d3.js b/src/diagrams/flowchart/dagre-d3.js
new file mode 100644
index 0000000000..a3c0b472aa
--- /dev/null
+++ b/src/diagrams/flowchart/dagre-d3.js
@@ -0,0 +1,15 @@
+/* global window */
+
+var dagreD3;
+
+if (require) {
+ try {
+ dagreD3 = require("dagre-d3");
+ } catch (e) {}
+}
+
+if (!dagreD3) {
+ dagreD3 = window.dagreD3;
+}
+
+module.exports = dagreD3;
diff --git a/src/diagrams/flowchart/flowRenderer.js b/src/diagrams/flowchart/flowRenderer.js
index aca1f974dd..1f3f9a9068 100644
--- a/src/diagrams/flowchart/flowRenderer.js
+++ b/src/diagrams/flowchart/flowRenderer.js
@@ -4,7 +4,7 @@
var graph = require('./graphDb');
var flow = require('./parser/flow');
var dot = require('./parser/dot');
-var dagreD3 = require('dagre-d3');
+var dagreD3 = require('./dagre-d3');
/**
* Function that adds the vertices found in the graph definition to the graph to be rendered.
* @param vert Object containing the vertices.
@@ -60,7 +60,13 @@ exports.addVertices = function (vert, g) {
verticeText = vertice.text;
}
- console.log(verticeText);
+ var labelTypeStr = '';
+ if(global.mermaid.htmlLabels) {
+ labelTypeStr = 'html';
+ } else {
+ verticeText = verticeText.replace(/
/g, "\n");
+ labelTypeStr = 'text';
+ }
var radious = 0;
var _shape = '';
@@ -80,6 +86,9 @@ exports.addVertices = function (vert, g) {
case 'odd':
_shape = 'rect_left_inv_arrow';
break;
+ case 'odd_right':
+ _shape = 'rect_left_inv_arrow';
+ break;
case 'circle':
_shape = 'circle';
break;
@@ -87,7 +96,7 @@ exports.addVertices = function (vert, g) {
_shape = 'rect';
}
// Add the node
- g.setNode(vertice.id, {labelType: "html",shape:_shape, label: verticeText, rx: radious, ry: radious, class: classStr, style: style, id:vertice.id});
+ g.setNode(vertice.id, {labelType: labelTypeStr, shape:_shape, label: verticeText, rx: radious, ry: radious, class: classStr, style: style, id:vertice.id});
});
};
@@ -147,12 +156,16 @@ exports.addEdges = function (edges, g) {
}
// Edge with text
else {
-
+ var edgeText = edge.text.replace(/
/g, "\n");
if(typeof edge.style === 'undefined'){
- g.setEdge(edge.start, edge.end,{labelType: "html",style: style, labelpos:'c', label: ''+edge.text+'', arrowheadStyle: "fill: #333", arrowhead: aHead},cnt);
- }else{
+ if (global.mermaid.htmlLabels){
+ g.setEdge(edge.start, edge.end,{labelType: "html",style: style, labelpos:'c', label: ''+edge.text+'', arrowheadStyle: "fill: #333", arrowhead: aHead},cnt);
+ }else{
+ g.setEdge(edge.start, edge.end,{labelType: "text", style: "stroke: #333; stroke-width: 1.5px;fill:none", labelpos:'c', label: edgeText, arrowheadStyle: "fill: #333", arrowhead: aHead},cnt);
+ }
+ }else{
g.setEdge(edge.start, edge.end, {
- labelType: "html",style: style, arrowheadStyle: "fill: #333", label: edge.text, arrowhead: aHead
+ labelType: "text", style: style, arrowheadStyle: "fill: #333", label: edgeText, arrowhead: aHead
},cnt);
}
}
@@ -183,10 +196,12 @@ exports.getClasses = function (text, isDot) {
var classes = graph.getClasses();
// Add default class if undefined
- if(typeof classes.default === 'undefined') {
+ if(typeof(classes.default) === 'undefined') {
classes.default = {id:'default'};
- classes.default.styles = ['fill:#eaeaea','stroke:#666','stroke-width:1.5px'];
- }
+ classes.default.styles = ['fill:#ffa','stroke:#666','stroke-width:3px'];
+ classes.default.nodeLabelStyles = ['fill:#000','stroke:none','font-weight:300','font-family:"Helvetica Neue",Helvetica,Arial,sans-serf','font-size:14px'];
+ classes.default.edgeLabelStyles = ['fill:#000','stroke:none','font-weight:300','font-family:"Helvetica Neue",Helvetica,Arial,sans-serf','font-size:14px'];
+ }
return classes;
};
@@ -312,6 +327,28 @@ exports.draw = function (text, id,isDot) {
return shapeSvg;
};
+ // Add custom shape for box with inverted arrow on right side
+ render.shapes().rect_right_inv_arrow = function (parent, bbox, node) {
+ var w = bbox.width,
+ h = bbox.height,
+ points = [
+ {x: 0, y: 0},
+ {x: w+h/2, y: 0},
+ {x: w, y: -h/2},
+ {x: w+h/2, y: -h},
+ {x: 0, y: -h},
+ ];
+ var shapeSvg = parent.insert("polygon", ":first-child")
+ .attr("points", points.map(function (d) {
+ return d.x + "," + d.y;
+ }).join(" "))
+ .attr("transform", "translate(" + (-w / 2) + "," + (h * 2 / 4) + ")");
+ node.intersect = function (point) {
+ return dagreD3.intersect.polygon(node, points, point);
+ };
+ return shapeSvg;
+ };
+
// Add our custom arrow - an empty arrowhead
render.arrows().none = function normal(parent, id, edge, type) {
var marker = parent.append("marker")
diff --git a/src/diagrams/flowchart/parser/flow.jison b/src/diagrams/flowchart/parser/flow.jison
index 1963641ffd..c73a9bc6ee 100644
--- a/src/diagrams/flowchart/parser/flow.jison
+++ b/src/diagrams/flowchart/parser/flow.jison
@@ -243,6 +243,10 @@ vertex: alphaNum SQS text SQE
{$$ = $1;yy.addVertex($1,$3,'odd');}
| alphaNum TAGEND text SQE SPACE
{$$ = $1;yy.addVertex($1,$3,'odd');}
+/* | alphaNum SQS text TAGSTART
+ {$$ = $1;yy.addVertex($1,$3,'odd_right');}
+ | alphaNum SQS text TAGSTART SPACE
+ {$$ = $1;yy.addVertex($1,$3,'odd_right');} */
| alphaNum
{$$ = $1;yy.addVertex($1);}
| alphaNum SPACE
diff --git a/src/main.js b/src/main.js
index 8fa9ab8101..f45deb761a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -94,6 +94,7 @@ var equals = function (val, variable){
global.mermaid = {
startOnLoad:true,
+ htmlLabels:true,
init:function(){
init();
},
@@ -109,6 +110,12 @@ exports.contentLoaded = function(){
// Check state of start config mermaid namespece
//console.log('global.mermaid.startOnLoad',global.mermaid.startOnLoad);
//console.log('mermaid_config',mermaid_config);
+ if (typeof mermaid_config !== 'undefined') {
+ if (equals(false, mermaid_config.htmlLabels)) {
+ global.mermaid.htmlLabels = false;
+ }
+ }
+
if(global.mermaid.startOnLoad) {
// For backwards compatability reasons also check mermaid_config variable
@@ -128,7 +135,7 @@ exports.contentLoaded = function(){
if(typeof document !== 'undefined'){
/**
- * Wait for coument loaded before starting the execution
+ * Wait for document loaded before starting the execution
*/
document.addEventListener('DOMContentLoaded', function(){
exports.contentLoaded();
diff --git a/src/utils.js b/src/utils.js
index 611b7c95ba..67d2913326 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -58,10 +58,19 @@ module.exports.cloneCssStyles = function(svg, classes){
for (var className in classes) {
if (classes.hasOwnProperty(className) && typeof(className) != "undefined") {
if (className === 'default') {
- defaultStyles = '.node' + ' { ' + classes[className].styles.join("; ") + '; }\n';
+ if (classes.default.styles instanceof Array) {
+ defaultStyles += "#" + svg.id.trim() + ' .node' + ' { ' + classes[className].styles.join("; ") + '; }\n';
+ }
+ if (classes.default.nodeLabelStyles instanceof Array) {
+ defaultStyles += "#" + svg.id.trim() + ' .node text ' + ' { ' + classes[className].nodeLabelStyles.join("; ") + '; }\n';
+ }
+ if (classes.default.edgeLabelStyles instanceof Array) {
+ defaultStyles += "#" + svg.id.trim() + ' .edgeLabel text ' + ' { ' + classes[className].edgeLabelStyles.join("; ") + '; }\n';
+ }
} else {
- embeddedStyles += '.' + className + ' { ' + classes[className].styles.join("; ") + '; }\n';
- //embeddedStyles += svg.id.trim() + ' .' + className + ' { ' + classes[className].styles.join("; ") + '; }\n';
+ if (classes[className].styles instanceof Array) {
+ embeddedStyles += "#" + svg.id.trim() + ' .' + className + ' { ' + classes[className].styles.join("; ") + '; }\n';
+ }
}
}
}
@@ -71,6 +80,7 @@ module.exports.cloneCssStyles = function(svg, classes){
s.setAttribute('type', 'text/css');
s.setAttribute('title', 'mermaid-svg-internal-css');
s.innerHTML = "/* /* */\n');
+ expect(svg.innerHTML).toBe('');
});
it('should handle stylesheet in document with no classes in SVG', function () {
var svg = document.createElement('svg');
- addStyleToDocument();
+ svg.setAttribute('id', 'mermaid-01');
+
+ addStyleToDocument('mermaid');
utils.cloneCssStyles(svg, {});
// Should not create style element if not needed
expect(svg.innerHTML).toBe('');
@@ -168,43 +167,34 @@ describe('when cloning CSS ',function() {
expect(stylesToArray(svg)).toEqual([ '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }', '.node-square { stroke: #beb; }']);
});
- it('should handle multiple stylesheets + styles in other SVG', function () {
+ it('should handle multiple stylesheets + ignore styles in other mermaid SVG', function () {
var svg = generateSVG();
addStyleToDocument();
addSecondStyleToDocument();
- addSVGwithStyleToDocument();
+ addMermaidSVGwithStyleToDocument();
utils.cloneCssStyles(svg, {});
- expect(stylesToArray(svg)).toEqual([ '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }', '.node-square { stroke: #beb; }', '.node-square { stroke: #bfb; }']);
- });
-
- it('should handle multiple stylesheets + ignore styles in mermaid SVG', function () {
- var svg = generateSVG();
- addStyleToDocument();
- addSecondStyleToDocument();
- addSVGwithStyleToDocument();
- addMermaidSVGwithStyleToDocument(3);
- utils.cloneCssStyles(svg, {});
- expect(stylesToArray(svg)).toEqual([ '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }', '.node-square { stroke: #beb; }', '.node-square { stroke: #bfb; }']);
+ expect(stylesToArray(svg)).toEqual([ '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }', '.node-square { stroke: #beb; }']);
});
it('should handle a default class together with stylesheet in document with classes in SVG', function () {
var svg = generateSVG();
addStyleToDocument();
utils.cloneCssStyles(svg, { "default": { "styles": ["stroke:#fff","stroke-width:1.5px"] } });
- expect(stylesToArray(svg)).toEqual([ '.node { stroke:#fff; stroke-width:1.5px; }', '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }']);
+ expect(stylesToArray(svg)).toEqual([ '#mermaid-01 .node { stroke:#fff; stroke-width:1.5px; }', '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }']);
});
- xit('should handle a default class together with stylesheet in document and classDefs', function () {
+ it('should handle a default class together with stylesheet in document and classDefs', function () {
var svg = generateSVG();
addStyleToDocument();
utils.cloneCssStyles(svg, { "default": { "styles": ["stroke:#fff","stroke-width:1.5px"] },
"node-square": { "styles": ["fill:#eee", "stroke:#aaa"] },
"node-circle": { "styles": ["fill:#444", "stroke:#111"] } });
- expect(stylesToArray(svg)).toEqual([ '.node { stroke:#fff; stroke-width:1.5px; }',
+ expect(stylesToArray(svg)).toEqual([ '#mermaid-01 .node { stroke:#fff; stroke-width:1.5px; }',
'.node { stroke: #eee; }',
'.node-square { stroke: #bbb; }',
- '.node-square { fill:#eee; stroke:#aaa; }',
- '.node-circle { fill:#444; stroke:#111; }' ]);
+ '#mermaid-01 .node-square { fill:#eee; stroke:#aaa; }',
+ '#mermaid-01 .node-circle { fill:#444; stroke:#111; }'
+ ]);
});
});
diff --git a/test/web.html b/test/web.html
index d7bc5dfbc6..321e5b7e7c 100644
--- a/test/web.html
+++ b/test/web.html
@@ -5,6 +5,7 @@
+
+
+
+
+
+
+ Style
+ Styling is applied in the following order:
+
+ - Node default style (see wiki)
+ - CSS on the page
+ - Class definitions inside the graph definition
+ - Inline styling inside the graph definition
+
+ and the last styling applied is the resulting styling. For instance, "Class definitions inside the graph definition" overrides styling from "CSS on the page".
+
+ CSS in the page head:
+
+ <style>
+ .node-square {
+ stroke-width: 4px;
+ stroke: #339933;
+ fill: #999900;
+ font-weight: 300;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
+ font-size: 14px; }
+ .node-circle { stroke-width: 0.5px; stroke: #339999; fill: #009900; }
+ .node-odd-override { stroke-width: 3.5px; stroke: #339900; fill: #009999; }
+ .node-odd { stroke-width: 3.5px; stroke: #339900; fill: #009999; }
+
+ </style>
+
+ Graph definition
+
+ graph TD;
+
+ noc[No class<br />using default];
+ cyr2((Class node-cyr-undefined<br />is undefined, using default));
+ class cyr2 node-cyr-undefined;
+ ndef[Default style];
+ noc-->ndef;
+ cyr2-->ndef;
+
+ sq[Class node-square<br />defined in page CSS];
+ class sq node-square;
+ ncss[Page CSS style];
+ sq-->ncss;
+
+ cyr[Class node-cyr<br />defined by classDef];
+ od2>Class node-odd-override<br />defined in page CSS<br />and defined by classDef];
+ ncdef[classDef style];
+ od2-->ncdef;
+ cyr-->ncdef;
+ class cyr node-cyr;
+ class od2 node-odd-override;
+ classDef node-odd-override fill:#BB00BB,stroke:#666622;
+ classDef node-cyr fill:#BB0099,stroke:#666622;
+
+ e1[Class node-cyr<br />defined by classDef<br />and inline style];
+ class e1 node-e1;
+ style e1 fill:#FF0000;
+ e2>Class node-odd-override<br />defined in page CSS<br />and defined by classDef<br />and inline style];
+ class e2 node-e2;
+ style e2 fill:#FF0000;
+ e((Inline style in<br />graph definition));
+ style e fill:#FF0000;
+ ninl[Inline style];
+ e-->ninl;
+ e1-->ninl;
+ e2-->ninl;
+ classDef node-e1 fill:#990066,stroke:#666622;
+ classDef node-e2 fill:#990066,stroke:#666622;
+
+
+
+ graph TD;
+
+ noc[No class
using default];
+ cyr2((Class node-cyr-undefined
is undefined, using default));
+ class cyr2 node-cyr-undefined;
+ ndef[Default style];
+ noc-->ndef;
+ cyr2-->ndef;
+
+ sq[Class node-square
defined in page CSS];
+ class sq node-square;
+ ncss[Page CSS style];
+ sq-->ncss;
+
+ cyr[Class node-cyr
defined by classDef];
+ od2>Class node-odd-override
defined in page CSS
and defined by classDef];
+ ncdef[classDef style];
+ od2-->|Simple edge label|ncdef;
+ cyr-->|Complex
multiline
edge label|ncdef;
+ class cyr node-cyr;
+ class od2 node-odd-override;
+ classDef node-odd-override fill:#BB00BB,stroke:#666622;
+ classDef node-cyr fill:#BB0099,stroke:#666622;
+
+ e1[Class node-cyr
defined by classDef
and inline style];
+ class e1 node-e1;
+ style e1 fill:#FF0000;
+ e2>Class node-odd-override
defined in page CSS
and defined by classDef
and inline style];
+ class e2 node-e2;
+ style e2 fill:#FF0000;
+ e((Inline style in
graph definition));
+ style e fill:#FF0000;
+ ninl[Inline style];
+ e-->ninl;
+ e1-->ninl;
+ e2-->ninl;
+ classDef node-e1 fill:#990066,stroke:#666622;
+ classDef node-e2 fill:#990066,stroke:#666622;
+
+
+
+
+
+