Skip to content

Commit 6234d8e

Browse files
png image export feature added, various technical fixes and improvements
1 parent 5e347b2 commit 6234d8e

File tree

8 files changed

+198
-5
lines changed

8 files changed

+198
-5
lines changed

gulpfile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ gulp.task("clean", function () {
3636
gulp.task("gatherLibs", ["clean"], function () {
3737
return gulp.src([
3838
"web/jsLib/joint.js",
39-
"web/jsLib/joint.shapes.uml.js"
39+
"web/jsLib/joint.shapes.uml.js",
40+
"web/jsLib/ImageExporter.js"
4041
])
4142
.pipe(uglify({
4243
output: {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "CacheUMLExplorer",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"description": "An UML Class explorer for InterSystems Caché",
55
"directories": {
66
"test": "test"

web/css/extras.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,29 @@
117117
left: 5px;
118118
}
119119

120+
.icon.download:before {
121+
content: "";
122+
border: 8px solid transparent;
123+
border-right-width: 0;
124+
border-left-color: #fff;
125+
left: 8px;
126+
right: auto;
127+
position: absolute;
128+
top: 16px;
129+
transform: translateY(-50%) rotate(90deg);
130+
}
131+
132+
.icon.download:after {
133+
content: "";
134+
background-color: #fff;
135+
width: 6px;
136+
height: 10px;
137+
border-radius: 1px;
138+
position: absolute;
139+
top: 5px;
140+
left: 9px;
141+
}
142+
120143
.icon.scaleNormal:after {
121144
content: "1:1";
122145
position: absolute;

web/css/interface.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,20 @@ html, body {
3434
font-size: 18pt;
3535
}
3636

37-
.ui-toolBar {
37+
.ui-rightBottomToolBar {
3838
position: absolute;
3939
bottom: 0;
4040
right: 0;
4141
padding: .5em;
4242
}
4343

44+
.ui-leftBottomToolBar {
45+
position: absolute;
46+
bottom: 0;
47+
left: 0;
48+
padding: .5em;
49+
}
50+
4451
#className {
4552
text-shadow: 1px 1px 0 white, -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white;
4653
}

web/css/treeView.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
cursor: pointer;
2222
border-radius: 5px;
2323
-webkit-transition: all .2s ease;
24-
user-select: none;
24+
-moz-transition: all .2s ease;
25+
-o-transition: all .2s ease;
26+
transition: all .2s ease;
2527
-webkit-user-select: none;
2628
-ms-user-select: none;
29+
user-select: none;
2730
}
2831

2932
.tv-class-name:hover, .tv-package-name:hover {
@@ -54,6 +57,9 @@
5457
border: 1px solid gray;
5558
border-radius: 2px 2px 0 2px;
5659
-webkit-transition: all .2s ease;
60+
-moz-transition: all .2s ease;
61+
-o-transition: all .2s ease;
62+
transition: all .2s ease;
5763
}
5864

5965
.tv-package-name:after {
@@ -69,6 +75,9 @@
6975
border: 1px solid gray;
7076
border-radius: 0 2px 2px 2px;
7177
-webkit-transition: all .2s ease;
78+
-moz-transition: all .2s ease;
79+
-o-transition: all .2s ease;
80+
transition: all .2s ease;
7281
}
7382

7483
.tv-package-name:hover:before {

web/index.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<script type="text/javascript" src="jsLib/joint.js"></script>
1515
<script type="text/javascript" src="jsLib/joint.shapes.uml.js"></script>
1616
<script type="text/javascript" src="jsLib/joint.layout.DirectedGraph.min.js"></script>
17+
<script type="text/javascript" src="jsLib/ImageExporter.js"></script>
1718
<script type="text/javascript" src="js/ClassView.js"></script>
1819
<script type="text/javascript" src="js/Lib.js"></script>
1920
<script type="text/javascript" src="js/CacheUMLExplorer.js"></script>
@@ -32,7 +33,10 @@
3233
<div class="ui-ClassInfo">
3334
<span id="className"></span>
3435
</div>
35-
<div class="ui-toolBar">
36+
<div class="ui-leftBottomToolBar">
37+
<div id="button.downloadSVG" class="icon download"></div>
38+
</div>
39+
<div class="ui-rightBottomToolBar">
3640
<div id="button.zoomIn" class="icon plus"></div>
3741
<div id="button.zoomNormal" class="icon scaleNormal"></div>
3842
<div id="button.zoomOut" class="icon minus"></div>
@@ -42,5 +46,6 @@
4246
</div>
4347
</div>
4448
</div>
49+
<canvas id="canvas" width="1000px" height="600px"></canvas>
4550
</body>
4651
</html>

web/js/CacheUMLExplorer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ CacheUMLExplorer.prototype.init = function () {
4343
}
4444
}
4545

46+
enableSVGDownload(this.classTree);
47+
4648
};

web/jsLib/ImageExporter.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* @see https://github.com/NYTimes/svg-crowbar - A bit of used code from here, thanks to it's author.
3+
*/
4+
var enableSVGDownload = function (classView) {
5+
6+
var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
7+
8+
window.URL = (window.URL || window.webkitURL);
9+
10+
var prefix = {
11+
xmlns: "http://www.w3.org/2000/xmlns/",
12+
xlink: "http://www.w3.org/1999/xlink",
13+
svg: "http://www.w3.org/2000/svg"
14+
};
15+
16+
function getSources(doc, emptySvgDeclarationComputed) {
17+
18+
var svgInfo = [],
19+
svgs = doc.querySelectorAll("svg");
20+
21+
[].forEach.call(svgs, function (svg) {
22+
23+
var par = svg.parentNode;
24+
svg = svg.cloneNode(true);
25+
par.appendChild(svg);
26+
var gGroup = svg.childNodes[0];
27+
28+
svg.setAttribute("version", "1.1");
29+
30+
// removing attributes so they aren't doubled up
31+
svg.removeAttribute("xmlns");
32+
svg.removeAttribute("xlink");
33+
34+
// These are needed for the svg
35+
if (!svg.hasAttributeNS(prefix.xmlns, "xmlns")) {
36+
svg.setAttributeNS(prefix.xmlns, "xmlns", prefix.svg);
37+
}
38+
39+
if (!svg.hasAttributeNS(prefix.xmlns, "xmlns:xlink")) {
40+
svg.setAttributeNS(prefix.xmlns, "xmlns:xlink", prefix.xlink);
41+
}
42+
43+
svg.setAttribute("width", gGroup.getBBox().width);
44+
svg.setAttribute("height", gGroup.getBBox().height);
45+
gGroup.setAttribute("transform", "");
46+
47+
setInlineStyles(svg, emptySvgDeclarationComputed);
48+
49+
var source = (new XMLSerializer()).serializeToString(svg);
50+
var rect = svg.getBoundingClientRect();
51+
svgInfo.push({
52+
top: rect.top,
53+
left: rect.left,
54+
width: rect.width,
55+
height: rect.height,
56+
class: svg.getAttribute("class"),
57+
id: svg.getAttribute("id"),
58+
childElementCount: svg.childElementCount,
59+
source: [doctype + source]
60+
});
61+
62+
par.removeChild(svg);
63+
64+
});
65+
return svgInfo;
66+
}
67+
68+
document.getElementById("button.downloadSVG").addEventListener("click", function () {
69+
70+
var emptySvg = window.document.createElementNS(prefix.svg, 'svg'),
71+
emptySvgDeclarationComputed = getComputedStyle(emptySvg),
72+
source = getSources(document, emptySvgDeclarationComputed)[0];
73+
74+
var filename = (classView || {}).SELECTED_CLASS_NAME || "classDiagram";
75+
76+
var img = new Image();
77+
var url = window.URL.createObjectURL(new Blob(source.source, { "type" : 'image/svg+xml;charset=utf-8'/*"text\/xml"*/ }));
78+
79+
var canvas = document.createElement('canvas');
80+
var ctx = canvas.getContext('2d');
81+
canvas.setAttribute("width", source.width);
82+
canvas.setAttribute("height", source.height);
83+
document.body.appendChild(canvas);
84+
85+
img.onload = function () {
86+
ctx.drawImage(img, 0, 0);
87+
var dataURL = canvas.toDataURL("image/png");
88+
var a = document.createElement("a");
89+
a.setAttribute("download", filename + ".png");
90+
a.setAttribute("href", dataURL/*url*/);
91+
document.body.appendChild(a);
92+
a.click();
93+
setTimeout(function () {
94+
a.parentNode.removeChild(a);
95+
canvas.parentNode.removeChild(canvas);
96+
window.URL.revokeObjectURL(url);
97+
}, 10);
98+
};
99+
100+
img.src = url;
101+
102+
});
103+
104+
function setInlineStyles(svg, emptySvgDeclarationComputed) {
105+
106+
function explicitlySetStyle (element) {
107+
var cSSStyleDeclarationComputed = getComputedStyle(element);
108+
var i, len, key, value;
109+
var computedStyleStr = "";
110+
for (i=0, len=cSSStyleDeclarationComputed.length; i<len; i++) {
111+
key=cSSStyleDeclarationComputed[i];
112+
value=cSSStyleDeclarationComputed.getPropertyValue(key);
113+
if (value!==emptySvgDeclarationComputed.getPropertyValue(key)) {
114+
computedStyleStr+=key+":"+value+";";
115+
}
116+
}
117+
element.setAttribute('style', computedStyleStr);
118+
}
119+
function traverse(obj){
120+
var tree = [];
121+
tree.push(obj);
122+
visit(obj);
123+
function visit(node) {
124+
if (node && node.hasChildNodes()) {
125+
var child = node.firstChild;
126+
while (child) {
127+
if (child.nodeType === 1 && child.nodeName != 'SCRIPT'){
128+
tree.push(child);
129+
visit(child);
130+
}
131+
child = child.nextSibling;
132+
}
133+
}
134+
}
135+
return tree;
136+
}
137+
// hardcode computed css styles inside svg
138+
var allElements = traverse(svg);
139+
var i = allElements.length;
140+
while (i--){
141+
explicitlySetStyle(allElements[i]);
142+
}
143+
}
144+
145+
146+
};

0 commit comments

Comments
 (0)