Skip to content

Commit

Permalink
Added SVG generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Roenbaeck committed Sep 14, 2010
1 parent 710b131 commit 6270e92
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 11 deletions.
220 changes: 209 additions & 11 deletions index.html
Expand Up @@ -267,16 +267,25 @@
}

/* http://www.ie7nomore.com/fun/menu2/ */
#actions, #settings {
/* TODO: position using relative instead */
#actions, #settings, #file, #generate {
position: absolute;
display: block;
top: 0px;
z-index: 8;
}
#actions {
#file {
left: 8px;
text-align: left;
}
#actions {
left: 50px;
text-align: left;
}
#generate {
left: 110px;
text-align: left;
}
#settings {
right: 8px;
text-align: right;
Expand Down Expand Up @@ -410,6 +419,9 @@
// which transform to use when generating SQL code
var DATABASE_TRANSFORM = 'SQLServer.xsl';

// used when creating svg elements
var SVGNS = "http://www.w3.org/2000/svg";

// create a singleton
var LayoutEngine = {
// normal distance in pixels
Expand Down Expand Up @@ -731,6 +743,25 @@
}
return schema;
},
toSVG: function() {
var type = document.implementation.createDocumentType("svg", "-//W3C//DTD SVG 1.0//EN", "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
var svg = document.implementation.createDocument(SVGNS, "svg", type);
var g = svg.createElementNS(SVGNS, 'g');
for(var i = 0; i < this.nodes.length; i++)
if(!this.filtered[i])
this.nodes[i].toSVG(svg, g);
var node;
if(DrawingEngine.showNames) {
for(i = 0; node = this.nodes[i]; i++)
if(!(node instanceof Edge) && !this.filtered[i])
node.toSVGName(svg, g);
}
svg.documentElement.appendChild(g);
return svg;
},
toSVGURL: function() {
return 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.toSVG()));
},
fromXML: function(xml) {
if(xml) {
this.empty();
Expand Down Expand Up @@ -1512,6 +1543,9 @@
toXML: function() {
return null;
},
toSVG: function() {
return null;
},
fromXML: function() {
return;
},
Expand Down Expand Up @@ -1539,6 +1573,39 @@
nameXOffset: function() {
return 20;
},
toSVGName: function(svg, g) {
var text = svg.createElementNS(SVGNS, 'text');
text.setAttributeNS(null, 'font-size', 10);
text.setAttributeNS(null, 'font-family', 'sans-serif');
text.appendChild(svg.createTextNode(this.getName()));
var i, edge, xAverage = 0;
for(i = 0; edge = this.edges[i]; i++)
xAverage += edge.xPosition;
xAverage /= this.edges.length;
var currentDistance, smallestDistance, yClosest;
for(i = 0; edge = this.edges[i]; i++) {
currentDistance = LayoutEngine.absolute(this.yPosition - edge.yPosition);
if(!i || ((this.xPosition < xAverage && edge.xPosition < this.xPosition) || (this.xPosition >= xAverage && edge.xPosition >= this.xPosition) && (currentDistance < smallestDistance))) {
smallestDistance = currentDistance;
yClosest = edge.yPosition;
}
}
var nameYOffset = -7 + (yClosest ? (this.yPosition < yClosest ? -3 : 3) : 0);
if(this.xPosition < xAverage) {
text.setAttributeNS(null, 'x', (this.xPosition - this.nameXOffset()).toFixed(2));
text.setAttributeNS(null, 'text-anchor', 'end');
}
else {
text.setAttributeNS(null, 'text-anchor', 'start');
text.setAttributeNS(null, 'x', (this.xPosition + this.nameXOffset()).toFixed(2));
}
text.setAttributeNS(null, 'y', (this.yPosition - nameYOffset).toFixed(2));
if(this.incomplete)
text.setAttributeNS(null, 'fill', '#e00');
else
text.setAttributeNS(null, 'fill', '#000');
g.appendChild(text);
},
drawName: function(context) {
var i, edge, xAverage = 0;
for(i = 0; edge = this.edges[i]; i++)
Expand Down Expand Up @@ -1794,6 +1861,31 @@
else
context.arc(this.xPosition, this.yPosition, 10, 0, 2*Math.PI, false);
},
toSVG: function(svg, g) {
var group = svg.createElementNS(SVGNS, 'g');
var circle = svg.createElementNS(SVGNS, 'circle');
circle.setAttributeNS(null, 'fill', '#fff');
circle.setAttributeNS(null, 'stroke', '#f88');
circle.setAttributeNS(null, 'stroke-width', 1.5);
circle.setAttributeNS(null, 'cx', this.xPosition.toFixed(2));
circle.setAttributeNS(null, 'cy', this.yPosition.toFixed(2));
if(this.historized)
circle.setAttributeNS(null, 'r', 12);
else
circle.setAttributeNS(null, 'r', 10);
group.appendChild(circle);
if(this.historized) {
circle = svg.createElementNS(SVGNS, 'circle');
circle.setAttributeNS(null, 'fill', '#fff');
circle.setAttributeNS(null, 'stroke', '#f88');
circle.setAttributeNS(null, 'stroke-width', 1.5);
circle.setAttributeNS(null, 'cx', this.xPosition.toFixed(2));
circle.setAttributeNS(null, 'cy', this.yPosition.toFixed(2));
circle.setAttributeNS(null, 'r', 7);
group.appendChild(circle);
}
g.appendChild(group);
},
draw: function(context) {
this.drawOutline(context);
if(this.historized) {
Expand Down Expand Up @@ -1934,6 +2026,17 @@
context.lineTo(this.xPosition - 10, this.yPosition - 10);
context.closePath();
},
toSVG: function(svg, g) {
var group = svg.createElementNS(SVGNS, 'g');
var rect = svg.createElementNS(SVGNS, 'rect');
rect.setAttributeNS(null, 'fill', '#b55');
rect.setAttributeNS(null, 'x', (this.xPosition - 10).toFixed(2));
rect.setAttributeNS(null, 'y', (this.yPosition - 10).toFixed(2));
rect.setAttributeNS(null, 'width', 20);
rect.setAttributeNS(null, 'height', 20);
group.appendChild(rect);
g.appendChild(group);
},
draw: function(context) {
this.drawOutline(context);
context.fillStyle = '#bb5555';
Expand Down Expand Up @@ -2049,6 +2152,21 @@
context.lineTo(this.xPosition - 9, this.yPosition - 6);
context.quadraticCurveTo(this.xPosition - 9, this.yPosition - 9, this.xPosition - 6, this.yPosition - 9);
},
toSVG: function(svg, g) {
var group = svg.createElementNS(SVGNS, 'g');
var rect = svg.createElementNS(SVGNS, 'rect');
rect.setAttributeNS(null, 'fill', '#fff');
rect.setAttributeNS(null, 'stroke', '#f88');
rect.setAttributeNS(null, 'stroke-width', 1.5);
rect.setAttributeNS(null, 'x', (this.xPosition - 9).toFixed(2));
rect.setAttributeNS(null, 'y', (this.yPosition - 9).toFixed(2));
rect.setAttributeNS(null, 'rx', 3);
rect.setAttributeNS(null, 'ry', 3);
rect.setAttributeNS(null, 'width', 18);
rect.setAttributeNS(null, 'height', 18);
group.appendChild(rect);
g.appendChild(group);
},
draw: function(context) {
this.drawOutline(context);
context.fillStyle = '#ffffff';
Expand Down Expand Up @@ -2216,6 +2334,41 @@
context.closePath();
}
},
toSVG: function(svg, g) {
var group = svg.createElementNS(SVGNS, 'g');
var path;
if(this.historized) {
path = svg.createElementNS(SVGNS, 'path');
path.setAttributeNS(null, 'fill', '#fff');
path.setAttributeNS(null, 'stroke', '#a8a8a8');
path.setAttributeNS(null, 'stroke-width', 1.5);
path.setAttributeNS(null, 'd', 'M ' + (this.xPosition - 18).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' L ' + this.xPosition.toFixed(2) + ',' + (this.yPosition + 17).toFixed(2) +
' ' + (this.xPosition + 18).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' ' + this.xPosition.toFixed(2) + ',' + (this.yPosition - 17).toFixed(2) +
' ' + (this.xPosition - 18).toFixed(2) + ',' + this.yPosition.toFixed(2));
group.appendChild(path);
path = svg.createElementNS(SVGNS, 'path');
path.setAttributeNS(null, 'fill', '#a8a8a8');
path.setAttributeNS(null, 'd', 'M ' + (this.xPosition - 12).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' L ' + this.xPosition.toFixed(2) + ',' + (this.yPosition + 11).toFixed(2) +
' ' + (this.xPosition + 12).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' ' + this.xPosition.toFixed(2) + ',' + (this.yPosition - 11).toFixed(2) +
' ' + (this.xPosition - 12).toFixed(2) + ',' + this.yPosition.toFixed(2));
group.appendChild(path);
}
else {
path = svg.createElementNS(SVGNS, 'path');
path.setAttributeNS(null, 'fill', '#a8a8a8');
path.setAttributeNS(null, 'd', 'M ' + (this.xPosition - 15).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' L ' + this.xPosition.toFixed(2) + ',' + (this.yPosition + 14).toFixed(2) +
' ' + (this.xPosition + 15).toFixed(2) + ',' + this.yPosition.toFixed(2) +
' ' + this.xPosition.toFixed(2) + ',' + (this.yPosition - 14).toFixed(2) +
' ' + (this.xPosition - 15).toFixed(2) + ',' + this.yPosition.toFixed(2));
group.appendChild(path);
}
g.appendChild(group);
},
draw: function(context) {
this.drawOutline(context);
if(this.historized) {
Expand Down Expand Up @@ -2293,6 +2446,29 @@
getName: function() {
return this.role;
},
toSVG: function(svg, g) {
var group = svg.createElementNS(SVGNS, 'g');
var path = svg.createElementNS(SVGNS, 'path');
path.setAttributeNS(null, 'fill', 'none');
path.setAttributeNS(null, 'stroke', '#000');
path.setAttributeNS(null, 'd', 'M ' + this.node.xPosition.toFixed(2) + ',' + this.node.yPosition.toFixed(2) +
' Q ' + this.controlPoint.xPosition.toFixed(2) + ',' + this.controlPoint.yPosition.toFixed(2) +
' ' + this.otherNode.xPosition.toFixed(2) + ',' + this.otherNode.yPosition.toFixed(2));
group.appendChild(path);
if(this.node instanceof Tie || this.otherNode instanceof Tie) {
var circle = svg.createElementNS(SVGNS, 'circle');
if(this.identifier)
circle.setAttributeNS(null, 'fill', '#766');
else
circle.setAttributeNS(null, 'fill', '#fff');
circle.setAttributeNS(null, 'stroke', '#000');
circle.setAttributeNS(null, 'cx', this.xPosition.toFixed(2));
circle.setAttributeNS(null, 'cy', this.yPosition.toFixed(2));
circle.setAttributeNS(null, 'r', 6);
group.appendChild(circle);
}
g.appendChild(group);
},
drawMoving: function(context) {
if((this.node.moving && !this.node.getFixed()) || (this.otherNode.moving && !this.otherNode.getFixed())) {
var nodeShadowOffsetX = 0;
Expand Down Expand Up @@ -3085,24 +3261,46 @@ <h1>Your browser does not support HTML5 canvas!</h1>
</canvas>

<div id="header" class="unselectable">
<div id="actions">
<div id="file">
<ul class="menu">
<li>Actions
<li>File
<ul class="submenu">
<li onclick="Model.setupMinimal()">Empty model...</li>
<li>Load XML file...<input class="invisible" type="file" onchange="FileHandler.handleFiles(this.files); FileHandler.replaceInput(this.parentNode, this);"/></li>
<li>Load model from cloud...</li>
<li>Save model to cloud...</li>
<li>Load model from local file...<input class="invisible" type="file" onchange="FileHandler.handleFiles(this.files); FileHandler.replaceInput(this.parentNode, this);"/></li>
<li onclick="ActionsMenu.loadFromURL('example.xml')">Load example model</li>
</ul>
</li>
</ul>
</div>

<div id="actions">
<ul class="menu">
<li>Layout
<ul class="submenu">
<li onclick="ActionsMenu.toggleNames()">Toggle names</li>
<li onclick="ActionsMenu.changeDetailLevel()">Change detail level</li>
<li onclick="ActionsMenu.toggleFixation()">Toggle fixation</li>
<li onclick="ActionsMenu.releaseAll()">Release all fixed</li>
<li onclick="ActionsMenu.resetZoom();ActionsMenu.centerModel()">Reset view</li>
<li onclick="ActionsMenu.randomizeLayout()">Randomize layout</li>
<li onclick="ActionsMenu.loadFromURL('example.xml')">Load example model</li>
<li onclick="new CodePopover(ActionsMenu.transform(Model.toXML(), 'xml2html.xsl', document), false)">Generate XML representation</li>
<li onclick="ActionsMenu.generateHTML()">Generate HTML documentation</li>
<li onclick="new CodePopover(ActionsMenu.transform(Model.toXML(), DATABASE_TRANSFORM, document), true)">Generate SQL code</li>
<li onclick="window.open(DrawingEngine.canvas.toDataURL('image/png'), '_newtab')">Generate PNG screenshot</li>
<li onclick="ActionsMenu.generatePNG()">Generate PNG hi-res model</li>
</ul>
</li>
</ul>
</div>

<div id="generate">
<ul class="menu">
<li>Generate
<ul class="submenu">
<li onclick="new CodePopover(ActionsMenu.transform(Model.toXML(), 'xml2html.xsl', document), false)">XML code</li>
<li onclick="ActionsMenu.generateHTML()">HTML documentation</li>
<li onclick="new CodePopover(ActionsMenu.transform(Model.toXML(), DATABASE_TRANSFORM, document), true)">SQL code</li>
<li onclick="window.open(DrawingEngine.canvas.toDataURL('image/png'), '_newtab')">PNG screenshot</li>
<li onclick="ActionsMenu.generatePNG()">PNG hi-res model</li>
<li onclick="window.open(Model.toSVGURL(), '_newtab')">SVG graphics</li>
<li onclick="new CodePopover(ActionsMenu.transform(Model.toSVG(), 'xml2html.xsl', document), false)">SVG code</li>
</ul>
</li>
</ul>
Expand Down
23 changes: 23 additions & 0 deletions xml2html.xsl
Expand Up @@ -27,6 +27,29 @@
</span>
</xsl:for-each>
<xsl:choose>
<xsl:when test="normalize-space(text())">
<span class="brackets">
<xsl:text>&gt;</xsl:text><br/>
</span>
<span class="tabs">
<xsl:value-of select="concat($indent, 'TAB')"/>
</span>
<span class="texts">
<xsl:value-of select="text()"/><br/>
</span>
<span class="tabs">
<xsl:value-of select="$indent"/>
</span>
<span class="brackets">
<xsl:text>&lt;/</xsl:text>
</span>
<span class="elements">
<xsl:value-of select="local-name(.)"/>
</span>
<span class="brackets">
<xsl:text>/&gt;</xsl:text><br/>
</span>
</xsl:when>
<xsl:when test="node()">
<span class="brackets">
<xsl:text>&gt;</xsl:text><br/>
Expand Down

0 comments on commit 6270e92

Please sign in to comment.