META-INF.resources.primefaces.mindmap.mindmap.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of primefaces Show documentation
Show all versions of primefaces Show documentation
PrimeFaces is one of the most popular UI libraries in Java EE Ecosystem and widely used by software companies, world renowned brands, banks, financial institutions, insurance companies, universities and more.
/**
* PrimeFaces Mindmap Widget
*/
PrimeFaces.widget.Mindmap = PrimeFaces.widget.DeferredWidget.extend({
init: function(cfg) {
this._super(cfg);
this.renderDeferred();
},
_render: function() {
this.cfg.width = this.jq.width();
this.cfg.height = this.jq.height();
this.cfg.centerX = this.cfg.width / 2;
this.cfg.centerY = this.cfg.height / 2;
this.raphael = new Raphael(this.id, this.cfg.width, this.cfg.height);
this.nodes = [];
if(this.cfg.model) {
//root
this.root = this.createNode(this.cfg.centerX, this.cfg.centerY, this.cfg.model);
//children
if(this.cfg.model.children) {
this.createSubNodes(this.root);
}
}
this.tooltip = $('').appendTo(document.body);
},
createNode: function(x, y, model) {
var node = this.raphael.ellipse(x, y, 40, 25).attr('opacity', 0)
.data('model', model)
.data('connections', [])
.data('widget', this.cfg.widgetVar);
var label = model.label,
nodeWidth = node.getBBox().width,
title = null;
var text = this.raphael.text(x, y, label).attr('opacity', 0);
if(nodeWidth <= text.getBBox().width) {
title = label;
label = label.substring(0, 12);
text.attr('text', label + '...');
}
text.data('node', node);
node.data('text', text);
//node options
if(model.fill) {
node.attr({fill: '#' + model.fill});
}
//title
if(title) {
node.data('title', title);
node.mouseover(this.mouseoverNode);
node.mouseout(this.mouseoutNode);
text.mouseover(this.mouseoverText);
text.mouseout(this.mouseoutText);
}
//show
node.animate({opacity:1}, this.cfg.effectSpeed);
text.animate({opacity:1}, this.cfg.effectSpeed);
//make draggable
node.drag(this.nodeDrag, this.nodeDragStart, this.nodeDragEnd);
text.drag(this.textDrag, this.textDragStart, this.textDragEnd);
//events
if(model.selectable) {
node.click(this.clickNode);
text.click(this.clickNodeText);
node.attr({cursor:'pointer'});
text.attr({cursor:'pointer'});
}
//add to nodes
this.nodes.push(node);
return node;
},
mouseoverNode: function() {
var _self = PF(this.data('widget'));
_self.showTooltip(this);
},
mouseoutNode: function() {
var _self = PF(this.data('widget'));
_self.hideTooltip(this);
},
mouseoverText: function() {
var node = this.data('node'),
_self = PF(node.data('widget'));
_self.showTooltip(node);
},
mouseoutText: function() {
var node = this.data('node'),
_self = PF(node.data('widget'));
_self.hideTooltip(node);
},
showTooltip: function(node) {
var title = node.data('title');
if(title) {
var _self = PF(node.data('widget')),
offset = _self.jq.offset();
_self.tooltip.text(title)
.css(
{
'left': offset.left + node.attr('cx') + 20,
'top':offset.top + node.attr('cy') + 10,
'z-index': ++PrimeFaces.zindex
})
.show();
}
},
hideTooltip: function(node) {
var title = node.data('title');
if(title) {
var _self = PF(node.data('widget'));
_self.tooltip.hide();
}
},
centerNode: function(node) {
var _self = this,
text = node.data('text');
text.animate({x: this.cfg.centerX, y: this.cfg.centerY}, this.cfg.effectSpeed, '<>');
node.animate({cx: this.cfg.centerX, cy: this.cfg.centerY}, this.cfg.effectSpeed, '<>',
function() {
_self.createSubNodes(node);
});
//remove event handlers
node.unclick(this.clickNode);
text.unclick(this.clickNodeText);
node.attr({cursor:'default'});
text.attr({cursor:'default'});
},
createSubNodes: function(node) {
var nodeModel = node.data('model');
if(nodeModel.children) {
var size = nodeModel.children.length,
radius = 150,
capacity = parseInt((radius*2) / 25),
angleFactor = (360 / Math.min(size, capacity)),
capacityCounter = 0;
//children
for(var i = 0 ; i < size; i++) {
var childModel = nodeModel.children[i];
capacityCounter++;
//coordinates
var angle = ((angleFactor * (i + 1)) / 180) * Math.PI,
x = node.attr('cx') + radius * Math.cos(angle),
y = node.attr('cy') + radius * Math.sin(angle);
var childNode = this.createNode(x, y, childModel);
//connection
var connection = this.raphael.connection(node, childNode, "#000", null, this.cfg.effectSpeed);
node.data('connections').push(connection);
childNode.data('connections').push(connection);
//new ring
if(capacityCounter === capacity) {
radius = radius + 125;
capacity = parseInt((radius*2) / 25);
angleFactor = (360 / Math.min(capacity, (size - (i + 1) )));
capacityCounter = 0;
}
}
}
//parent
var parentModel = nodeModel.parent;
if(parentModel) {
parentModel.selectable = true;
var parentNode = this.createNode(60, 40, parentModel);
//connection
var parentConnection = this.raphael.connection(node, parentNode, "#000", null, this.cfg.effectSpeed);
node.data('connections').push(parentConnection);
parentNode.data('connections').push(parentConnection);
}
},
handleNodeClick: function(node) {
if(node.dragged) {
node.dragged = false;
return;
}
var _self = this,
clickTimeout = node.data('clicktimeout');
if(clickTimeout) {
clearTimeout(clickTimeout);
node.removeData('clicktimeout');
_self.handleDblclickNode(node);
}
else {
var timeout = setTimeout(function() {
_self.expandNode(node);
}, 300);
node.data('clicktimeout', timeout);
}
},
clickNode: function() {
var _self = PF(this.data('widget'));
_self.handleNodeClick(this);
},
clickNodeText: function() {
var node = this.data('node'),
_self = PF(node.data('widget'));
_self.handleNodeClick(node);
},
handleDblclickNode: function(node) {
if(this.hasBehavior('dblselect')) {
var key = node.data('model').key;
var ext = {
params: [
{name: this.id + '_nodeKey', value: key}
]
};
this.callBehavior('dblselect', ext);
}
},
expandNode: function(node) {
var $this = this,
key = node.data('model').key,
ext = {
update: this.id,
params: [
{name: this.id + '_nodeKey', value: key}
],
onsuccess: function(responseXML, status, xhr) {
PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
widget: $this,
handle: function(content) {
var nodeModel = JSON.parse(content);
//update model
node.data('model', nodeModel);
node.data('connections', []);
//remove other nodes
for(var j = 0; j < this.nodes.length; j++) {
var otherNode = this.nodes[j],
nodeKey = otherNode.data('model').key;
if(nodeKey !== key) {
this.removeNode(otherNode);
}
}
this.nodes = [];
this.nodes.push(node);
this.centerNode(node);
}
});
return true;
}
};
this.callBehavior('select', ext);
},
removeNode: function(node) {
//test
node.data('text').remove();
//connections
var connections = node.data('connections');
for(var i = 0; i < connections.length; i++) {
connections[i].line.remove();
}
//data
node.removeData();
//ellipse
node.animate({opacity:0}, this.cfg.effectSpeed, null, function() {
this.remove();
});
},
nodeDragStart: function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
},
nodeDrag: function(dx, dy) {
//update location
this.attr({cx: this.ox + dx, cy: this.oy + dy});
//drag text
this.data('text').attr({x: this.attr('cx'), y: this.attr('cy')});
//update connections
var _self = PF(this.data('widget'));
_self.updateConnections(this);
//flag to prevent drag to invoke nodeClick
this.dragged = true;
},
nodeDragEnd: function () {
},
textDragStart: function () {
this.ox = this.attr("x");
this.oy = this.attr("y");
},
textDrag: function(dx, dy) {
var node = this.data('node');
//update location
this.attr({x: this.ox + dx, y: this.oy + dy});
//drag node
node.attr({cx: this.attr('x'), cy: this.attr('y')});
//update connections
var _self = PF(node.data('widget'));
_self.updateConnections(node);
//flag to prevent drag to invoke nodeClick
node.dragged = true;
},
textDragEnd: function () {
}
,updateConnections: function(node) {
var connections = node.data('connections');
for(var i = 0; i < connections.length; i++) {
this.raphael.connection(connections[i]);
}
this.raphael.safari();
}
});
Raphael.fn.connection = function (obj1, obj2, line, bg, effectSpeed) {
if (obj1.line && obj1.from && obj1.to) {
line = obj1;
obj1 = line.from;
obj2 = line.to;
}
var bb1 = obj1.getBBox(),
bb2 = obj2.getBBox(),
p = [{x: bb1.x + bb1.width / 2, y: bb1.y - 1},
{x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + 1},
{x: bb1.x - 1, y: bb1.y + bb1.height / 2},
{x: bb1.x + bb1.width + 1, y: bb1.y + bb1.height / 2},
{x: bb2.x + bb2.width / 2, y: bb2.y - 1},
{x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + 1},
{x: bb2.x - 1, y: bb2.y + bb2.height / 2},
{x: bb2.x + bb2.width + 1, y: bb2.y + bb2.height / 2}],
d = {}, dis = [];
for (var i = 0; i < 4; i++) {
for (var j = 4; j < 8; j++) {
var dx = Math.abs(p[i].x - p[j].x),
dy = Math.abs(p[i].y - p[j].y);
if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
dis.push(dx + dy);
d[dis[dis.length - 1]] = [i, j];
}
}
}
if (dis.length == 0) {
var res = [0, 4];
} else {
res = d[Math.min.apply(Math, dis)];
}
var x1 = p[res[0]].x,
y1 = p[res[0]].y,
x4 = p[res[1]].x,
y4 = p[res[1]].y;
dx = Math.max(Math.abs(x1 - x4) / 2, 10);
dy = Math.max(Math.abs(y1 - y4) / 2, 10);
var x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(",");
if (line && line.line) {
line.bg && line.bg.attr({path: path});
line.line.attr({path: path});
} else {
var color = typeof line == "string" ? line : "#000",
path = this.path(path).attr({stroke: color, fill: "none"}).attr('opacity', 0).animate({opacity:1}, effectSpeed);
path.toBack();
return {
bg: bg && bg.split && this.path(path).attr({stroke: bg.split("|")[0], fill: "none", "stroke-width": bg.split("|")[1] || 3}),
line: path,
from: obj1,
to: obj2
};
}
};