seeddms-code/styles/bootstrap/cytoscape/cytoscape-grid-guide.js

3158 lines
88 KiB
JavaScript
Raw Normal View History

2018-12-03 06:39:51 +00:00
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict"
module.exports = createRBTree
var RED = 0
var BLACK = 1
function RBNode(color, key, value, left, right, count) {
this._color = color
this.key = key
this.value = value
this.left = left
this.right = right
this._count = count
}
function cloneNode(node) {
return new RBNode(node._color, node.key, node.value, node.left, node.right, node._count)
}
function repaint(color, node) {
return new RBNode(color, node.key, node.value, node.left, node.right, node._count)
}
function recount(node) {
node._count = 1 + (node.left ? node.left._count : 0) + (node.right ? node.right._count : 0)
}
function RedBlackTree(compare, root) {
this._compare = compare
this.root = root
}
var proto = RedBlackTree.prototype
Object.defineProperty(proto, "keys", {
get: function() {
var result = []
this.forEach(function(k,v) {
result.push(k)
})
return result
}
})
Object.defineProperty(proto, "values", {
get: function() {
var result = []
this.forEach(function(k,v) {
result.push(v)
})
return result
}
})
//Returns the number of nodes in the tree
Object.defineProperty(proto, "length", {
get: function() {
if(this.root) {
return this.root._count
}
return 0
}
})
//Insert a new item into the tree
proto.insert = function(key, value) {
var cmp = this._compare
//Find point to insert new node at
var n = this.root
var n_stack = []
var d_stack = []
while(n) {
var d = cmp(key, n.key)
n_stack.push(n)
d_stack.push(d)
if(d <= 0) {
n = n.left
} else {
n = n.right
}
}
//Rebuild path to leaf node
n_stack.push(new RBNode(RED, key, value, null, null, 1))
for(var s=n_stack.length-2; s>=0; --s) {
var n = n_stack[s]
if(d_stack[s] <= 0) {
n_stack[s] = new RBNode(n._color, n.key, n.value, n_stack[s+1], n.right, n._count+1)
} else {
n_stack[s] = new RBNode(n._color, n.key, n.value, n.left, n_stack[s+1], n._count+1)
}
}
//Rebalance tree using rotations
//console.log("start insert", key, d_stack)
for(var s=n_stack.length-1; s>1; --s) {
var p = n_stack[s-1]
var n = n_stack[s]
if(p._color === BLACK || n._color === BLACK) {
break
}
var pp = n_stack[s-2]
if(pp.left === p) {
if(p.left === n) {
var y = pp.right
if(y && y._color === RED) {
//console.log("LLr")
p._color = BLACK
pp.right = repaint(BLACK, y)
pp._color = RED
s -= 1
} else {
//console.log("LLb")
pp._color = RED
pp.left = p.right
p._color = BLACK
p.right = pp
n_stack[s-2] = p
n_stack[s-1] = n
recount(pp)
recount(p)
if(s >= 3) {
var ppp = n_stack[s-3]
if(ppp.left === pp) {
ppp.left = p
} else {
ppp.right = p
}
}
break
}
} else {
var y = pp.right
if(y && y._color === RED) {
//console.log("LRr")
p._color = BLACK
pp.right = repaint(BLACK, y)
pp._color = RED
s -= 1
} else {
//console.log("LRb")
p.right = n.left
pp._color = RED
pp.left = n.right
n._color = BLACK
n.left = p
n.right = pp
n_stack[s-2] = n
n_stack[s-1] = p
recount(pp)
recount(p)
recount(n)
if(s >= 3) {
var ppp = n_stack[s-3]
if(ppp.left === pp) {
ppp.left = n
} else {
ppp.right = n
}
}
break
}
}
} else {
if(p.right === n) {
var y = pp.left
if(y && y._color === RED) {
//console.log("RRr", y.key)
p._color = BLACK
pp.left = repaint(BLACK, y)
pp._color = RED
s -= 1
} else {
//console.log("RRb")
pp._color = RED
pp.right = p.left
p._color = BLACK
p.left = pp
n_stack[s-2] = p
n_stack[s-1] = n
recount(pp)
recount(p)
if(s >= 3) {
var ppp = n_stack[s-3]
if(ppp.right === pp) {
ppp.right = p
} else {
ppp.left = p
}
}
break
}
} else {
var y = pp.left
if(y && y._color === RED) {
//console.log("RLr")
p._color = BLACK
pp.left = repaint(BLACK, y)
pp._color = RED
s -= 1
} else {
//console.log("RLb")
p.left = n.right
pp._color = RED
pp.right = n.left
n._color = BLACK
n.right = p
n.left = pp
n_stack[s-2] = n
n_stack[s-1] = p
recount(pp)
recount(p)
recount(n)
if(s >= 3) {
var ppp = n_stack[s-3]
if(ppp.right === pp) {
ppp.right = n
} else {
ppp.left = n
}
}
break
}
}
}
}
//Return new tree
n_stack[0]._color = BLACK
return new RedBlackTree(cmp, n_stack[0])
}
//Visit all nodes inorder
function doVisitFull(visit, node) {
if(node.left) {
var v = doVisitFull(visit, node.left)
if(v) { return v }
}
var v = visit(node.key, node.value)
if(v) { return v }
if(node.right) {
return doVisitFull(visit, node.right)
}
}
//Visit half nodes in order
function doVisitHalf(lo, compare, visit, node) {
var l = compare(lo, node.key)
if(l <= 0) {
if(node.left) {
var v = doVisitHalf(lo, compare, visit, node.left)
if(v) { return v }
}
var v = visit(node.key, node.value)
if(v) { return v }
}
if(node.right) {
return doVisitHalf(lo, compare, visit, node.right)
}
}
//Visit all nodes within a range
function doVisit(lo, hi, compare, visit, node) {
var l = compare(lo, node.key)
var h = compare(hi, node.key)
var v
if(l <= 0) {
if(node.left) {
v = doVisit(lo, hi, compare, visit, node.left)
if(v) { return v }
}
if(h > 0) {
v = visit(node.key, node.value)
if(v) { return v }
}
}
if(h > 0 && node.right) {
return doVisit(lo, hi, compare, visit, node.right)
}
}
proto.forEach = function rbTreeForEach(visit, lo, hi) {
if(!this.root) {
return
}
switch(arguments.length) {
case 1:
return doVisitFull(visit, this.root)
break
case 2:
return doVisitHalf(lo, this._compare, visit, this.root)
break
case 3:
if(this._compare(lo, hi) >= 0) {
return
}
return doVisit(lo, hi, this._compare, visit, this.root)
break
}
}
//First item in list
Object.defineProperty(proto, "begin", {
get: function() {
var stack = []
var n = this.root
while(n) {
stack.push(n)
n = n.left
}
return new RedBlackTreeIterator(this, stack)
}
})
//Last item in list
Object.defineProperty(proto, "end", {
get: function() {
var stack = []
var n = this.root
while(n) {
stack.push(n)
n = n.right
}
return new RedBlackTreeIterator(this, stack)
}
})
//Find the ith item in the tree
proto.at = function(idx) {
if(idx < 0) {
return new RedBlackTreeIterator(this, [])
}
var n = this.root
var stack = []
while(true) {
stack.push(n)
if(n.left) {
if(idx < n.left._count) {
n = n.left
continue
}
idx -= n.left._count
}
if(!idx) {
return new RedBlackTreeIterator(this, stack)
}
idx -= 1
if(n.right) {
if(idx >= n.right._count) {
break
}
n = n.right
} else {
break
}
}
return new RedBlackTreeIterator(this, [])
}
proto.ge = function(key) {
var cmp = this._compare
var n = this.root
var stack = []
var last_ptr = 0
while(n) {
var d = cmp(key, n.key)
stack.push(n)
if(d <= 0) {
last_ptr = stack.length
}
if(d <= 0) {
n = n.left
} else {
n = n.right
}
}
stack.length = last_ptr
return new RedBlackTreeIterator(this, stack)
}
proto.gt = function(key) {
var cmp = this._compare
var n = this.root
var stack = []
var last_ptr = 0
while(n) {
var d = cmp(key, n.key)
stack.push(n)
if(d < 0) {
last_ptr = stack.length
}
if(d < 0) {
n = n.left
} else {
n = n.right
}
}
stack.length = last_ptr
return new RedBlackTreeIterator(this, stack)
}
proto.lt = function(key) {
var cmp = this._compare
var n = this.root
var stack = []
var last_ptr = 0
while(n) {
var d = cmp(key, n.key)
stack.push(n)
if(d > 0) {
last_ptr = stack.length
}
if(d <= 0) {
n = n.left
} else {
n = n.right
}
}
stack.length = last_ptr
return new RedBlackTreeIterator(this, stack)
}
proto.le = function(key) {
var cmp = this._compare
var n = this.root
var stack = []
var last_ptr = 0
while(n) {
var d = cmp(key, n.key)
stack.push(n)
if(d >= 0) {
last_ptr = stack.length
}
if(d < 0) {
n = n.left
} else {
n = n.right
}
}
stack.length = last_ptr
return new RedBlackTreeIterator(this, stack)
}
//Finds the item with key if it exists
proto.find = function(key) {
var cmp = this._compare
var n = this.root
var stack = []
while(n) {
var d = cmp(key, n.key)
stack.push(n)
if(d === 0) {
return new RedBlackTreeIterator(this, stack)
}
if(d <= 0) {
n = n.left
} else {
n = n.right
}
}
return new RedBlackTreeIterator(this, [])
}
//Removes item with key from tree
proto.remove = function(key) {
var iter = this.find(key)
if(iter) {
return iter.remove()
}
return this
}
//Returns the item at `key`
proto.get = function(key) {
var cmp = this._compare
var n = this.root
while(n) {
var d = cmp(key, n.key)
if(d === 0) {
return n.value
}
if(d <= 0) {
n = n.left
} else {
n = n.right
}
}
return
}
//Iterator for red black tree
function RedBlackTreeIterator(tree, stack) {
this.tree = tree
this._stack = stack
}
var iproto = RedBlackTreeIterator.prototype
//Test if iterator is valid
Object.defineProperty(iproto, "valid", {
get: function() {
return this._stack.length > 0
}
})
//Node of the iterator
Object.defineProperty(iproto, "node", {
get: function() {
if(this._stack.length > 0) {
return this._stack[this._stack.length-1]
}
return null
},
enumerable: true
})
//Makes a copy of an iterator
iproto.clone = function() {
return new RedBlackTreeIterator(this.tree, this._stack.slice())
}
//Swaps two nodes
function swapNode(n, v) {
n.key = v.key
n.value = v.value
n.left = v.left
n.right = v.right
n._color = v._color
n._count = v._count
}
//Fix up a double black node in a tree
function fixDoubleBlack(stack) {
var n, p, s, z
for(var i=stack.length-1; i>=0; --i) {
n = stack[i]
if(i === 0) {
n._color = BLACK
return
}
//console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key)
p = stack[i-1]
if(p.left === n) {
//console.log("left child")
s = p.right
if(s.right && s.right._color === RED) {
//console.log("case 1: right sibling child red")
s = p.right = cloneNode(s)
z = s.right = cloneNode(s.right)
p.right = s.left
s.left = p
s.right = z
s._color = p._color
n._color = BLACK
p._color = BLACK
z._color = BLACK
recount(p)
recount(s)
if(i > 1) {
var pp = stack[i-2]
if(pp.left === p) {
pp.left = s
} else {
pp.right = s
}
}
stack[i-1] = s
return
} else if(s.left && s.left._color === RED) {
//console.log("case 1: left sibling child red")
s = p.right = cloneNode(s)
z = s.left = cloneNode(s.left)
p.right = z.left
s.left = z.right
z.left = p
z.right = s
z._color = p._color
p._color = BLACK
s._color = BLACK
n._color = BLACK
recount(p)
recount(s)
recount(z)
if(i > 1) {
var pp = stack[i-2]
if(pp.left === p) {
pp.left = z
} else {
pp.right = z
}
}
stack[i-1] = z
return
}
if(s._color === BLACK) {
if(p._color === RED) {
//console.log("case 2: black sibling, red parent", p.right.value)
p._color = BLACK
p.right = repaint(RED, s)
return
} else {
//console.log("case 2: black sibling, black parent", p.right.value)
p.right = repaint(RED, s)
continue
}
} else {
//console.log("case 3: red sibling")
s = cloneNode(s)
p.right = s.left
s.left = p
s._color = p._color
p._color = RED
recount(p)
recount(s)
if(i > 1) {
var pp = stack[i-2]
if(pp.left === p) {
pp.left = s
} else {
pp.right = s
}
}
stack[i-1] = s
stack[i] = p
if(i+1 < stack.length) {
stack[i+1] = n
} else {
stack.push(n)
}
i = i+2
}
} else {
//console.log("right child")
s = p.left
if(s.left && s.left._color === RED) {
//console.log("case 1: left sibling child red", p.value, p._color)
s = p.left = cloneNode(s)
z = s.left = cloneNode(s.left)
p.left = s.right
s.right = p
s.left = z
s._color = p._color
n._color = BLACK
p._color = BLACK
z._color = BLACK
recount(p)
recount(s)
if(i > 1) {
var pp = stack[i-2]
if(pp.right === p) {
pp.right = s
} else {
pp.left = s
}
}
stack[i-1] = s
return
} else if(s.right && s.right._color === RED) {
//console.log("case 1: right sibling child red")
s = p.left = cloneNode(s)
z = s.right = cloneNode(s.right)
p.left = z.right
s.right = z.left
z.right = p
z.left = s
z._color = p._color
p._color = BLACK
s._color = BLACK
n._color = BLACK
recount(p)
recount(s)
recount(z)
if(i > 1) {
var pp = stack[i-2]
if(pp.right === p) {
pp.right = z
} else {
pp.left = z
}
}
stack[i-1] = z
return
}
if(s._color === BLACK) {
if(p._color === RED) {
//console.log("case 2: black sibling, red parent")
p._color = BLACK
p.left = repaint(RED, s)
return
} else {
//console.log("case 2: black sibling, black parent")
p.left = repaint(RED, s)
continue
}
} else {
//console.log("case 3: red sibling")
s = cloneNode(s)
p.left = s.right
s.right = p
s._color = p._color
p._color = RED
recount(p)
recount(s)
if(i > 1) {
var pp = stack[i-2]
if(pp.right === p) {
pp.right = s
} else {
pp.left = s
}
}
stack[i-1] = s
stack[i] = p
if(i+1 < stack.length) {
stack[i+1] = n
} else {
stack.push(n)
}
i = i+2
}
}
}
}
//Removes item at iterator from tree
iproto.remove = function() {
var stack = this._stack
if(stack.length === 0) {
return this.tree
}
//First copy path to node
var cstack = new Array(stack.length)
var n = stack[stack.length-1]
cstack[cstack.length-1] = new RBNode(n._color, n.key, n.value, n.left, n.right, n._count)
for(var i=stack.length-2; i>=0; --i) {
var n = stack[i]
if(n.left === stack[i+1]) {
cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count)
} else {
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
}
}
//Get node
n = cstack[cstack.length-1]
//console.log("start remove: ", n.value)
//If not leaf, then swap with previous node
if(n.left && n.right) {
//console.log("moving to leaf")
//First walk to previous leaf
var split = cstack.length
n = n.left
while(n.right) {
cstack.push(n)
n = n.right
}
//Copy path to leaf
var v = cstack[split-1]
cstack.push(new RBNode(n._color, v.key, v.value, n.left, n.right, n._count))
cstack[split-1].key = n.key
cstack[split-1].value = n.value
//Fix up stack
for(var i=cstack.length-2; i>=split; --i) {
n = cstack[i]
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
}
cstack[split-1].left = cstack[split]
}
//console.log("stack=", cstack.map(function(v) { return v.value }))
//Remove leaf node
n = cstack[cstack.length-1]
if(n._color === RED) {
//Easy case: removing red leaf
//console.log("RED leaf")
var p = cstack[cstack.length-2]
if(p.left === n) {
p.left = null
} else if(p.right === n) {
p.right = null
}
cstack.pop()
for(var i=0; i<cstack.length; ++i) {
cstack[i]._count--
}
return new RedBlackTree(this.tree._compare, cstack[0])
} else {
if(n.left || n.right) {
//Second easy case: Single child black parent
//console.log("BLACK single child")
if(n.left) {
swapNode(n, n.left)
} else if(n.right) {
swapNode(n, n.right)
}
//Child must be red, so repaint it black to balance color
n._color = BLACK
for(var i=0; i<cstack.length-1; ++i) {
cstack[i]._count--
}
return new RedBlackTree(this.tree._compare, cstack[0])
} else if(cstack.length === 1) {
//Third easy case: root
//console.log("ROOT")
return new RedBlackTree(this.tree._compare, null)
} else {
//Hard case: Repaint n, and then do some nasty stuff
//console.log("BLACK leaf no children")
for(var i=0; i<cstack.length; ++i) {
cstack[i]._count--
}
var parent = cstack[cstack.length-2]
fixDoubleBlack(cstack)
//Fix up links
if(parent.left === n) {
parent.left = null
} else {
parent.right = null
}
}
}
return new RedBlackTree(this.tree._compare, cstack[0])
}
//Returns key
Object.defineProperty(iproto, "key", {
get: function() {
if(this._stack.length > 0) {
return this._stack[this._stack.length-1].key
}
return
},
enumerable: true
})
//Returns value
Object.defineProperty(iproto, "value", {
get: function() {
if(this._stack.length > 0) {
return this._stack[this._stack.length-1].value
}
return
},
enumerable: true
})
//Returns the position of this iterator in the sorted list
Object.defineProperty(iproto, "index", {
get: function() {
var idx = 0
var stack = this._stack
if(stack.length === 0) {
var r = this.tree.root
if(r) {
return r._count
}
return 0
} else if(stack[stack.length-1].left) {
idx = stack[stack.length-1].left._count
}
for(var s=stack.length-2; s>=0; --s) {
if(stack[s+1] === stack[s].right) {
++idx
if(stack[s].left) {
idx += stack[s].left._count
}
}
}
return idx
},
enumerable: true
})
//Advances iterator to next element in list
iproto.next = function() {
var stack = this._stack
if(stack.length === 0) {
return
}
var n = stack[stack.length-1]
if(n.right) {
n = n.right
while(n) {
stack.push(n)
n = n.left
}
} else {
stack.pop()
while(stack.length > 0 && stack[stack.length-1].right === n) {
n = stack[stack.length-1]
stack.pop()
}
}
}
//Checks if iterator is at end of tree
Object.defineProperty(iproto, "hasNext", {
get: function() {
var stack = this._stack
if(stack.length === 0) {
return false
}
if(stack[stack.length-1].right) {
return true
}
for(var s=stack.length-1; s>0; --s) {
if(stack[s-1].left === stack[s]) {
return true
}
}
return false
}
})
//Update value
iproto.update = function(value) {
var stack = this._stack
if(stack.length === 0) {
throw new Error("Can't update empty node!")
}
var cstack = new Array(stack.length)
var n = stack[stack.length-1]
cstack[cstack.length-1] = new RBNode(n._color, n.key, value, n.left, n.right, n._count)
for(var i=stack.length-2; i>=0; --i) {
n = stack[i]
if(n.left === stack[i+1]) {
cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count)
} else {
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
}
}
return new RedBlackTree(this.tree._compare, cstack[0])
}
//Moves iterator backward one element
iproto.prev = function() {
var stack = this._stack
if(stack.length === 0) {
return
}
var n = stack[stack.length-1]
if(n.left) {
n = n.left
while(n) {
stack.push(n)
n = n.right
}
} else {
stack.pop()
while(stack.length > 0 && stack[stack.length-1].left === n) {
n = stack[stack.length-1]
stack.pop()
}
}
}
//Checks if iterator is at start of tree
Object.defineProperty(iproto, "hasPrev", {
get: function() {
var stack = this._stack
if(stack.length === 0) {
return false
}
if(stack[stack.length-1].left) {
return true
}
for(var s=stack.length-1; s>0; --s) {
if(stack[s-1].right === stack[s]) {
return true
}
}
return false
}
})
//Default comparison function
function defaultCompare(a, b) {
if(a < b) {
return -1
}
if(a > b) {
return 1
}
return 0
}
//Build a tree
function createRBTree(compare) {
return new RedBlackTree(compare || defaultCompare, null)
}
},{}],2:[function(require,module,exports){
module.exports = function (cytoscape, cy, $, apiRegistered) {
// Needed because parent nodes cannot be moved in Cytoscape.js < v3.2
2016-08-31 14:09:37 +00:00
function moveTopDown(node, dx, dy) {
var nodes = node.union(node.descendants());
2018-12-03 06:39:51 +00:00
nodes.filter(":childless").positions(function (node, i) {
if(typeof node === "number") {
node = i;
}
2016-08-31 14:09:37 +00:00
var pos = node.position();
return {
x: pos.x + dx,
y: pos.y + dy
};
});
}
function getTopMostNodes(nodes) {
var nodesMap = {};
for (var i = 0; i < nodes.length; i++) {
nodesMap[nodes[i].id()] = true;
}
2018-12-03 06:39:51 +00:00
var roots = nodes.filter(function (ele, i) {
if(typeof ele === "number") {
ele = i;
}
2016-08-31 14:09:37 +00:00
var parent = ele.parent()[0];
while(parent != null){
if(nodesMap[parent.id()]){
return false;
}
parent = parent.parent()[0];
}
return true;
});
return roots;
}
2018-12-03 06:39:51 +00:00
// If extension api functions are not registed to cytoscape yet register them here.
// Note that ideally these functions should not be directly registered to core from cytoscape.js
// extensions
if ( !apiRegistered ) {
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
cytoscape( "collection", "align", function (horizontal, vertical, alignTo) {
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
var eles = getTopMostNodes(this.nodes(":visible"));
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
var modelNode = alignTo ? alignTo : eles[0];
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
eles = eles.not(modelNode);
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
horizontal = horizontal ? horizontal : "none";
vertical = vertical ? vertical : "none";
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
// 0 for center
var xFactor = 0;
var yFactor = 0;
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
if (vertical == "left")
xFactor = -1;
else if (vertical == "right")
xFactor = 1;
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
if (horizontal == "top")
yFactor = -1;
else if (horizontal == "bottom")
yFactor = 1;
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
for (var i = 0; i < eles.length; i++) {
var node = eles[i];
var oldPos = $.extend({}, node.position());
var newPos = $.extend({}, node.position());
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
if (vertical != "none")
newPos.x = modelNode.position("x") + xFactor * (modelNode.outerWidth() - node.outerWidth()) / 2;
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
if (horizontal != "none")
newPos.y = modelNode.position("y") + yFactor * (modelNode.outerHeight() - node.outerHeight()) / 2;
moveTopDown(node, newPos.x - oldPos.x, newPos.y - oldPos.y);
}
return this;
});
}
2016-08-31 14:09:37 +00:00
if (cy.undoRedo) {
function getNodePositions() {
var positionsAndSizes = {};
var nodes = cy.nodes();
for (var i = 0; i < nodes.length; i++) {
var ele = nodes[i];
positionsAndSizes[ele.id()] = {
x: ele.position("x"),
y: ele.position("y")
};
}
return positionsAndSizes;
}
function returnToPositions(nodesData) {
var currentPositions = {};
2018-12-03 06:39:51 +00:00
cy.nodes().positions(function (ele, i) {
if(typeof ele === "number") {
ele = i;
}
2016-08-31 14:09:37 +00:00
currentPositions[ele.id()] = {
x: ele.position("x"),
y: ele.position("y")
};
var data = nodesData[ele.id()];
return {
x: data.x,
y: data.y
};
});
return currentPositions
}
2018-12-03 06:39:51 +00:00
var ur = cy.undoRedo(null, true);
2016-08-31 14:09:37 +00:00
ur.action("align", function (args) {
var nodesData;
if (args.firstTime){
nodesData = getNodePositions();
args.nodes.align(args.horizontal, args.vertical, args.alignTo);
}
else
nodesData = returnToPositions(args);
return nodesData;
}, function (nodesData) {
return returnToPositions(nodesData);
});
}
};
2018-12-03 06:39:51 +00:00
},{}],3:[function(require,module,exports){
2016-08-31 14:09:37 +00:00
var debounce = (function(){
/**
* lodash 3.1.1 (Custom Build) <https://lodash.com/>
* Build: `lodash modern modularize exports="npm" -o ./`
* Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Available under MIT license <https://lodash.com/license>
*/
/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';
/* Native method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max,
nativeNow = Date.now;
/**
* Gets the number of milliseconds that have elapsed since the Unix epoch
* (1 January 1970 00:00:00 UTC).
*
* @static
* @memberOf _
* @category Date
* @example
*
* _.defer(function(stamp) {
* console.log(_.now() - stamp);
* }, _.now());
* // => logs the number of milliseconds it took for the deferred function to be invoked
*/
var now = nativeNow || function() {
return new Date().getTime();
};
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked. The debounced function comes with a `cancel` method to cancel
* delayed invocations. Provide an options object to indicate that `func`
* should be invoked on the leading and/or trailing edge of the `wait` timeout.
* Subsequent calls to the debounced function return the result of the last
* `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
* on the trailing edge of the timeout only if the the debounced function is
* invoked more than once during the `wait` timeout.
*
* See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
* for details over the differences between `_.debounce` and `_.throttle`.
*
* @static
* @memberOf _
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0] The number of milliseconds to delay.
* @param {Object} [options] The options object.
* @param {boolean} [options.leading=false] Specify invoking on the leading
* edge of the timeout.
* @param {number} [options.maxWait] The maximum time `func` is allowed to be
* delayed before it's invoked.
* @param {boolean} [options.trailing=true] Specify invoking on the trailing
* edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // avoid costly calculations while the window size is in flux
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
*
* // invoke `sendMail` when the click event is fired, debouncing subsequent calls
* jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }));
*
* // ensure `batchLog` is invoked once after 1 second of debounced calls
* var source = new EventSource('/stream');
* jQuery(source).on('message', _.debounce(batchLog, 250, {
* 'maxWait': 1000
* }));
*
* // cancel a debounced call
* var todoChanges = _.debounce(batchLog, 1000);
* Object.observe(models.todo, todoChanges);
*
* Object.observe(models, function(changes) {
* if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
* todoChanges.cancel();
* }
* }, ['delete']);
*
* // ...at some point `models.todo` is changed
* models.todo.completed = true;
*
* // ...before 1 second has passed `models.todo` is deleted
* // which cancels the debounced `todoChanges` call
* delete models.todo;
*/
function debounce(func, wait, options) {
var args,
maxTimeoutId,
result,
stamp,
thisArg,
timeoutId,
trailingCall,
lastCalled = 0,
maxWait = false,
trailing = true;
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
wait = wait < 0 ? 0 : (+wait || 0);
if (options === true) {
var leading = true;
trailing = false;
} else if (isObject(options)) {
leading = !!options.leading;
maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
function cancel() {
if (timeoutId) {
clearTimeout(timeoutId);
}
if (maxTimeoutId) {
clearTimeout(maxTimeoutId);
}
lastCalled = 0;
maxTimeoutId = timeoutId = trailingCall = undefined;
}
function complete(isCalled, id) {
if (id) {
clearTimeout(id);
}
maxTimeoutId = timeoutId = trailingCall = undefined;
if (isCalled) {
lastCalled = now();
result = func.apply(thisArg, args);
if (!timeoutId && !maxTimeoutId) {
args = thisArg = undefined;
}
}
}
function delayed() {
var remaining = wait - (now() - stamp);
if (remaining <= 0 || remaining > wait) {
complete(trailingCall, maxTimeoutId);
} else {
timeoutId = setTimeout(delayed, remaining);
}
}
function maxDelayed() {
complete(trailing, timeoutId);
}
function debounced() {
args = arguments;
stamp = now();
thisArg = this;
trailingCall = trailing && (timeoutId || !leading);
if (maxWait === false) {
var leadingCall = leading && !timeoutId;
} else {
if (!maxTimeoutId && !leading) {
lastCalled = stamp;
}
var remaining = maxWait - (stamp - lastCalled),
isCalled = remaining <= 0 || remaining > maxWait;
if (isCalled) {
if (maxTimeoutId) {
maxTimeoutId = clearTimeout(maxTimeoutId);
}
lastCalled = stamp;
result = func.apply(thisArg, args);
}
else if (!maxTimeoutId) {
maxTimeoutId = setTimeout(maxDelayed, remaining);
}
}
if (isCalled && timeoutId) {
timeoutId = clearTimeout(timeoutId);
}
else if (!timeoutId && wait !== maxWait) {
timeoutId = setTimeout(delayed, wait);
}
if (leadingCall) {
isCalled = true;
result = func.apply(thisArg, args);
}
if (isCalled && !timeoutId && !maxTimeoutId) {
args = thisArg = undefined;
}
return result;
}
debounced.cancel = cancel;
return debounced;
}
/**
* Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(1);
* // => false
*/
function isObject(value) {
// Avoid a V8 JIT bug in Chrome 19-20.
// See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
var type = typeof value;
return !!value && (type == 'object' || type == 'function');
}
return debounce;
})();
module.exports = debounce;
},{}],4:[function(require,module,exports){
module.exports = function (opts, cy, $, debounce) {
var options = opts;
var changeOptions = function (opts) {
options = opts;
};
var $canvas = $( '<canvas></canvas>' );
var $container = $( cy.container() );
var ctx = $canvas[ 0 ].getContext( '2d' );
$container.append( $canvas );
2018-12-03 06:39:51 +00:00
var resetCanvas = function () {
$canvas
.attr('height', 0)
.attr('width', 0)
.css( {
'position': 'absolute',
'top': 0,
'left': 0,
'z-index': options.gridStackOrder
});
};
resetCanvas();
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
var drawGrid = function() {
2016-08-31 14:09:37 +00:00
var zoom = cy.zoom();
var canvasWidth = $container.width();
var canvasHeight = $container.height();
var increment = options.gridSpacing*zoom;
var pan = cy.pan();
var initialValueX = pan.x%increment;
var initialValueY = pan.y%increment;
2018-12-03 06:39:51 +00:00
ctx.strokeStyle = options.gridColor;
2016-08-31 14:09:37 +00:00
ctx.lineWidth = options.lineWidth;
2018-12-03 06:39:51 +00:00
var data = '\t<svg width="'+ canvasWidth + '" height="'+ canvasHeight + '" xmlns="http://www.w3.org/2000/svg">\n\
<defs>\n\
<pattern id="horizontalLines" width="' + increment + '" height="' + increment + '" patternUnits="userSpaceOnUse">\n\
<path d="M ' + increment + ' 0 L 0 0 0 ' + 0 + '" fill="none" stroke="' + options.gridColor + '" stroke-width="' + options.lineWidth + '" />\n\
</pattern>\n\
<pattern id="verticalLines" width="' + increment + '" height="' + increment + '" patternUnits="userSpaceOnUse">\n\
<path d="M ' + 0 + ' 0 L 0 0 0 ' + increment + '" fill="none" stroke="' + options.gridColor + '" stroke-width="' + options.lineWidth + '" />\n\
</pattern>\n\
</defs>\n\
<rect width="100%" height="100%" fill="url(#horizontalLines)" transform="translate('+ 0 + ', ' + initialValueY + ')" />\n\
<rect width="100%" height="100%" fill="url(#verticalLines)" transform="translate('+ initialValueX + ', ' + 0 + ')" />\n\
</svg>\n';
var img = new Image();
data = encodeURIComponent(data);
img.onload = function () {
clearDrawing();
ctx.drawImage(img, 0, 0);
};
img.src = "data:image/svg+xml," + data;
2016-08-31 14:09:37 +00:00
};
2018-12-03 06:39:51 +00:00
2016-08-31 14:09:37 +00:00
var clearDrawing = function() {
var width = $container.width();
var height = $container.height();
ctx.clearRect( 0, 0, width, height );
};
var resizeCanvas = debounce(function() {
$canvas
.attr( 'height', $container.height() )
.attr( 'width', $container.width() )
.css( {
'position': 'absolute',
'top': 0,
'left': 0,
'z-index': options.gridStackOrder
} );
setTimeout( function() {
var canvasBb = $canvas.offset();
var containerBb = $container.offset();
$canvas
.attr( 'height', $container.height() )
.attr( 'width', $container.width() )
.css( {
'top': -( canvasBb.top - containerBb.top ),
'left': -( canvasBb.left - containerBb.left )
} );
drawGrid();
}, 0 );
}, 250);
return {
initCanvas: resizeCanvas,
resizeCanvas: resizeCanvas,
2018-12-03 06:39:51 +00:00
resetCanvas: resetCanvas,
2016-08-31 14:09:37 +00:00
clearCanvas: clearDrawing,
drawGrid: drawGrid,
changeOptions: changeOptions,
sizeCanvas: drawGrid
};
};
2018-12-03 06:39:51 +00:00
},{}],5:[function(require,module,exports){
module.exports = function (cy, snap, resize, snapToGridDuringDrag, drawGrid, guidelines, parentPadding, $, opts) {
var feature = function (func) {
return function (enable) {
func(enable);
};
};
var controller = {
snapToGridDuringDrag: new feature(setDiscreteDrag),
resize: new feature(setResize),
snapToGridOnRelease: new feature(setSnapToGrid),
drawGrid: new feature(setDrawGrid),
guidelines: new feature(setGuidelines),
parentPadding: new feature(setParentPadding)
};
function applyToCyTarget(func, allowParent) {
return function (e) {
var cyTarget = e.target || e.cyTarget;
if (!cyTarget.is(":parent") || allowParent)
func(cyTarget);
}
}
function applyToAllNodesButNoParent(func) {
return function () {
cy.nodes().not(":parent").each(function (ele, i) {
if(typeof ele === "number") {
ele = i;
2016-08-31 14:09:37 +00:00
}
2018-12-03 06:39:51 +00:00
func(ele);
});
};
}
function applyToAllNodes(func) {
return function () {
cy.nodes().each(function (ele, i) {
if(typeof ele === "number") {
ele = i;
}
func(ele);
});
};
}
function eventStatus(enable) {
return enable ? "on" : "off";
}
// Discrete Drag
function setDiscreteDrag(enable) {
cy[eventStatus(enable)]("tapstart", "node", snapToGridDuringDrag.onTapStartNode);
}
// Resize
var resizeAllNodes = applyToAllNodesButNoParent(resize.resizeNode);
var resizeNode = applyToCyTarget(resize.resizeNode);
var recoverAllNodeDimensions = applyToAllNodesButNoParent(resize.recoverNodeDimensions);
function setResize(enable) {
cy[eventStatus(enable)]("ready", resizeAllNodes);
// cy[eventStatus(enable)]("style", "node", resizeNode);
enable ? resizeAllNodes() : recoverAllNodeDimensions();
}
// Snap To Grid
var snapAllNodes = applyToAllNodes(snap.snapNodesTopDown);
var recoverSnapAllNodes = applyToAllNodes(snap.recoverSnapNode);
var snapCyTarget = applyToCyTarget(snap.snapNode, true);
function setSnapToGrid(enable) {
cy[eventStatus(enable)]("add", "node", snapCyTarget);
cy[eventStatus(enable)]("ready", snapAllNodes);
cy[eventStatus(enable)]("free", "node", snap.onFreeNode);
if (enable) {
snapAllNodes();
} else {
recoverSnapAllNodes();
}
}
// Draw Grid
var drawGridOnZoom = function () {
if (currentOptions.zoomDash) drawGrid.drawGrid()
};
var drawGridOnPan = function () {
if (currentOptions.panGrid) drawGrid.drawGrid()
};
function setDrawGrid(enable) {
cy[eventStatus(enable)]('zoom', drawGridOnZoom);
cy[eventStatus(enable)]('pan', drawGridOnPan);
if (enable) {
drawGrid.initCanvas();
$(window).on('resize', drawGrid.resizeCanvas);
} else {
drawGrid.clearCanvas();
drawGrid.resetCanvas();
$(window).off('resize', drawGrid.resizeCanvas);
}
}
// Guidelines
var activeTopMostNodes = null;
var guidelinesGrabHandler = function(e){
var cyTarget = e.target || e.cyTarget;
var nodes = cyTarget.selected() ? e.cy.$(":selected") : cyTarget;
activeTopMostNodes = guidelines.getTopMostNodes(nodes.nodes());
guidelines.lines.init(activeTopMostNodes);
}
var guidelinesDragHandler = function(e){
if (this.id() == activeTopMostNodes.id()){
guidelines.lines.update(activeTopMostNodes);
if (currentOptions.snapToAlignmentLocationDuringDrag)
guidelines.lines.snapToAlignmentLocation(activeTopMostNodes);
}
};
var guidelinesFreeHandler = function(e){
if (currentOptions.snapToAlignmentLocationOnRelease)
guidelines.lines.snapToAlignmentLocation(activeTopMostNodes);
guidelines.lines.destroy();
activeTopMostNodes = null;
};
var guidelinesWindowResizeHandler = function(e){
guidelines.lines.resize();
};
var guidelinesTapHandler = function(e){
guidelines.getMousePos(e);
};
var guidelinesPanHandler = function(e){
if (activeTopMostNodes){
guidelines.setMousePos(cy.pan());
guidelines.lines.init(activeTopMostNodes);
}
}
function setGuidelines(enable) {
if (enable){
guidelines.resizeCanvas();
cy.on("tapstart", "node", guidelinesTapHandler);
cy.on("grab", guidelinesGrabHandler);
cy.on("pan", guidelinesPanHandler);
cy.on("drag", "node", guidelinesDragHandler);
cy.on("free", guidelinesFreeHandler);
$(window).on("resize", guidelinesWindowResizeHandler);
}
else{
cy.off("tapstart", "node", guidelinesTapHandler);
cy.off("grab", guidelinesGrabHandler);
cy.off("pan", guidelinesPanHandler);
cy.off("drag", "node", guidelinesDragHandler);
cy.off("free", guidelinesFreeHandler);
guidelines.resetCanvas();
$(window).off("resize", guidelinesWindowResizeHandler);
}
}
// Parent Padding
var setAllParentPaddings = function (enable) {
parentPadding.setPaddingOfParent(cy.nodes(":parent"), enable);
};
var enableParentPadding = function (node) {
parentPadding.setPaddingOfParent(node, true);
};
function setParentPadding(enable) {
setAllParentPaddings(enable);
cy[eventStatus(enable)]('ready', setAllParentPaddings);
cy[eventStatus(enable)]("add", "node:parent", applyToCyTarget(enableParentPadding, true));
}
// Sync with options: Enables/disables changed via options.
var latestOptions = {};
var currentOptions;
var specialOpts = {
drawGrid: ["gridSpacing", "zoomDash", "panGrid", "gridStackOrder", "gridColor", "lineWidth", "lineDash"],
guidelines: ["gridSpacing", "guidelinesStackOrder", "guidelinesTolerance", "guidelinesStyle", "distributionGuidelines", "range", "minDistRange", "geometricGuidelineRange"],
resize: ["gridSpacing"],
parentPadding: ["gridSpacing", "parentSpacing"],
snapToGridOnRelease: ["gridSpacing"]
};
function syncWithOptions(options) {
currentOptions = $.extend(true, {}, options);
options.guidelines = options.initPosAlignment || options.distributionGuidelines || options.geometricGuideline;
for (var key in options)
if (latestOptions[key] != options[key])
if (controller.hasOwnProperty(key)) {
controller[key](options[key]);
} else {
for (var optsKey in specialOpts) {
var opts = specialOpts[optsKey];
if (opts.indexOf(key) >= 0) {
if(optsKey == "drawGrid") {
drawGrid.changeOptions(options);
if (options.drawGrid)
drawGrid.resizeCanvas();
}
if (optsKey == "snapToGridOnRelease"){
snap.changeOptions(options);
if (options.snapToGridOnRelease)
snapAllNodes();
}
if(optsKey == "guidelines")
guidelines.changeOptions(options);
if (optsKey == "resize") {
resize.changeOptions(options);
if (options.resize)
resizeAllNodes();
}
if (optsKey == "parentPadding")
parentPadding.changeOptions(options);
}
}
}
latestOptions = $.extend(true, latestOptions, options);
}
return {
init: syncWithOptions,
syncWithOptions: syncWithOptions
};
2016-08-31 14:09:37 +00:00
};
2018-12-03 06:39:51 +00:00
2016-08-31 14:09:37 +00:00
},{}],6:[function(require,module,exports){
module.exports = function (opts, cy, $, debounce) {
2018-12-03 06:39:51 +00:00
var RBTree = require("functional-red-black-tree");
var options = opts;
var changeOptions = function (opts) {
options = opts;
// RBTree always returns null, when low == high
// to avoid this:
if (options.guidelinesTolerance == 0)
options.guidelinesTolerance = 0.001;
};
var getCyScratch = function () {
var sc = cy.scratch("_guidelines");
if (!sc)
sc = cy.scratch("_guidelines", {});
return sc;
};
/* Resize canvas */
var resizeCanvas = debounce(function () {
clearDrawing();
$canvas
.attr('height', $container.height())
.attr('width', $container.width())
.css({
'position': 'absolute',
'top': 0,
'left': 0,
'z-index': options.guidelinesStackOrder
});
setTimeout(function () {
var canvasBb = $canvas.offset();
var containerBb = $container.offset();
$canvas
.attr('height', $container.height())
.attr('width', $container.width())
.css({
'top': -( canvasBb.top - containerBb.top ),
'left': -( canvasBb.left - containerBb.left )
});
}, 0);
}, 250);
/* Clear canvas */
var clearDrawing = function () {
var width = $container.width();
var height = $container.height();
ctx.clearRect(0, 0, width, height);
};
/* Create a canvas */
var $canvas = $('<canvas></canvas>');
var $container = $(cy.container());
var ctx = $canvas[0].getContext('2d');
$container.append($canvas);
var resetCanvas = function () {
$canvas
.attr('height', 0)
.attr('width', 0)
.css( {
'position': 'absolute',
'top': 0,
'left': 0,
'z-index': options.gridStackOrder
});
};
resetCanvas();
/* Global variables */
var VTree = null;
var HTree = null;
var nodeInitPos;
var excludedNodes;
var lines = {};
var panInitPos = {};
var alignedLocations = {"h" : null, "v" : null};
/**
* Get positions of sides of a node
* @param node : a node
* @return : object of positions
*/
lines.getDims = function (node) {
var pos = node.renderedPosition();
var width = node.renderedWidth();
var height = node.renderedHeight();
var padding = {
left: Number(node.renderedStyle("padding-left").replace("px", "")),
right: Number(node.renderedStyle("padding-right").replace("px", "")),
top: Number(node.renderedStyle("padding-top").replace("px", "")),
bottom: Number(node.renderedStyle("padding-bottom").replace("px", ""))
};
return {
horizontal: {
center: (pos.x),
left: Math.round(pos.x - (padding.left + width / 2)),
right: Math.round(pos.x + (padding.right + width / 2))
},
vertical: {
center: (pos.y),
top: Math.round(pos.y - (padding.top + height / 2)),
bottom: Math.round(pos.y + (padding.bottom + height / 2))
}
};
};
/**
* Initialize trees and initial position of node
* @param activeNodes : top most active nodes
*/
lines.init = function (activeNodes) {
VTree = RBTree();
HTree = RBTree();
// TODO: seperate initialization of nodeInitPos
// not necessary to init trees when geometric and distribution alignments are disabled,
// but reference guideline is enables
if (!nodeInitPos){
panInitPos.x = cy.pan("x"); panInitPos.y = cy.pan("y");
nodeInitPos = activeNodes.renderedPosition();
}
var nodes = cy.nodes(":visible");
excludedNodes = activeNodes.union(activeNodes.ancestors());
excludedNodes = excludedNodes.union(activeNodes.descendants());
nodes.not(excludedNodes).each(function (node, i) {
if(typeof node === "number") {
node = i;
2016-08-31 14:09:37 +00:00
}
2018-12-03 06:39:51 +00:00
var dims = lines.getDims(node);
["left", "center", "right"].forEach(function (val) {
var hKey = dims.horizontal[val];
if (HTree.get(hKey))
HTree.get(hKey).push(node);
else
HTree = HTree.insert(hKey, [node]);
});
["top", "center", "bottom"].forEach(function (val) {
var vKey = dims.vertical[val];
if (VTree.get(vKey))
VTree.get(vKey).push(node);
else
VTree = VTree.insert(vKey, [node]);
});
});
ctx.lineWidth=options.lineWidth;
lines.update(activeNodes);
};
/* Destroy gobal variables */
lines.destroy = function () {
lines.clear();
VTree = null; HTree = null;
nodeInitPos = null;
mouseInitPos = {};
alignedLocations = {"h" : null, "v" : null};
if (nodeToAlign){
nodeToAlign.unlock();
nodeToAlign = undefined;
}
};
lines.clear = clearDrawing;
/**
* Draw straight line
* @param from : initial position
* @param to : final position
* @param color : color of the line
* @param lineStyle : whether line is solid or dashed
*/
lines.drawLine = function (from, to, color, lineStyle) {
ctx.setLineDash(lineStyle);
ctx.beginPath();
ctx.moveTo(from.x, from.y);
ctx.lineTo(to.x, to.y);
ctx.strokeStyle = color;
ctx.stroke();
};
/**
* Draw an arrow
* @param position : position of the arrow
* @param type : type/directşon of the arrow
*/
lines.drawArrow = function(position, type){
if (type == "right"){
// right arrow
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(position.x-5, position.y-5);
ctx.lineTo(position.x, position.y);
ctx.lineTo(position.x-5, position.y+5);
ctx.stroke();
}
else if (type == "left"){
// left arrow
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(position.x+5, position.y-5);
ctx.lineTo(position.x, position.y);
ctx.lineTo(position.x+5, position.y+5);
ctx.stroke();
}
else if (type == "top"){
// up arrow
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(position.x-5, position.y+5);
ctx.lineTo(position.x, position.y);
ctx.lineTo(position.x+5, position.y+5);
ctx.stroke();
}
else if (type == "bottom"){
// down arrow
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(position.x-5, position.y-5);
ctx.lineTo(position.x, position.y);
ctx.lineTo(position.x+5, position.y-5);
ctx.stroke();
}
}
/**
* Draw a cross - x
* @param position : position of the cross
*/
lines.drawCross = function(position){
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(position.x - 5, position.y + 5);
ctx.lineTo(position.x + 5, position.y - 5);
ctx.moveTo(position.x - 5, position.y - 5);
ctx.lineTo(position.x + 5, position.y + 5);
ctx.stroke();
};
/**
* Calculate the amount of offset for distribution guidelines
* @param nodes - list of nodes
* @param type - horizontal or vertical
*/
calculateOffset = function(nodes, type){
var minNode = nodes[0], min = lines.getDims(minNode)[type]["center"];
var maxNode = nodes[0], max = lines.getDims(maxNode)[type]["center"];
for (var i = 0; i < nodes.length; i++){
var node = nodes[i];
if (lines.getDims(node)[type]["center"] < min){
min = lines.getDims(node)[type]["center"]; minNode = node;
}
if (lines.getDims(node)[type]["center"] > max){
max = lines.getDims(node)[type]["center"]; maxNode = node;
}
}
if (type == "horizontal")
var offset = (min + max) / 2 < lines.getDims(nodes[1])[type]["center"] ? max + (0.5*maxNode.width() + options.guidelinesStyle.distGuidelineOffset)*cy.zoom() : min - (0.5*minNode.width() + options.guidelinesStyle.distGuidelineOffset)*cy.zoom();
else
var offset = (min + max) / 2 < lines.getDims(nodes[1])[type]["center"] ? max + (0.5*maxNode.height() + options.guidelinesStyle.distGuidelineOffset)*cy.zoom() : min - (0.5*minNode.height() + options.guidelinesStyle.distGuidelineOffset)*cy.zoom();
return offset;
}
/** Guidelines for horizontally distributed alignment
* @param: node the node to be aligned
*/
lines.horizontalDistribution = function(node){
// variables
var leftNode = null, rightNode = null;
var nodeDim = lines.getDims(node);
var Xcenter = nodeDim["horizontal"]["center"];
var Ycenter = nodeDim["vertical"]["center"];
// Find nodes in range and check if they align
HTree.forEach(function(key, nodes){
for (var i = 0; i < nodes.length; i++){
var left = nodes[i];
var leftDim = lines.getDims(left);
if (Math.abs(leftDim["vertical"]["center"] - nodeDim["vertical"]["center"]) < options.guidelinesStyle.range*cy.zoom()){
if ((leftDim["horizontal"]["right"]) == key &&
nodeDim["horizontal"]["left"] - leftDim["horizontal"]["right"] > options.guidelinesStyle.minDistRange){
var ripo = Math.round(2*Xcenter)-key;
HTree.forEach(function($, rightNodes){
for (var j = 0; j < rightNodes.length; j++){
var right = rightNodes[j];
if (Math.abs(lines.getDims(right)["vertical"]["center"] - Ycenter) < options.guidelinesStyle.range*cy.zoom()){
if (Math.abs(ripo - lines.getDims(right)["horizontal"]["left"]) < 2*options.guidelinesTolerance){
leftNode = left; rightNode = right;
}
}
}
}, ripo - options.guidelinesTolerance, ripo + options.guidelinesTolerance);
}
}
}
}, Xcenter - options.guidelinesStyle.range*cy.zoom(), Xcenter);
// Draw the lines
if (leftNode){
alignedLocations.hd = Xcenter - (lines.getDims(rightNode)["horizontal"]["left"] + lines.getDims(leftNode)["horizontal"]["right"]) / 2.0;
if (!options.geometricGuideline || alignedLocations.h == null || Math.abs(alignedLocations.h) > Math.abs(alignedLocations.hd)){
alignedLocations.h = alignedLocations.hd;
}
var offset = calculateOffset([leftNode, node, rightNode], "vertical");
lines.drawLine({
x: lines.getDims(leftNode)["horizontal"]["right"],
y: offset
}, {
x: nodeDim["horizontal"]["left"],
y: offset
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(rightNode)["horizontal"]["left"],
y: offset
}, {
x: nodeDim["horizontal"]["right"],
y: offset
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(leftNode)["horizontal"]["right"],
y: offset
}, {
x: lines.getDims(leftNode)["horizontal"]["right"],
y: lines.getDims(leftNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(rightNode)["horizontal"]["left"],
y: offset
}, {
x: lines.getDims(rightNode)["horizontal"]["left"],
y: lines.getDims(rightNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: nodeDim["horizontal"]["left"],
y: offset
}, {
x: nodeDim["horizontal"]["left"],
y: Ycenter
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: nodeDim["horizontal"]["right"],
y: offset
}, {
x: nodeDim["horizontal"]["right"],
y: Ycenter
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawArrow({
x: lines.getDims(leftNode)["horizontal"]["right"],
y: offset}, "left");
lines.drawArrow({
x: nodeDim["horizontal"]["left"],
y: offset}, "right");
lines.drawArrow({
x: nodeDim["horizontal"]["right"],
y: offset}, "left");
lines.drawArrow({
x: lines.getDims(rightNode)["horizontal"]["left"],
y: offset}, "right");
}
else{
var state = lines.horizontalDistributionNext(node,"left" );
if (!state)
lines.horizontalDistributionNext(node,"right" );
}
}
/** Guidelines for horizontally distributed alignment
* @param: node the node to be aligned
*/
lines.verticalDistribution = function(node){
// variables
var belowNode = null, aboveNode = null;
var nodeDim = lines.getDims(node);
var Xcenter = nodeDim["horizontal"]["center"];
var Ycenter = nodeDim["vertical"]["center"];
// Find nodes in range and check if they align
VTree.forEach(function(key, nodes){
for (var i = 0; i < nodes.length; i++){
var below = nodes[i];
var belowDim = lines.getDims(below);
if (Math.abs(belowDim["horizontal"]["center"] - nodeDim["horizontal"]["center"]) < options.guidelinesStyle.range*cy.zoom()){
if (belowDim["vertical"]["bottom"] == key &&
nodeDim["vertical"]["top"] - belowDim["vertical"]["bottom"] > options.guidelinesStyle.minDistRange){
var abpo = Math.round((2*Ycenter)-key);
VTree.forEach(function($, aboveNodes){
//if (aboveNodes){
for (var j = 0; j < aboveNodes.length; j++){
var above = aboveNodes[j];
if (Math.abs(lines.getDims(above)["horizontal"]["center"] - Xcenter) < options.guidelinesStyle.range*cy.zoom()){
if (Math.abs(abpo - lines.getDims(above)["vertical"]["top"]) < 2*options.guidelinesTolerance){
belowNode = below; aboveNode = above;
}
}
}
//}
}, abpo - options.guidelinesTolerance, abpo + options.guidelinesTolerance);
}
}
}
}, Ycenter - options.guidelinesStyle.range*cy.zoom(), Ycenter);
if (belowNode){
alignedLocations.vd = Ycenter - (lines.getDims(belowNode)["vertical"]["bottom"] + lines.getDims(aboveNode)["vertical"]["top"]) / 2.0;
if (!options.geometricGuideline || alignedLocations.v == null || Math.abs(alignedLocations.v) > Math.abs(alignedLocations.vd)){
alignedLocations.v = alignedLocations.vd;
}
var offset = calculateOffset([belowNode, node, aboveNode], "horizontal");
lines.drawLine({
y: lines.getDims(belowNode)["vertical"]["bottom"],//renderedPosition("x"),
x: offset
}, {
y: nodeDim["vertical"]["top"],
x: offset
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
y: lines.getDims(aboveNode)["vertical"]["top"],//renderedPosition("x"),
x: offset
}, {
y: nodeDim["vertical"]["bottom"],
x: offset
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
y: lines.getDims(belowNode)["vertical"]["bottom"],//renderedPosition("x"),
x: offset
}, {
y: lines.getDims(belowNode)["vertical"]["bottom"],
x: lines.getDims(belowNode)["horizontal"]["center"]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
y: lines.getDims(aboveNode)["vertical"]["top"],//renderedPosition("x"),
x: offset
}, {
y: lines.getDims(aboveNode)["vertical"]["top"],
x: lines.getDims(aboveNode)["horizontal"]["center"]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
y: nodeDim["vertical"]["bottom"],//renderedPosition("x"),
x: offset
}, {
y: nodeDim["vertical"]["bottom"],//renderedPosition("x"),
x: Xcenter
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
y: nodeDim["vertical"]["top"],//renderedPosition("x"),
x: offset
}, {
y: nodeDim["vertical"]["top"],//renderedPosition("x"),
x: Xcenter
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawArrow({
x: offset,
y: lines.getDims(belowNode)["vertical"]["bottom"]}, "top");
lines.drawArrow({
x: offset,
y: nodeDim["vertical"]["top"]}, "bottom");
lines.drawArrow({
x: offset,
y: lines.getDims(aboveNode)["vertical"]["top"]}, "bottom");
lines.drawArrow({
x: offset,
y: nodeDim["vertical"]["bottom"]}, "top");
}
else{
var state = lines.verticalDistributionNext(node,"below" );
if (!state)
lines.verticalDistributionNext(node,"above" );
}
}
/**
* Find geometric alignment lines and draw them
* @param type: horizontal or vertical
* @param node: the node to be aligned
*/
lines.searchForLine = function (type, node) {
// variables
var position, target, center, axis, otherAxis, Tree, closestKey;
var dims = lines.getDims(node)[type];
var targetKey = Number.MAX_SAFE_INTEGER;
// initialize Tree
if ( type == "horizontal"){
Tree = HTree;
axis = "y";
otherAxis = "x";
alignedLocations.h = null;
} else{
Tree = VTree;
axis = "x";
otherAxis = "y";
alignedLocations.v = null;
}
center = node.renderedPosition(axis);
// check if node aligned in any dimension:
// {center, left, right} or {center, top, bottom}
for (var dimKey in dims) {
position = dims[dimKey];
// find the closest alignment in range of tolerance
Tree.forEach(function (exKey, nodes) {
for (var i = 0; i < nodes.length; i++){
var n = nodes[i];
if (options.centerToEdgeAlignment || (dimKey != "center" && n.renderedPosition(otherAxis) != exKey) || (dimKey == "center" && n.renderedPosition(otherAxis) == exKey)){
var dif = Math.abs(center - n.renderedPosition(axis));
if ( dif < targetKey && dif < options.guidelinesStyle.geometricGuidelineRange*cy.zoom()){
target = n;
targetKey = dif;
closestKey = exKey;
}
}
}
}, position - Number(options.guidelinesTolerance), position + Number(options.guidelinesTolerance));
// if alignment found, draw lines and break
if (target) {
targetKey = lines.getDims(node)[type][dimKey];
// Draw horizontal or vertical alignment line
if (type == "horizontal") {
alignedLocations.h = targetKey - closestKey;
lines.drawLine({
x: targetKey,
y: node.renderedPosition("y")
}, {
x: targetKey,
y: target.renderedPosition("y")
}, options.guidelinesStyle.strokeStyle, options.guidelinesStyle.lineDash);
} else {
alignedLocations.v = targetKey - closestKey;
lines.drawLine({
x: node.renderedPosition("x"),
y: targetKey
}, {
x: target.renderedPosition("x"),
y: targetKey
}, options.guidelinesStyle.strokeStyle, options.guidelinesStyle.lineDash);
}
break;
}
}
};
lines.horizontalDistributionNext = function(node, type){
// variables
var leftNode = null, rightNode = null;
var nodeDim = lines.getDims(node);
var Xcenter = nodeDim["horizontal"]["center"];
var Ycenter = nodeDim["vertical"]["center"];
var side = "right", otherSide = "left";
var lowerBound = Xcenter;
if (type == "left"){
side = "left"; otherSide = "right";
var lowerBound = Xcenter - options.guidelinesStyle.range*cy.zoom();
}
var compare = {
"left": function (x, y) { return y - x > options.guidelinesStyle.minDistRange},
"right": function (x, y) { return x - y > options.guidelinesStyle.minDistRange}
}
// Find nodes in range and check if they align
HTree.forEach(function(key, nodes){
for (var i = 0; i < nodes.length; i++){
var left = nodes[i];
var leftDim = lines.getDims(left);
if (Math.abs(leftDim["vertical"]["center"] - nodeDim["vertical"]["center"]) < options.guidelinesStyle.range*cy.zoom()){
if ((leftDim["horizontal"][otherSide]) == key &&
compare[type](leftDim["horizontal"][otherSide], nodeDim["horizontal"][side])){
var ll = leftDim["horizontal"][side]-(nodeDim["horizontal"][side] - key);
HTree.forEach(function($, rightNodes){
for (var j = 0; j < rightNodes.length; j++){
var right = rightNodes[j];
if (Math.abs(lines.getDims(right)["vertical"]["center"] - Ycenter) < options.guidelinesStyle.range*cy.zoom()){
if (Math.abs(ll - lines.getDims(right)["horizontal"][otherSide]) < 2*options.guidelinesTolerance){
leftNode = left; rightNode = right;
}
}
}
}, ll - options.guidelinesTolerance, ll + options.guidelinesTolerance);
}
}
}
}, lowerBound, lowerBound + options.guidelinesStyle.range*cy.zoom());
// Draw the lines
if (leftNode){
alignedLocations.hd =(lines.getDims(node)["horizontal"][side] - lines.getDims(leftNode)["horizontal"][otherSide]) - (lines.getDims(leftNode)["horizontal"][side] - lines.getDims(rightNode)["horizontal"][otherSide]);
if (!options.geometricGuideline || alignedLocations.h == null || Math.abs(alignedLocations.h) > Math.abs(alignedLocations.hd)){
alignedLocations.h = alignedLocations.hd;
}
lines.drawDH(node, leftNode, rightNode, type);
return true;
}
else if (!options.geometricGuideline){
alignedLocations.h = null;
}
return false;
}
lines.drawDH = function(node, leftNode, rightNode, type){
var Ycenter = lines.getDims(node)["vertical"]["center"];
var side = "right", otherSide = "left";
if (type == "left"){
side = "left"; otherSide = "right";
}
var offset = calculateOffset([leftNode, node, rightNode], "vertical");
lines.drawLine({
x: lines.getDims(leftNode)["horizontal"][otherSide],
y: offset
}, {
x: lines.getDims(node)["horizontal"][side],
y: offset
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(node)["horizontal"][side],
y: offset
}, {
x: lines.getDims(node)["horizontal"][side],
y: Ycenter,//lines.getDims(leftNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(rightNode)["horizontal"][otherSide],
y: offset
}, {
x: lines.getDims(leftNode)["horizontal"][side],
y: offset
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(rightNode)["horizontal"][otherSide],
y: offset
}, {
x: lines.getDims(rightNode)["horizontal"][otherSide],
y: lines.getDims(rightNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(leftNode)["horizontal"][otherSide],
y: offset
}, {
x: lines.getDims(leftNode)["horizontal"][otherSide],
y: lines.getDims(leftNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawLine({
x: lines.getDims(leftNode)["horizontal"][side],
y: offset
}, {
x: lines.getDims(leftNode)["horizontal"][side],
y: lines.getDims(leftNode)["vertical"]["center"]
}, options.guidelinesStyle.horizontalDistColor, options.guidelinesStyle.horizontalDistLine);
lines.drawArrow({
x: lines.getDims(node)["horizontal"][side],
y: offset}, otherSide);
lines.drawArrow({
x: lines.getDims(leftNode)["horizontal"][otherSide],
y: offset}, side);
lines.drawArrow({
x: lines.getDims(leftNode)["horizontal"][side],
y: offset}, otherSide);
lines.drawArrow({
x: lines.getDims(rightNode)["horizontal"][otherSide],
y: offset}, side);
}
lines.verticalDistributionNext = function(node, type){
// variables
var belowNode = null, aboveNode = null;
var nodeDim = lines.getDims(node);
var Xcenter = nodeDim["horizontal"]["center"];
var Ycenter = nodeDim["vertical"]["center"];
var side = "top", otherSide = "bottom";
var lowerBound = Ycenter - options.guidelinesStyle.range*cy.zoom();
if (type == "above"){
side = "bottom"; otherSide = "top";
lowerBound = Ycenter;
}
var compare = {
"below": function (x, y) { return y - x > options.guidelinesStyle.minDistRange},
"above": function (x, y) { return x - y > options.guidelinesStyle.minDistRange}
}
// Find nodes in range and check if they align
VTree.forEach(function(key, nodes){
for (var i = 0; i < nodes.length; i++){
var below = nodes[i];
var belowDim = lines.getDims(below);
if (Math.abs(belowDim["horizontal"]["center"] - nodeDim["horizontal"]["center"]) < options.guidelinesStyle.range*cy.zoom()){
if (belowDim["vertical"][otherSide] == key &&
compare[type](belowDim["vertical"][otherSide], nodeDim["vertical"][side])){
var ll = belowDim["vertical"][side]-(nodeDim["vertical"][side]-key);
VTree.forEach(function($, aboveNodes){
for (var j = 0; j < aboveNodes.length; j++){
var above = aboveNodes[j];
if (Math.abs(lines.getDims(above)["horizontal"]["center"] - Xcenter) < options.guidelinesStyle.range*cy.zoom()){
if (Math.abs(ll - lines.getDims(above)["vertical"][otherSide]) < 2*options.guidelinesTolerance){
belowNode = below; aboveNode = above;
}
}
}
}, ll - options.guidelinesTolerance, ll + options.guidelinesTolerance);
}
}
}
}, lowerBound, lowerBound+options.guidelinesStyle.range*cy.zoom());
if (belowNode){
alignedLocations.vd =(lines.getDims(node)["vertical"][side] - lines.getDims(belowNode)["vertical"][otherSide]) - (lines.getDims(belowNode)["vertical"][side] - lines.getDims(aboveNode)["vertical"][otherSide]);
if (!options.geometricGuideline || alignedLocations.v == null || Math.abs(alignedLocations.v) > Math.abs(alignedLocations.vd)){
alignedLocations.v = alignedLocations.vd;
}
lines.drawDV(node, belowNode, aboveNode, type);
return true;
}
else if (!options.geometricGuideline){
alignedLocations.v = null;
}
return false;
}
lines.drawDV = function(node, belowNode, aboveNode, type){
var nodeDim = lines.getDims(node);
var Xcenter = nodeDim["horizontal"]["center"];
var side = "top", otherSide = "bottom";
if (type == "above"){
side = "bottom"; otherSide = "top";
}
var offset = calculateOffset([belowNode, node, aboveNode], "horizontal");
lines.drawLine({
x: offset,
y: nodeDim["vertical"][side]
}, {
x: offset,
y: lines.getDims(belowNode)["vertical"][otherSide]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
x: offset,
y: lines.getDims(belowNode)["vertical"][side]
}, {
x: offset,
y: lines.getDims(aboveNode)["vertical"][otherSide]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
x: Xcenter,
y: nodeDim["vertical"][side]
}, {
x: offset,
y: nodeDim["vertical"][side]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
x: lines.getDims(belowNode)["horizontal"]["center"],
y: lines.getDims(belowNode)["vertical"][otherSide]
}, {
x: offset,
y: lines.getDims(belowNode)["vertical"][otherSide]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
x: lines.getDims(belowNode)["horizontal"]["center"],
y: lines.getDims(belowNode)["vertical"][side]
}, {
x: offset,
y: lines.getDims(belowNode)["vertical"][side]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawLine({
x: offset,//lines.getDims(aboveNode)["horizontal"]["center"],
y: lines.getDims(aboveNode)["vertical"][otherSide]
}, {
x: lines.getDims(aboveNode)["horizontal"]["center"],
y: lines.getDims(aboveNode)["vertical"][otherSide]
}, options.guidelinesStyle.verticalDistColor, options.guidelinesStyle.verticalDistLine);
lines.drawArrow({
x: offset,
y: nodeDim["vertical"][side]}, otherSide);
lines.drawArrow({
x: offset,
y: lines.getDims(belowNode)["vertical"][otherSide]}, side);
lines.drawArrow({
x: offset,
y: lines.getDims(belowNode)["vertical"][side]}, otherSide);
lines.drawArrow({
x: offset,
y: lines.getDims(aboveNode)["vertical"][otherSide]}, side);
}
lines.update = function (activeNodes) {
lines.clear();
if (options.initPosAlignment){
mouseLine(activeNodes);
}
activeNodes.each(function (node, i) {
if(typeof node === "number") {
node = i;
2016-08-31 14:09:37 +00:00
}
2018-12-03 06:39:51 +00:00
if (options.geometricGuideline){
lines.searchForLine("horizontal", node);
lines.searchForLine("vertical", node);
}
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
if (options.distributionGuidelines){
lines.horizontalDistribution(node);
lines.verticalDistribution(node);
}
});
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
};
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
lines.resize = function () {
resizeCanvas();
};
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
function getTopMostNodes(nodes) {
var nodesMap = {};
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
for (var i = 0; i < nodes.length; i++) {
nodesMap[nodes[i].id()] = true;
}
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
var roots = nodes.filter(function (ele, i) {
if(typeof ele === "number") {
ele = i;
}
var parent = ele.parent()[0];
while (parent != null) {
if (nodesMap[parent.id()]) {
return false;
}
parent = parent.parent()[0];
}
return true;
});
return roots;
}
var mouseInitPos = {};
var mouseRelativePos = {};
var getMousePos = function(e){
mouseInitPos = e.renderedPosition || e.cyRenderedPosition;
mouseRelativePos.x = mouseInitPos.x;
mouseRelativePos.y = mouseInitPos.y;
}
var setMousePos = function(panCurrPos){
mouseRelativePos.x += (panCurrPos.x - panInitPos.x);
mouseRelativePos.y += (panCurrPos.y - panInitPos.y);
panInitPos.x = panCurrPos.x; panInitPos.y = panCurrPos.y;
};
var mouseLine = function(node){
var nodeCurrentPos = node.renderedPosition();
if (Math.abs(nodeInitPos.y - nodeCurrentPos.y) < options.guidelinesTolerance){
lines.drawLine({
"x" : mouseRelativePos.x,
"y" : mouseInitPos.y
}, {
"x" : nodeCurrentPos.x,
"y" : mouseInitPos.y
}, options.guidelinesStyle.initPosAlignmentColor, options.guidelinesStyle.initPosAlignmentLine);
if (mouseInitPos.y == mouseRelativePos.y){
lines.drawCross(mouseRelativePos);
}
else{
lines.drawCross(mouseInitPos);
}
}
else if (Math.abs(nodeInitPos.x - nodeCurrentPos.x) < options.guidelinesTolerance){
lines.drawLine({
"x" : mouseInitPos.x,
"y" : mouseRelativePos.y
}, {
"x" : mouseInitPos.x,
"y" : nodeCurrentPos.y
}, options.guidelinesStyle.initPosAlignmentColor, options.guidelinesStyle.initPosAlignmentLine);
if (mouseInitPos.x == mouseRelativePos.x){
lines.drawCross(mouseRelativePos);
}
else{
lines.drawCross(mouseInitPos);
}
}
}
function moveNodes(positionDiff, nodes) {
// Get the descendants of top most nodes. Note that node.position() can move just the simple nodes.
var topMostNodes = getTopMostNodes(nodes);
var nodesToMove = topMostNodes.union(topMostNodes.descendants());
nodesToMove.filter(":childless").forEach(function(node, i) {
if(typeof node === "number") {
node = i;
}
var newPos = {x: positionDiff.x + node.renderedPosition("x"),
y: positionDiff.y + node.renderedPosition("y")};
node.renderedPosition(newPos);
});
}
var tappedNode;
cy.on("tapstart", "node", function(){tappedNode = this});
var currMousePos, oldMousePos = {"x": 0, "y": 0};
cy.on("mousemove", function(e){
currMousePos = e.renderedPosition || e.cyRenderedPosition;
if (nodeToAlign)
nodeToAlign.each(function (node, i){
if(typeof node === "number") {
node = i;
}
if (node.locked() && (Math.abs(currMousePos.x - oldMousePos.x) > 2*options.guidelinesTolerance
|| Math.abs(currMousePos.y - oldMousePos.y) > 2*options.guidelinesTolerance)){
node.unlock();
var diff = {};
diff.x = currMousePos.x - tappedNode.renderedPosition("x");
diff.y = currMousePos.y - tappedNode.renderedPosition("y");;
moveNodes(diff, node);
};
});
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
});
var nodeToAlign;
lines.snapToAlignmentLocation = function(activeNodes){
nodeToAlign = activeNodes;
activeNodes.each(function (node, i){
if(typeof node === "number") {
node = i;
}
var newPos = node.renderedPosition();
if (alignedLocations.h){
oldMousePos = currMousePos;
newPos.x -= alignedLocations.h;
node.renderedPosition(newPos);
}
if (alignedLocations.v){
oldMousePos = currMousePos;
newPos.y -= alignedLocations.v;
node.renderedPosition(newPos);
};
if (alignedLocations.v || alignedLocations.h){
alignedLocations.h = null;
alignedLocations.v = null;
nodeToAlign.lock();
}
});
lines.update(activeNodes);
}
return {
changeOptions: changeOptions,
lines: lines,
getTopMostNodes: getTopMostNodes,
getMousePos: getMousePos,
setMousePos: setMousePos,
resizeCanvas: resizeCanvas,
resetCanvas: resetCanvas,
}
};
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
},{"functional-red-black-tree":1}],7:[function(require,module,exports){
;(function(){ 'use strict';
2016-08-31 14:09:37 +00:00
2018-12-03 06:39:51 +00:00
// registers the extension on a cytoscape lib ref
var register = function(cytoscape, $){
if(!cytoscape || !$){ return; } // can't register if cytoscape unspecified
// flag that indicates if extension api functions are registed to cytoscape
// note that ideally these functions should not be directly registered to core from cytoscape.js
// extensions
var apiRegistered = false;
var defaults = {
// On/Off Modules
/* From the following four snap options, at most one should be true at a given time */
snapToGridOnRelease: true, // Snap to grid on release
snapToGridDuringDrag: false, // Snap to grid during drag
snapToAlignmentLocationOnRelease: false, // Snap to alignment location on release
snapToAlignmentLocationDuringDrag: false, // Snap to alignment location during drag
distributionGuidelines: false, //Distribution guidelines
geometricGuideline: false, // Geometric guidelines
initPosAlignment: false, // Guideline to initial mouse position
centerToEdgeAlignment: false, // Center tı edge alignment
resize: false, // Adjust node sizes to cell sizes
parentPadding: false, // Adjust parent sizes to cell sizes by padding
drawGrid: true, // Draw grid background
// General
gridSpacing: 20, // Distance between the lines of the grid.
zoomDash: true, // Determines whether the size of the dashes should change when the drawing is zoomed in and out if grid is drawn.
panGrid: false, // Determines whether the grid should move then the user moves the graph if grid is drawn.
gridStackOrder: -1, // Namely z-index
gridColor: '#dedede', // Color of grid lines
lineWidth: 1.0, // Width of grid lines
guidelinesStackOrder: 4, // z-index of guidelines
guidelinesTolerance: 2.00, // Tolerance distance for rendered positions of nodes' interaction.
guidelinesStyle: { // Set ctx properties of line. Properties are here:
strokeStyle: "#8b7d6b", // color of geometric guidelines
geometricGuidelineRange: 400, // range of geometric guidelines
range: 100, // max range of distribution guidelines
minDistRange: 10, // min range for distribution guidelines
distGuidelineOffset: 10, // shift amount of distribution guidelines
horizontalDistColor: "#ff0000", // color of horizontal distribution alignment
verticalDistColor: "#00ff00", // color of vertical distribution alignment
initPosAlignmentColor: "#0000ff", // color of alignment to initial location
lineDash: [0, 0], // line style of geometric guidelines
horizontalDistLine: [0, 0], // line style of horizontal distribıtion guidelines
verticalDistLine: [0, 0], // line style of vertical distribıtion guidelines
initPosAlignmentLine: [0, 0], // line style of alignment to initial mouse position
},
// Parent Padding
parentSpacing: -1 // -1 to set paddings of parents to gridSpacing
};
var _snapOnRelease = require("./snap_on_release");
var _snapToGridDuringDrag = require("./snap_during_drag");
var _drawGrid = require("./draw_grid");
var _resize = require("./resize");
var _eventsController = require("./events_controller");
var _guidelines = require("./guidelines");
var _parentPadding = require("./parentPadding");
var _alignment = require("./alignment");
var debounce = require("./debounce");
function getScratch(cy) {
if (!cy.scratch("_gridGuide")) {
cy.scratch("_gridGuide", { });
}
return cy.scratch("_gridGuide");
}
cytoscape( 'core', 'gridGuide', function(opts){
var cy = this;
// access the scratch pad for cy
var scratchPad = getScratch(cy);
// extend the already existing options for the instance or the default options
var options = $.extend(true, {}, scratchPad.options || defaults, opts);
// reset the options for the instance
scratchPad.options = options;
if (!scratchPad.initialized) {
var snap, resize, snapToGridDuringDrag, drawGrid, eventsController, guidelines, parentPadding, alignment;
snap = _snapOnRelease(cy, options.gridSpacing);
resize = _resize(options.gridSpacing);
snapToGridDuringDrag = _snapToGridDuringDrag(cy, snap);
drawGrid = _drawGrid(options, cy, $, debounce);
guidelines = _guidelines(options, cy, $, debounce);
parentPadding = _parentPadding(options, cy);
eventsController = _eventsController(cy, snap, resize, snapToGridDuringDrag, drawGrid, guidelines, parentPadding, $, options);
alignment = _alignment(cytoscape, cy, $, apiRegistered);
// mark that api functions are registered to cytoscape
apiRegistered = true;
eventsController.init(options);
// init params in scratchPad
scratchPad.initialized = true;
scratchPad.eventsController = eventsController;
}
else {
var eventsController = scratchPad.eventsController;
eventsController.syncWithOptions(options);
}
return this; // chainability
} ) ;
};
if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module
module.exports = register;
}
if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module
define('cytoscape-grid-guide', function(){
return register;
});
}
if( typeof cytoscape !== 'undefined' && $ ){ // expose to global cytoscape (i.e. window.cytoscape)
register( cytoscape, $ );
}
2016-08-31 14:09:37 +00:00
})();
2018-12-03 06:39:51 +00:00
},{"./alignment":2,"./debounce":3,"./draw_grid":4,"./events_controller":5,"./guidelines":6,"./parentPadding":8,"./resize":9,"./snap_during_drag":10,"./snap_on_release":11}],8:[function(require,module,exports){
2016-08-31 14:09:37 +00:00
module.exports = function (opts, cy) {
var options = opts;
var ppClass = "_gridParentPadding";
function initPadding() {
var padding = options.parentSpacing < 0 ? options.gridSpacing : options.parentSpacing;
cy.style()
.selector('.' + ppClass)
.style("compound-sizing-wrt-labels", "exclude")
.style("padding-left", padding)
.style("padding-right", padding)
.style("padding-top", padding)
.style("padding-bottom", padding)
.update();
}
function changeOptions(opts) {
options = opts;
padding = options.parentSpacing < 0 ? options.gridSpacing : options.parentSpacing;
initPadding();
}
function setPaddingOfParent(node, enable) {
if (enable)
node.addClass(ppClass);
else
node.removeClass(ppClass);
}
return {
changeOptions: changeOptions,
setPaddingOfParent: setPaddingOfParent
};
};
},{}],9:[function(require,module,exports){
module.exports = function (gridSpacing) {
var changeOptions = function (opts) {
gridSpacing = Number(opts.gridSpacing);
};
var getScratch = function (node) {
if (!node.scratch("_gridGuide"))
node.scratch("_gridGuide", {});
return node.scratch("_gridGuide");
};
function resizeNode(node) {
var width = node.width();
var height = node.height();
var newWidth = Math.round((width - gridSpacing) / (gridSpacing * 2)) * (gridSpacing * 2);
var newHeight = Math.round((height - gridSpacing) / (gridSpacing * 2)) * (gridSpacing * 2);
newWidth = newWidth > 0 ? newWidth + gridSpacing : gridSpacing;
newHeight = newHeight > 0 ? newHeight + gridSpacing : gridSpacing;
if (width != newWidth || height != newHeight) {
node.style({
"width": newWidth,
"height": newHeight
});
getScratch(node).resize = {
oldWidth: width,
oldHeight: height
};
}
}
function recoverNodeDimensions(node) {
var oldSizes = getScratch(node).resize;
if (oldSizes)
node.style({
"width": oldSizes.oldWidth,
"height": oldSizes.oldHeight
});
}
return {
resizeNode: resizeNode,
recoverNodeDimensions: recoverNodeDimensions,
changeOptions: changeOptions
};
};
},{}],10:[function(require,module,exports){
2018-12-03 06:39:51 +00:00
module.exports = function (cy, snap) {
var snapToGridDuringDrag = {};
var attachedNode;
var draggedNodes;
var startPos;
var endPos;
snapToGridDuringDrag.onTapStartNode = function (e) {
// If user intends to do box selection, then return. Related issue #28
if (e.originalEvent.altKey || e.originalEvent.ctrlKey
|| e.originalEvent.metaKey || e.originalEvent.shiftKey){
return;
}
var cyTarget = e.target || e.cyTarget;
if (cyTarget.selected())
draggedNodes = e.cy.$(":selected");
else
draggedNodes = cyTarget;
startPos = e.position || e.cyPosition;
if (cyTarget.grabbable() && !cyTarget.locked()){
attachedNode = cyTarget;
attachedNode.lock();
//attachedNode.trigger("grab");
cy.on("tapdrag", onTapDrag);
cy.on("tapend", onTapEndNode);
}
};
var onTapEndNode = function (e) {
//attachedNode.trigger("free");
cy.off("tapdrag", onTapDrag);
cy.off("tapend", onTapEndNode);
attachedNode.unlock();
e.preventDefault();
};
var getDist = function () {
return {
x: endPos.x - startPos.x,
y: endPos.y - startPos.y
}
};
var onTapDrag = function (e) {
var nodePos = attachedNode.position();
endPos = e.position || e.cyPosition;
endPos = snap.snapPos(endPos);
var dist = getDist();
if (dist.x != 0 || dist.y != 0) {
attachedNode.unlock();
var nodes = draggedNodes.union(draggedNodes.descendants());
nodes.filter(":childless").positions(function (node, i) {
if(typeof node === "number") {
node = i;
}
var pos = node.position();
return snap.snapPos({
x: pos.x + dist.x,
y: pos.y + dist.y
});
});
startPos = endPos;
attachedNode.lock();
attachedNode.trigger("drag");
}
};
return snapToGridDuringDrag;
};
},{}],11:[function(require,module,exports){
module.exports = function (cy, gridSpacing) {
2016-08-31 14:09:37 +00:00
var snap = { };
snap.changeOptions = function (opts) {
gridSpacing = opts.gridSpacing;
};
var getScratch = function (node) {
if (!node.scratch("_gridGuide"))
node.scratch("_gridGuide", {});
return node.scratch("_gridGuide");
};
snap.snapPos = function (pos) {
var newPos = {
x: (Math.floor(pos.x / gridSpacing) + 0.5) * gridSpacing,
y: (Math.floor(pos.y / gridSpacing) + 0.5) * gridSpacing
};
return newPos;
};
snap.snapNode = function (node) {
var pos = node.position();
var newPos = snap.snapPos(pos);
node.position(newPos);
};
snap.snapNodesTopDown = function (nodes) {
// getTOpMostNodes -> nodes
cy.startBatch();
2018-12-03 06:39:51 +00:00
nodes.union(nodes.descendants()).filter(":childless").positions(function (node, i) {
if(typeof node === "number") {
node = i;
}
2016-08-31 14:09:37 +00:00
var pos = node.position();
return snap.snapPos(pos);
});
cy.endBatch();
};
snap.onFreeNode = function (e) {
var nodes;
2018-12-03 06:39:51 +00:00
var cyTarget = e.target || e.cyTarget;
if (cyTarget.selected())
2016-08-31 14:09:37 +00:00
nodes = e.cy.$(":selected");
else
2018-12-03 06:39:51 +00:00
nodes = cyTarget;
2016-08-31 14:09:37 +00:00
snap.snapNodesTopDown(nodes);
};
snap.recoverSnapNode = function (node) {
var snapScratch = getScratch(node).snap;
if (snapScratch) {
node.position(snapScratch.oldPos);
}
};
return snap;
};
2018-12-03 06:39:51 +00:00
2016-08-31 14:09:37 +00:00
},{}]},{},[7]);