Comparing version 0.9.4 to 0.9.5
@@ -5,7 +5,7 @@ { | ||
"dependencies": { | ||
"lodash": "2.4.1", | ||
"lodash": "3.10.1", | ||
"backbone": "1.2.1", | ||
"jquery": "2.0.3", | ||
"graphlib": "1.0.1", | ||
"dagre": "0.7.1" | ||
"graphlib": "1.0.7", | ||
"dagre": "0.7.4" | ||
}, | ||
@@ -21,3 +21,4 @@ "devDependencies": { | ||
"dist/joint.css" | ||
] | ||
], | ||
"license": "MPL-2.0" | ||
} |
@@ -43,5 +43,2 @@ var graph = new joint.dia.Graph; | ||
cellView.model.trigger('signal', cellView.model); | ||
var halo = new joint.ui.Halo({ graph: graph, paper: paper, cellView: cellView }); | ||
halo.render(); | ||
}); | ||
@@ -48,0 +45,0 @@ |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -8,5 +8,2 @@ | ||
*/ | ||
// Geometry library. | ||
// (c) 2011-2013 client IO | ||
(function(root, factory) { | ||
@@ -17,3 +14,3 @@ | ||
// AMD. Register as an anonymous module. | ||
define('g', [], factory); | ||
define([], factory); | ||
@@ -35,2 +32,7 @@ } else if (typeof exports === 'object') { | ||
// Geometry library. | ||
// (c) 2011-2013 client IO | ||
var g = (function() { | ||
// Declare shorthands to the most used math functions. | ||
@@ -207,2 +209,5 @@ var math = Math; | ||
return point(ref).move(this, this.distance(ref)); | ||
}, | ||
clone: function() { | ||
return point(this); | ||
} | ||
@@ -331,2 +336,5 @@ }; | ||
return ((this.end.x - this.start.x) * (p.y - this.start.y) - (this.end.y - this.start.y) * (p.x - this.start.x)) / 2; | ||
}, | ||
clone: function() { | ||
return line(this); | ||
} | ||
@@ -356,2 +364,8 @@ }; | ||
}, | ||
// @return {boolean} true if rectangles are equal. | ||
equals: function(r) { | ||
var mr = g.rect(this).normalize(); | ||
var nr = g.rect(r).normalize(); | ||
return mr.x === nr.x && mr.y === nr.y && mr.width === nr.width && mr.height === nr.height; | ||
}, | ||
origin: function() { | ||
@@ -372,3 +386,3 @@ return point(this.x, this.y); | ||
}, | ||
// @return {boolean} true if rectangles intersect | ||
// @return {rect} if rectangles intersect, {null} if not. | ||
intersect: function(r) { | ||
@@ -380,8 +394,14 @@ var myOrigin = this.origin(); | ||
// No intersection found | ||
if (rCorner.x <= myOrigin.x || | ||
rCorner.y <= myOrigin.y || | ||
rOrigin.x >= myCorner.x || | ||
rOrigin.y >= myCorner.y) return false; | ||
return true; | ||
rOrigin.y >= myCorner.y) return null; | ||
var x = Math.max(myOrigin.x, rOrigin.x); | ||
var y = Math.max(myOrigin.y, rOrigin.y); | ||
return rect(x, y, Math.min(myCorner.x, rCorner.x) - x, Math.min(myCorner.y, rCorner.y) - y); | ||
}, | ||
// @return {string} (left|right|top|bottom) side which is nearest to point | ||
@@ -555,2 +575,14 @@ // @see Squeak Smalltalk, Rectangle>>sideNearestTo: | ||
return rect(this.x + (this.width - w) / 2, this.y + (this.height - h) / 2, w, h); | ||
}, | ||
snapToGrid: function(gx, gy) { | ||
var origin = this.origin().snapToGrid(gx, gy); | ||
var corner = this.corner().snapToGrid(gx, gy); | ||
this.x = origin.x; | ||
this.y = origin.y; | ||
this.width = corner.x - origin.x; | ||
this.height = corner.y - origin.y; | ||
return this; | ||
}, | ||
clone: function() { | ||
return rect(this); | ||
} | ||
@@ -603,2 +635,5 @@ }; | ||
return result; | ||
}, | ||
clone: function() { | ||
return ellipse(this); | ||
} | ||
@@ -779,2 +814,7 @@ }; | ||
})(); | ||
return g; | ||
})); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -8,2 +8,13 @@ | ||
*/ | ||
if (typeof exports === 'object') { | ||
var graphlib = require('graphlib'); | ||
var dagre = require('dagre'); | ||
} | ||
// In the browser, these variables are set to undefined because of JavaScript hoisting. | ||
// In that case, should grab them from the window object. | ||
graphlib = graphlib || (typeof window !== 'undefined' && window.graphlib); | ||
dagre = dagre || (typeof window !== 'undefined' && window.dagre); | ||
// create graphlib.Graph from existing joint.dia.Graph | ||
@@ -161,3 +172,4 @@ joint.dia.Graph.prototype.toGraphLib = function(opt) { | ||
.sortBy(function(cluster) { return -cluster.getAncestors().length; }) | ||
.invoke('fitEmbeds', { padding: opt.clusterPadding }); | ||
.invoke('fitEmbeds', { padding: opt.clusterPadding }) | ||
.value(); | ||
} | ||
@@ -164,0 +176,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.dia.Graph.prototype.toGraphLib=function(a){a=a||{};var b=_.pick(a,"directed","compound","multigraph"),c=new graphlib.Graph(b),d=a.setNodeLabel||_.noop,e=a.setEdgeLabel||_.noop,f=a.setEdgeName||_.noop;return this.get("cells").each(function(a){if(a.isLink()){var b=a.get("source"),g=a.get("target");if(!b.id||!g.id)return;c.setEdge(b.id,g.id,e(a),f(a))}else c.setNode(a.id,d(a)),c.isCompound()&&a.has("parent")&&c.setParent(a.id,a.get("parent"))}),c},joint.dia.Graph.prototype.fromGraphLib=function(a,b){b=b||{};var c=b.importNode||_.noop,d=b.importEdge||_.noop;a.nodes().forEach(function(d){c.call(this,d,a,this,b)},this),a.edges().forEach(function(c){d.call(this,c,a,this,b)},this)},joint.layout.DirectedGraph={layout:function(a,b){b=_.defaults(b||{},{resizeClusters:!0,clusterPadding:10});var c=a.toGraphLib({directed:!0,multigraph:!0,compound:!0,setNodeLabel:function(a){return{width:a.get("size").width,height:a.get("size").height,rank:a.get("rank")}},setEdgeLabel:function(a){return{minLen:a.get("minLen")||1}},setEdgeName:function(a){return a.id}}),d={};return b.rankDir&&(d.rankdir=b.rankDir),b.nodeSep&&(d.nodesep=b.nodeSep),b.edgeSep&&(d.edgesep=b.edgeSep),b.rankSep&&(d.ranksep=b.rankSep),b.marginX&&(d.marginx=b.marginX),b.marginY&&(d.marginy=b.marginY),c.setGraph(d),dagre.layout(c,{debugTiming:!!b.debugTiming}),a.fromGraphLib(c,{importNode:function(a,c){var d=this.getCell(a),e=c.node(a);b.setPosition?b.setPosition(d,e):d.set("position",{x:e.x-e.width/2,y:e.y-e.height/2})},importEdge:function(a,c){var d=this.getCell(a.name),e=c.edge(a);b.setLinkVertices&&(b.setVertices?b.setVertices(d,e.points):d.set("vertices",e.points))}}),b.resizeClusters&&_.chain(c.nodes()).filter(function(a){return c.children(a).length>0}).map(a.getCell,a).sortBy(function(a){return-a.getAncestors().length}).invoke("fitEmbeds",{padding:b.clusterPadding}),c.graph()}}; | ||
if("object"==typeof exports)var graphlib=require("graphlib"),dagre=require("dagre");graphlib=graphlib||"undefined"!=typeof window&&window.graphlib,dagre=dagre||"undefined"!=typeof window&&window.dagre,joint.dia.Graph.prototype.toGraphLib=function(a){a=a||{};var b=_.pick(a,"directed","compound","multigraph"),c=new graphlib.Graph(b),d=a.setNodeLabel||_.noop,e=a.setEdgeLabel||_.noop,f=a.setEdgeName||_.noop;return this.get("cells").each(function(a){if(a.isLink()){var b=a.get("source"),g=a.get("target");if(!b.id||!g.id)return;c.setEdge(b.id,g.id,e(a),f(a))}else c.setNode(a.id,d(a)),c.isCompound()&&a.has("parent")&&c.setParent(a.id,a.get("parent"))}),c},joint.dia.Graph.prototype.fromGraphLib=function(a,b){b=b||{};var c=b.importNode||_.noop,d=b.importEdge||_.noop;a.nodes().forEach(function(d){c.call(this,d,a,this,b)},this),a.edges().forEach(function(c){d.call(this,c,a,this,b)},this)},joint.layout.DirectedGraph={layout:function(a,b){b=_.defaults(b||{},{resizeClusters:!0,clusterPadding:10});var c=a.toGraphLib({directed:!0,multigraph:!0,compound:!0,setNodeLabel:function(a){return{width:a.get("size").width,height:a.get("size").height,rank:a.get("rank")}},setEdgeLabel:function(a){return{minLen:a.get("minLen")||1}},setEdgeName:function(a){return a.id}}),d={};return b.rankDir&&(d.rankdir=b.rankDir),b.nodeSep&&(d.nodesep=b.nodeSep),b.edgeSep&&(d.edgesep=b.edgeSep),b.rankSep&&(d.ranksep=b.rankSep),b.marginX&&(d.marginx=b.marginX),b.marginY&&(d.marginy=b.marginY),c.setGraph(d),dagre.layout(c,{debugTiming:!!b.debugTiming}),a.fromGraphLib(c,{importNode:function(a,c){var d=this.getCell(a),e=c.node(a);b.setPosition?b.setPosition(d,e):d.set("position",{x:e.x-e.width/2,y:e.y-e.height/2})},importEdge:function(a,c){var d=this.getCell(a.name),e=c.edge(a);b.setLinkVertices&&(b.setVertices?b.setVertices(d,e.points):d.set("vertices",e.points))}}),b.resizeClusters&&_.chain(c.nodes()).filter(function(a){return c.children(a).length>0}).map(a.getCell,a).sortBy(function(a){return-a.getAncestors().length}).invoke("fitEmbeds",{padding:b.clusterPadding}).value(),c.graph()}}; |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.chess={},joint.shapes.chess.KingWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"><path d="M 22.5,11.63 L 22.5,6" style="fill:none; stroke:#000000; stroke-linejoin:miter;" /> <path d="M 20,8 L 25,8" style="fill:none; stroke:#000000; stroke-linejoin:miter;" /> <path d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25" style="fill:#ffffff; stroke:#000000; stroke-linecap:butt; stroke-linejoin:miter;" /> <path d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z " style="fill:#ffffff; stroke:#000000;" /> <path d="M 11.5,30 C 17,27 27,27 32.5,30" style="fill:none; stroke:#000000;" /> <path d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5" style="fill:none; stroke:#000000;" /> <path d="M 11.5,37 C 17,34 27,34 32.5,37" style="fill:none; stroke:#000000;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.KingWhite",size:{width:42,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.KingBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 22.5,11.63 L 22.5,6" style="fill:none; stroke:#000000; stroke-linejoin:miter;" id="path6570" /> <path d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25" style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;" /> <path d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z " style="fill:#000000; stroke:#000000;" /> <path d="M 20,8 L 25,8" style="fill:none; stroke:#000000; stroke-linejoin:miter;" /> <path d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.51,26.6 L 22.5,24.5 C 20,18 9.906,14 6.997,19.85 C 4.5,25.5 11.85,28.85 11.85,28.85" style="fill:none; stroke:#ffffff;" /> <path d="M 11.5,30 C 17,27 27,27 32.5,30 M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5 M 11.5,37 C 17,34 27,34 32.5,37" style="fill:none; stroke:#ffffff;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.KingBlack",size:{width:42,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.QueenWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z" transform="translate(-1,-1)" /> <path d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z" transform="translate(15.5,-5.5)" /> <path d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z" transform="translate(32,-1)" /> <path d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z" transform="translate(7,-4.5)" /> <path d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z" transform="translate(24,-4)" /> <path d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38,14 L 31,25 L 31,11 L 25.5,24.5 L 22.5,9.5 L 19.5,24.5 L 14,10.5 L 14,25 L 7,14 L 9,26 z " style="stroke-linecap:butt;" /> <path d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z " style="stroke-linecap:butt;" /> <path d="M 11.5,30 C 15,29 30,29 33.5,30" style="fill:none;" /> <path d="M 12,33.5 C 18,32.5 27,32.5 33,33.5" style="fill:none;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.QueenWhite",size:{width:42,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.QueenBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <g style="fill:#000000; stroke:none;"> <circle cx="6" cy="12" r="2.75" /> <circle cx="14" cy="9" r="2.75" /> <circle cx="22.5" cy="8" r="2.75" /> <circle cx="31" cy="9" r="2.75" /> <circle cx="39" cy="12" r="2.75" /> </g> <path d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z" style="stroke-linecap:butt; stroke:#000000;" /> <path d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z" style="stroke-linecap:butt;" /> <path d="M 11,38.5 A 35,35 1 0 0 34,38.5" style="fill:none; stroke:#000000; stroke-linecap:butt;" /> <path d="M 11,29 A 35,35 1 0 1 34,29" style="fill:none; stroke:#ffffff;" /> <path d="M 12.5,31.5 L 32.5,31.5" style="fill:none; stroke:#ffffff;" /> <path d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5" style="fill:none; stroke:#ffffff;" /> <path d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5" style="fill:none; stroke:#ffffff;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.QueenBlack",size:{width:42,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.RookWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z " style="stroke-linecap:butt;" /> <path d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z " style="stroke-linecap:butt;" /> <path d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14" style="stroke-linecap:butt;" /> <path d="M 34,14 L 31,17 L 14,17 L 11,14" /> <path d="M 31,17 L 31,29.5 L 14,29.5 L 14,17" style="stroke-linecap:butt; stroke-linejoin:miter;" /> <path d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" /> <path d="M 11,14 L 34,14" style="fill:none; stroke:#000000; stroke-linejoin:miter;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.RookWhite",size:{width:32,height:34}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.RookBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z " style="stroke-linecap:butt;" /> <path d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z " style="stroke-linecap:butt;" /> <path d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z " style="stroke-linecap:butt;" /> <path d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z " style="stroke-linecap:butt;stroke-linejoin:miter;" /> <path d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z " style="stroke-linecap:butt;" /> <path d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z " style="stroke-linecap:butt;" /> <path d="M 12,35.5 L 33,35.5 L 33,35.5" style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" /> <path d="M 13,31.5 L 32,31.5" style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" /> <path d="M 14,29.5 L 31,29.5" style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" /> <path d="M 14,16.5 L 31,16.5" style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" /> <path d="M 11,14 L 34,14" style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.RookBlack",size:{width:32,height:34}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.BishopWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;"> <path d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" /> <path d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" /> <path d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" /> </g> <path d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18" style="fill:none; stroke:#000000; stroke-linejoin:miter;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.BishopWhite",size:{width:38,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.BishopBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <g style="fill:#000000; stroke:#000000; stroke-linecap:butt;"> <path d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" /> <path d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" /> <path d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" /> </g> <path d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18" style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.BishopBlack",size:{width:38,height:38}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.KnightWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18" style="fill:#ffffff; stroke:#000000;" /> <path d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10" style="fill:#ffffff; stroke:#000000;" /> <path d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z" style="fill:#000000; stroke:#000000;" /> <path d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z" transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)" style="fill:#000000; stroke:#000000;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.KnightWhite",size:{width:38,height:37}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.KnightBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"> <path d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18" style="fill:#000000; stroke:#000000;" /> <path d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10" style="fill:#000000; stroke:#000000;" /> <path d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z" style="fill:#ffffff; stroke:#ffffff;" /> <path d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z" transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)" style="fill:#ffffff; stroke:#ffffff;" /> <path d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z " style="fill:#ffffff; stroke:none;" /> </g></g></g>',defaults:joint.util.deepSupplement({type:"chess.KnightBlack",size:{width:38,height:37}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.PawnWhite=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><path d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z " style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" /></g></g>',defaults:joint.util.deepSupplement({type:"chess.PawnWhite",size:{width:28,height:33}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.chess.PawnBlack=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><path d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z " style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" /></g></g>',defaults:joint.util.deepSupplement({type:"chess.PawnBlack",size:{width:28,height:33}},joint.shapes.basic.Generic.prototype.defaults)}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.devs={},joint.shapes.devs.Model=joint.shapes.basic.Generic.extend(_.extend({},joint.shapes.basic.PortsModelInterface,{markup:'<g class="rotatable"><g class="scalable"><rect class="body"/></g><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',portMarkup:'<g class="port port<%= id %>"><circle class="port-body"/><text class="port-label"/></g>',defaults:joint.util.deepSupplement({type:"devs.Model",size:{width:1,height:1},inPorts:[],outPorts:[],attrs:{".":{magnet:!1},".body":{width:150,height:250,stroke:"#000000"},".port-body":{r:10,magnet:!0,stroke:"#000000"},text:{"pointer-events":"none"},".label":{text:"Model","ref-x":.5,"ref-y":10,ref:".body","text-anchor":"middle",fill:"#000000"},".inPorts .port-label":{x:-15,dy:4,"text-anchor":"end",fill:"#000000"},".outPorts .port-label":{x:15,dy:4,fill:"#000000"}}},joint.shapes.basic.Generic.prototype.defaults),getPortAttrs:function(a,b,c,d,e){var f={},g="port"+b,h=d+">."+g,i=h+">.port-label",j=h+">.port-body";return f[i]={text:a},f[j]={port:{id:a||_.uniqueId(e),type:e}},f[h]={ref:".body","ref-y":(b+.5)*(1/c)},".outPorts"===d&&(f[h]["ref-dx"]=0),f}})),joint.shapes.devs.Atomic=joint.shapes.devs.Model.extend({defaults:joint.util.deepSupplement({type:"devs.Atomic",size:{width:80,height:80},attrs:{".body":{fill:"salmon"},".label":{text:"Atomic"},".inPorts .port-body":{fill:"PaleGreen"},".outPorts .port-body":{fill:"Tomato"}}},joint.shapes.devs.Model.prototype.defaults)}),joint.shapes.devs.Coupled=joint.shapes.devs.Model.extend({defaults:joint.util.deepSupplement({type:"devs.Coupled",size:{width:200,height:300},attrs:{".body":{fill:"seaGreen"},".label":{text:"Coupled"},".inPorts .port-body":{fill:"PaleGreen"},".outPorts .port-body":{fill:"Tomato"}}},joint.shapes.devs.Model.prototype.defaults)}),joint.shapes.devs.Link=joint.dia.Link.extend({defaults:{type:"devs.Link",attrs:{".connection":{"stroke-width":2}}}}),joint.shapes.devs.ModelView=joint.dia.ElementView.extend(joint.shapes.basic.PortsViewInterface),joint.shapes.devs.AtomicView=joint.shapes.devs.ModelView,joint.shapes.devs.CoupledView=joint.shapes.devs.ModelView; |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.erd={},joint.shapes.erd.Entity=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><polygon class="outer"/><polygon class="inner"/></g><text/></g>',defaults:joint.util.deepSupplement({type:"erd.Entity",size:{width:150,height:60},attrs:{".outer":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"100,0 100,60 0,60 0,0"},".inner":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"95,5 95,55 5,55 5,5",display:"none"},text:{text:"Entity","font-family":"Arial","font-size":14,ref:".outer","ref-x":.5,"ref-y":.5,"x-alignment":"middle","y-alignment":"middle"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.erd.WeakEntity=joint.shapes.erd.Entity.extend({defaults:joint.util.deepSupplement({type:"erd.WeakEntity",attrs:{".inner":{display:"auto"},text:{text:"Weak Entity"}}},joint.shapes.erd.Entity.prototype.defaults)}),joint.shapes.erd.Relationship=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><polygon class="outer"/><polygon class="inner"/></g><text/></g>',defaults:joint.util.deepSupplement({type:"erd.Relationship",size:{width:80,height:80},attrs:{".outer":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,0 80,40 40,80 0,40"},".inner":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,5 75,40 40,75 5,40",display:"none"},text:{text:"Relationship","font-family":"Arial","font-size":12,ref:".","ref-x":.5,"ref-y":.5,"x-alignment":"middle","y-alignment":"middle"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.erd.IdentifyingRelationship=joint.shapes.erd.Relationship.extend({defaults:joint.util.deepSupplement({type:"erd.IdentifyingRelationship",attrs:{".inner":{display:"auto"},text:{text:"Identifying"}}},joint.shapes.erd.Relationship.prototype.defaults)}),joint.shapes.erd.Attribute=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><ellipse class="outer"/><ellipse class="inner"/></g><text/></g>',defaults:joint.util.deepSupplement({type:"erd.Attribute",size:{width:100,height:50},attrs:{ellipse:{transform:"translate(50, 25)"},".outer":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:50,ry:25,fill:"#E67E22"},".inner":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:45,ry:20,fill:"#E67E22",display:"none"},text:{"font-family":"Arial","font-size":14,ref:".","ref-x":.5,"ref-y":.5,"x-alignment":"middle","y-alignment":"middle"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.erd.Multivalued=joint.shapes.erd.Attribute.extend({defaults:joint.util.deepSupplement({type:"erd.Multivalued",attrs:{".inner":{display:"block"},text:{text:"multivalued"}}},joint.shapes.erd.Attribute.prototype.defaults)}),joint.shapes.erd.Derived=joint.shapes.erd.Attribute.extend({defaults:joint.util.deepSupplement({type:"erd.Derived",attrs:{".outer":{"stroke-dasharray":"3,5"},text:{text:"derived"}}},joint.shapes.erd.Attribute.prototype.defaults)}),joint.shapes.erd.Key=joint.shapes.erd.Attribute.extend({defaults:joint.util.deepSupplement({type:"erd.Key",attrs:{ellipse:{"stroke-width":4},text:{text:"key","font-weight":"800","text-decoration":"underline"}}},joint.shapes.erd.Attribute.prototype.defaults)}),joint.shapes.erd.Normal=joint.shapes.erd.Attribute.extend({defaults:joint.util.deepSupplement({type:"erd.Normal",attrs:{text:{text:"Normal"}}},joint.shapes.erd.Attribute.prototype.defaults)}),joint.shapes.erd.ISA=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><polygon/></g><text/></g>',defaults:joint.util.deepSupplement({type:"erd.ISA",size:{width:100,height:50},attrs:{polygon:{points:"0,0 50,50 100,0",fill:"#F1C40F",stroke:"#F39C12","stroke-width":2},text:{text:"ISA","font-size":18,ref:"polygon","ref-x":.5,"ref-y":.3,"x-alignment":"middle","y-alignment":"middle"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.erd.Line=joint.dia.Link.extend({defaults:{type:"erd.Line"},cardinality:function(a){this.set("labels",[{position:-20,attrs:{text:{dy:-8,text:a}}}])}}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.fsa={},joint.shapes.fsa.State=joint.shapes.basic.Circle.extend({defaults:joint.util.deepSupplement({type:"fsa.State",attrs:{circle:{"stroke-width":3},text:{"font-weight":"800"}}},joint.shapes.basic.Circle.prototype.defaults)}),joint.shapes.fsa.StartState=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><circle/></g></g>',defaults:joint.util.deepSupplement({type:"fsa.StartState",size:{width:20,height:20},attrs:{circle:{transform:"translate(10, 10)",r:10,fill:"#000000"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.fsa.EndState=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><circle class="outer"/><circle class="inner"/></g></g>',defaults:joint.util.deepSupplement({type:"fsa.EndState",size:{width:20,height:20},attrs:{".outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#000000"},".inner":{transform:"translate(10, 10)",r:6,fill:"#000000"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.fsa.Arrow=joint.dia.Link.extend({defaults:joint.util.deepSupplement({type:"fsa.Arrow",attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}},smooth:!0},joint.dia.Link.prototype.defaults)}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.org={},joint.shapes.org.Member=joint.dia.Element.extend({markup:'<g class="rotatable"><g class="scalable"><rect class="card"/><image/></g><text class="rank"/><text class="name"/></g>',defaults:joint.util.deepSupplement({type:"org.Member",size:{width:180,height:70},attrs:{rect:{width:170,height:60},".card":{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"pointer-events":"visiblePainted",rx:10,ry:10},image:{width:48,height:48,ref:".card","ref-x":10,"ref-y":5},".rank":{"text-decoration":"underline",ref:".card","ref-x":.9,"ref-y":.2,"font-family":"Courier New","font-size":14,"text-anchor":"end"},".name":{"font-weight":"800",ref:".card","ref-x":.9,"ref-y":.6,"font-family":"Courier New","font-size":14,"text-anchor":"end"}}},joint.dia.Element.prototype.defaults)}),joint.shapes.org.Arrow=joint.dia.Link.extend({defaults:{type:"org.Arrow",source:{selector:".card"},target:{selector:".card"},attrs:{".connection":{stroke:"#585858","stroke-width":3}},z:-1}}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -149,2 +149,3 @@ | ||
type: 'pn.Link', | ||
attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} | ||
@@ -151,0 +152,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.pn={},joint.shapes.pn.Place=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><circle class="root"/><g class="tokens" /></g><text class="label"/></g>',defaults:joint.util.deepSupplement({type:"pn.Place",size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.pn.PlaceView=joint.dia.ElementView.extend({initialize:function(){joint.dia.ElementView.prototype.initialize.apply(this,arguments),this.model.on("change:tokens",function(){this.renderTokens(),this.update()},this)},render:function(){joint.dia.ElementView.prototype.render.apply(this,arguments),this.renderTokens(),this.update()},renderTokens:function(){var a=this.$(".tokens").empty();a[0].className.baseVal="tokens";var b=this.model.get("tokens");if(b)switch(b){case 1:a[0].className.baseVal+=" one",a.append(V("<circle/>").node);break;case 2:a[0].className.baseVal+=" two",a.append(V("<circle/>").node,V("<circle/>").node);break;case 3:a[0].className.baseVal+=" three",a.append(V("<circle/>").node,V("<circle/>").node,V("<circle/>").node);break;default:a[0].className.baseVal+=" alot",a.append(V("<text/>").text(b+"").node)}}}),joint.shapes.pn.Transition=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><rect class="root"/></g></g><text class="label"/>',defaults:joint.util.deepSupplement({type:"pn.Transition",size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.pn.Link=joint.dia.Link.extend({defaults:joint.util.deepSupplement({attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}},joint.dia.Link.prototype.defaults)}); | ||
joint.shapes.pn={},joint.shapes.pn.Place=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><circle class="root"/><g class="tokens" /></g><text class="label"/></g>',defaults:joint.util.deepSupplement({type:"pn.Place",size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.pn.PlaceView=joint.dia.ElementView.extend({initialize:function(){joint.dia.ElementView.prototype.initialize.apply(this,arguments),this.model.on("change:tokens",function(){this.renderTokens(),this.update()},this)},render:function(){joint.dia.ElementView.prototype.render.apply(this,arguments),this.renderTokens(),this.update()},renderTokens:function(){var a=this.$(".tokens").empty();a[0].className.baseVal="tokens";var b=this.model.get("tokens");if(b)switch(b){case 1:a[0].className.baseVal+=" one",a.append(V("<circle/>").node);break;case 2:a[0].className.baseVal+=" two",a.append(V("<circle/>").node,V("<circle/>").node);break;case 3:a[0].className.baseVal+=" three",a.append(V("<circle/>").node,V("<circle/>").node,V("<circle/>").node);break;default:a[0].className.baseVal+=" alot",a.append(V("<text/>").text(b+"").node)}}}),joint.shapes.pn.Transition=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><rect class="root"/></g></g><text class="label"/>',defaults:joint.util.deepSupplement({type:"pn.Transition",size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.pn.Link=joint.dia.Link.extend({defaults:joint.util.deepSupplement({type:"pn.Link",attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}},joint.dia.Link.prototype.defaults)}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,1 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
This Source Code Form is subject to the terms of the Mozilla Public | ||
License, v. 2.0. If a copy of the MPL was not distributed with this | ||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
joint.shapes.uml={},joint.shapes.uml.Class=joint.shapes.basic.Generic.extend({markup:['<g class="rotatable">','<g class="scalable">','<rect class="uml-class-name-rect"/><rect class="uml-class-attrs-rect"/><rect class="uml-class-methods-rect"/>',"</g>",'<text class="uml-class-name-text"/><text class="uml-class-attrs-text"/><text class="uml-class-methods-text"/>',"</g>"].join(""),defaults:joint.util.deepSupplement({type:"uml.Class",attrs:{rect:{width:200},".uml-class-name-rect":{stroke:"black","stroke-width":2,fill:"#3498db"},".uml-class-attrs-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-methods-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-name-text":{ref:".uml-class-name-rect","ref-y":.5,"ref-x":.5,"text-anchor":"middle","y-alignment":"middle","font-weight":"bold",fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-attrs-text":{ref:".uml-class-attrs-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-methods-text":{ref:".uml-class-methods-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"}},name:[],attributes:[],methods:[]},joint.shapes.basic.Generic.prototype.defaults),initialize:function(){this.on("change:name change:attributes change:methods",function(){this.updateRectangles(),this.trigger("uml-update")},this),this.updateRectangles(),joint.shapes.basic.Generic.prototype.initialize.apply(this,arguments)},getClassName:function(){return this.get("name")},updateRectangles:function(){var a=this.get("attrs"),b=[{type:"name",text:this.getClassName()},{type:"attrs",text:this.get("attributes")},{type:"methods",text:this.get("methods")}],c=0;_.each(b,function(b){var d=_.isArray(b.text)?b.text:[b.text],e=20*d.length+20;a[".uml-class-"+b.type+"-text"].text=d.join("\n"),a[".uml-class-"+b.type+"-rect"].height=e,a[".uml-class-"+b.type+"-rect"].transform="translate(0,"+c+")",c+=e})}}),joint.shapes.uml.ClassView=joint.dia.ElementView.extend({initialize:function(){joint.dia.ElementView.prototype.initialize.apply(this,arguments),this.listenTo(this.model,"uml-update",function(){this.update(),this.resize()})}}),joint.shapes.uml.Abstract=joint.shapes.uml.Class.extend({defaults:joint.util.deepSupplement({type:"uml.Abstract",attrs:{".uml-class-name-rect":{fill:"#e74c3c"},".uml-class-attrs-rect":{fill:"#c0392b"},".uml-class-methods-rect":{fill:"#c0392b"}}},joint.shapes.uml.Class.prototype.defaults),getClassName:function(){return["<<Abstract>>",this.get("name")]}}),joint.shapes.uml.AbstractView=joint.shapes.uml.ClassView,joint.shapes.uml.Interface=joint.shapes.uml.Class.extend({defaults:joint.util.deepSupplement({type:"uml.Interface",attrs:{".uml-class-name-rect":{fill:"#f1c40f"},".uml-class-attrs-rect":{fill:"#f39c12"},".uml-class-methods-rect":{fill:"#f39c12"}}},joint.shapes.uml.Class.prototype.defaults),getClassName:function(){return["<<Interface>>",this.get("name")]}}),joint.shapes.uml.InterfaceView=joint.shapes.uml.ClassView,joint.shapes.uml.Generalization=joint.dia.Link.extend({defaults:{type:"uml.Generalization",attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"}}}}),joint.shapes.uml.Implementation=joint.dia.Link.extend({defaults:{type:"uml.Implementation",attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"},".connection":{"stroke-dasharray":"3,3"}}}}),joint.shapes.uml.Aggregation=joint.dia.Link.extend({defaults:{type:"uml.Aggregation",attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"white"}}}}),joint.shapes.uml.Composition=joint.dia.Link.extend({defaults:{type:"uml.Composition",attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"black"}}}}),joint.shapes.uml.Association=joint.dia.Link.extend({defaults:{type:"uml.Association"}}),joint.shapes.uml.State=joint.shapes.basic.Generic.extend({markup:['<g class="rotatable">','<g class="scalable">','<rect class="uml-state-body"/>',"</g>",'<path class="uml-state-separator"/>','<text class="uml-state-name"/>','<text class="uml-state-events"/>',"</g>"].join(""),defaults:joint.util.deepSupplement({type:"uml.State",attrs:{".uml-state-body":{width:200,height:200,rx:10,ry:10,fill:"#ecf0f1",stroke:"#bdc3c7","stroke-width":3},".uml-state-separator":{stroke:"#bdc3c7","stroke-width":2},".uml-state-name":{ref:".uml-state-body","ref-x":.5,"ref-y":5,"text-anchor":"middle",fill:"#000000","font-family":"Courier New","font-size":14},".uml-state-events":{ref:".uml-state-separator","ref-x":5,"ref-y":5,fill:"#000000","font-family":"Courier New","font-size":14}},name:"State",events:[]},joint.shapes.basic.Generic.prototype.defaults),initialize:function(){this.on({"change:name":this.updateName,"change:events":this.updateEvents,"change:size":this.updatePath},this),this.updateName(),this.updateEvents(),this.updatePath(),joint.shapes.basic.Generic.prototype.initialize.apply(this,arguments)},updateName:function(){this.attr(".uml-state-name/text",this.get("name"))},updateEvents:function(){this.attr(".uml-state-events/text",this.get("events").join("\n"))},updatePath:function(){var a="M 0 20 L "+this.get("size").width+" 20";this.attr(".uml-state-separator/d",a,{silent:!0})}}),joint.shapes.uml.StartState=joint.shapes.basic.Circle.extend({defaults:joint.util.deepSupplement({type:"uml.StartState",attrs:{circle:{fill:"#34495e",stroke:"#2c3e50","stroke-width":2,rx:1}}},joint.shapes.basic.Circle.prototype.defaults)}),joint.shapes.uml.EndState=joint.shapes.basic.Generic.extend({markup:'<g class="rotatable"><g class="scalable"><circle class="outer"/><circle class="inner"/></g></g>',defaults:joint.util.deepSupplement({type:"uml.EndState",size:{width:20,height:20},attrs:{"circle.outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#2c3e50"},"circle.inner":{transform:"translate(10, 10)",r:6,fill:"#34495e"}}},joint.shapes.basic.Generic.prototype.defaults)}),joint.shapes.uml.Transition=joint.dia.Link.extend({defaults:{type:"uml.Transition",attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z",fill:"#34495e",stroke:"#2c3e50"},".connection":{stroke:"#2c3e50"}}}}); |
@@ -1,2 +0,2 @@ | ||
/*! JointJS v0.9.3 - JavaScript diagramming library 2015-07-23 | ||
/*! JointJS v0.9.5 - JavaScript diagramming library 2015-10-12 | ||
@@ -8,10 +8,2 @@ | ||
*/ | ||
// Vectorizer. | ||
// ----------- | ||
// A tiny library for making your live easier when dealing with SVG. | ||
// The only Vectorizer dependency is the Geometry library. | ||
// Copyright © 2012 - 2015 client IO (http://client.io) | ||
(function(root, factory) { | ||
@@ -21,4 +13,4 @@ | ||
// AMD. | ||
define('V', ['g'], function(g) { | ||
// AMD. Register as an anonymous module. | ||
define(['g'], function(g) { | ||
return factory(g); | ||
@@ -46,2 +38,15 @@ }); | ||
// Vectorizer. | ||
// ----------- | ||
// A tiny library for making your live easier when dealing with SVG. | ||
// The only Vectorizer dependency is the Geometry library. | ||
// Copyright © 2012 - 2015 client IO (http://client.io) | ||
var V; | ||
var Vectorizer; | ||
V = Vectorizer = (function() { | ||
var SVGsupported = typeof window === 'object' && !!(window.SVGAngle || document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1')); | ||
@@ -67,4 +72,13 @@ | ||
// Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm). | ||
// IE would otherwise collapse all spaces into one. This is used in the text() method but it is | ||
// also exposed so that the programmer can use it in case he needs to. This is useful e.g. in tests | ||
// when you want to compare the actual DOM text content without having to add the unicode character in | ||
// the place of all spaces. | ||
function sanitizeText(text) { | ||
return (text || '').replace(/ /g, '\u00A0'); | ||
} | ||
function isObject(o) { | ||
return Object(o) === Object(o); | ||
return o === Object(o); | ||
} | ||
@@ -81,7 +95,31 @@ | ||
var svg = '<svg xmlns="' + ns.xmlns + '" xmlns:xlink="' + ns.xlink + '" version="' + SVGversion + '">' + (content || '') + '</svg>'; | ||
var parser = new DOMParser(); | ||
parser.async = false; | ||
return parser.parseFromString(svg, 'text/xml').documentElement; | ||
var xml = parseXML(svg, { async: false }); | ||
return xml.documentElement; | ||
} | ||
function parseXML(data, opt) { | ||
opt = opt || {}; | ||
var xml; | ||
try { | ||
var parser = new DOMParser(); | ||
if (typeof opt.async !== 'undefined') { | ||
parser.async = opt.async; | ||
} | ||
xml = parser.parseFromString(data, 'text/xml'); | ||
} catch (error) { | ||
xml = undefined; | ||
} | ||
if (!xml || xml.getElementsByTagName('parsererror').length) { | ||
throw new Error('Invalid XML: ' + data); | ||
} | ||
return xml; | ||
} | ||
// Create SVG element. | ||
@@ -342,8 +380,5 @@ // ------------------- | ||
box = this.node.getBBox(); | ||
// Opera returns infinite values in some cases. | ||
// Note that Infinity | 0 produces 0 as opposed to Infinity || 0. | ||
// We also have to create new object as the standard says that you can't | ||
// We are creating a new object as the standard says that you can't | ||
// modify the attributes of a bbox. | ||
box = { x: box.x | 0, y: box.y | 0, width: box.width | 0, height: box.height | 0 }; | ||
box = { x: box.x, y: box.y, width: box.width, height: box.height }; | ||
@@ -373,2 +408,5 @@ } catch (e) { | ||
// Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm). | ||
// IE would otherwise collapse all spaces into one. | ||
content = sanitizeText(content); | ||
opt = opt || {}; | ||
@@ -506,7 +544,8 @@ var lines = content.split('\n'); | ||
// Make sure the textContent is never empty. If it is, add an additional | ||
// space (an invisible character) so that following lines are correctly | ||
// Make sure the textContent is never empty. If it is, add a dummy | ||
// character and make it invisible, making the following lines correctly | ||
// relatively positioned. `dy=1em` won't work with empty lines otherwise. | ||
vLine.addClass('v-empty-line'); | ||
vLine.node.textContent = ' '; | ||
vLine.node.style.opacity = 0; | ||
vLine.node.textContent = '-'; | ||
} | ||
@@ -611,6 +650,3 @@ | ||
// Map DOM elements to `VElement`s. | ||
for (var i = 0, len = nodes.length; i < len; i++) { | ||
nodes[i] = V(nodes[i]); | ||
} | ||
return nodes; | ||
return Array.prototype.map.call(nodes, V); | ||
}, | ||
@@ -1345,3 +1381,3 @@ | ||
// Shift all the textg annotations after character `index` by `offset` positions. | ||
// Shift all the text annotations after character `index` by `offset` positions. | ||
V.shiftAnnotations = function(annotations, index, offset) { | ||
@@ -1362,3 +1398,11 @@ | ||
V.sanitizeText = sanitizeText; | ||
return V; | ||
})(); | ||
return V; | ||
})); |
568
Gruntfile.js
module.exports = function(grunt) { | ||
// Configurable area. | ||
// ------------------ | ||
require('time-grunt')(grunt); | ||
require('load-grunt-tasks')(grunt); | ||
var banner = '/*! <%= pkg.title %> v<%= pkg.version %> - <%= pkg.description %> <%= grunt.template.today("yyyy-mm-dd") %> \n\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n'; | ||
var js = { | ||
libs: { | ||
jquery: ['lib/jquery/dist/jquery.js'], | ||
backbone: ['lib/lodash/dist/lodash.js', 'lib/backbone/backbone.js'] | ||
}, | ||
helpers: { | ||
vectorizer: ['src/vectorizer.js'], | ||
geometry: ['src/geometry.js'] | ||
}, | ||
core: [ | ||
'src/core.js', 'src/joint.dia.graph.js', 'src/joint.dia.cell.js', 'src/joint.dia.element.js', 'src/joint.dia.link.js', 'src/joint.dia.paper.js', | ||
'src/core.js', | ||
'src/joint.dia.graph.js', | ||
'src/joint.dia.cell.js', | ||
'src/joint.dia.element.js', | ||
'src/joint.dia.link.js', | ||
'src/joint.dia.paper.js', | ||
'plugins/joint.shapes.basic.js', | ||
'plugins/routers/joint.routers.orthogonal.js', 'plugins/routers/joint.routers.manhattan.js', 'plugins/routers/joint.routers.metro.js', | ||
'plugins/connectors/joint.connectors.normal.js', 'plugins/connectors/joint.connectors.rounded.js', 'plugins/connectors/joint.connectors.smooth.js' | ||
'plugins/routers/joint.routers.orthogonal.js', | ||
'plugins/routers/joint.routers.manhattan.js', | ||
'plugins/routers/joint.routers.metro.js', | ||
'plugins/routers/joint.routers.oneSide.js', | ||
'plugins/connectors/joint.connectors.normal.js', | ||
'plugins/connectors/joint.connectors.rounded.js', | ||
'plugins/connectors/joint.connectors.smooth.js', | ||
'plugins/connectors/joint.connectors.jumpover.js' | ||
], | ||
geometry: ['src/geometry.js'], | ||
vectorizer: ['src/vectorizer.js'], | ||
plugins: { | ||
@@ -36,3 +42,3 @@ | ||
'layout.DirectedGraph': ['plugins/layout/DirectedGraph/lib/dagre.js', 'plugins/layout/DirectedGraph/joint.layout.DirectedGraph.js'] | ||
'layout.DirectedGraph': ['plugins/layout/DirectedGraph/joint.layout.DirectedGraph.js'] | ||
} | ||
@@ -50,5 +56,2 @@ }; | ||
// Main. | ||
// ----- | ||
function allJSPlugins() { | ||
@@ -72,9 +75,26 @@ | ||
var buildWrapper = { | ||
head: 'build/wrapper-head.js.partial', | ||
foot: 'build/wrapper-foot.js.partial' | ||
}; | ||
function allMinifiedJSPlugins() { | ||
grunt.template.addDelimiters("square", "[%", "%]"); | ||
var files = []; | ||
for (var name in js.plugins) { | ||
files.push('build/min/joint.' + name + '.min.js'); | ||
} | ||
return files; | ||
} | ||
function allMinifiedCSSPlugins() { | ||
var files = []; | ||
for (var name in css.plugins) { | ||
files.push('build/min/joint.' + name + '.min.css'); | ||
} | ||
return files; | ||
} | ||
grunt.template.addDelimiters('square', '[%', '%]'); | ||
var config = { | ||
@@ -84,140 +104,113 @@ | ||
qunit: { | ||
all: ['test/**/*.html'] | ||
}, | ||
jscs: { | ||
jointjs: [ | ||
'src/*.js', | ||
'plugins/*.js', | ||
'plugins/connectors/*.js', | ||
'plugins/routers/*.js', | ||
'plugins/layout/DirectedGraph/*.js' | ||
], | ||
options: { | ||
config: '.jscsrc' | ||
webpack: { | ||
joint: { | ||
files: { | ||
'./dist/joint.webpack-bundle.js' : './dist/joint.min.js' | ||
}, | ||
entry: './dist/joint.min.js', | ||
output: { | ||
path: './dist/', | ||
filename: 'joint.webpack-bundle.js', | ||
library: 'joint' | ||
}, | ||
resolve: { | ||
alias: { | ||
g: './geometry.min.js', | ||
V: './vectorizer.min.js' | ||
} | ||
} | ||
} | ||
}, | ||
concat: { | ||
options: { | ||
banner: '/*! <%= pkg.title %> v<%= pkg.version %> - <%= pkg.description %> <%= grunt.template.today("yyyy-mm-dd") %> \n\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n' | ||
}, | ||
dist: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
browserify: { | ||
joint: { | ||
files: { | ||
'dist/joint.js': [].concat( | ||
js.libs.jquery, js.libs.backbone, | ||
js.helpers.geometry, js.helpers.vectorizer, | ||
js.core | ||
), | ||
'dist/joint.clean.js': [].concat( | ||
js.core | ||
), | ||
'dist/joint.css': [].concat( | ||
css.core | ||
), | ||
'dist/vectorizer.js': js.helpers.vectorizer, | ||
'dist/geometry.js': js.helpers.geometry | ||
} | ||
}, | ||
distbuild: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
'dist/joint.browserify-bundle.js': 'dist/joint.min.js' | ||
}, | ||
files: { | ||
'dist/joint.clean.build.js': [ | ||
buildWrapper.head, | ||
'dist/joint.clean.js', | ||
buildWrapper.foot | ||
] | ||
} | ||
}, | ||
allinone: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
browserifyOptions: { | ||
standalone: 'joint' | ||
} | ||
}, | ||
files: { | ||
'dist/joint.all.js': [].concat( | ||
js.libs.jquery, js.libs.backbone, | ||
js.helpers.geometry, js.helpers.vectorizer, | ||
js.core, allJSPlugins() | ||
), | ||
'dist/joint.all.clean.js': [].concat( | ||
js.core, allJSPlugins() | ||
), | ||
'dist/joint.all.css': [].concat( | ||
css.core, allCSSPlugins() | ||
) | ||
} | ||
}, | ||
allinonebuild: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
files: { | ||
'dist/joint.all.clean.build.js': [ | ||
buildWrapper.head, | ||
'dist/joint.all.clean.js', | ||
buildWrapper.foot | ||
] | ||
} | ||
}, | ||
concat: { | ||
options: { | ||
banner: banner, | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
nojquery: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
geometry: { | ||
files: { | ||
'dist/joint.nojquery.js': [].concat( | ||
js.libs.backbone, | ||
js.helpers.geometry, js.helpers.vectorizer, | ||
js.core | ||
'dist/geometry.js': [].concat( | ||
['build/wrappers/geometry.head.js'], | ||
js.geometry, | ||
['build/wrappers/geometry.foot.js'] | ||
), | ||
'dist/joint.nojquery.css': [].concat( | ||
css.core | ||
'dist/geometry.min.js': [].concat( | ||
['build/wrappers/geometry.head.js'], | ||
['build/min/geometry.min.js'], | ||
['build/wrappers/geometry.foot.js'] | ||
) | ||
} | ||
}, | ||
nobackbone: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
vectorizer: { | ||
files: { | ||
'dist/joint.nobackbone.js': [].concat( | ||
js.libs.jquery, | ||
js.helpers.geometry, js.helpers.vectorizer, | ||
js.core | ||
'dist/vectorizer.js': [].concat( | ||
['build/wrappers/vectorizer.head.js'], | ||
js.vectorizer, | ||
['build/wrappers/vectorizer.foot.js'] | ||
), | ||
'dist/joint.nojquery.css': [].concat( | ||
css.core | ||
'dist/vectorizer.min.js': [].concat( | ||
['build/wrappers/vectorizer.head.js'], | ||
['build/min/vectorizer.min.js'], | ||
['build/wrappers/vectorizer.foot.js'] | ||
) | ||
} | ||
}, | ||
nojquerynobackbone: { | ||
options: { | ||
process: { | ||
delimiters: 'square' | ||
} | ||
}, | ||
joint: { | ||
files: { | ||
'dist/joint.nojquerynobackbone.js': [].concat( | ||
js.helpers.geometry, js.helpers.vectorizer, | ||
js.core | ||
'dist/joint.core.js': [].concat( | ||
['build/wrappers/joint.head.js'], | ||
js.geometry, | ||
js.vectorizer, | ||
js.core, | ||
['build/wrappers/joint.foot.js'] | ||
), | ||
'dist/joint.nojquerynobackbone.css': [].concat( | ||
'dist/joint.core.min.js': [].concat( | ||
['build/wrappers/joint.head.js'], | ||
['build/min/geometry.min.js'], | ||
['build/min/vectorizer.min.js'], | ||
['build/min/joint.min.js'], | ||
['build/wrappers/joint.foot.js'] | ||
), | ||
'dist/joint.core.css': [].concat( | ||
css.core | ||
), | ||
'dist/joint.core.min.css': [].concat( | ||
['build/min/joint.min.css'] | ||
), | ||
'dist/joint.js': [].concat( | ||
['build/wrappers/joint.head.js'], | ||
js.geometry, | ||
js.vectorizer, | ||
js.core, | ||
allJSPlugins(), | ||
['build/wrappers/joint.foot.js'] | ||
), | ||
'dist/joint.min.js': [].concat( | ||
['build/wrappers/joint.head.js'], | ||
['build/min/geometry.min.js'], | ||
['build/min/vectorizer.min.js'], | ||
['build/min/joint.min.js'], | ||
allMinifiedJSPlugins(), | ||
['build/wrappers/joint.foot.js'] | ||
), | ||
'dist/joint.css': [].concat( | ||
css.core, | ||
allCSSPlugins() | ||
), | ||
'dist/joint.min.css': [].concat( | ||
['build/min/joint.min.css'], | ||
allMinifiedCSSPlugins() | ||
) | ||
@@ -227,92 +220,67 @@ } | ||
}, | ||
uglify: { | ||
options: { | ||
report: 'min', | ||
banner: '/*! <%= pkg.title %> v<%= pkg.version %> - <%= pkg.description %> <%= grunt.template.today("yyyy-mm-dd") %> \n\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n' | ||
}, | ||
dist: { | ||
files: { | ||
'dist/joint.min.js': 'dist/joint.js', | ||
'dist/joint.clean.min.js': 'dist/joint.clean.js' | ||
} | ||
}, | ||
allinone: { | ||
files: { | ||
'dist/joint.all.min.js': 'dist/joint.all.js', | ||
'dist/joint.all.clean.min.js': 'dist/joint.all.clean.js' | ||
} | ||
}, | ||
nojquery: { | ||
files: { | ||
'dist/joint.nojquery.min.js': 'dist/joint.nojquery.js' | ||
} | ||
}, | ||
nobackbone: { | ||
files: { | ||
'dist/joint.nobackbone.min.js': 'dist/joint.nobackbone.js' | ||
} | ||
}, | ||
nojquerynobackbone: { | ||
files: { | ||
'dist/joint.nojquerynobackbone.min.js': 'dist/joint.nojquerynobackbone.js' | ||
} | ||
}, | ||
build: { | ||
files: { | ||
'dist/joint.all.clean.build.min.js': ['dist/joint.all.clean.build.js'], | ||
'dist/joint.clean.build.min.js': ['dist/joint.clean.build.js'] | ||
} | ||
} | ||
copy: { | ||
}, | ||
cssmin: { | ||
options: { | ||
report: 'min', | ||
banner: '/*! <%= pkg.title %> v<%= pkg.version %> - <%= pkg.description %> <%= grunt.template.today("yyyy-mm-dd") %> \n\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n' | ||
}, | ||
dist: { | ||
joint: { | ||
files: { | ||
'dist/joint.min.css': [].concat( | ||
'build/min/joint.min.css': [].concat( | ||
css.core | ||
) | ||
} | ||
} | ||
}, | ||
jscs: { | ||
options: { | ||
config: '.jscsrc' | ||
}, | ||
allinone: { | ||
files: { | ||
'dist/joint.all.min.css': [].concat( | ||
css.core, allCSSPlugins() | ||
) | ||
src: [ | ||
'src/*.js', | ||
'plugins/*.js', | ||
'plugins/connectors/*.js', | ||
'plugins/routers/*.js', | ||
'plugins/layout/DirectedGraph/*.js' | ||
] | ||
}, | ||
mochaTest: { | ||
server: { | ||
src: [ | ||
'test/*-nodejs/*' | ||
], | ||
options: { | ||
reporter: 'spec' | ||
} | ||
} | ||
}, | ||
qunit: { | ||
all: ['test/**/*.html'], | ||
joint: ['test/jointjs/*.html'], | ||
geometry: ['test/geometry/*.html'], | ||
vectorizer: ['test/vectorizer/*.html'] | ||
}, | ||
uglify: { | ||
geometry: { | ||
src: js.geometry, | ||
dest: 'build/min/geometry.min.js' | ||
}, | ||
nojquery: { | ||
files: { | ||
'dist/joint.nojquery.min.css': [].concat( | ||
css.core | ||
) | ||
} | ||
joint: { | ||
src: js.core, | ||
dest: 'build/min/joint.min.js' | ||
}, | ||
nobackbone: { | ||
files: { | ||
'dist/joint.nojquery.min.css': [].concat( | ||
css.core | ||
) | ||
} | ||
}, | ||
nojquerynobackbone: { | ||
files: { | ||
'dist/joint.nojquerynobackbone.min.css': [].concat( | ||
css.core | ||
) | ||
} | ||
vectorizer: { | ||
src: js.vectorizer, | ||
dest: 'build/min/vectorizer.min.js' | ||
} | ||
}, | ||
browserify: { | ||
build: { | ||
files: { | ||
'dist/joint.clean.browserify-bundle.js': 'dist/joint.clean.build.js' | ||
}, | ||
options: { | ||
browserifyOptions: { | ||
standalone: 'joint' | ||
} | ||
} | ||
watch: { | ||
joint: { | ||
files: [].concat( | ||
js.geometry, | ||
js.vectorizer, | ||
js.core, | ||
allJSPlugins(), | ||
css.core, | ||
allCSSPlugins() | ||
), | ||
tasks: ['build'] | ||
} | ||
@@ -322,3 +290,67 @@ } | ||
// Create a separate target for all the plugins. | ||
function enableCodeCoverage() { | ||
// Replace all qunit configurations with the 'urls' method. | ||
// Append all URLs with ?coverage=true&grunt | ||
// This will run all qunit tests with test coverage enabled and report results back to grunt. | ||
var reporter = grunt.option('reporter') || 'lcov'; | ||
// Serve up the test files via an express app. | ||
var express = require('express'); | ||
var serveStatic = require('serve-static'); | ||
var app = express(); | ||
var host = 'localhost'; | ||
var port = 3000; | ||
app.use('/', serveStatic(__dirname)); | ||
app.listen(port, host); | ||
var name, files; | ||
for (name in config.qunit) { | ||
// Resolve the paths for all files referenced in the task. | ||
files = grunt.file.expand(config.qunit[name]); | ||
config.qunit[name] = { options: { urls: [] } }; | ||
files.forEach(function(file) { | ||
var url = 'http://' + host + ':' + port + '/' + file + '?coverage=true&reporter=' + reporter; | ||
config.qunit[name].options.urls.push(url); | ||
}); | ||
} | ||
var reporterToFileExtension = { | ||
lcov: 'info' | ||
}; | ||
var reports = []; | ||
grunt.event.on('qunit.report', function(data) { | ||
reports.push(data); | ||
}); | ||
var fs = require('fs'); | ||
process.on('exit', function() { | ||
var ext = reporterToFileExtension[reporter]; | ||
var outputFile = grunt.option('output') || 'coverage' + (ext ? '.' + ext : ''); | ||
var data; | ||
switch (reporter) { | ||
case 'lcov': | ||
data = reports.join('\n'); | ||
break; | ||
} | ||
fs.writeFileSync(outputFile, data); | ||
}); | ||
} | ||
// Create targets for all the plugins. | ||
Object.keys(js.plugins).forEach(function(name) { | ||
@@ -330,9 +362,22 @@ | ||
config.concat[name].files['dist/joint.' + name + '.js'] = js.plugins[name]; | ||
config.uglify[name].files['dist/joint.' + name + '.min.js'] = js.plugins[name]; | ||
config.uglify[name].files['build/min/joint.' + name + '.min.js'] = js.plugins[name]; | ||
config.copy[name] = { files: [] }; | ||
config.copy[name].files.push({ | ||
nonull: true, | ||
src: ['build/min/joint.' + name + '.min.js'], | ||
dest: 'dist/joint.' + name + '.min.js' | ||
}); | ||
if (css.plugins[name]) { | ||
config.cssmin[name] = { files: {} }; | ||
config.concat[name].files['dist/joint.' + name + '.css'] = css.plugins[name]; | ||
config.cssmin[name] = { files: {} }; | ||
config.cssmin[name].files['dist/joint.' + name + '.min.css'] = css.plugins[name]; | ||
config.cssmin[name].files['build/min/joint.' + name + '.min.css'] = css.plugins[name]; | ||
config.copy[name].files.push({ | ||
nonull: true, | ||
src: ['build/min/joint.' + name + '.min.css'], | ||
dest: 'dist/joint.' + name + '.min.css' | ||
}); | ||
} | ||
@@ -343,39 +388,66 @@ }); | ||
grunt.loadNpmTasks('grunt-browserify'); | ||
grunt.loadNpmTasks('grunt-contrib-concat'); | ||
grunt.loadNpmTasks('grunt-contrib-uglify'); | ||
grunt.loadNpmTasks('grunt-contrib-cssmin'); | ||
grunt.loadNpmTasks('grunt-contrib-qunit'); | ||
grunt.loadNpmTasks('grunt-jscs'); | ||
var allPluginTasks = { | ||
concat: [], | ||
copy: [], | ||
cssmin: [], | ||
uglify: [] | ||
}; | ||
// Default task(s). | ||
grunt.registerTask('default', ['concat:dist', 'uglify:dist', 'cssmin:dist']); | ||
grunt.registerTask('allinone', ['concat:allinone', 'uglify:allinone', 'cssmin:allinone']); | ||
// Separate tasks for all the plugins. | ||
// Register tasks for all the plugins. | ||
Object.keys(js.plugins).forEach(function(name) { | ||
grunt.registerTask(name, ['concat:' + name, 'uglify:' + name].concat(css.plugins[name] ? ['cssmin:' + name] : [])); | ||
}); | ||
var pluginTasks = [ | ||
'newer:concat:' + name, | ||
'newer:uglify:' + name | ||
]; | ||
// One task that build everything but separately. Compare this to the 'all' task that builds | ||
// everything into one file (for JS and CSS). | ||
allPluginTasks.concat.push('newer:concat:' + name); | ||
allPluginTasks.uglify.push('newer:uglify:' + name); | ||
var allTasks = [ | ||
'concat:dist', 'uglify:dist', 'cssmin:dist', | ||
'concat:nojquery', 'uglify:nojquery', 'cssmin:nojquery', | ||
'concat:nobackbone', 'uglify:nobackbone', 'cssmin:nobackbone', | ||
'concat:nojquerynobackbone', 'uglify:nojquerynobackbone', 'cssmin:nojquerynobackbone' | ||
]; | ||
if (css.plugins[name]) { | ||
pluginTasks.push('newer:cssmin:' + name); | ||
allPluginTasks.cssmin.push('newer:cssmin:' + name); | ||
} | ||
Object.keys(js.plugins).forEach(function(name) { | ||
pluginTasks.push('newer:copy:' + name); | ||
allPluginTasks.copy.push('newer:copy:' + name); | ||
allTasks = allTasks.concat(['concat:' + name, 'uglify:' + name].concat(css.plugins[name] ? ['cssmin:' + name] : [])); | ||
grunt.registerTask(name, pluginTasks); | ||
}); | ||
grunt.registerTask('all', allTasks); | ||
if (grunt.option('coverage')) { | ||
enableCodeCoverage(); | ||
} | ||
grunt.registerTask('build', [ | ||
'concat:distbuild', 'concat:allinonebuild', 'uglify:build', 'browserify' | ||
grunt.registerTask('concat:plugins', allPluginTasks.concat); | ||
grunt.registerTask('copy:plugins', allPluginTasks.copy); | ||
grunt.registerTask('cssmin:plugins', allPluginTasks.cssmin); | ||
grunt.registerTask('uglify:plugins', allPluginTasks.uglify); | ||
grunt.registerTask('build:plugins', [ | ||
'uglify:plugins', | ||
'cssmin:plugins', | ||
'concat:plugins', | ||
'copy:plugins' | ||
]); | ||
grunt.registerTask('build:joint', [ | ||
'build:plugins', | ||
'newer:uglify:geometry', | ||
'newer:uglify:vectorizer', | ||
'newer:uglify:joint', | ||
'newer:cssmin:joint', | ||
'newer:concat:geometry', | ||
'newer:concat:vectorizer', | ||
'newer:concat:joint' | ||
]); | ||
grunt.registerTask('build', ['build:joint']); | ||
grunt.registerTask('all', ['build', 'newer:browserify', 'newer:webpack']); | ||
grunt.registerTask('test:server', ['mochaTest:server']); | ||
grunt.registerTask('test:client', ['qunit:all', 'jscs']); | ||
grunt.registerTask('test', ['test:server', 'test:client']); | ||
grunt.registerTask('default', ['build', 'watch']); | ||
}; |
@@ -1,2 +0,1 @@ | ||
// Use the build file that includes all the plugins, but none of the dependencies: | ||
module.exports = require('./dist/joint.all.clean.build'); | ||
module.exports = require('./dist/joint'); |
{ | ||
"name": "blanket", | ||
"description": "seamless js code coverage", | ||
"version": "1.1.5", | ||
"version": "1.1.7", | ||
"homepage": "https://github.com/alex-seville/blanket", | ||
@@ -26,23 +26,28 @@ "author": { | ||
"engines": { | ||
"node": "0.10.7" | ||
"node": ">=0.10.7" | ||
}, | ||
"dependencies": { | ||
"esprima": "~1.0.2", | ||
"falafel": "~0.1.6", | ||
"xtend": "~2.0.3" | ||
"esprima": "~1.2.2", | ||
"falafel": "~0.3.1", | ||
"xtend": "~3.0.0" | ||
}, | ||
"devDependencies": { | ||
"async": "~0.9.0", | ||
"coffee-script": "~1.7.1", | ||
"grunt": "~0.4.5", | ||
"grunt-contrib-clean": "~0.5.0", | ||
"grunt-contrib-concat": "~0.4.0", | ||
"grunt-contrib-jshint": "~0.10.0", | ||
"grunt-contrib-uglify": "~0.4.0", | ||
"load-grunt-tasks": "~0.4.0", | ||
"mocha": "~1.20.1", | ||
"phantomjs": "1.8.2-0", | ||
"coffee-script": "1.4.0", | ||
"mocha": "1.7.4", | ||
"requirejs": "~2.1.4", | ||
"requirejs": "~2.1.14", | ||
"travis-cov": "*", | ||
"grunt": "0.3.17" | ||
"uglify-save-license": "~0.4.1" | ||
}, | ||
"scripts": { | ||
"test": "grunt --verbose blanketTest", | ||
"travis-cov": { | ||
"threshold": 70, | ||
"removeKey": "branchFcn" | ||
}, | ||
"test": "grunt --verbose blanketTest" | ||
}, | ||
"config": { | ||
"blanket": { | ||
@@ -53,3 +58,10 @@ "pattern": "test", | ||
}, | ||
"loader": "./node-loaders/coffee-script" | ||
"loader": "./node-loaders/coffee-script", | ||
"data-cover-reporter-options": { | ||
"shortnames": true | ||
} | ||
}, | ||
"travis-cov": { | ||
"threshold": 70, | ||
"removeKey": "branchFcn" | ||
} | ||
@@ -56,0 +68,0 @@ }, |
@@ -11,3 +11,3 @@ # Blanket.js | ||
[![Build Status](https://travis-ci.org/alex-seville/blanket.png)](https://travis-ci.org/alex-seville/blanket) | ||
[![Build Status](https://travis-ci.org/alex-seville/blanket.svg)](https://travis-ci.org/alex-seville/blanket) | ||
@@ -17,3 +17,3 @@ * [Getting Started](#getting-started) | ||
* [Mechanism](#mechanism) | ||
* [Grunt-Blanket](#grunt-blanket) | ||
* [Grunt Integration](#grunt-integration) | ||
* [Compatibility & Features List](#compatibility-and-features-list) | ||
@@ -27,3 +27,3 @@ * [Roll Your Own](#roll-your-own) | ||
**NOTE:** Blanket.js will throw XHR cross domain errors if run with the file:// protocol. See [Special Features Guide](https://github.com/alex-seville/blanket/blob/master/docs/special_features.md) for more details and workarounds. | ||
**NOTE:** Blanket.js will throw XHR cross domain errors if run with the file:// protocol. See [Special Features Guide](docs/special_features.md) for more details and workarounds. | ||
@@ -36,12 +36,16 @@ | ||
**Browser** | ||
* [Getting Started](https://github.com/alex-seville/blanket/blob/master/docs/getting_started_browser.md) (Basic QUnit usage) | ||
* [Intermediate](https://github.com/alex-seville/blanket/blob/master/docs/intermediate_browser.md) (Other test runners, global options) | ||
* [Advanced](https://github.com/alex-seville/blanket/blob/master/docs/advanced_browser.md) (writing your own reporters/adapters) | ||
* [Special Features Guide](https://github.com/alex-seville/blanket/blob/master/docs/special_features.md) | ||
* [Getting Started](docs/getting_started_browser.md) (Basic QUnit usage) | ||
* [Intermediate](docs/intermediate_browser.md) (Other test runners, global options) | ||
* [Advanced](docs/advanced_browser.md) (writing your own reporters/adapters) | ||
* [Special Features Guide](docs/special_features.md) | ||
**Node** | ||
* [Getting Started](https://github.com/alex-seville/blanket/blob/master/docs/getting_started_node.md) (basic mocha setup) | ||
* [Intermediate](https://github.com/alex-seville/blanket/blob/master/docs/intermediate_node.md) (mocha testrunner, travis-ci reporter) | ||
* [Getting Started](docs/getting_started_node.md) (basic mocha setup) | ||
* [Intermediate](docs/intermediate_node.md) (mocha testrunner, travis-ci reporter) | ||
* [Intermediate 2](docs/intermediate_node_2.md) (mocha, htmlcov, package.json setup) | ||
**Configuration** | ||
* [Options](docs/options.md) (Browser and Node) | ||
## Philosophy | ||
@@ -64,12 +68,24 @@ | ||
1. Loading your source files using a modified [RequireJS](http://requirejs.org/)/[Require](http://nodejs.org/api/globals.html#globals_require) script | ||
1. Loading your source files | ||
2. Parsing the code using [Esprima](http://esprima.org) and [node-falafel](https://github.com/substack/node-falafel), and instrumenting the file by adding code tracking lines. | ||
3. Connecting to hooks in the test runner to output the coverage details after the tests have completed. | ||
## Grunt-blanket | ||
## Grunt Integration | ||
You've got a few options for using Grunt with Blanket: | ||
**grunt-blanket** | ||
A Grunt plugin has been created to allow you to use Blanket like a "traditional" code coverage tool (creating instrumented copies of physical files, as opposed to live-instrumenting). | ||
The plugin runs as a standlone project and can be found [here](https://github.com/alex-seville/grunt-blanket). | ||
**grunt-blanket-qunit** | ||
Runs the QUnit-based Blanket report headlessly using PhantomJS. Results are displayed on the console, and the task will cause Grunt to fail if any of your configured coverage thresholds are not met. Minimum code coverage thresholds can be configured per-file, per-module, and globally. | ||
See: | ||
* [Plugin Repo](https://github.com/ModelN/grunt-blanket-qunit) | ||
* [Blog Walkthrough](http://www.geekdave.com/2013/07/20/code-coverage-enforcement-for-qunit-using-grunt-and-blanket/) | ||
## Compatibility and Features List | ||
@@ -76,0 +92,0 @@ |
@@ -11,3 +11,6 @@ { | ||
], | ||
"version": "0.9.4", | ||
"scripts": { | ||
"test": "make test" | ||
}, | ||
"version": "0.9.5", | ||
"homepage": "http://jointjs.com", | ||
@@ -47,9 +50,11 @@ "author": { | ||
"jquery": "2.1.3", | ||
"lodash": "~2.2.1", | ||
"graphlib": "1.0.1", | ||
"dagre": "0.7.1" | ||
"lodash": "3.10.1", | ||
"graphlib": "1.0.7", | ||
"dagre": "0.7.4" | ||
}, | ||
"devDependencies": { | ||
"express": "4.13.0", | ||
"grunt": "~0.4.1", | ||
"grunt-browserify": "3.7.0", | ||
"grunt-contrib-copy": "0.8.0", | ||
"grunt-contrib-concat": "~0.3.0", | ||
@@ -59,6 +64,13 @@ "grunt-contrib-uglify": "~0.2.0", | ||
"grunt-contrib-qunit": "~0.3.0", | ||
"grunt-contrib-watch": "~0.5.3", | ||
"grunt-jscs": "~1.6.0", | ||
"mocha": "~1.13.0", | ||
"should": "~2.0.2" | ||
"grunt-mocha-test": "0.12.7", | ||
"grunt-newer": "1.1.0", | ||
"grunt-webpack": "^1.0.11", | ||
"load-grunt-tasks": "3.2.0", | ||
"mocha": "1.21.5", | ||
"serve-static": "1.10.0", | ||
"should": "~2.0.2", | ||
"time-grunt": "^1.2.1" | ||
} | ||
} |
@@ -270,6 +270,6 @@ // JointJS library. | ||
// var portTextSelector = portSelector + '>text'; | ||
// var portCircleSelector = portSelector + '>circle'; | ||
// var portBodySelector = portSelector + '>.port-body'; | ||
// | ||
// attrs[portTextSelector] = { text: portName }; | ||
// attrs[portCircleSelector] = { port: { id: portName || _.uniqueId(type) , type: type } }; | ||
// attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } }; | ||
// attrs[portSelector] = { ref: 'rect', 'ref-y': (index + 0.5) * (1 / total) }; | ||
@@ -342,3 +342,3 @@ // | ||
return selector + '>g:nth-child(' + (index + 1) + ')>circle'; | ||
return selector + '>g:nth-child(' + (index + 1) + ')>.port-body'; | ||
} | ||
@@ -345,0 +345,0 @@ }; |
@@ -142,2 +142,3 @@ // JointJS library. | ||
type: 'pn.Link', | ||
attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} | ||
@@ -144,0 +145,0 @@ |
@@ -0,1 +1,12 @@ | ||
if (typeof exports === 'object') { | ||
var graphlib = require('graphlib'); | ||
var dagre = require('dagre'); | ||
} | ||
// In the browser, these variables are set to undefined because of JavaScript hoisting. | ||
// In that case, should grab them from the window object. | ||
graphlib = graphlib || (typeof window !== 'undefined' && window.graphlib); | ||
dagre = dagre || (typeof window !== 'undefined' && window.dagre); | ||
// create graphlib.Graph from existing joint.dia.Graph | ||
@@ -153,3 +164,4 @@ joint.dia.Graph.prototype.toGraphLib = function(opt) { | ||
.sortBy(function(cluster) { return -cluster.getAncestors().length; }) | ||
.invoke('fitEmbeds', { padding: opt.clusterPadding }); | ||
.invoke('fitEmbeds', { padding: opt.clusterPadding }) | ||
.value(); | ||
} | ||
@@ -156,0 +168,0 @@ |
@@ -1,2 +0,2 @@ | ||
joint.routers.manhattan = (function() { | ||
joint.routers.manhattan = (function(g, _) { | ||
@@ -13,5 +13,2 @@ 'use strict'; | ||
// tells how to divide the paper when creating the elements map | ||
mapGridSize: 100, | ||
// should be source or target not to be consider as an obstacle | ||
@@ -25,3 +22,3 @@ excludeEnds: [], // 'source', 'target' | ||
// fallback route | ||
maximumLoops: 500, | ||
maximumLoops: 2000, | ||
@@ -43,3 +40,3 @@ // possible starting directions from an element | ||
// maximum change of the direction | ||
maxAllowedDirectionChange: 1, | ||
maxAllowedDirectionChange: 90, | ||
@@ -75,11 +72,9 @@ // padding applied on the element bounding boxes | ||
return [0, this.step / 2, this.step]; | ||
return { | ||
0: 0, | ||
45: this.step / 2, | ||
90: this.step / 2 | ||
}; | ||
}, | ||
// heurestic method to determine the distance between two points | ||
estimateCost: function(from, to) { | ||
return from.manhattanDistance(to); | ||
}, | ||
// a simple route used in situations, when main routing method fails | ||
@@ -91,5 +86,3 @@ // (exceed loops, inaccessible). | ||
var prevDirIndexes = opts.prevDirIndexes || {}; | ||
var point = (prevDirIndexes[from] || 0) % 2 | ||
var point = ((opts.previousDirAngle || 0) % 180 === 0) | ||
? g.point(from.x, to.y) | ||
@@ -106,2 +99,136 @@ : g.point(to.x, from.y); | ||
// Map of obstacles | ||
// Helper structure to identify whether a point lies in an obstacle. | ||
function ObstacleMap(opt) { | ||
this.map = {}; | ||
this.options = opt; | ||
// tells how to divide the paper when creating the elements map | ||
this.mapGridSize = 100; | ||
} | ||
ObstacleMap.prototype.build = function(graph, link) { | ||
var opt = this.options; | ||
// source or target element could be excluded from set of obstacles | ||
var excludedEnds = _.chain(opt.excludeEnds) | ||
.map(link.get, link) | ||
.pluck('id') | ||
.map(graph.getCell, graph).value(); | ||
// Exclude any embedded elements from the source and the target element. | ||
var excludedAncestors = []; | ||
var source = graph.getCell(link.get('source').id); | ||
if (source) { | ||
excludedAncestors = _.union(excludedAncestors, _.map(source.getAncestors(), 'id')); | ||
}; | ||
var target = graph.getCell(link.get('target').id); | ||
if (target) { | ||
excludedAncestors = _.union(excludedAncestors, _.map(target.getAncestors(), 'id')); | ||
} | ||
// builds a map of all elements for quicker obstacle queries (i.e. is a point contained | ||
// in any obstacle?) (a simplified grid search) | ||
// The paper is divided to smaller cells, where each of them holds an information which | ||
// elements belong to it. When we query whether a point is in an obstacle we don't need | ||
// to go through all obstacles, we check only those in a particular cell. | ||
var mapGridSize = this.mapGridSize; | ||
_.chain(graph.getElements()) | ||
// remove source and target element if required | ||
.difference(excludedEnds) | ||
// remove all elements whose type is listed in excludedTypes array | ||
.reject(function(element) { | ||
// reject any element which is an ancestor of either source or target | ||
return _.contains(opt.excludeTypes, element.get('type')) || _.contains(excludedAncestors, element.id); | ||
}) | ||
// change elements (models) to their bounding boxes | ||
.invoke('getBBox') | ||
// expand their boxes by specific padding | ||
.invoke('moveAndExpand', opt.paddingBox) | ||
// build the map | ||
.foldl(function(map, bbox) { | ||
var origin = bbox.origin().snapToGrid(mapGridSize); | ||
var corner = bbox.corner().snapToGrid(mapGridSize); | ||
for (var x = origin.x; x <= corner.x; x += mapGridSize) { | ||
for (var y = origin.y; y <= corner.y; y += mapGridSize) { | ||
var gridKey = x + '@' + y; | ||
map[gridKey] = map[gridKey] || []; | ||
map[gridKey].push(bbox); | ||
} | ||
} | ||
return map; | ||
}, this.map).value(); | ||
return this; | ||
}; | ||
ObstacleMap.prototype.isPointAccessible = function(point) { | ||
var mapKey = point.clone().snapToGrid(this.mapGridSize).toString(); | ||
return _.every(this.map[mapKey], function(obstacle) { | ||
return !obstacle.containsPoint(point); | ||
}); | ||
}; | ||
// Sorted Set | ||
// Set of items sorted by given value. | ||
function SortedSet() { | ||
this.items = []; | ||
this.hash = {}; | ||
this.values = {}; | ||
this.OPEN = 1; | ||
this.CLOSE = 2; | ||
} | ||
SortedSet.prototype.add = function(item, value) { | ||
if (this.hash[item]) { | ||
// item removal | ||
this.items.splice(this.items.indexOf(item), 1); | ||
} else { | ||
this.hash[item] = this.OPEN; | ||
} | ||
this.values[item] = value; | ||
var index = _.sortedIndex(this.items, item, function(i) { | ||
return this.values[i]; | ||
}, this); | ||
this.items.splice(index, 0, item); | ||
}; | ||
SortedSet.prototype.remove = function(item) { | ||
this.hash[item] = this.CLOSE; | ||
}; | ||
SortedSet.prototype.isOpen = function(item) { | ||
return this.hash[item] === this.OPEN; | ||
}; | ||
SortedSet.prototype.isClose = function(item) { | ||
return this.hash[item] === this.CLOSE; | ||
}; | ||
SortedSet.prototype.isEmpty = function() { | ||
return this.items.length === 0; | ||
}; | ||
SortedSet.prototype.pop = function() { | ||
var item = this.items.shift(); | ||
this.remove(item); | ||
return item; | ||
}; | ||
// reconstructs a route by concating points with their parents | ||
@@ -131,17 +258,15 @@ function reconstructRoute(parents, point) { | ||
return route; | ||
}; | ||
} | ||
// find points around the rectangle taking given directions in the account | ||
function getRectPoints(bbox, directionList, opts) { | ||
function getRectPoints(bbox, directionList, opt) { | ||
var step = opts.step; | ||
var step = opt.step; | ||
var center = bbox.center(); | ||
var startPoints = _.chain(opt.directionMap).pick(directionList).map(function(direction) { | ||
var startPoints = _.chain(opts.directionMap).pick(directionList).map(function(direction) { | ||
var x = direction.x * bbox.width / 2; | ||
var y = direction.y * bbox.height / 2; | ||
var point = g.point(center).offset(x, y).snapToGrid(step); | ||
var point = center.clone().offset(x, y); | ||
@@ -153,3 +278,3 @@ if (bbox.containsPoint(point)) { | ||
return point; | ||
return point.snapToGrid(step); | ||
@@ -162,179 +287,132 @@ }).value(); | ||
// returns a direction index from start point to end point | ||
function getDirection(start, end, dirLen) { | ||
function getDirectionAngle(start, end, dirLen) { | ||
var dirAngle = 360 / dirLen; | ||
var q = 360 / dirLen; | ||
return Math.floor(g.normalizeAngle(start.theta(end) + q / 2) / q) * q; | ||
} | ||
var q = Math.floor(start.theta(end) / dirAngle); | ||
function getDirectionChange(angle1, angle2) { | ||
return dirLen - q; | ||
var dirChange = Math.abs(angle1 - angle2); | ||
return dirChange > 180 ? 360 - dirChange : dirChange; | ||
} | ||
// heurestic method to determine the distance between two points | ||
function estimateCost(from, endPoints) { | ||
var min = Infinity; | ||
for (var i = 0, len = endPoints.length; i < len; i++) { | ||
var cost = from.manhattanDistance(endPoints[i]); | ||
if (cost < min) min = cost; | ||
}; | ||
return min; | ||
} | ||
// finds the route between to points/rectangles implementing A* alghoritm | ||
function findRoute(start, end, map, opt) { | ||
var startDirections = opt.reversed ? opt.endDirections : opt.startDirections; | ||
var endDirections = opt.reversed ? opt.startDirections : opt.endDirections; | ||
var step = opt.step; | ||
var startPoints, endPoints; | ||
var startCenter, endCenter; | ||
// set of points we start pathfinding from | ||
var startSet = start instanceof g.rect | ||
? getRectPoints(start, startDirections, opt) | ||
: [start]; | ||
if (start instanceof g.rect) { | ||
startPoints = getRectPoints(start, opt.startDirections, opt); | ||
startCenter = start.center(); | ||
} else { | ||
startCenter = start.clone().snapToGrid(step); | ||
startPoints = [start]; | ||
} | ||
// set of points we want the pathfinding to finish at | ||
var endSet = end instanceof g.rect | ||
? getRectPoints(end, endDirections, opt) | ||
: [end]; | ||
if (end instanceof g.rect) { | ||
endPoints = getRectPoints(end, opt.endDirections, opt); | ||
endCenter = end.center(); | ||
} else { | ||
endCenter = end.clone().snapToGrid(step); | ||
endPoints = [end]; | ||
} | ||
var startCenter = startSet.length > 1 ? start.center() : startSet[0]; | ||
var endCenter = endSet.length > 1 ? end.center() : endSet[0]; | ||
// take into account only accessible end points | ||
startPoints = _.filter(startPoints, map.isPointAccessible, map); | ||
endPoints = _.filter(endPoints, map.isPointAccessible, map); | ||
// take into account only accessible end points | ||
var endPoints = _.filter(endSet, function(point) { | ||
// Check if there is a accessible end point. | ||
// We would have to use a fallback route otherwise. | ||
if (startPoints.length > 0 && endPoints.length > 0) { | ||
var mapKey = g.point(point).snapToGrid(opt.mapGridSize).toString(); | ||
// The set of tentative points to be evaluated, initially containing the start points. | ||
var openSet = new SortedSet(); | ||
// Keeps reference to a point that is immediate predecessor of given element. | ||
var parents = {}; | ||
// Cost from start to a point along best known path. | ||
var costs = {}; | ||
var accesible = _.every(map[mapKey], function(obstacle) { | ||
return !obstacle.containsPoint(point); | ||
_.each(startPoints, function(point) { | ||
var key = point.toString(); | ||
openSet.add(key, estimateCost(point, endPoints)); | ||
costs[key] = 0; | ||
}); | ||
return accesible; | ||
}); | ||
if (endPoints.length) { | ||
var step = opt.step; | ||
var penalties = opt.penalties; | ||
// choose the end point with the shortest estimated path cost | ||
var endPoint = _.chain(endPoints).invoke('snapToGrid', step).min(function(point) { | ||
return opt.estimateCost(startCenter, point); | ||
}).value(); | ||
var parents = {}; | ||
var costFromStart = {}; | ||
var totalCost = {}; | ||
// directions | ||
var dir, dirChange; | ||
var dirs = opt.directions; | ||
var dirLen = dirs.length; | ||
var dirHalfLen = dirLen / 2; | ||
var dirIndexes = opt.previousDirIndexes || {}; | ||
var loopsRemain = opt.maximumLoops; | ||
var endPointsKeys = _.invoke(endPoints, 'toString'); | ||
// The set of point already evaluated. | ||
var closeHash = {}; // keeps only information whether a point was evaluated' | ||
// The set of tentative points to be evaluated, initially containing the start points | ||
var openHash = {}; // keeps only information whether a point is to be evaluated' | ||
var openSet = _.chain(startSet).invoke('snapToGrid', step).each(function(point) { | ||
var key = point.toString(); | ||
costFromStart[key] = 0; // Cost from start along best known path. | ||
totalCost[key] = opt.estimateCost(point, endPoint); | ||
dirIndexes[key] = dirIndexes[key] || getDirection(startCenter, point, dirLen); | ||
openHash[key] = true; | ||
}).map(function(point) { | ||
return point.toString(); | ||
}).sortBy(function(pointKey) { | ||
return totalCost[pointKey]; | ||
}).value(); | ||
var loopCounter = opt.maximumLoops; | ||
var maxAllowedDirectionChange = opt.maxAllowedDirectionChange; | ||
// main route finding loop | ||
while (openSet.length && loopCounter--) { | ||
while (!openSet.isEmpty() && loopsRemain > 0) { | ||
var currentKey = openSet[0]; | ||
// remove current from the open list | ||
var currentKey = openSet.pop(); | ||
var currentPoint = g.point(currentKey); | ||
var currentDist = costs[currentKey]; | ||
var previousDirAngle = currentDirAngle; | ||
var currentDirAngle = parents[currentKey] | ||
? getDirectionAngle(parents[currentKey], currentPoint, dirLen) | ||
: opt.previousDirAngle != null ? opt.previousDirAngle : getDirectionAngle(startCenter, currentPoint, dirLen); | ||
if (endPoint.equals(currentPoint)) { | ||
opt.previousDirIndexes = _.pick(dirIndexes, currentKey); | ||
return reconstructRoute(parents, currentPoint); | ||
// Check if we reached any endpoint | ||
if (endPointsKeys.indexOf(currentKey) >= 0) { | ||
// We don't want to allow route to enter the end point in opposite direction. | ||
dirChange = getDirectionChange(currentDirAngle, getDirectionAngle(currentPoint, endCenter, dirLen)); | ||
if (currentPoint.equals(endCenter) || dirChange < 180) { | ||
opt.previousDirAngle = currentDirAngle; | ||
return reconstructRoute(parents, currentPoint); | ||
} | ||
} | ||
// remove current from the open list | ||
openSet.splice(0, 1); | ||
openHash[neighborKey] = null; | ||
// Go over all possible directions and find neighbors. | ||
for (var i = 0; i < dirLen; i++) { | ||
// add current to the close list | ||
closeHash[neighborKey] = true; | ||
var currentDirIndex = dirIndexes[currentKey]; | ||
var currentDist = costFromStart[currentKey]; | ||
for (var dirIndex = 0; dirIndex < dirLen; dirIndex++) { | ||
var dirChange = Math.abs(dirIndex - currentDirIndex); | ||
if (dirChange > dirHalfLen) { | ||
dirChange = dirLen - dirChange; | ||
} | ||
dir = dirs[i]; | ||
dirChange = getDirectionChange(currentDirAngle, dir.angle); | ||
// if the direction changed rapidly don't use this point | ||
if (dirChange > maxAllowedDirectionChange) { | ||
if (dirChange > opt.maxAllowedDirectionChange) { | ||
continue; | ||
} | ||
var dir = dirs[dirIndex]; | ||
var neighborPoint = g.point(currentPoint).offset(dir.offsetX, dir.offsetY); | ||
var neighborPoint = currentPoint.clone().offset(dir.offsetX, dir.offsetY); | ||
var neighborKey = neighborPoint.toString(); | ||
if (closeHash[neighborKey]) { | ||
// Closed points from the openSet were already evaluated. | ||
if (openSet.isClose(neighborKey) || !map.isPointAccessible(neighborPoint)) { | ||
continue; | ||
} | ||
// is point accesible - no obstacle in the way | ||
// The current direction is ok to proccess. | ||
var costFromStart = currentDist + dir.cost + opt.penalties[dirChange]; | ||
var mapKey = g.point(neighborPoint).snapToGrid(opt.mapGridSize).toString(); | ||
var isAccesible = _.every(map[mapKey], function(obstacle) { | ||
return !obstacle.containsPoint(neighborPoint); | ||
}); | ||
if (!isAccesible) { | ||
continue; | ||
} | ||
var inOpenSet = _.has(openHash, neighborKey); | ||
var costToNeighbor = currentDist + dir.cost; | ||
if (!inOpenSet || costToNeighbor < costFromStart[neighborKey]) { | ||
if (!openSet.isOpen(neighborKey) || costFromStart < costs[neighborKey]) { | ||
// neighbor point has not been processed yet or the cost of the path | ||
// from start is lesser than previously calcluated. | ||
parents[neighborKey] = currentPoint; | ||
dirIndexes[neighborKey] = dirIndex; | ||
costFromStart[neighborKey] = costToNeighbor; | ||
totalCost[neighborKey] = costToNeighbor + | ||
opt.estimateCost(neighborPoint, endPoint) + | ||
penalties[dirChange]; | ||
if (!inOpenSet) { | ||
var openIndex = _.sortedIndex(openSet, neighborKey, function(openKey) { | ||
return totalCost[openKey]; | ||
}); | ||
openSet.splice(openIndex, 0, neighborKey); | ||
openHash[neighborKey] = true; | ||
} | ||
costs[neighborKey] = costFromStart; | ||
openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints)); | ||
}; | ||
}; | ||
loopsRemain--; | ||
} | ||
@@ -346,8 +424,7 @@ } | ||
return opt.fallbackRoute(startCenter, endCenter, opt); | ||
}; | ||
} | ||
// initiation of the route finding | ||
function router(oldVertices, opt) { | ||
// resolve some of the options | ||
function resolveOptions(opt) { | ||
// resolve some of the options | ||
opt.directions = _.result(opt, 'directions'); | ||
@@ -357,96 +434,33 @@ opt.penalties = _.result(opt, 'penalties'); | ||
// enable/disable linkView perpendicular option | ||
this.options.perpendicular = !!opt.perpendicular; | ||
_.each(opt.directions, function(direction) { | ||
// As route changes its shape rapidly when we start finding route from different point | ||
// it's necessary to start from the element that was not interacted with | ||
// (the position was changed) at very last. | ||
var reverseRouting = opt.reversed = (this.lastEndChange === 'source'); | ||
var point1 = new g.point(0, 0); | ||
var point2 = new g.point(direction.offsetX, direction.offsetY); | ||
var angle = g.normalizeAngle(point1.theta(point2)); | ||
var sourceBBox = reverseRouting ? g.rect(this.targetBBox) : g.rect(this.sourceBBox); | ||
var targetBBox = reverseRouting ? g.rect(this.sourceBBox) : g.rect(this.targetBBox); | ||
direction.angle = angle; | ||
}); | ||
} | ||
// expand boxes by specific padding | ||
sourceBBox.moveAndExpand(opt.paddingBox); | ||
targetBBox.moveAndExpand(opt.paddingBox); | ||
// initiation of the route finding | ||
function router(vertices, opt) { | ||
// building an elements map | ||
resolveOptions(opt); | ||
var link = this.model; | ||
var graph = this.paper.model; | ||
// enable/disable linkView perpendicular option | ||
this.options.perpendicular = !!opt.perpendicular; | ||
// source or target element could be excluded from set of obstacles | ||
var excludedEnds = _.chain(opt.excludeEnds) | ||
.map(link.get, link) | ||
.pluck('id') | ||
.map(graph.getCell, graph).value(); | ||
// expand boxes by specific padding | ||
var sourceBBox = g.rect(this.sourceBBox).moveAndExpand(opt.paddingBox); | ||
var targetBBox = g.rect(this.targetBBox).moveAndExpand(opt.paddingBox); | ||
var mapGridSize = opt.mapGridSize; | ||
var excludeAncestors = []; | ||
var sourceId = link.get('source').id; | ||
if (sourceId !== undefined) { | ||
var source = graph.getCell(sourceId); | ||
if (source !== undefined) { | ||
excludeAncestors = _.union(excludeAncestors, _.map(source.getAncestors(), 'id')); | ||
}; | ||
} | ||
var targetId = link.get('target').id; | ||
if (targetId !== undefined) { | ||
var target = graph.getCell(targetId); | ||
if (target !== undefined) { | ||
excludeAncestors = _.union(excludeAncestors, _.map(target.getAncestors(), 'id')); | ||
} | ||
} | ||
// builds a map of all elements for quicker obstacle queries (i.e. is a point contained | ||
// in any obstacle?) (a simplified grid search) | ||
// The paper is divided to smaller cells, where each of them holds an information which | ||
// elements belong to it. When we query whether a point is in an obstacle we don't need | ||
// to go through all obstacles, we check only those in a particular cell. | ||
var map = _.chain(graph.getElements()) | ||
// remove source and target element if required | ||
.difference(excludedEnds) | ||
// remove all elements whose type is listed in excludedTypes array | ||
.reject(function(element) { | ||
// reject any element which is an ancestor of either source or target | ||
return _.contains(opt.excludeTypes, element.get('type')) || _.contains(excludeAncestors, element.id); | ||
}) | ||
// change elements (models) to their bounding boxes | ||
.invoke('getBBox') | ||
// expand their boxes by specific padding | ||
.invoke('moveAndExpand', opt.paddingBox) | ||
// build the map | ||
.foldl(function(res, bbox) { | ||
var origin = bbox.origin().snapToGrid(mapGridSize); | ||
var corner = bbox.corner().snapToGrid(mapGridSize); | ||
for (var x = origin.x; x <= corner.x; x += mapGridSize) { | ||
for (var y = origin.y; y <= corner.y; y += mapGridSize) { | ||
var gridKey = x + '@' + y; | ||
res[gridKey] = res[gridKey] || []; | ||
res[gridKey].push(bbox); | ||
} | ||
} | ||
return res; | ||
}, {}).value(); | ||
// pathfinding | ||
var map = (new ObstacleMap(opt)).build(this.paper.model, this.model); | ||
var oldVertices = _.map(vertices, g.point); | ||
var newVertices = []; | ||
var tailPoint = sourceBBox.center().snapToGrid(opt.step); | ||
var points = _.map(oldVertices, g.point); | ||
var tailPoint = sourceBBox.center(); | ||
// find a route by concating all partial routes (routes need to go through the vertices) | ||
// startElement -> vertex[1] -> ... -> vertex[n] -> endElement | ||
for (var i = 0, len = points.length; i <= len; i++) { | ||
for (var i = 0, len = oldVertices.length; i <= len; i++) { | ||
@@ -456,3 +470,3 @@ var partialRoute = null; | ||
var from = to || sourceBBox; | ||
var to = points[i]; | ||
var to = oldVertices[i]; | ||
@@ -480,3 +494,2 @@ if (!to) { | ||
if (leadPoint && leadPoint.equals(tailPoint)) { | ||
// remove the first point if the previous partial route had the same point as last | ||
@@ -488,8 +501,7 @@ partialRoute.shift(); | ||
newVertices = newVertices.concat(partialRoute); | ||
Array.prototype.push.apply(newVertices, partialRoute); | ||
}; | ||
// we might have to reverse the result if we swapped source and target at the beginning | ||
return reverseRouting ? newVertices.reverse() : newVertices; | ||
}; | ||
return newVertices; | ||
} | ||
@@ -502,2 +514,2 @@ // public function | ||
})(); | ||
})(g, _); |
@@ -30,3 +30,3 @@ joint.routers.metro = (function() { | ||
}, | ||
maxAllowedDirectionChange: 45, | ||
// a simple route used in situations, when main routing method fails | ||
@@ -33,0 +33,0 @@ // (exceed loops, inaccessible). |
JointJS - JavaScript diagramming library | ||
======================================== | ||
[![Build Status](https://travis-ci.org/clientIO/joint.svg?branch=master)](https://travis-ci.org/clientIO/joint) | ||
JointJS is a JavaScript diagramming library. It can be used to create either static diagrams or, and more | ||
@@ -46,5 +48,47 @@ importantly, fully interactive diagramming tools and application builders. | ||
`open test/joint.dia/index.html` | ||
Before running tests, be sure to run a full build: | ||
``` | ||
grunt all | ||
``` | ||
To run all tests: | ||
``` | ||
grunt test | ||
``` | ||
To run only the server-side tests: | ||
``` | ||
grunt test:server | ||
``` | ||
To run only the client-side tests: | ||
``` | ||
grunt test:client | ||
``` | ||
To run code style checks: | ||
``` | ||
grunt jscs | ||
``` | ||
Code Coverage | ||
------------- | ||
It is possible to generate code coverage reports using the existing qunit tests. For example, to output the coverage report in [lcov format](http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php) for the joint unit tests: | ||
``` | ||
grunt qunit:joint --reporter lcov --coverage | ||
``` | ||
And for all unit tests: | ||
``` | ||
grunt qunit:all --reporter lcov --coverage | ||
``` | ||
By default, the output will be saved to `coverage.info` at the root of the project directory. You can change the output file like this: | ||
``` | ||
grunt qunit:joint --reporter lcov --output customfilename.info --coverage | ||
``` | ||
License | ||
@@ -51,0 +95,0 @@ ------- |
@@ -242,3 +242,15 @@ // JointJS library. | ||
return (evt.originalEvent && evt.originalEvent.changedTouches && evt.originalEvent.changedTouches.length) ? evt.originalEvent.changedTouches[0] : evt; | ||
var touchEvt = evt.originalEvent && evt.originalEvent.changedTouches && evt.originalEvent.changedTouches[0]; | ||
if (touchEvt) { | ||
for (var property in evt) { | ||
// copy all the properties from the input event that are not | ||
// defined on the touch event (functions included). | ||
if (touchEvt[property] === undefined) { | ||
touchEvt[property] = evt[property]; | ||
} | ||
} | ||
return touchEvt; | ||
} | ||
return evt; | ||
}, | ||
@@ -249,5 +261,4 @@ | ||
var raf; | ||
var client = typeof window != 'undefined'; | ||
if (client) { | ||
if (typeof window !== 'undefined') { | ||
@@ -277,3 +288,7 @@ raf = window.requestAnimationFrame || | ||
return client ? _.bind(raf, window) : raf; | ||
return function(callback, context) { | ||
return context | ||
? raf(_.bind(callback, context)) | ||
: raf(callback); | ||
}; | ||
@@ -624,2 +639,20 @@ })(), | ||
// Sets attributes on the given element and its descendants based on the selector. | ||
// `attrs` object: { [SELECTOR1]: { attrs1 }, [SELECTOR2]: { attrs2}, ... } e.g. { 'input': { color : 'red' }} | ||
setAttributesBySelector: function(element, attrs) { | ||
var $element = $(element); | ||
_.each(attrs, function(attrs, selector) { | ||
var $elements = $element.find(selector).addBack().filter(selector); | ||
// Make a special case for setting classes. | ||
// We do not want to overwrite any existing class. | ||
if (_.has(attrs, 'class')) { | ||
$elements.addClass(attrs['class']); | ||
attrs = _.omit(attrs, 'class'); | ||
} | ||
$elements.attr(attrs); | ||
}); | ||
}, | ||
// Return a new object with all for sides (top, bottom, left and right) in it. | ||
@@ -789,3 +822,3 @@ // Value of each side is taken from the given argument (either number or object). | ||
return _.template(tpl, { | ||
return _.template(tpl)({ | ||
color: args.color || 'blue', | ||
@@ -806,3 +839,3 @@ opacity: _.isFinite(args.opacity) ? args.opacity : 1, | ||
return _.template(tpl, { | ||
return _.template(tpl)({ | ||
color: args.color || 'red', | ||
@@ -821,3 +854,3 @@ width: _.isFinite(args.width) ? args.width : 1, | ||
return _.template('<filter><feGaussianBlur stdDeviation="${stdDeviation}"/></filter>', { | ||
return _.template('<filter><feGaussianBlur stdDeviation="${stdDeviation}"/></filter>')({ | ||
stdDeviation: _.isFinite(args.y) ? [x, args.y] : x | ||
@@ -838,3 +871,3 @@ }); | ||
return _.template(tpl, { | ||
return _.template(tpl)({ | ||
dx: args.dx || 0, | ||
@@ -853,3 +886,3 @@ dy: args.dy || 0, | ||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${b} ${h} 0 0 0 0 0 1 0"/></filter>', { | ||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${b} ${h} 0 0 0 0 0 1 0"/></filter>')({ | ||
a: 0.2126 + 0.7874 * (1 - amount), | ||
@@ -871,3 +904,3 @@ b: 0.7152 - 0.7152 * (1 - amount), | ||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${h} ${i} 0 0 0 0 0 1 0"/></filter>', { | ||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${h} ${i} 0 0 0 0 0 1 0"/></filter>')({ | ||
a: 0.393 + 0.607 * (1 - amount), | ||
@@ -890,3 +923,3 @@ b: 0.769 - 0.769 * (1 - amount), | ||
return _.template('<filter><feColorMatrix type="saturate" values="${amount}"/></filter>', { | ||
return _.template('<filter><feColorMatrix type="saturate" values="${amount}"/></filter>')({ | ||
amount: 1 - amount | ||
@@ -899,3 +932,3 @@ }); | ||
return _.template('<filter><feColorMatrix type="hueRotate" values="${angle}"/></filter>', { | ||
return _.template('<filter><feColorMatrix type="hueRotate" values="${angle}"/></filter>')({ | ||
angle: args.angle || 0 | ||
@@ -910,3 +943,3 @@ }); | ||
return _.template('<filter><feComponentTransfer><feFuncR type="table" tableValues="${amount} ${amount2}"/><feFuncG type="table" tableValues="${amount} ${amount2}"/><feFuncB type="table" tableValues="${amount} ${amount2}"/></feComponentTransfer></filter>', { | ||
return _.template('<filter><feComponentTransfer><feFuncR type="table" tableValues="${amount} ${amount2}"/><feFuncG type="table" tableValues="${amount} ${amount2}"/><feFuncB type="table" tableValues="${amount} ${amount2}"/></feComponentTransfer></filter>')({ | ||
amount: amount, | ||
@@ -920,3 +953,3 @@ amount2: 1 - amount | ||
return _.template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}"/><feFuncG type="linear" slope="${amount}"/><feFuncB type="linear" slope="${amount}"/></feComponentTransfer></filter>', { | ||
return _.template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}"/><feFuncG type="linear" slope="${amount}"/><feFuncB type="linear" slope="${amount}"/></feComponentTransfer></filter>')({ | ||
amount: _.isFinite(args.amount) ? args.amount : 1 | ||
@@ -931,3 +964,3 @@ }); | ||
return _.template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}" intercept="${amount2}"/><feFuncG type="linear" slope="${amount}" intercept="${amount2}"/><feFuncB type="linear" slope="${amount}" intercept="${amount2}"/></feComponentTransfer></filter>', { | ||
return _.template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}" intercept="${amount2}"/><feFuncG type="linear" slope="${amount}" intercept="${amount2}"/><feFuncB type="linear" slope="${amount}" intercept="${amount2}"/></feComponentTransfer></filter>')({ | ||
amount: amount, | ||
@@ -1141,3 +1174,3 @@ amount2: .5 - amount / 2 | ||
var prefixes = _.map(['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], function(d, i) { | ||
var k = Math.pow(10, abs(8 - i) * 3); | ||
var k = Math.pow(10, Math.abs(8 - i) * 3); | ||
return { | ||
@@ -1144,0 +1177,0 @@ scale: i > 8 ? function(d) { return d / k; } : function(d) { return d * k; }, |
// Geometry library. | ||
// (c) 2011-2013 client IO | ||
(function(root, factory) { | ||
var g = (function() { | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. | ||
define('g', [], factory); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS, but | ||
// only CommonJS-like environments that support module.exports, | ||
// like Node. | ||
module.exports = factory(); | ||
} else { | ||
// Browser globals. | ||
root.g = factory(); | ||
} | ||
}(this, function() { | ||
// Declare shorthands to the most used math functions. | ||
@@ -197,2 +177,5 @@ var math = Math; | ||
return point(ref).move(this, this.distance(ref)); | ||
}, | ||
clone: function() { | ||
return point(this); | ||
} | ||
@@ -321,2 +304,5 @@ }; | ||
return ((this.end.x - this.start.x) * (p.y - this.start.y) - (this.end.y - this.start.y) * (p.x - this.start.x)) / 2; | ||
}, | ||
clone: function() { | ||
return line(this); | ||
} | ||
@@ -346,2 +332,8 @@ }; | ||
}, | ||
// @return {boolean} true if rectangles are equal. | ||
equals: function(r) { | ||
var mr = g.rect(this).normalize(); | ||
var nr = g.rect(r).normalize(); | ||
return mr.x === nr.x && mr.y === nr.y && mr.width === nr.width && mr.height === nr.height; | ||
}, | ||
origin: function() { | ||
@@ -362,3 +354,3 @@ return point(this.x, this.y); | ||
}, | ||
// @return {boolean} true if rectangles intersect | ||
// @return {rect} if rectangles intersect, {null} if not. | ||
intersect: function(r) { | ||
@@ -370,8 +362,14 @@ var myOrigin = this.origin(); | ||
// No intersection found | ||
if (rCorner.x <= myOrigin.x || | ||
rCorner.y <= myOrigin.y || | ||
rOrigin.x >= myCorner.x || | ||
rOrigin.y >= myCorner.y) return false; | ||
return true; | ||
rOrigin.y >= myCorner.y) return null; | ||
var x = Math.max(myOrigin.x, rOrigin.x); | ||
var y = Math.max(myOrigin.y, rOrigin.y); | ||
return rect(x, y, Math.min(myCorner.x, rCorner.x) - x, Math.min(myCorner.y, rCorner.y) - y); | ||
}, | ||
// @return {string} (left|right|top|bottom) side which is nearest to point | ||
@@ -545,2 +543,14 @@ // @see Squeak Smalltalk, Rectangle>>sideNearestTo: | ||
return rect(this.x + (this.width - w) / 2, this.y + (this.height - h) / 2, w, h); | ||
}, | ||
snapToGrid: function(gx, gy) { | ||
var origin = this.origin().snapToGrid(gx, gy); | ||
var corner = this.corner().snapToGrid(gx, gy); | ||
this.x = origin.x; | ||
this.y = origin.y; | ||
this.width = corner.x - origin.x; | ||
this.height = corner.y - origin.y; | ||
return this; | ||
}, | ||
clone: function() { | ||
return rect(this); | ||
} | ||
@@ -593,2 +603,5 @@ }; | ||
return result; | ||
}, | ||
clone: function() { | ||
return ellipse(this); | ||
} | ||
@@ -769,2 +782,2 @@ }; | ||
})); | ||
})(); |
@@ -216,3 +216,3 @@ // JointJS. | ||
if (this == cell || this.isEmbeddedIn(cell)) { | ||
if (this === cell || this.isEmbeddedIn(cell)) { | ||
@@ -332,3 +332,3 @@ throw new Error('Recursive embedding not allowed.'); | ||
while (parentId) { | ||
if (parentId == cellId) { | ||
if (parentId === cellId) { | ||
return true; | ||
@@ -345,3 +345,3 @@ } | ||
// at least whether it's a direct child of given cell. | ||
return parentId == cellId; | ||
return parentId === cellId; | ||
} | ||
@@ -388,3 +388,3 @@ }, | ||
if (embedClone.get('source').id == this.id) { | ||
if (embedClone.get('source').id === this.id) { | ||
@@ -394,3 +394,3 @@ embedClone.prop('source', { id: clone.id }); | ||
if (embedClone.get('target').id == this.id) { | ||
if (embedClone.get('target').id === this.id) { | ||
@@ -469,2 +469,5 @@ embedClone.prop('target', { id: clone.id }); | ||
// Remove the top-level property from the array of properties. | ||
pathArray.shift(); | ||
opt = opt || {}; | ||
@@ -474,3 +477,3 @@ opt.propertyPath = path; | ||
if (pathArray.length == 1) { | ||
if (pathArray.length === 0) { | ||
// Property is not nested. We can simply use `set()`. | ||
@@ -487,3 +490,3 @@ return this.set(property, value, opt); | ||
var prevProperty = property; | ||
_.each(_.rest(pathArray), function(key) { | ||
_.each(pathArray, function(key) { | ||
initializer = initializer[prevProperty] = (_.isFinite(Number(key)) ? [] : {}); | ||
@@ -712,3 +715,2 @@ prevProperty = key; | ||
this.listenTo(this.model, 'remove', this.remove); | ||
this.listenTo(this.model, 'change:attrs', this.onChangeAttrs); | ||
@@ -715,0 +717,0 @@ }, |
@@ -55,2 +55,3 @@ // JointJS library. | ||
tx = tx || 0; | ||
ty = ty || 0; | ||
@@ -66,2 +67,39 @@ | ||
opt.translateBy = opt.translateBy || this.id; | ||
var position = this.get('position') || { x: 0, y: 0 }; | ||
if (opt.restrictedArea && opt.translateBy === this.id) { | ||
// We are restricting the translation for the element itself only. We get | ||
// the bounding box of the element including all its embeds. | ||
// All embeds have to be translated the exact same way as the element. | ||
var bbox = this.getBBox({ deep: true }); | ||
var ra = opt.restrictedArea; | ||
//- - - - - - - - - - - - -> ra.x + ra.width | ||
// - - - -> position.x | | ||
// -> bbox.x | ||
// ▓▓▓▓▓▓▓ | | ||
// ░░░░░░░▓▓▓▓▓▓▓ | ||
// ░░░░░░░░░ | | ||
// ▓▓▓▓▓▓▓▓░░░░░░░ | ||
// ▓▓▓▓▓▓▓▓ | | ||
// <-dx-> | restricted area right border | ||
// <-width-> | ░ translated element | ||
// <- - bbox.width - -> ▓ embedded element | ||
var dx = position.x - bbox.x; | ||
var dy = position.y - bbox.y; | ||
// Find the maximal/minimal coordinates that the element can be translated | ||
// while complies the restrictions. | ||
var x = Math.max(ra.x + dx, Math.min(ra.x + ra.width + dx - bbox.width, position.x + tx)); | ||
var y = Math.max(ra.y + dy, Math.min(ra.y + ra.height + dy - bbox.height, position.y + ty)); | ||
// recalculate the translation taking the resctrictions into account. | ||
tx = x - position.x; | ||
ty = y - position.y; | ||
} | ||
var translatedPosition = { | ||
x: position.x + tx, | ||
y: position.y + ty | ||
}; | ||
// To find out by how much an element was translated in event 'change:position' handlers. | ||
@@ -71,5 +109,2 @@ opt.tx = tx; | ||
var position = this.get('position') || { x: 0, y: 0 }; | ||
var translatedPosition = { x: position.x + tx || 0, y: position.y + ty || 0 }; | ||
if (opt.transition) { | ||
@@ -105,3 +140,3 @@ | ||
opt = opt || 0; | ||
opt = opt || {}; | ||
@@ -177,4 +212,17 @@ var collection = this.collection; | ||
getBBox: function() { | ||
getBBox: function(opt) { | ||
opt = opt || {}; | ||
if (opt.deep && this.collection) { | ||
// Get all the embedded elements using breadth first algorithm, | ||
// that doesn't use recursion. | ||
var elements = this.getEmbeddedCells({ deep: true, breadthFirst: true }); | ||
// Add the model itself. | ||
elements.push(this); | ||
return this.collection.getBBox(elements); | ||
} | ||
var position = this.get('position'); | ||
@@ -570,3 +618,3 @@ var size = this.get('size'); | ||
var markup = this.model.markup || this.model.get('markup'); | ||
var markup = this.model.get('markup') || this.model.markup; | ||
@@ -695,15 +743,11 @@ if (markup) { | ||
findParentsByKey: function(key) { | ||
prepareEmbedding: function(opt) { | ||
var bbox = this.model.getBBox(); | ||
opt = opt || {}; | ||
return key == 'bbox' | ||
? this.paper.model.findModelsInArea(bbox) | ||
: this.paper.model.findModelsFromPoint(bbox[key]()); | ||
}, | ||
var model = opt.model || this.model; | ||
var paper = opt.paper || this.paper; | ||
prepareEmbedding: function() { | ||
// Bring the model to the front with all his embeds. | ||
this.model.toFront({ deep: true, ui: true }); | ||
model.toFront({ deep: true, ui: true }); | ||
@@ -713,7 +757,7 @@ // Move to front also all the inbound and outbound links that are connected | ||
// links connected to them would stay in the background. | ||
_.invoke(this.paper.model.getConnectedLinks(this.model, { deep: true }), 'toFront', { ui: true }); | ||
_.invoke(paper.model.getConnectedLinks(model, { deep: true }), 'toFront', { ui: true }); | ||
// Before we start looking for suitable parent we remove the current one. | ||
var parentId = this.model.get('parent'); | ||
parentId && this.paper.model.getCell(parentId).unembed(this.model, { ui: true }); | ||
var parentId = model.get('parent'); | ||
parentId && paper.model.getCell(parentId).unembed(model, { ui: true }); | ||
}, | ||
@@ -723,12 +767,11 @@ | ||
opt = opt || this.paper.options; | ||
opt = opt || {}; | ||
var candidates = this.findParentsByKey(opt.findParentBy); | ||
var model = opt.model || this.model; | ||
var paper = opt.paper || this.paper; | ||
// don't account element itself or any of its descendents | ||
candidates = _.reject(candidates, function(el) { | ||
return this.model.id == el.id || el.isEmbeddedIn(this.model); | ||
}, this); | ||
var paperOptions = paper.options; | ||
var candidates = paper.model.findModelsUnderElement(model, { searchBy: paperOptions.findParentBy }); | ||
if (opt.frontParentOnly) { | ||
if (paperOptions.frontParentOnly) { | ||
// pick the element with the highest `z` index | ||
@@ -754,4 +797,4 @@ candidates = candidates.slice(-1); | ||
var view = candidate.findView(this.paper); | ||
if (opt.validateEmbedding.call(this.paper, this, view)) { | ||
var view = candidate.findView(paper); | ||
if (paperOptions.validateEmbedding.call(paper, this, view)) { | ||
@@ -778,5 +821,9 @@ // flip to the new candidate | ||
finalizeEmbedding: function() { | ||
finalizeEmbedding: function(opt) { | ||
opt = opt || {}; | ||
var candidateView = this._candidateEmbedView; | ||
var model = opt.model || this.model; | ||
var paper = opt.paper || this.paper; | ||
@@ -786,3 +833,3 @@ if (candidateView) { | ||
// We finished embedding. Candidate view is chosen to become the parent of the model. | ||
candidateView.model.embed(this.model, { ui: true }); | ||
candidateView.model.embed(model, { ui: true }); | ||
candidateView.unhighlight(null, { embedding: true }); | ||
@@ -793,3 +840,3 @@ | ||
_.invoke(this.paper.model.getConnectedLinks(this.model, { deep: true }), 'reparent', { ui: true }); | ||
_.invoke(paper.model.getConnectedLinks(model, { deep: true }), 'reparent', { ui: true }); | ||
}, | ||
@@ -802,8 +849,10 @@ | ||
var paper = this.paper; | ||
// target is a valid magnet start linking | ||
if (evt.target.getAttribute('magnet') && this.paper.options.validateMagnet.call(this.paper, this, evt.target)) { | ||
if (evt.target.getAttribute('magnet') && paper.options.validateMagnet.call(paper, this, evt.target)) { | ||
this.model.trigger('batch:start', { batchName: 'add-link' }); | ||
var link = this.paper.getDefaultLink(this, evt.target); | ||
var link = paper.getDefaultLink(this, evt.target); | ||
link.set({ | ||
@@ -813,3 +862,3 @@ source: { | ||
selector: this.getSelector(evt.target), | ||
port: $(evt.target).attr('port') | ||
port: evt.target.getAttribute('port') | ||
}, | ||
@@ -819,5 +868,5 @@ target: { x: x, y: y } | ||
this.paper.model.addCell(link); | ||
paper.model.addCell(link); | ||
this._linkView = this.paper.findViewByModel(link); | ||
this._linkView = paper.findViewByModel(link); | ||
this._linkView.pointerdown(evt, x, y); | ||
@@ -827,5 +876,8 @@ this._linkView.startArrowheadMove('target'); | ||
} else { | ||
this._dx = x; | ||
this._dy = y; | ||
this.restrictedArea = paper.getRestrictedArea(this); | ||
joint.dia.CellView.prototype.pointerdown.apply(this, arguments); | ||
@@ -856,7 +908,7 @@ this.notify('element:pointerdown', evt, x, y); | ||
// translate as the previous one could be calculated with a different grid size. | ||
this.model.translate( | ||
g.snapToGrid(position.x, grid) - position.x + g.snapToGrid(x - this._dx, grid), | ||
g.snapToGrid(position.y, grid) - position.y + g.snapToGrid(y - this._dy, grid) | ||
); | ||
var tx = g.snapToGrid(position.x, grid) - position.x + g.snapToGrid(x - this._dx, grid); | ||
var ty = g.snapToGrid(position.y, grid) - position.y + g.snapToGrid(y - this._dy, grid); | ||
this.model.translate(tx, ty, { restrictedArea: this.restrictedArea, ui: true }); | ||
if (this.paper.options.embeddingMode) { | ||
@@ -889,4 +941,14 @@ | ||
var linkView = this._linkView; | ||
var linkModel = linkView.model; | ||
// let the linkview deal with this event | ||
this._linkView.pointerup(evt, x, y); | ||
linkView.pointerup(evt, x, y); | ||
// If the link pinning is not allowed and the link is not connected to an element | ||
// we remove the link, because the link was never connected to any target element. | ||
if (!this.paper.options.linkPinning && !_.has(linkModel.get('target'), 'id')) { | ||
linkModel.remove({ ui: true }); | ||
} | ||
delete this._linkView; | ||
@@ -893,0 +955,0 @@ |
@@ -6,7 +6,14 @@ // JointJS, the JavaScript diagramming library. | ||
initialize: function() { | ||
cellNamespace: joint.shapes, | ||
initialize: function(models, opt) { | ||
// Backbone automatically doesn't trigger re-sort if models attributes are changed later when | ||
// they're already in the collection. Therefore, we're triggering sort manually here. | ||
this.on('change:z', this.sort, this); | ||
// Set the optional namespace where all model classes are defined. | ||
if (opt.cellNamespace) { | ||
this.cellNamespace = opt.cellNamespace; | ||
} | ||
}, | ||
@@ -16,16 +23,10 @@ | ||
if (attrs.type === 'link') { | ||
var namespace = options.collection.cellNamespace; | ||
return new joint.dia.Link(attrs, options); | ||
} | ||
// Find the model class in the namespace or use the default one. | ||
var ModelClass = (attrs.type === 'link') | ||
? joint.dia.Link | ||
: joint.util.getByPath(namespace, attrs.type, '.') || joint.dia.Element; | ||
var module = attrs.type.split('.')[0]; | ||
var entity = attrs.type.split('.')[1]; | ||
if (joint.shapes[module] && joint.shapes[module][entity]) { | ||
return new joint.shapes[module][entity](attrs, options); | ||
} | ||
return new joint.dia.Element(attrs, options); | ||
return new ModelClass(attrs, options); | ||
}, | ||
@@ -50,2 +51,4 @@ | ||
if (!cell.isLink()) return false; | ||
var source = cell.get('source'); | ||
@@ -66,2 +69,4 @@ var target = cell.get('target'); | ||
if (!cell.isLink()) return; | ||
if (opt.outbound) { | ||
@@ -91,2 +96,37 @@ | ||
getNeighbors: function(model, opt) { | ||
opt = opt || {}; | ||
var neighbors = _.transform(this.getConnectedLinks(model, opt), function(res, link) { | ||
var source = link.get('source'); | ||
var target = link.get('target'); | ||
var loop = link.hasLoop(opt); | ||
// Discard if it is a point, or if the neighbor was already added. | ||
if (opt.inbound && _.has(source, 'id') && !res[source.id]) { | ||
var sourceElement = this.get(source.id); | ||
if (loop || (sourceElement !== model && (!opt.deep || !sourceElement.isEmbeddedIn(model)))) { | ||
res[source.id] = sourceElement; | ||
} | ||
} | ||
// Discard if it is a point, or if the neighbor was already added. | ||
if (opt.outbound && _.has(target, 'id') && !res[target.id]) { | ||
var targetElement = this.get(target.id); | ||
if (loop || targetElement !== model && (!opt.deep || !targetElement.isEmbeddedIn(model))) { | ||
res[target.id] = targetElement; | ||
} | ||
} | ||
}, {}, this); | ||
return _.values(neighbors); | ||
}, | ||
getCommonAncestor: function(/* cells */) { | ||
@@ -151,6 +191,11 @@ | ||
opt = opt || {}; | ||
// Passing `cellModel` function in the options object to graph allows for | ||
// setting models based on attribute objects. This is especially handy | ||
// when processing JSON graphs that are in a different than JointJS format. | ||
this.set('cells', new joint.dia.GraphCells([], { model: opt && opt.cellModel })); | ||
Backbone.Model.prototype.set.call(this, 'cells', new joint.dia.GraphCells([], { | ||
model: opt.cellModel, | ||
cellNamespace: opt.cellNamespace | ||
})); | ||
@@ -161,3 +206,3 @@ // Make all the events fired in the `cells` collection available. | ||
this.get('cells').on('remove', this.removeCell, this); | ||
this.get('cells').on('remove', this._removeCell, this); | ||
}, | ||
@@ -181,11 +226,55 @@ | ||
this.set(_.omit(json, 'cells'), opt); | ||
this.resetCells(json.cells, opt); | ||
return this.set(json, opt); | ||
}, | ||
set: function(key, val, opt) { | ||
var attrs; | ||
// Handle both `key`, value and {key: value} style arguments. | ||
if (typeof key === 'object') { | ||
attrs = key; | ||
opt = val; | ||
} else { | ||
(attrs = {})[key] = val; | ||
} | ||
// Make sure that `cells` attribute is handled separately via resetCells(). | ||
if (attrs.hasOwnProperty('cells')) { | ||
this.resetCells(attrs.cells, opt); | ||
attrs = _.omit(attrs, 'cells'); | ||
} | ||
// The rest of the attributes are applied via original set method. | ||
return Backbone.Model.prototype.set.call(this, attrs, opt); | ||
}, | ||
clear: function(opt) { | ||
this.trigger('batch:start'); | ||
this.get('cells').remove(this.get('cells').models, opt); | ||
this.trigger('batch:stop'); | ||
opt = _.extend({}, opt, { clear: true }); | ||
var collection = this.get('cells'); | ||
if (collection.length === 0) return this; | ||
this.trigger('batch:start', { batchName: 'clear' }); | ||
// The elements come after the links. | ||
var cells = collection.sortBy(function(cell) { | ||
return cell.isLink() ? 1 : 2; | ||
}); | ||
do { | ||
// Remove all the cells one by one. | ||
// Note that all the links are removed first, so it's | ||
// safe to remove the elements without removing the connected | ||
// links first. | ||
cells.shift().remove(opt); | ||
} while (cells.length > 0); | ||
this.trigger('batch:stop', { batchName: 'clear' }); | ||
return this; | ||
}, | ||
@@ -195,9 +284,10 @@ | ||
if (cell instanceof Backbone.Model && _.isUndefined(cell.get('z'))) { | ||
var attrs = (cell instanceof Backbone.Model) ? cell.attributes : cell; | ||
cell.set('z', this.maxZIndex() + 1, { silent: true }); | ||
if (_.isUndefined(attrs.z)) { | ||
attrs.z = this.maxZIndex() + 1; | ||
} | ||
} else if (_.isUndefined(cell.z)) { | ||
cell.z = this.maxZIndex() + 1; | ||
if (!_.isString(attrs.type)) { | ||
throw new TypeError('dia.Graph: cell type must be a string.'); | ||
} | ||
@@ -249,16 +339,19 @@ | ||
removeCell: function(cell, collection, options) { | ||
_removeCell: function(cell, collection, options) { | ||
// Applications might provide a `disconnectLinks` option set to `true` in order to | ||
// disconnect links when a cell is removed rather then removing them. The default | ||
// is to remove all the associated links. | ||
if (options && options.disconnectLinks) { | ||
options = options || {}; | ||
this.disconnectLinks(cell, options); | ||
if (!options.clear) { | ||
// Applications might provide a `disconnectLinks` option set to `true` in order to | ||
// disconnect links when a cell is removed rather then removing them. The default | ||
// is to remove all the associated links. | ||
if (options.disconnectLinks) { | ||
} else { | ||
this.disconnectLinks(cell, options); | ||
this.removeLinks(cell, options); | ||
} else { | ||
this.removeLinks(cell, options); | ||
} | ||
} | ||
// Silently remove the cell from the cells collection. Silently, because | ||
@@ -277,2 +370,7 @@ // `joint.dia.Cell.prototype.remove` already triggers the `remove` event which is | ||
getCells: function() { | ||
return this.get('cells').toArray(); | ||
}, | ||
getElements: function() { | ||
@@ -300,31 +398,5 @@ | ||
getNeighbors: function(el) { | ||
getNeighbors: function(model, opt) { | ||
var links = this.getConnectedLinks(el); | ||
var neighbors = []; | ||
var cells = this.get('cells'); | ||
_.each(links, function(link) { | ||
var source = link.get('source'); | ||
var target = link.get('target'); | ||
// Discard if it is a point. | ||
if (!source.x) { | ||
var sourceElement = cells.get(source.id); | ||
if (sourceElement !== el) { | ||
neighbors.push(sourceElement); | ||
} | ||
} | ||
if (!target.x) { | ||
var targetElement = cells.get(target.id); | ||
if (targetElement !== el) { | ||
neighbors.push(targetElement); | ||
} | ||
} | ||
}); | ||
return neighbors; | ||
return this.get('cells').getNeighbors(model, opt); | ||
}, | ||
@@ -347,3 +419,3 @@ | ||
// Find all views at given point | ||
// Find all elements at given point | ||
findModelsFromPoint: function(p) { | ||
@@ -356,3 +428,3 @@ | ||
// Find all views in given area | ||
// Find all elements in given area | ||
findModelsInArea: function(r) { | ||
@@ -365,2 +437,18 @@ | ||
// Find all elements under the given element. | ||
findModelsUnderElement: function(element, opt) { | ||
opt = _.defaults(opt || {}, { searchBy: 'bbox' }); | ||
var bbox = element.getBBox(); | ||
var elements = (opt.searchBy == 'bbox') | ||
? this.findModelsInArea(bbox) | ||
: this.findModelsFromPoint(bbox[opt.searchBy]()); | ||
// don't account element itself or any of its descendents | ||
return _.reject(elements, function(el) { | ||
return element.id == el.id || el.isEmbeddedIn(element); | ||
}); | ||
}, | ||
// Return the bounding box of all `elements`. | ||
@@ -367,0 +455,0 @@ getBBox: function(/* elements */) { |
@@ -14,3 +14,3 @@ // JointJS library. | ||
origin: { x: 0, y: 0 }, // x,y coordinates in top-left corner | ||
gridSize: 50, | ||
gridSize: 1, | ||
perpendicularLinks: false, | ||
@@ -21,2 +21,14 @@ elementView: joint.dia.ElementView, | ||
// Restrict the translation of elements by given bounding box. | ||
// Option accepts a boolean: | ||
// true - the translation is restricted to the paper area | ||
// false - no restrictions | ||
// A method: | ||
// restrictTranslate: function(elementView) { | ||
// var parentId = elementView.model.get('parent'); | ||
// return parentId && this.model.getCell(parentId).getBBox(); | ||
// }, | ||
// Or a bounding box: | ||
// restrictTranslate: { x: 10, y: 10, width: 790, height: 590 } | ||
restrictTranslate: false, | ||
// Marks all available magnets with 'available-magnet' class name and all available cells with | ||
@@ -32,2 +44,10 @@ // 'available-cell' class name. Marks them when dragging a link is started and unmark | ||
// A connector that is used by links with no connector defined on the model. | ||
// e.g. { name: 'rounded', args: { radius: 5 }} or a function | ||
defaultConnector: { name: 'normal' }, | ||
// A router that is used by links with no router defined on the model. | ||
// e.g. { name: 'oneSide', args: { padding: 10 }} or a function | ||
defaultRouter: null, | ||
/* CONNECTING */ | ||
@@ -70,3 +90,13 @@ | ||
labelMove: false | ||
} | ||
}, | ||
// When set to true the links can be pinned to the paper. | ||
// i.e. link source/target can be a point e.g. link.get('source') ==> { x: 100, y: 100 }; | ||
linkPinning: true, | ||
// Allowed number of mousemove events after which the pointerclick event will be still triggered. | ||
clickThreshold: 0, | ||
// The namespace, where all the cell views are defined. | ||
cellViewNamespace: joint.shapes | ||
}, | ||
@@ -97,3 +127,3 @@ | ||
if (this.options) options = _.extend({}, _.result(this, 'options'), options); | ||
if (this.options) options = _.merge({}, _.result(this, 'options'), options); | ||
this.options = options; | ||
@@ -104,3 +134,3 @@ }, | ||
_.bindAll(this, 'addCell', 'sortCells', 'resetCells', 'pointerup', 'asyncRenderCells'); | ||
_.bindAll(this, 'pointerup'); | ||
@@ -119,5 +149,6 @@ this.svg = V('svg').node; | ||
this.listenTo(this.model, 'add', this.onAddCell); | ||
this.listenTo(this.model, 'reset', this.resetCells); | ||
this.listenTo(this.model, 'sort', this.sortCells); | ||
this.listenTo(this.model, 'add', this.onCellAdded); | ||
this.listenTo(this.model, 'remove', this.removeView); | ||
this.listenTo(this.model, 'reset', this.resetViews); | ||
this.listenTo(this.model, 'sort', this.sortViews); | ||
@@ -127,3 +158,5 @@ $(document).on('mouseup touchend', this.pointerup); | ||
// Hold the value when mouse has been moved: when mouse moved, no click event will be triggered. | ||
this._mousemoved = false; | ||
this._mousemoved = 0; | ||
// Hash of all cell views. | ||
this._views = {}; | ||
@@ -137,3 +170,3 @@ // default cell highlighting | ||
//clean up all DOM elements/views to prevent memory leaks | ||
this.removeCells(); | ||
this.removeViews(); | ||
@@ -334,30 +367,73 @@ $(document).off('mouseup touchend', this.pointerup); | ||
createViewForModel: function(cell) { | ||
// Returns a geometry rectangle represeting the entire | ||
// paper area (coordinates from the left paper border to the right one | ||
// and the top border to the bottom one). | ||
getArea: function() { | ||
var view; | ||
var transformationMatrix = this.viewport.getCTM().inverse(); | ||
var noTransformationBBox = { x: 0, y: 0, width: this.options.width, height: this.options.height }; | ||
var type = cell.get('type'); | ||
var module = type.split('.')[0]; | ||
var entity = type.split('.')[1]; | ||
return g.rect(V.transformRect(noTransformationBBox, transformationMatrix)); | ||
}, | ||
// If there is a special view defined for this model, use that one instead of the default `elementView`/`linkView`. | ||
if (joint.shapes[module] && joint.shapes[module][entity + 'View']) { | ||
getRestrictedArea: function() { | ||
view = new joint.shapes[module][entity + 'View']({ model: cell, interactive: this.options.interactive }); | ||
var restrictedArea; | ||
} else if (cell instanceof joint.dia.Element) { | ||
if (_.isFunction(this.options.restrictTranslate)) { | ||
// A method returning a bounding box | ||
restrictedArea = this.options.restrictTranslate.aply(this, arguments); | ||
} else if (this.options.restrictTranslate === true) { | ||
// The paper area | ||
restrictedArea = this.getArea(); | ||
} else { | ||
// Either false or a bounding box | ||
restrictedArea = this.options.restrictTranslate || null; | ||
} | ||
view = new this.options.elementView({ model: cell, interactive: this.options.interactive }); | ||
return restrictedArea; | ||
}, | ||
createViewForModel: function(cell) { | ||
// A class taken from the paper options. | ||
var optionalViewClass; | ||
// A default basic class (either dia.ElementView or dia.LinkView) | ||
var defaultViewClass; | ||
// A special class defined for this model in the corresponding namespace. | ||
// e.g. joint.shapes.basic.Rect searches for joint.shapes.basic.RectView | ||
var namespace = this.options.cellViewNamespace; | ||
var type = cell.get('type') + 'View'; | ||
var namespaceViewClass = joint.util.getByPath(namespace, type, '.'); | ||
if (cell.isLink()) { | ||
optionalViewClass = this.options.linkView; | ||
defaultViewClass = joint.dia.LinkView; | ||
} else { | ||
view = new this.options.linkView({ model: cell, interactive: this.options.interactive }); | ||
optionalViewClass = this.options.elementView; | ||
defaultViewClass = joint.dia.ElementView; | ||
} | ||
return view; | ||
// a) the paper options view is a class (deprecated) | ||
// 1. search the namespace for a view | ||
// 2. if no view was found, use view from the paper options | ||
// b) the paper options view is a function | ||
// 1. call the function from the paper options | ||
// 2. if no view was return, search the namespace for a view | ||
// 3. if no view was found, use the default | ||
var ViewClass = (optionalViewClass.prototype instanceof Backbone.View) | ||
? namespaceViewClass || optionalViewClass | ||
: optionalViewClass.call(this, cell) || namespaceViewClass || defaultViewClass; | ||
return new ViewClass({ | ||
model: cell, | ||
interactive: this.options.interactive | ||
}); | ||
}, | ||
onAddCell: function(cell, graph, options) { | ||
onCellAdded: function(cell, graph, opt) { | ||
if (this.options.async && options.async !== false && _.isNumber(options.position)) { | ||
if (this.options.async && opt.async !== false && _.isNumber(opt.position)) { | ||
@@ -367,7 +443,7 @@ this._asyncCells = this._asyncCells || []; | ||
if (options.position == 0) { | ||
if (opt.position == 0) { | ||
if (this._frameId) throw 'another asynchronous rendering in progress'; | ||
if (this._frameId) throw new Error('another asynchronous rendering in progress'); | ||
this.asyncRenderCells(this._asyncCells); | ||
this.asyncRenderViews(this._asyncCells, opt); | ||
delete this._asyncCells; | ||
@@ -378,10 +454,22 @@ } | ||
this.addCell(cell); | ||
this.renderView(cell); | ||
} | ||
}, | ||
addCell: function(cell) { | ||
removeView: function(cell) { | ||
var view = this.createViewForModel(cell); | ||
var view = this._views[cell.id]; | ||
if (view) { | ||
view.remove(); | ||
delete this._views[cell.id]; | ||
} | ||
return view; | ||
}, | ||
renderView: function(cell) { | ||
var view = this._views[cell.id] = this.createViewForModel(cell); | ||
V(this.viewport).append(view.el); | ||
@@ -394,5 +482,7 @@ view.paper = this; | ||
$(view.el).find('image').on('dragstart', function() { return false; }); | ||
return view; | ||
}, | ||
beforeRenderCells: function(cells) { | ||
beforeRenderViews: function(cells) { | ||
@@ -406,14 +496,18 @@ // Make sure links are always added AFTER elements. | ||
afterRenderCells: function() { | ||
afterRenderViews: function() { | ||
this.sortCells(); | ||
this.sortViews(); | ||
}, | ||
resetCells: function(cellsCollection, opt) { | ||
resetViews: function(cellsCollection, opt) { | ||
$(this.viewport).empty(); | ||
// clearing views removes any event listeners | ||
this.removeViews(); | ||
var cells = cellsCollection.models.slice(); | ||
cells = this.beforeRenderCells(cells, opt); | ||
// `beforeRenderViews()` can return changed cells array (e.g sorted). | ||
cells = this.beforeRenderViews(cells, opt) || cells; | ||
@@ -428,37 +522,39 @@ if (this._frameId) { | ||
this.asyncRenderCells(cells, opt); | ||
// Sort the cells once all elements rendered (see asyncRenderCells()). | ||
this.asyncRenderViews(cells, opt); | ||
// Sort the cells once all elements rendered (see asyncRenderViews()). | ||
} else { | ||
_.each(cells, this.addCell, this); | ||
_.each(cells, this.renderView, this); | ||
// Sort the cells in the DOM manually as we might have changed the order they | ||
// were added to the DOM (see above). | ||
this.sortCells(); | ||
this.sortViews(); | ||
} | ||
}, | ||
removeCells: function() { | ||
removeViews: function() { | ||
this.model.get('cells').each(function(cell) { | ||
var view = this.findViewByModel(cell); | ||
view && view.remove(); | ||
}, this); | ||
_.invoke(this._views, 'remove'); | ||
this._views = {}; | ||
}, | ||
asyncBatchAdded: _.identity, | ||
asyncBatchAdded: _.noop, | ||
asyncRenderCells: function(cells, opt) { | ||
asyncRenderViews: function(cells, opt) { | ||
var done = false; | ||
if (this._frameId) { | ||
_.each(_.range(this.options.async && this.options.async.batchSize || 50), function() { | ||
var batchSize = (this.options.async && this.options.async.batchSize) || 50; | ||
var batchCells = cells.splice(0, batchSize); | ||
var collection = this.model.get('cells'); | ||
var cell = cells.shift(); | ||
done = !cell; | ||
if (!done) this.addCell(cell); | ||
_.each(batchCells, function(cell) { | ||
// The cell has to be part of the graph collection. | ||
// There is a chance in asynchronous rendering | ||
// that a cell was removed before it's rendered to the paper. | ||
if (cell.collection === collection) this.renderView(cell); | ||
}, this); | ||
@@ -469,6 +565,7 @@ | ||
if (done) { | ||
if (!cells.length) { | ||
// No cells left to render. | ||
delete this._frameId; | ||
this.afterRenderCells(opt); | ||
this.afterRenderViews(opt); | ||
this.trigger('render:done', opt); | ||
@@ -478,9 +575,10 @@ | ||
this._frameId = joint.util.nextFrame(_.bind(function() { | ||
this.asyncRenderCells(cells, opt); | ||
}, this)); | ||
// Schedule a next batch to render. | ||
this._frameId = joint.util.nextFrame(function() { | ||
this.asyncRenderViews(cells, opt); | ||
}, this); | ||
} | ||
}, | ||
sortCells: function() { | ||
sortViews: function() { | ||
@@ -551,12 +649,17 @@ // Run insertion sort algorithm in order to efficiently sort DOM elements according to their | ||
// be a selector or a jQuery object. | ||
findView: function(el) { | ||
findView: function($el) { | ||
var $el = this.$(el); | ||
var el = _.isString($el) | ||
? this.viewport.querySelector($el) | ||
: $el instanceof $ ? $el[0] : $el; | ||
if ($el.length === 0 || $el[0] === this.el) { | ||
while (el && el !== this.el && el !== document) { | ||
return undefined; | ||
var id = el.getAttribute('model-id'); | ||
if (id) return this._views[id]; | ||
el = el.parentNode; | ||
} | ||
return $el.data('view') || this.findView($el.parent()); | ||
return undefined; | ||
}, | ||
@@ -568,5 +671,4 @@ | ||
var id = _.isString(cell) ? cell : cell.id; | ||
var $view = this.$('[model-id="' + id + '"]'); | ||
return $view.length ? $view.data('view') : undefined; | ||
return this._views[id]; | ||
}, | ||
@@ -579,6 +681,6 @@ | ||
var views = _.map(this.model.getElements(), this.findViewByModel); | ||
var views = _.map(this.model.getElements(), this.findViewByModel, this); | ||
return _.filter(views, function(view) { | ||
return view && g.rect(V(view.el).bbox(false, this.viewport)).containsPoint(p); | ||
return view && g.rect(view.vel.bbox(false, this.viewport)).containsPoint(p); | ||
}, this); | ||
@@ -592,6 +694,6 @@ }, | ||
var views = _.map(this.model.getElements(), this.findViewByModel); | ||
var views = _.map(this.model.getElements(), this.findViewByModel, this); | ||
return _.filter(views, function(view) { | ||
return view && r.intersect(g.rect(V(view.el).bbox(false, this.viewport))); | ||
return view && r.intersect(g.rect(view.vel.bbox(false, this.viewport))); | ||
}, this); | ||
@@ -695,3 +797,3 @@ }, | ||
// Trigger event when mouse not moved. | ||
if (!this._mousemoved) { | ||
if (this._mousemoved <= this.options.clickThreshold) { | ||
@@ -715,3 +817,3 @@ evt = joint.util.normalizeEvent(evt); | ||
this._mousemoved = false; | ||
this._mousemoved = 0; | ||
}, | ||
@@ -781,4 +883,4 @@ | ||
// Mouse moved. | ||
this._mousemoved = true; | ||
// Mouse moved counter. | ||
this._mousemoved++; | ||
@@ -785,0 +887,0 @@ var localPoint = this.snapToGrid({ x: evt.clientX, y: evt.clientY }); |
@@ -9,30 +9,7 @@ // Vectorizer. | ||
(function(root, factory) { | ||
var V; | ||
var Vectorizer; | ||
if (typeof define === 'function' && define.amd) { | ||
V = Vectorizer = (function() { | ||
// AMD. | ||
define('V', ['g'], function(g) { | ||
return factory(g); | ||
}); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS, but | ||
// only CommonJS-like environments that support module.exports, | ||
// like Node. | ||
var g = require('./geometry'); | ||
module.exports = factory(g); | ||
} else { | ||
// Browser globals. | ||
var g = root.g; | ||
root.Vectorizer = root.V = factory(g); | ||
} | ||
}(this, function(g) { | ||
var SVGsupported = typeof window === 'object' && !!(window.SVGAngle || document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1')); | ||
@@ -58,4 +35,13 @@ | ||
// Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm). | ||
// IE would otherwise collapse all spaces into one. This is used in the text() method but it is | ||
// also exposed so that the programmer can use it in case he needs to. This is useful e.g. in tests | ||
// when you want to compare the actual DOM text content without having to add the unicode character in | ||
// the place of all spaces. | ||
function sanitizeText(text) { | ||
return (text || '').replace(/ /g, '\u00A0'); | ||
} | ||
function isObject(o) { | ||
return Object(o) === Object(o); | ||
return o === Object(o); | ||
} | ||
@@ -72,7 +58,31 @@ | ||
var svg = '<svg xmlns="' + ns.xmlns + '" xmlns:xlink="' + ns.xlink + '" version="' + SVGversion + '">' + (content || '') + '</svg>'; | ||
var parser = new DOMParser(); | ||
parser.async = false; | ||
return parser.parseFromString(svg, 'text/xml').documentElement; | ||
var xml = parseXML(svg, { async: false }); | ||
return xml.documentElement; | ||
} | ||
function parseXML(data, opt) { | ||
opt = opt || {}; | ||
var xml; | ||
try { | ||
var parser = new DOMParser(); | ||
if (typeof opt.async !== 'undefined') { | ||
parser.async = opt.async; | ||
} | ||
xml = parser.parseFromString(data, 'text/xml'); | ||
} catch (error) { | ||
xml = undefined; | ||
} | ||
if (!xml || xml.getElementsByTagName('parsererror').length) { | ||
throw new Error('Invalid XML: ' + data); | ||
} | ||
return xml; | ||
} | ||
// Create SVG element. | ||
@@ -333,8 +343,5 @@ // ------------------- | ||
box = this.node.getBBox(); | ||
// Opera returns infinite values in some cases. | ||
// Note that Infinity | 0 produces 0 as opposed to Infinity || 0. | ||
// We also have to create new object as the standard says that you can't | ||
// We are creating a new object as the standard says that you can't | ||
// modify the attributes of a bbox. | ||
box = { x: box.x | 0, y: box.y | 0, width: box.width | 0, height: box.height | 0 }; | ||
box = { x: box.x, y: box.y, width: box.width, height: box.height }; | ||
@@ -364,2 +371,5 @@ } catch (e) { | ||
// Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm). | ||
// IE would otherwise collapse all spaces into one. | ||
content = sanitizeText(content); | ||
opt = opt || {}; | ||
@@ -497,7 +507,8 @@ var lines = content.split('\n'); | ||
// Make sure the textContent is never empty. If it is, add an additional | ||
// space (an invisible character) so that following lines are correctly | ||
// Make sure the textContent is never empty. If it is, add a dummy | ||
// character and make it invisible, making the following lines correctly | ||
// relatively positioned. `dy=1em` won't work with empty lines otherwise. | ||
vLine.addClass('v-empty-line'); | ||
vLine.node.textContent = ' '; | ||
vLine.node.style.opacity = 0; | ||
vLine.node.textContent = '-'; | ||
} | ||
@@ -602,6 +613,3 @@ | ||
// Map DOM elements to `VElement`s. | ||
for (var i = 0, len = nodes.length; i < len; i++) { | ||
nodes[i] = V(nodes[i]); | ||
} | ||
return nodes; | ||
return Array.prototype.map.call(nodes, V); | ||
}, | ||
@@ -1336,3 +1344,3 @@ | ||
// Shift all the textg annotations after character `index` by `offset` positions. | ||
// Shift all the text annotations after character `index` by `offset` positions. | ||
V.shiftAnnotations = function(annotations, index, offset) { | ||
@@ -1353,3 +1361,6 @@ | ||
V.sanitizeText = sanitizeText; | ||
return V; | ||
})); | ||
})(); |
@@ -43,3 +43,3 @@ module('basic', { | ||
equal(textEls[0].textContent, 'my rectangle', 'text element has a proper content'); | ||
equal(textEls[0].textContent, V.sanitizeText('my rectangle'), 'text element has a proper content'); | ||
@@ -67,3 +67,3 @@ }); | ||
equal(textEls[0].textContent, 'my rectangle', 'text element has a proper content'); | ||
equal(textEls[0].textContent, V.sanitizeText('my rectangle'), 'text element has a proper content'); | ||
@@ -100,3 +100,3 @@ start(); | ||
equal(textEls[0].textContent, 'my rectangle', 'text element has a proper content'); | ||
equal(textEls[0].textContent, V.sanitizeText('my rectangle'), 'text element has a proper content'); | ||
@@ -235,2 +235,47 @@ start(); | ||
test('translate() with restrictedArea option', function(assert) { | ||
var rect = new joint.shapes.basic.Rect({ | ||
position: { x: 20, y: 30 }, | ||
size: { width: 120, height: 80 } | ||
}); | ||
var embed = new joint.shapes.basic.Rect({ | ||
position: { x: 100, y: 70 }, | ||
size: { width: 120, height: 80 } | ||
}); | ||
this.graph.addCell(rect); | ||
rect.translate(1000, 0, { restrictedArea: { x: 0, y: 0, height: 1000, width: 150 }}); | ||
assert.equal(rect.prop('position/x'), 30, 'restrictedArea is respected when the element is translated to the left.'); | ||
rect.translate(0, 1000, { restrictedArea: { x: 0, y: 0, height: 150, width: 1000 }}); | ||
assert.equal(rect.prop('position/y'), 70, 'restrictedArea is respected when the element is translated to the bottom.'); | ||
rect.translate(-1000, 0, { restrictedArea: { x: 10, y: 0, height: 1000, width: 1000 }}); | ||
assert.equal(rect.prop('position/x'), 10, 'restrictedArea is respected when the element is translated to the right.'); | ||
rect.translate(0, -1000, { restrictedArea: { x: 0, y: 10, height: 1000, width: 1000 }}); | ||
assert.equal(rect.prop('position/y'), 10, 'restrictedArea is respected when the element is translated to the top.'); | ||
rect.position(50, 50).embed(embed); | ||
this.graph.addCell(embed); | ||
rect.translate(1000, 0, { restrictedArea: { x: 0, y: 0, height: 1000, width: 500 }}); | ||
assert.equal(rect.prop('position/x'), 330, 'restrictedArea is respected when the element and its embeds are translated to the left.'); | ||
rect.translate(0, 1000, { restrictedArea: { x: 0, y: 0, height: 500, width: 1000 }}); | ||
assert.equal(rect.prop('position/y'), 400, 'restrictedArea is respected when the element and its embeds are translated to the bottom.'); | ||
rect.position(50, 50); | ||
embed.position(20,20); | ||
rect.translate(-1000, 0, { restrictedArea: { x: 10, y: 0, height: 1000, width: 1000 }}); | ||
assert.equal(rect.prop('position/x'), 40, 'restrictedArea is respected when the element and its embeds are translated to the right.'); | ||
rect.translate(0, -1000, { restrictedArea: { x: 0, y: 10, height: 1000, width: 1000 }}); | ||
assert.equal(rect.prop('position/y'), 40, 'restrictedArea is respected when the element and its embeds are translated to the top.'); | ||
}); | ||
test('resize()', function() { | ||
@@ -582,4 +627,4 @@ | ||
equal(textEls[0].textContent, 'my rectangle', 'text element has a proper content'); | ||
equal(textEls[1].textContent, 'my rectangle', 'text element of the cloned element has a proper content'); | ||
equal(textEls[0].textContent, V.sanitizeText('my rectangle'), 'text element has a proper content'); | ||
equal(textEls[1].textContent, V.sanitizeText('my rectangle'), 'text element of the cloned element has a proper content'); | ||
@@ -586,0 +631,0 @@ checkBbox(this.paper, r2, 20, 30, 120, 80, 'cloned element is at the exact same position as the original element'); |
@@ -7,2 +7,4 @@ module('Browserify'); | ||
assert.ok(typeof joint.dia === 'object', 'Joint object should have "dia" object'); | ||
assert.ok(typeof joint.dia.Graph === 'function', 'joint.dia.Graph should be a function'); | ||
assert.ok(typeof joint.dia.Paper === 'function', 'joint.dia.Paper should be a function'); | ||
}); |
@@ -29,4 +29,4 @@ module('connectors', { | ||
var r2 = r1.clone().translate(300); | ||
this.graph.addCell([r1,r2]); | ||
@@ -68,2 +68,12 @@ | ||
var customCalled = 0; | ||
var l3 = l0.clone().set('connector', function() { | ||
customCalled += 1; | ||
}); | ||
this.graph.addCell(l3); | ||
equal(customCalled, 1, | ||
'A link with the custom connector was succesfully added to the graph'); | ||
}); |
@@ -718,1 +718,49 @@ module('links', { | ||
}); | ||
test('defaultRouter', function(assert) { | ||
assert.expect(1); | ||
this.paper.options.defaultRouter = function(vertices) { | ||
assert.ok(vertices.length > 0, 'Default router was used for the model with no router defined.'); | ||
}; | ||
var linkDefaultRouter = new joint.dia.Link({ | ||
source: { x: 0, y: 0 }, | ||
target: { x: 0, y: 0 }, | ||
vertices: [{ x: 50, y: 50}] | ||
}); | ||
var linkOwnRouter = new joint.dia.Link({ | ||
source: { x: 0, y: 0 }, | ||
target: { x: 0, y: 0 }, | ||
router: { name: 'orthogonal' }, | ||
vertices: [] | ||
}); | ||
this.graph.addCells([linkDefaultRouter, linkOwnRouter]); | ||
}); | ||
test('defaultConnector', function(assert) { | ||
assert.expect(1); | ||
this.paper.options.defaultConnector = function(s, t, vertices) { | ||
assert.ok(vertices.length > 0, 'Default connector was used for the model with no connector defined.'); | ||
}; | ||
var linkDefaultConnector = new joint.dia.Link({ | ||
source: { x: 0, y: 0 }, | ||
target: { x: 0, y: 0 }, | ||
vertices: [{ x: 50, y: 50}] | ||
}); | ||
var linkOwnConnector = new joint.dia.Link({ | ||
source: { x: 0, y: 0 }, | ||
target: { x: 0, y: 0 }, | ||
connector: { name: 'normal' }, | ||
vertices: [] | ||
}); | ||
this.graph.addCells([linkDefaultConnector, linkOwnConnector]); | ||
}); |
@@ -38,2 +38,40 @@ module('paper', { | ||
test('graph.clear()', function(assert) { | ||
var graph = this.graph; | ||
var r1 = new joint.shapes.basic.Rect; | ||
var r2 = new joint.shapes.basic.Rect; | ||
var r3 = new joint.shapes.basic.Rect; | ||
var r4 = new joint.shapes.basic.Rect; | ||
var l1 = new joint.shapes.basic.Rect({ source: { id: r1.id }, target: { id: r2.id }}); | ||
var l2 = new joint.shapes.basic.Rect({ source: { id: r2.id }, target: { id: r3.id }}); | ||
var l3 = new joint.shapes.basic.Rect({ source: { id: r2.id }, target: { id: r4.id }}); | ||
graph.addCells([r1, r2, l1, r3, l2, r4]); | ||
r3.embed(r2); | ||
r3.embed(l3); | ||
graph.clear(); | ||
assert.equal(graph.getCells().length, 0, 'all the links and elements (even embeddes) were removed.'); | ||
assert.equal(graph.get('cells').length, 0, 'collection length is exactly 0 (Backbone v1.2.1 was showing negative values.)'); | ||
}); | ||
test('graph.getCells(), graph.getLinks(), graph.getElements()', function(assert) { | ||
var graph = this.graph; | ||
var r1 = new joint.shapes.basic.Rect({ id: 'r1' }); | ||
var r2 = new joint.shapes.basic.Rect({ id: 'r2' }); | ||
var l1 = new joint.dia.Link({ id: 'l1' }); | ||
graph.addCells([r1, r2, l1]); | ||
assert.deepEqual(_.pluck(graph.getCells(), 'id'), ['r1', 'r2', 'l1'], | ||
'getCells() returns all the cells in the graph.'); | ||
assert.deepEqual(_.pluck(graph.getLinks(), 'id'), ['l1'], | ||
'getLinks() returns only the link in the graph.'); | ||
assert.deepEqual(_.pluck(graph.getElements(), 'id'), ['r1', 'r2'], | ||
'getElements() returns only the elements in the graph'); | ||
}); | ||
test('graph.fromJSON(), graph.toJSON()', function() { | ||
@@ -75,2 +113,23 @@ | ||
test('graph.fetch()', function(assert) { | ||
var json = { | ||
"cells":[ | ||
{"type":"basic.Circle","size":{"width":100,"height":60},"position":{"x":110,"y":480},"id":"bbb9e641-9756-4f42-997a-f4818b89f374","embeds":"","z":0}, | ||
{"type":"link","source":{"id":"bbb9e641-9756-4f42-997a-f4818b89f374"},"target":{"id":"cbd1109e-4d34-4023-91b0-f31bce1318e6"},"id":"b4289c08-07ea-49d2-8dde-e67eb2f2a06a","z":1}, | ||
{"type":"basic.Rect","position":{"x":420,"y":410},"size":{"width":100,"height":60},"id":"cbd1109e-4d34-4023-91b0-f31bce1318e6","embeds":"","z":2} | ||
] | ||
}; | ||
var ajaxStub = sinon.stub($, 'ajax').yieldsTo('success', json); | ||
this.graph.url = 'test.url'; | ||
this.graph.fetch(); | ||
assert.equal(this.graph.getElements().length, 2, 'all the element were fetched.'); | ||
assert.equal(this.graph.getLinks().length, 1, 'all the links were fetched.'); | ||
ajaxStub.restore(); | ||
}); | ||
test('graph.getBBox()', function() { | ||
@@ -108,1 +167,339 @@ | ||
}); | ||
test('paper.getArea()', function(assert) { | ||
this.paper.setOrigin(0,0); | ||
this.paper.setDimensions(1000, 800); | ||
assert.ok(this.paper.getArea() instanceof g.rect, 'Paper area is a geometry rectangle.'); | ||
assert.deepEqual( | ||
_.pick(this.paper.getArea(), 'x', 'y', 'width', 'height'), | ||
{ x: 0, y: 0, width: 1000, height: 800 }, | ||
'Paper area returns correct results for unscaled, untranslated viewport.'); | ||
this.paper.setOrigin(100,100); | ||
assert.deepEqual( | ||
_.pick(this.paper.getArea(), 'x', 'y', 'width', 'height'), | ||
{ x: -100, y: -100, width: 1000, height: 800 }, | ||
'Paper area returns correct results for unscaled, but translated viewport.'); | ||
V(this.paper.viewport).scale(2,2); | ||
assert.deepEqual( | ||
_.pick(this.paper.getArea(), 'x', 'y', 'width', 'height'), | ||
{ x: -50, y: -50, width: 500, height: 400 }, | ||
'Paper area returns correct results for scaled and translated viewport.'); | ||
}); | ||
test('graph.findModelsUnderElement()', function(assert) { | ||
var rect = new joint.shapes.basic.Rect({ | ||
size: { width: 100, height: 100 }, | ||
position: { x: 100, y: 100 } | ||
}); | ||
var under = rect.clone(); | ||
var away = rect.clone().translate(200,200); | ||
this.graph.addCells([rect,under,away]); | ||
assert.deepEqual(this.graph.findModelsUnderElement(away), [], 'There are no models under the element.'); | ||
assert.deepEqual(this.graph.findModelsUnderElement(rect), [under], 'There is a model under the element.'); | ||
under.translate(50,50); | ||
assert.deepEqual(this.graph.findModelsUnderElement(rect, { searchBy: 'origin' }), [], 'There is no model under the element if searchBy origin option used.'); | ||
assert.deepEqual(this.graph.findModelsUnderElement(rect, { searchBy: 'corner' }), [under], 'There is a model under the element if searchBy corner options used.'); | ||
var embedded = rect.clone().addTo(this.graph); | ||
rect.embed(embedded); | ||
assert.deepEqual(this.graph.findModelsUnderElement(rect), [under], 'There is 1 model under the element found and 1 embedded element is omitted.'); | ||
assert.deepEqual(this.graph.findModelsUnderElement(under), [rect, embedded], 'There are 2 models under the element. Parent and its embed.'); | ||
assert.deepEqual(this.graph.findModelsUnderElement(embedded), [rect, under], 'There are 2 models under the element. The element\'s parent and one other element.'); | ||
}); | ||
test('paper.options: linkView & elementView', function(assert) { | ||
assert.expect(8); | ||
var customElementView = joint.dia.ElementView.extend({ custom: true }); | ||
var customLinkView = joint.dia.LinkView.extend({ custom: true }); | ||
var element = new joint.shapes.basic.Rect(); | ||
var link = new joint.dia.Link(); | ||
// Custom View via class | ||
this.paper.options.elementView = customElementView; | ||
this.paper.options.linkView = customLinkView; | ||
this.graph.addCell(element); | ||
assert.equal(element.findView(this.paper).constructor, customElementView, | ||
'custom element view used when "elementView" option contains one.'); | ||
this.graph.addCell(link); | ||
assert.equal(link.findView(this.paper).constructor, customLinkView, | ||
'custom link view used when "linkView" option contains one.'); | ||
// Custom View via function | ||
element.remove(); | ||
link.remove(); | ||
this.paper.options.elementView = function(el) { | ||
assert.ok(el === element, | ||
'"elementView" option function executed with correct parameters.'); | ||
return customElementView; | ||
}; | ||
this.paper.options.linkView = function(l) { | ||
assert.ok(l === link, | ||
'"linkView" option function executed with correct parameters.'); | ||
return customLinkView; | ||
}; | ||
this.graph.addCell(element); | ||
assert.equal(element.findView(this.paper).constructor, customElementView, | ||
'the custom element view was used when "elementView" option function returns one.'); | ||
this.graph.addCell(link); | ||
assert.equal(link.findView(this.paper).constructor, customLinkView, | ||
'the custom link view was used when "linkView" option function returns one.'); | ||
// Default View via function | ||
element.remove(); | ||
link.remove(); | ||
this.paper.options.elementView = function(el) { | ||
return null; | ||
}; | ||
this.paper.options.linkView = function(l) { | ||
return null; | ||
}; | ||
this.graph.addCell(element); | ||
assert.equal(element.findView(this.paper).constructor, joint.dia.ElementView, | ||
'the default element view was used when "elementView" option function returns no view.'); | ||
this.graph.addCell(link); | ||
assert.equal(link.findView(this.paper).constructor, joint.dia.LinkView, | ||
'the default link view was used when "linkView" option function returns no view.'); | ||
}); | ||
test('paper.options: cellViewNamespace', function(assert) { | ||
var customElementView = joint.dia.ElementView.extend({ custom: true }); | ||
var customLinkView = joint.dia.LinkView.extend({ custom: true }); | ||
var element = new joint.shapes.basic.Rect({ type: 'elements.Element' }); | ||
var link = new joint.dia.Link({ type: 'links.Link' }); | ||
this.paper.options.cellViewNamespace = { | ||
elements: { ElementView: customElementView }, | ||
links: { LinkView: customLinkView } | ||
}; | ||
this.graph.addCells([element, link]); | ||
assert.equal(element.findView(this.paper).constructor, customElementView, | ||
'the custom element view was found in the custom namespace.'); | ||
assert.equal(link.findView(this.paper).constructor, customLinkView, | ||
'the custom link view was found in the custom namespace.'); | ||
}); | ||
test('graph.options: cellNamespace', function(assert) { | ||
var elementJSON = { id: 'a', type: 'elements.Element' }; | ||
var linkJSON = { id: 'b', type: 'link' }; | ||
var nonExistingJSON = { id: 'c', type: 'elements.NonExisting' }; | ||
var graph = new joint.dia.Graph({}, { cellNamespace: { | ||
elements: { Element: joint.shapes.basic.Rect } | ||
}}); | ||
graph.addCell(elementJSON); | ||
var element = graph.getCell('a'); | ||
assert.equal(element.constructor, joint.shapes.basic.Rect, | ||
'The class was found in the custom namespace based on the type provided.'); | ||
graph.addCell(linkJSON); | ||
var link = graph.getCell('b'); | ||
assert.equal(link.constructor, joint.dia.Link, | ||
'The default link model is created when type equals "link".'); | ||
graph.addCell(nonExistingJSON); | ||
var nonExisting = graph.getCell('c'); | ||
assert.equal(nonExisting.constructor, joint.dia.Element, | ||
'If there is no class based on the type in the namespace, the default element model is used.'); | ||
}); | ||
test('paper.options: linkPinning', function(assert) { | ||
assert.expect(5); | ||
var source = new joint.shapes.basic.Rect({ id: 'source', position: { x: 100, y: 100 }, size: { width: 100, height: 100 }}); | ||
var target = new joint.shapes.basic.Rect({ id: 'target', position: { x: 400, y: 100 }, size: { width: 100, height: 100 }}); | ||
var link = new joint.dia.Link({ id: 'link', source: { id: source.id }, target: { id: target.id }}); | ||
var newLink; // to be created. | ||
this.graph.addCells([source, target, link]); | ||
var linkView = link.findView(this.paper); | ||
var sourceView = source.findView(this.paper); | ||
var targetView = target.findView(this.paper); | ||
var arrowhead = linkView.el.querySelector('.marker-arrowhead[end=target]'); | ||
this.paper.options.linkPinning = false; | ||
linkView.pointerdown({ target: arrowhead, type: 'mousedown' }, 0, 0); | ||
linkView.pointermove({ target: this.paper.el, type: 'mousemove' }, 50 , 50); | ||
linkView.pointerup({ target: this.paper.el, type: 'mouseup' }, 50 , 50); | ||
assert.deepEqual(link.get('target'), { id: target.id }, 'pinning disabled: when the arrowhead is dragged&dropped to the blank paper area, the arrowhead is return to its original position.'); | ||
this.paper.options.linkPinning = true; | ||
linkView.pointerdown({ target: arrowhead, type: 'mousedown' }, 0, 0); | ||
linkView.pointermove({ target: this.paper.el, type: 'mousemove' }, 50 , 50); | ||
linkView.pointerup({ target: this.paper.el, type: 'mouseup' }, 50 , 50); | ||
assert.deepEqual(link.get('target'), { x: 50, y: 50 }, 'pinning enabled: when the arrowhead is dragged&dropped to the blank paper area, the arrowhead is set to a point.'); | ||
this.paper.options.linkPinning = false; | ||
linkView.pointerdown({ target: arrowhead, type: 'mousedown' }, 0, 0); | ||
linkView.pointermove({ target: targetView.el, type: 'mousemove' }, 450 , 150); | ||
linkView.pointerup({ target: targetView.el, type: 'mouseup' }, 450 , 150); | ||
assert.deepEqual(link.get('target'), { id: 'target' }, 'pinning disabled: it\'s still possible to connect link to elements.'); | ||
this.paper.options.linkPinning = true; | ||
source.attr('.', { magnet: true }); | ||
sourceView.pointerdown({ target: sourceView.el, type: 'mousedown' }, 150, 150); | ||
sourceView.pointermove({ target: this.paper.el, type: 'mousemove' }, 150 , 400); | ||
sourceView.pointerup({ target: this.paper.el, type: 'mouseup' }, 150 , 400); | ||
newLink = _.reject(this.graph.getLinks(), { id: 'link' })[0]; | ||
if (newLink) { | ||
assert.deepEqual(newLink.get('target'), { x: 150, y: 400 }, 'pinning enabled: when there was a link created from a magnet a dropped into the blank paper area, the link target is set to a point.'); | ||
newLink.remove(); | ||
} | ||
this.paper.options.linkPinning = false; | ||
sourceView.pointerdown({ target: sourceView.el, type: 'mousedown' }, 150, 150); | ||
sourceView.pointermove({ target: this.paper.el, type: 'mousemove' }, 150 , 400); | ||
sourceView.pointerup({ target: this.paper.el, type: 'mouseup' }, 150 , 400); | ||
newLink = _.reject(this.graph.getLinks(), { id: 'link' })[0]; | ||
assert.notOk(newLink, 'pinning disabled: when there was a link created from a magnet a dropped into the blank paper area, the link was removed after the drop.'); | ||
}); | ||
test('graph.getNeighbors()', function(assert) { | ||
var graph = this.graph; | ||
var Element = joint.shapes.basic.Rect; | ||
var Link = joint.dia.Link; | ||
function neighbors(el, opt) { return _.chain(graph.getNeighbors(el, opt)).pluck('id').sort().value(); } | ||
var r1 = new Element({ id: 'R1' }); | ||
var r2 = new Element({ id: 'R2' }); | ||
var r3 = new Element({ id: 'R3' }); | ||
var r4 = new Element({ id: 'R4' }); | ||
var r5 = new Element({ id: 'R5' }); | ||
var r6 = new Element({ id: 'R6' }); | ||
var l1 = new Link({ id: 'L1' }); | ||
var l2 = new Link({ id: 'L2' }); | ||
var l3 = new Link({ id: 'L3' }); | ||
var l4 = new Link({ id: 'L4' }); | ||
graph.addCells([r1, r2, r3, r4, r5, r6, l1, l2]); | ||
l1.set('source', { id: 'R1' }).set('target', { id: 'R2' }); | ||
l2.set('source', { id: 'R2' }).set('target', { id: 'R3' }); | ||
// | ||
// [R1] --L1--> [R2] --L2--> [R3] | ||
// | ||
// [R4] | ||
// | ||
assert.deepEqual(neighbors(r4), [], 'Returns an empty array if the element has no neighbors.'); | ||
assert.deepEqual(neighbors(r1), ['R2'], 'Element has only outbound link. The neighbor was found.'); | ||
assert.deepEqual(neighbors(r3), ['R2'], 'Element has only inbound link. The neighbor was found.'); | ||
assert.deepEqual(neighbors(r2), ['R1','R3'], 'Elment has both outbound an inbound links. The neighbors were found.'); | ||
graph.addCells([l3]); | ||
l3.set('source', { id: 'R2' }).set('target', { id: 'R4' }); | ||
// | ||
// L2--> [R3] | ||
// | | ||
// [R1] --L1--> [R2] --| | ||
// | | ||
// L3--> [R4] | ||
// | ||
assert.deepEqual(neighbors(r2, { inbound: true }), ['R1'], 'The inbound links were found.'); | ||
assert.deepEqual(neighbors(r2, { outbound: true }), ['R3','R4'], 'The outbound links were found.'); | ||
graph.addCells([l4]); | ||
l1.set('source', { id: 'R1' }).set('target', { id: 'R2' }); | ||
l2.set('source', { id: 'R2' }).set('target', { id: 'R3' }); | ||
l3.set('source', { id: 'R2' }).set('target', { id: 'R3' }); | ||
l4.set('source', { id: 'R1' }).set('target', { id: 'R2' }); | ||
// | ||
// [R1] --L1,L4--> [R2] --L2,L3--> [R3] | ||
// | ||
assert.deepEqual(neighbors(r2), ['R1','R3'], 'There are no duplicates in the result.'); | ||
l1.set('source', { id: 'R1' }).set('target', { id: 'R1' }); | ||
l2.remove(); | ||
l3.remove(); | ||
l4.set('source', { id: 'R1' }).set('target', { id: 'R1' }); | ||
// [R1] <--L1,L4 | ||
// | | | ||
// ------- | ||
assert.deepEqual(neighbors(r1), ['R1'], 'Being a self-neighbor is detected.'); | ||
graph.addCells([l2,l3]); | ||
r1.embed(r2); | ||
l1.set('source', { id: 'R1' }).set('target', { id: 'R3' }); | ||
l2.set('source', { id: 'R5' }).set('target', { id: 'R1' }); | ||
l3.set('source', { id: 'R2' }).set('target', { id: 'R4' }); | ||
l4.set('source', { id: 'R6' }).set('target', { id: 'R2' }); | ||
// | ||
// ░░░░░░░░░░░<-L2-- [R5] | ||
// ░R1░░░░░░░░--L1-> [R3] | ||
// ░░░░▓▓▓▓▓▓▓ | ||
// ░░░░▓▓▓R2▓▓--L3-> [R4] | ||
// ░░░░▓▓▓▓▓▓▓<-L4-- [R6] | ||
assert.deepEqual(neighbors(r1), ['R3','R5'], 'Embedded elements are not taken into account by default.'); | ||
assert.deepEqual(neighbors(r2), ['R4','R6'], 'Parent elements are not taken into account by default.'); | ||
assert.deepEqual(neighbors(r1, { deep: true }), ['R3','R4','R5','R6'], 'The neighbours of the element and all its embdes were found in the deep mode. But not the embdes themselves.'); | ||
assert.deepEqual(neighbors(r2, { deep: true }), ['R4','R6'], 'Parent elements are not taken into account in the deep mode.'); | ||
assert.deepEqual(neighbors(r1, { deep: true, outbound: true }), ['R3','R4'], 'The outbound neighbours of the element and all its embdes were found in the deep mode.'); | ||
assert.deepEqual(neighbors(r1, { deep: true, inbound: true }), ['R5','R6'], 'The inbound neighbours of the element and all its embdes were found in the deep mode.'); | ||
l1.set('source', { id: 'R1' }).set('target', { id: 'R2' }); | ||
l2.remove(); | ||
l3.remove(); | ||
l4.remove(); | ||
// | ||
// ░░░░░░░░░░░ | ||
// ░R1░░░░░░░░------ | ||
// ░░░░▓▓▓▓▓▓▓ L1| | ||
// ░░░░▓▓▓R2▓▓<----- | ||
// ░░░░▓▓▓▓▓▓▓ | ||
assert.deepEqual(neighbors(r1), ['R2'], 'A connected embedded elements is found in the shallow mode.'); | ||
assert.deepEqual(neighbors(r1, { deep: true }), ['R1','R2'], 'All the connected embedded elements are found in the deep mode.'); | ||
assert.deepEqual(neighbors(r1, { deep: true, inbound: true }), ['R1'], 'All the inbound connected embedded elements are found in the deep mode.'); | ||
assert.deepEqual(neighbors(r1, { deep: true, outbound: true }), ['R2'], 'All the outbound connected embedded elements are found in the deep mode.'); | ||
}); |
@@ -7,5 +7,3 @@ require.config({ | ||
'backbone': 'lib/backbone/backbone', | ||
'lodash': 'lib/lodash/dist/lodash', | ||
'g': 'src/geometry', | ||
'V': 'src/vectorizer' | ||
'lodash': 'lib/lodash/lodash' | ||
}, | ||
@@ -20,27 +18,42 @@ map: { | ||
module('RequireJS'); | ||
(function() { | ||
var buildFiles = [ | ||
'dist/joint.clean.build', | ||
'dist/joint.clean.build.min' | ||
'dist/joint.core', | ||
'dist/joint.core.min', | ||
'dist/joint', | ||
'dist/joint.min' | ||
]; | ||
while (buildFiles.length > 0) { | ||
test('require joint build files', function(assert) { | ||
(function(buildFile) { | ||
var done = assert.async(); | ||
test('sanity checks for distribution file: "' + buildFile + '"', function(assert) { | ||
require(buildFiles, function() { | ||
var done = assert.async(); | ||
var modules = Array.prototype.slice.call(arguments); | ||
var buildFile, joint; | ||
require([buildFile], function(joint) { | ||
assert.ok(buildFiles.length === modules.length, 'expected ' + buildFiles.length + ' build file(s) to be loaded'); | ||
for (var i = 0; i < modules.length; i++) { | ||
buildFile = buildFiles[i]; | ||
joint = modules[i]; | ||
test('sanity checks for build file: "' + buildFile + '"', function(assert) { | ||
assert.ok(typeof joint !== 'undefined', 'Should be able to require joint module'); | ||
assert.ok(typeof joint.dia === 'object', 'Joint should have "dia" object'); | ||
done(); | ||
assert.ok(typeof joint.dia.Graph === 'function', 'joint.dia.Graph should be a function'); | ||
assert.ok(typeof joint.dia.Paper === 'function', 'joint.dia.Paper should be a function'); | ||
}); | ||
}); | ||
})( buildFiles.pop() ); | ||
} | ||
} | ||
done(); | ||
}); | ||
}); | ||
})(); |
@@ -29,4 +29,4 @@ module('routers', { | ||
var r2 = r1.clone().translate(300); | ||
this.graph.addCell([r1,r2]); | ||
@@ -65,3 +65,3 @@ | ||
// One vertex. | ||
var r1 = new joint.shapes.basic.Rect({ position: { x: 200, y: 60 }, size: { width: 50, height: 30 } }); | ||
@@ -126,3 +126,3 @@ var r2 = new joint.shapes.basic.Rect({ position: { x: 125, y: 60 }, size: { width: 50, height: 30 } }); | ||
var r2 = r1.clone().translate(300); | ||
var r3 = r2.clone().translate(300); | ||
@@ -151,3 +151,3 @@ | ||
equal(d, "M 140 120 160 120 460 120 460 80 600 80 620 80", | ||
equal(d, "M 140 120 160 120 600 120 600 80 620 80", | ||
'Source has been moved. Route recalculated starting from target.'); | ||
@@ -173,3 +173,3 @@ | ||
equal(d, "M 140 120 180 120 280 120 280 140 580 140 580 20 620 20", | ||
equal(d, "M 140 120 180 120 280 120 280 0 580 0 580 20 620 20", | ||
"The option paddingBox was passed. The source and target element and obstacles are avoided taken this padding in account."); | ||
@@ -194,3 +194,3 @@ | ||
equal(d, "M 140 120 680 120 680 20 680 20", | ||
equal(d, "M 80 80 80 20 680 20 680 20", | ||
'The default fallback router made an orthogonal link.'); | ||
@@ -253,3 +253,3 @@ | ||
equal(d, "M 140 80 160 80 800 80 800 60 760 60 760 80 740 80", | ||
equal(d, "M 140 80 160 80 800 80 800 100 760 100 760 80 740 80", | ||
"Set excludeEnds parameter to 'target' makes routing ignore target element."); | ||
@@ -278,3 +278,3 @@ | ||
var r2 = r1.clone().translate(300,300); | ||
var r3 = r2.clone().translate(300,300); | ||
@@ -297,3 +297,3 @@ | ||
equal(d, "M 140 80 160 80 400 320 440 320 600 480 600 680 620 680", | ||
equal(d, "M 140 80 160 80 400 320 440 320 680 560 680 620 680 630", | ||
'Route avoids an obstacle.'); | ||
@@ -352,1 +352,20 @@ | ||
}); | ||
test('custom routing', function(assert) { | ||
var r1 = new joint.shapes.basic.Rect({ position: { x: 20, y: 30 }, size: { width: 120, height: 80 }}); | ||
var r2 = r1.clone().translate(300,300); | ||
var l = new joint.dia.Link({ source: { id: r1.id }, target: { id: r2.id }}); | ||
this.graph.addCell([r1, r2, l]); | ||
var called = 0; | ||
l.set('router', function(oldVertices) { | ||
assert.deepEqual(oldVertices, []); | ||
called += 1; | ||
return oldVertices; | ||
}); | ||
assert.equal(called, 1); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
5
112
45
1
2305677
18
129
4
35014
+ Addeddagre@0.7.4(transitive)
+ Addedgraphlib@1.0.7(transitive)
+ Addedlodash@3.10.1(transitive)
- Removeddagre@0.7.1(transitive)
- Removedgraphlib@1.0.1(transitive)
- Removedlodash@2.2.12.4.2(transitive)
Updateddagre@0.7.4
Updatedgraphlib@1.0.7
Updatedlodash@3.10.1