1519 lines
70 KiB
JavaScript
Executable File
1519 lines
70 KiB
JavaScript
Executable File
/*
|
|
* jQuery OrgChart Plugin
|
|
* https://github.com/dabeng/OrgChart
|
|
*
|
|
* Demos of jQuery OrgChart Plugin
|
|
* http://dabeng.github.io/OrgChart/
|
|
*
|
|
* Copyright 2016, dabeng
|
|
* http://dabeng.github.io/
|
|
*
|
|
* Licensed under the MIT license:
|
|
* http://www.opensource.org/licenses/MIT
|
|
*/
|
|
'use strict';
|
|
|
|
(function(factory) {
|
|
if (typeof module === 'object' && typeof module.exports === 'object') {
|
|
factory(require('jquery'), window, document);
|
|
} else {
|
|
factory(jQuery, window, document);
|
|
}
|
|
}(function($, window, document, undefined) {
|
|
$.fn.orgchart = function(options) {
|
|
var defaultOptions = {
|
|
'nodeTitle': 'name',
|
|
'nodeId': 'id',
|
|
'toggleSiblingsResp': false,
|
|
'depth': 999,
|
|
'chartClass': '',
|
|
'exportButton': false,
|
|
'exportFilename': 'OrgChart',
|
|
'exportFileextension': 'png',
|
|
'parentNodeSymbol': 'fa-users',
|
|
'draggable': false,
|
|
'direction': 't2b',
|
|
'pan': false,
|
|
'zoom': false,
|
|
'zoominLimit': 10,
|
|
'zoomoutLimit': 0.1,
|
|
'menu': false,
|
|
'menu_edit': 'edit',
|
|
'menu_delete': 'delete',
|
|
'unit_name': 'Unit'
|
|
|
|
};
|
|
|
|
switch (options) {
|
|
case 'buildHierarchy':
|
|
return buildHierarchy.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'addChildren':
|
|
return addChildren.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'addParent':
|
|
return addParent.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'addSiblings':
|
|
return addSiblings.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'removeNodes':
|
|
return removeNodes.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'removeNode':
|
|
return removeNode.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'getHierarchy':
|
|
return getHierarchy.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'hideParent':
|
|
return hideParent.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'showParent':
|
|
return showParent.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'hideChildren':
|
|
return hideChildren.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'showChildren':
|
|
return showChildren.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'hideSiblings':
|
|
return hideSiblings.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'showSiblings':
|
|
return showSiblings.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'getNodeState':
|
|
return getNodeState.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'getRelatedNodes':
|
|
return getRelatedNodes.apply(this, Array.prototype.splice.call(arguments, 1));
|
|
case 'updateNode':
|
|
return updateNode.apply(this, Array.prototype.splice.call(arguments, 1))
|
|
case 'setMenuEnabled':
|
|
return setMenuEnabled.apply(this, Array.prototype.splice.call(arguments, 1))
|
|
default: // initiation time
|
|
var opts = $.extend(defaultOptions, options);
|
|
}
|
|
|
|
// build the org-chart
|
|
var $chartContainer = this;
|
|
if ($chartContainer.children().length)
|
|
{
|
|
$chartContainer.empty();
|
|
}
|
|
window.draggedId = -1;
|
|
window.modId = 10000;
|
|
var data = opts.data;
|
|
var $chart = $('<div>', {
|
|
'data': { 'options': opts },
|
|
'class': 'orgchart' + (opts.chartClass !== '' ? ' ' + opts.chartClass : '') + (opts.direction !== 't2b' ? ' ' + opts.direction : ''),
|
|
'click': function(event) {
|
|
if (!$(event.target).closest('.node').length)
|
|
{
|
|
$chart.find('.node.focused').removeClass('focused');
|
|
}
|
|
}
|
|
});
|
|
if ($.type(data) === 'object') {
|
|
if (data instanceof $) { // ul datasource
|
|
buildHierarchy($chart, buildJsonDS(data.children()), 0, opts);
|
|
} else { // local json datasource
|
|
buildHierarchy($chart, opts.ajaxURL ? data : attachRel(data, '00'), 0, opts);
|
|
}
|
|
} else {
|
|
$.ajax({
|
|
'url': data,
|
|
'dataType': 'json',
|
|
'beforeSend': function () {
|
|
$chart.append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>');
|
|
}
|
|
})
|
|
.done(function(data, textStatus, jqXHR) {
|
|
buildHierarchy($chart, opts.ajaxURL ? data : attachRel(data, '00'), 0, opts);
|
|
})
|
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
|
console.log(errorThrown);
|
|
})
|
|
.always(function() {
|
|
$chart.children('.spinner').remove();
|
|
});
|
|
}
|
|
$chartContainer.append($chart);
|
|
|
|
// append the export button
|
|
if (opts.exportButton && !$chartContainer.find('.oc-export-btn').length) {
|
|
var $exportBtn = $('<button>', {
|
|
'class': 'oc-export-btn' + (opts.chartClass !== '' ? ' ' + opts.chartClass : ''),
|
|
'text': 'Export',
|
|
'click': function(e) {
|
|
e.preventDefault();
|
|
if ($(this).children('.spinner').length) {
|
|
return false;
|
|
}
|
|
var $mask = $chartContainer.find('.mask');
|
|
if (!$mask.length) {
|
|
$chartContainer.append('<div class="mask"><i class="fa fa-circle-o-notch fa-spin spinner"></i></div>');
|
|
} else {
|
|
$mask.removeClass('hidden');
|
|
}
|
|
var sourceChart = $chartContainer.addClass('canvasContainer').find('.orgchart:visible').get(0);
|
|
var flag = opts.direction === 'l2r' || opts.direction === 'r2l';
|
|
html2canvas(sourceChart, {
|
|
'width': flag ? sourceChart.clientHeight : sourceChart.clientWidth,
|
|
'height': flag ? sourceChart.clientWidth : sourceChart.clientHeight,
|
|
'onclone': function(cloneDoc) {
|
|
$(cloneDoc).find('.canvasContainer').css('overflow', 'visible')
|
|
.find('.orgchart:visible:first').css('transform', '');
|
|
},
|
|
'onrendered': function(canvas) {
|
|
$chartContainer.find('.mask').addClass('hidden');
|
|
if (opts.exportFileextension.toLowerCase() === 'pdf') {
|
|
var doc = {};
|
|
var docWidth = Math.floor(canvas.width * 0.2646);
|
|
var docHeight = Math.floor(canvas.height * 0.2646);
|
|
if (docWidth > docHeight) {
|
|
doc = new jsPDF('l', 'mm', [docWidth, docHeight]);
|
|
} else {
|
|
doc = new jsPDF('p', 'mm', [docHeight, docWidth]);
|
|
}
|
|
doc.addImage(canvas.toDataURL(), 'png', 0, 0);
|
|
doc.save(opts.exportFilename + '.pdf');
|
|
} else {
|
|
var isWebkit = 'WebkitAppearance' in document.documentElement.style;
|
|
var isFf = !!window.sidebar;
|
|
var isEdge = navigator.appName === 'Microsoft Internet Explorer' || (navigator.appName === "Netscape" && navigator.appVersion.indexOf('Edge') > -1);
|
|
|
|
if ((!isWebkit && !isFf) || isEdge) {
|
|
window.navigator.msSaveBlob(canvas.msToBlob(), opts.exportFilename + '.png');
|
|
}
|
|
else {
|
|
$chartContainer.find('.oc-download-btn').attr('href', canvas.toDataURL())[0].click();
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.then(function() {
|
|
$chartContainer.removeClass('canvasContainer');
|
|
}, function() {
|
|
$chartContainer.removeClass('canvasContainer');
|
|
});
|
|
}
|
|
});
|
|
$chartContainer.append($exportBtn);
|
|
if (opts.exportFileextension.toLowerCase() !== 'pdf') {
|
|
var downloadBtn = '<a class="oc-download-btn' + (opts.chartClass !== '' ? ' ' + opts.chartClass : '') + '"'
|
|
+ ' download="' + opts.exportFilename + '.png"></a>';
|
|
$exportBtn.after(downloadBtn);
|
|
}
|
|
}
|
|
|
|
if (opts.pan) {
|
|
$chartContainer.css('overflow', 'hidden');
|
|
$chart.on('mousedown touchstart',function(e){
|
|
var $this = $(this);
|
|
if ($(e.target).closest('.node').length || (e.touches && e.touches.length > 1)) {
|
|
$this.data('panning', false);
|
|
return;
|
|
} else {
|
|
$this.css('cursor', 'move').data('panning', true);
|
|
}
|
|
var lastX = 0;
|
|
var lastY = 0;
|
|
var lastTf = $this.css('transform');
|
|
if (lastTf !== 'none') {
|
|
var temp = lastTf.split(',');
|
|
if (lastTf.indexOf('3d') === -1) {
|
|
lastX = parseInt(temp[4]);
|
|
lastY = parseInt(temp[5]);
|
|
} else {
|
|
lastX = parseInt(temp[12]);
|
|
lastY = parseInt(temp[13]);
|
|
}
|
|
}
|
|
var startX = 0;
|
|
var startY = 0;
|
|
if (!e.targetTouches) { // pand on desktop
|
|
startX = e.pageX - lastX;
|
|
startY = e.pageY - lastY;
|
|
} else if (e.targetTouches.length === 1) { // pan on mobile device
|
|
startX = e.targetTouches[0].pageX - lastX;
|
|
startY = e.targetTouches[0].pageY - lastY;
|
|
} else if (e.targetTouches.length > 1) {
|
|
return;
|
|
}
|
|
$chart.on('mousemove touchmove',function(e) {
|
|
if (!$this.data('panning')) {
|
|
return;
|
|
}
|
|
var newX = 0;
|
|
var newY = 0;
|
|
if (!e.targetTouches) { // pand on desktop
|
|
newX = e.pageX - startX;
|
|
newY = e.pageY - startY;
|
|
} else if (e.targetTouches.length === 1) { // pan on mobile device
|
|
newX = e.targetTouches[0].pageX - startX;
|
|
newY = e.targetTouches[0].pageY - startY;
|
|
} else if (e.targetTouches.length > 1) {
|
|
return;
|
|
}
|
|
var lastTf = $this.css('transform');
|
|
if (lastTf === 'none') {
|
|
if (lastTf.indexOf('3d') === -1) {
|
|
$this.css('transform', 'matrix(1, 0, 0, 1, ' + newX + ', ' + newY + ')');
|
|
} else {
|
|
$this.css('transform', 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + newX + ', ' + newY + ', 0, 1)');
|
|
}
|
|
} else {
|
|
var matrix = lastTf.split(',');
|
|
if (lastTf.indexOf('3d') === -1) {
|
|
matrix[4] = ' ' + newX;
|
|
matrix[5] = ' ' + newY + ')';
|
|
} else {
|
|
matrix[12] = ' ' + newX;
|
|
matrix[13] = ' ' + newY;
|
|
}
|
|
$this.css('transform', matrix.join(','));
|
|
}
|
|
});
|
|
});
|
|
$(document).on('mouseup touchend',function(e) {
|
|
if ($chart.data('panning')) {
|
|
$chart.data('panning', false).css('cursor', 'default').off('mousemove');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (opts.zoom) {
|
|
$chartContainer.on('wheel', function(event) {
|
|
event.preventDefault();
|
|
var newScale = 1 + (event.originalEvent.deltaY > 0 ? -0.2 : 0.2);
|
|
setChartScale($chart, newScale);
|
|
});
|
|
|
|
$chartContainer.on('touchstart',function(e){
|
|
if(e.touches && e.touches.length === 2) {
|
|
$chart.data('pinching', true);
|
|
var dist = getPinchDist(e);
|
|
$chart.data('pinchDistStart', dist);
|
|
}
|
|
});
|
|
$(document).on('touchmove',function(e) {
|
|
if($chart.data('pinching')) {
|
|
var dist = getPinchDist(e);
|
|
$chart.data('pinchDistEnd', dist);
|
|
}
|
|
})
|
|
.on('touchend',function(e) {
|
|
if($chart.data('pinching')) {
|
|
$chart.data('pinching', false);
|
|
var diff = $chart.data('pinchDistEnd') - $chart.data('pinchDistStart');
|
|
if (diff > 0) {
|
|
setChartScale($chart, 1.2);
|
|
} else if (diff < 0) {
|
|
setChartScale($chart, 0.8);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return $chartContainer;
|
|
};
|
|
|
|
function getPinchDist(e) {
|
|
return Math.sqrt((e.touches[0].clientX - e.touches[1].clientX) * (e.touches[0].clientX - e.touches[1].clientX) +
|
|
(e.touches[0].clientY - e.touches[1].clientY) * (e.touches[0].clientY - e.touches[1].clientY));
|
|
}
|
|
|
|
function setChartScale($chart, newScale) {
|
|
var opts = $chart.data('options');
|
|
var lastTf = $chart.css('transform');
|
|
var matrix = '';
|
|
var targetScale = 1;
|
|
if (lastTf === 'none') {
|
|
$chart.css('transform', 'scale(' + newScale + ',' + newScale + ')');
|
|
} else {
|
|
matrix = lastTf.split(',');
|
|
if (lastTf.indexOf('3d') === -1) {
|
|
targetScale = window.parseFloat(matrix[3]) * newScale;
|
|
if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) {
|
|
$chart.css('transform', lastTf + ' scale(' + newScale + ',' + newScale + ')');
|
|
}
|
|
} else {
|
|
targetScale = window.parseFloat(matrix[1]) * newScale;
|
|
if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) {
|
|
$chart.css('transform', lastTf + ' scale3d(' + newScale + ',' + newScale + ', 1)');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function buildJsonDS($li) {
|
|
var subObj = {
|
|
'name': $li.contents().eq(0).text().trim(),
|
|
'relationship': ($li.parent().parent().is('li') ? '1': '0') + ($li.siblings('li').length ? 1: 0) + ($li.children('ul').length ? 1 : 0)
|
|
};
|
|
if ($li[0].id) {
|
|
subObj.id = $li[0].id;
|
|
}
|
|
$li.children('ul').children().each(function() {
|
|
if (!subObj.children) { subObj.children = []; }
|
|
subObj.children.push(buildJsonDS($(this)));
|
|
});
|
|
return subObj;
|
|
}
|
|
|
|
function attachRel(data, flags) {
|
|
data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0);
|
|
if (data.children) {
|
|
data.children.forEach(function(item) {
|
|
attachRel(item, '1' + (data.children.length > 1 ? 1 : 0));
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function loopChart($chart) {
|
|
var $tr = $chart.find('tr:first');
|
|
var subObj =
|
|
{
|
|
'id': parseInt($tr.find('.node')[0].id),
|
|
'ddsId': parseInt($tr.find('.ddsid').text()),
|
|
'typeId': parseInt($tr.find('.typeid').text()),
|
|
'symbol': $tr.find('.symbol').text(),
|
|
'eqNr': parseInt($tr.find('.eqnr').text()),
|
|
'isUnit': $tr.find('.unit').text(),
|
|
'assignState': parseInt($tr.find('.assignstate').text()),
|
|
'name': $tr.find('.title').text(),
|
|
'info': $tr.find('.info').text(),
|
|
'tactIdentifier': $tr.find('.tactidentifier').text(),
|
|
'sign': $tr.find('.sign').text(),
|
|
'designator': $tr.find('.designator').text(),
|
|
'title': $tr.find('.content').text()
|
|
};
|
|
//console.error(JSON.stringify(subObj));
|
|
|
|
$tr.siblings(':last').children().each(function() {
|
|
if (!subObj.children)
|
|
{
|
|
subObj.children = [];
|
|
}
|
|
subObj.children.push(loopChart($(this)));
|
|
});
|
|
return subObj;
|
|
}
|
|
|
|
function updateNode($updateData)
|
|
{
|
|
|
|
var $obj = JSON.parse($updateData);
|
|
// console.error($updateData);
|
|
var $chart = $(this).find('.orgchart');
|
|
$chart.find('tr').each(function ()
|
|
{
|
|
|
|
var node = $( this );
|
|
if(parseInt(node.find('.ddsid').text()) !== $obj.ddsId)
|
|
{
|
|
return true;
|
|
}
|
|
var opts = node.closest('.orgchart').data('options');
|
|
if($obj.image)
|
|
{
|
|
node.find('.image').html($obj.image);
|
|
}
|
|
node.find('.typeid').text($obj.typeId);
|
|
node.find('.ddsid').text($obj.ddsId);
|
|
node.find('.symbol').text($obj.symbol);
|
|
node.find('.eqnr').text($obj.eqNr) ;
|
|
node.find('.tactidentifier').text($obj.tactIdentifier) ;
|
|
node.find('.designator').text($obj.designator) ;
|
|
node.find('.info').text($obj.info);
|
|
node.find('.sign').text($obj.sign);
|
|
node.find('.assignstate').text($obj.assignState);
|
|
node.find('.title').text($obj[opts.nodeTitle]);
|
|
node.find('.unit').text( $obj.isUnit === 'true' ? opts.unit_name : $obj.eqNr);
|
|
return false;
|
|
});
|
|
return true;
|
|
|
|
}
|
|
|
|
function setMenuEnabled($isEnabled)
|
|
{
|
|
|
|
}
|
|
|
|
function getHierarchy($chart)
|
|
{
|
|
var $chart = $chart || $(this).find('.orgchart');
|
|
// console.error(JSON.stringify($chart))
|
|
// console.error(JSON.stringify($(this).find('.orgchart').find('.node:first')))
|
|
if (!$chart.find('.node:first')[0].id)
|
|
{
|
|
return 'Error: Nodes of orghcart to be exported must have id attribute!';
|
|
}
|
|
return loopChart($chart);
|
|
}
|
|
|
|
// detect the exist/display state of related node
|
|
function getNodeState($node, relation)
|
|
{
|
|
var $target = {};
|
|
if (relation === 'parent') {
|
|
$target = $node.closest('.nodes').siblings(':first');
|
|
} else if (relation === 'children') {
|
|
$target = $node.closest('tr').siblings();
|
|
} else if (relation === 'siblings') {
|
|
$target = $node.closest('table').parent().siblings();
|
|
}
|
|
if ($target.length) {
|
|
if ($target.is(':visible')) {
|
|
return { 'exist': true, 'visible': true };
|
|
}
|
|
return { 'exist': true, 'visible': false };
|
|
}
|
|
return { 'exist': false, 'visible': false };
|
|
}
|
|
|
|
// find the related nodes
|
|
function getRelatedNodes($node, relation) {
|
|
if (relation === 'parent') {
|
|
return $node.closest('.nodes').parent().children(':first').find('.node');
|
|
} else if (relation === 'children') {
|
|
return $node.closest('table').children(':last').children().find('.node:first');
|
|
} else if (relation === 'siblings') {
|
|
return $node.closest('table').parent().siblings().find('.node:first');
|
|
}
|
|
}
|
|
|
|
// recursively hide the ancestor node and sibling nodes of the specified node
|
|
function hideParent($node) {
|
|
var $temp = $node.closest('table').closest('tr').siblings();
|
|
if ($temp.eq(0).find('.spinner').length) {
|
|
$node.closest('.orgchart').data('inAjax', false);
|
|
}
|
|
// hide the sibling nodes
|
|
if (getNodeState($node, 'siblings').visible) {
|
|
hideSiblings($node);
|
|
}
|
|
// hide the lines
|
|
var $lines = $temp.slice(1);
|
|
$lines.css('visibility', 'hidden');
|
|
// hide the superior nodes with transition
|
|
var $parent = $temp.eq(0).find('.node');
|
|
var grandfatherVisible = getNodeState($parent, 'parent').visible;
|
|
if ($parent.length && $parent.is(':visible')) {
|
|
$parent.addClass('slide slide-down').one('transitionend', function() {
|
|
$parent.removeClass('slide');
|
|
$lines.removeAttr('style');
|
|
$temp.addClass('hidden');
|
|
});
|
|
}
|
|
// if the current node has the parent node, hide it recursively
|
|
if ($parent.length && grandfatherVisible) {
|
|
hideParent($parent);
|
|
}
|
|
}
|
|
|
|
// show the parent node of the specified node
|
|
function showParent($node) {
|
|
// just show only one superior level
|
|
var $temp = $node.closest('table').closest('tr').siblings().removeClass('hidden');
|
|
// just show only one line
|
|
$temp.eq(2).children().slice(1, -1).addClass('hidden');
|
|
// show parent node with animation
|
|
var parent = $temp.eq(0).find('.node')[0];
|
|
repaint(parent);
|
|
$(parent).addClass('slide').removeClass('slide-down').one('transitionend', function() {
|
|
$(parent).removeClass('slide');
|
|
if (isInAction($node)) {
|
|
switchVerticalArrow($node.children('.topEdge'));
|
|
}
|
|
});
|
|
}
|
|
|
|
// recursively hide the descendant nodes of the specified node
|
|
function hideChildren($node) {
|
|
var $temp = $node.closest('tr').siblings();
|
|
if ($temp.last().find('.spinner').length) {
|
|
$node.closest('.orgchart').data('inAjax', false);
|
|
}
|
|
var $visibleNodes = $temp.last().find('.node:visible');
|
|
var isVerticalDesc = $temp.last().is('.verticalNodes') ? true : false;
|
|
if (!isVerticalDesc) {
|
|
var $lines = $visibleNodes.closest('table').closest('tr').prevAll('.lines').css('visibility', 'hidden');
|
|
}
|
|
$visibleNodes.addClass('slide slide-up').eq(0).one('transitionend', function() {
|
|
$visibleNodes.removeClass('slide');
|
|
if (isVerticalDesc) {
|
|
$temp.addClass('hidden');
|
|
} else {
|
|
$lines.removeAttr('style').addClass('hidden').siblings('.nodes').addClass('hidden');
|
|
$temp.last().find('.verticalNodes').addClass('hidden');
|
|
}
|
|
if (isInAction($node)) {
|
|
switchVerticalArrow($node.children('.bottomEdge'));
|
|
}
|
|
});
|
|
}
|
|
|
|
// show the children nodes of the specified node
|
|
function showChildren($node) {
|
|
var $levels = $node.closest('tr').siblings();
|
|
var isVerticalDesc = $levels.is('.verticalNodes') ? true : false;
|
|
var $descendants = isVerticalDesc
|
|
? $levels.removeClass('hidden').find('.node:visible')
|
|
: $levels.removeClass('hidden').eq(2).children().find('.node:first');
|
|
// the two following statements are used to enforce browser to repaint
|
|
repaint($descendants.get(0));
|
|
$descendants.addClass('slide').removeClass('slide-up').eq(0).one('transitionend', function() {
|
|
$descendants.removeClass('slide');
|
|
if (isInAction($node)) {
|
|
switchVerticalArrow($node.children('.bottomEdge'));
|
|
}
|
|
console.log("ShowChildren: " + $node)
|
|
});
|
|
}
|
|
|
|
// hide the sibling nodes of the specified node
|
|
function hideSiblings($node, direction) {
|
|
var $nodeContainer = $node.closest('table').parent();
|
|
if ($nodeContainer.siblings().find('.spinner').length) {
|
|
$node.closest('.orgchart').data('inAjax', false);
|
|
}
|
|
if (direction) {
|
|
if (direction === 'left') {
|
|
$nodeContainer.prevAll().find('.node:visible').addClass('slide slide-right');
|
|
} else {
|
|
$nodeContainer.nextAll().find('.node:visible').addClass('slide slide-left');
|
|
}
|
|
} else {
|
|
$nodeContainer.prevAll().find('.node:visible').addClass('slide slide-right');
|
|
$nodeContainer.nextAll().find('.node:visible').addClass('slide slide-left');
|
|
}
|
|
var $animatedNodes = $nodeContainer.siblings().find('.slide');
|
|
var $lines = $animatedNodes.closest('.nodes').prevAll('.lines').css('visibility', 'hidden');
|
|
$animatedNodes.eq(0).one('transitionend', function() {
|
|
$lines.removeAttr('style');
|
|
var $siblings = direction ? (direction === 'left' ? $nodeContainer.prevAll(':not(.hidden)') : $nodeContainer.nextAll(':not(.hidden)')) : $nodeContainer.siblings();
|
|
$nodeContainer.closest('.nodes').prev().children(':not(.hidden)')
|
|
.slice(1, direction ? $siblings.length * 2 + 1 : -1).addClass('hidden');
|
|
$animatedNodes.removeClass('slide');
|
|
$siblings.find('.node:visible:gt(0)').removeClass('slide-left slide-right').addClass('slide-up')
|
|
.end().find('.lines, .nodes, .verticalNodes').addClass('hidden')
|
|
.end().addClass('hidden');
|
|
|
|
if (isInAction($node)) {
|
|
switchHorizontalArrow($node);
|
|
}
|
|
});
|
|
}
|
|
|
|
// show the sibling nodes of the specified node
|
|
function showSiblings($node, direction) {
|
|
console.log("show");
|
|
|
|
// firstly, show the sibling td tags
|
|
var $siblings = $();
|
|
if (direction) {
|
|
if (direction === 'left') {
|
|
$siblings = $node.closest('table').parent().prevAll().removeClass('hidden');
|
|
} else {
|
|
$siblings = $node.closest('table').parent().nextAll().removeClass('hidden');
|
|
}
|
|
} else {
|
|
$siblings = $node.closest('table').parent().siblings().removeClass('hidden');
|
|
}
|
|
// secondly, show the lines
|
|
var $upperLevel = $node.closest('table').closest('tr').siblings();
|
|
if (direction) {
|
|
$upperLevel.eq(2).children('.hidden').slice(0, $siblings.length * 2).removeClass('hidden');
|
|
} else {
|
|
$upperLevel.eq(2).children('.hidden').removeClass('hidden');
|
|
}
|
|
// thirdly, do some cleaning stuff
|
|
if (!getNodeState($node, 'parent').visible) {
|
|
$upperLevel.removeClass('hidden');
|
|
var parent = $upperLevel.find('.node')[0];
|
|
repaint(parent);
|
|
$(parent).addClass('slide').removeClass('slide-down').one('transitionend', function() {
|
|
$(this).removeClass('slide');
|
|
});
|
|
}
|
|
// lastly, show the sibling nodes with animation
|
|
$siblings.find('.node:visible').addClass('slide').removeClass('slide-left slide-right').eq(-1).one('transitionend', function() {
|
|
$siblings.find('.node:visible').removeClass('slide');
|
|
if (isInAction($node)) {
|
|
switchHorizontalArrow($node);
|
|
$node.children('.topEdge').removeClass('fa-chevron-up').addClass('fa-chevron-down');
|
|
}
|
|
});
|
|
}
|
|
|
|
// start up loading status for requesting new nodes
|
|
function startLoading($arrow, $node, options) {
|
|
var $chart = $node.closest('.orgchart');
|
|
if (typeof $chart.data('inAjax') !== 'undefined' && $chart.data('inAjax') === true) {
|
|
return false;
|
|
}
|
|
|
|
$arrow.addClass('hidden');
|
|
$node.append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>');
|
|
$node.children().not('.spinner').css('opacity', 0.2);
|
|
$chart.data('inAjax', true);
|
|
$('.oc-export-btn' + (options.chartClass !== '' ? '.' + options.chartClass : '')).prop('disabled', true);
|
|
return true;
|
|
}
|
|
|
|
// terminate loading status for requesting new nodes
|
|
function endLoading($arrow, $node, options) {
|
|
var $chart = $node.closest('div.orgchart');
|
|
$arrow.removeClass('hidden');
|
|
$node.find('.spinner').remove();
|
|
$node.children().removeAttr('style');
|
|
$chart.data('inAjax', false);
|
|
$('.oc-export-btn' + (options.chartClass !== '' ? '.' + options.chartClass : '')).prop('disabled', false);
|
|
}
|
|
|
|
// whether the cursor is hovering over the node
|
|
function isInAction($node) {
|
|
return $node.children('.edge').attr('class').indexOf('fa-') > -1 ? true : false;
|
|
}
|
|
|
|
function switchVerticalArrow($arrow) {
|
|
$arrow.toggleClass('fa-chevron-up').toggleClass('fa-chevron-down');
|
|
}
|
|
|
|
function switchHorizontalArrow($node)
|
|
{
|
|
var opts = $node.closest('.orgchart').data('options');
|
|
if (opts.toggleSiblingsResp && (typeof opts.ajaxURL === 'undefined' || $node.closest('.nodes').data('siblingsLoaded')))
|
|
{
|
|
var $prevSib = $node.closest('table').parent().prev();
|
|
if ($prevSib.length)
|
|
{
|
|
if ($prevSib.is('.hidden'))
|
|
{
|
|
$node.children('.leftEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right');
|
|
} else
|
|
{
|
|
$node.children('.leftEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left');
|
|
}
|
|
}
|
|
var $nextSib = $node.closest('table').parent().next();
|
|
if ($nextSib.length)
|
|
{
|
|
if ($nextSib.is('.hidden'))
|
|
{
|
|
$node.children('.rightEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left');
|
|
} else
|
|
{
|
|
$node.children('.rightEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right');
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
var $sibs = $node.closest('table').parent().siblings();
|
|
var sibsVisible = $sibs.length ? !$sibs.is('.hidden') : false;
|
|
$node.children('.leftEdge').toggleClass('fa-chevron-right', sibsVisible).toggleClass('fa-chevron-left', !sibsVisible);
|
|
$node.children('.rightEdge').toggleClass('fa-chevron-left', sibsVisible).toggleClass('fa-chevron-right', !sibsVisible);
|
|
}
|
|
}
|
|
|
|
function repaint(node)
|
|
{
|
|
if (node)
|
|
{
|
|
node.style.offsetWidth = node.offsetWidth;
|
|
}
|
|
}
|
|
|
|
// create node
|
|
function createNode(nodeData, level, opts)
|
|
{
|
|
if(parseInt(nodeData.id) < 0)
|
|
nodeData.id = window.modId++;
|
|
console.log("create node NOW: "+ nodeData.id);
|
|
$.each(nodeData.children, function (index, child)
|
|
{
|
|
|
|
child.parentId = nodeData.id;
|
|
});
|
|
|
|
var dtd = $.Deferred();
|
|
console.log("create node");
|
|
// construct the content of node
|
|
var $nodeDiv = $('<div' + (opts.draggable ? ' draggable="true" ' : '')
|
|
+ (nodeData[opts.nodeId] ? ' id="' + nodeData[opts.nodeId] + '"' : '')
|
|
+ (nodeData.parentId ? ' data-parent="' + nodeData.parentId + '"' : '')
|
|
+ ' >')
|
|
.append('<div style="display: none;" class="ddsid">' + nodeData.ddsId + '</div>')
|
|
.append('<div style="display: none;" class="modid">' + nodeData[opts.nodeId] + '</div>')
|
|
.append('<div style="display: none;" class="typeid">' + nodeData.typeId + '</div>')
|
|
.append('<div style="display: none;" class="symbol">' + nodeData.symbol + '</div>')
|
|
.append('<div style="display: none;" class="eqnr">' + nodeData.eqNr + '</div>')
|
|
.append('<div style="display: none;" class="info">' + nodeData.info + '</div>')
|
|
.append('<div style="display: none;" class="assignstate">' + nodeData.assignState + '</div>')
|
|
.append('<div style="display: none;" class="tactidentifier">' + nodeData.tactIdentifier + '</div>')
|
|
.append('<div style="display: none;" class="sign">' + nodeData.sign + '</div>')
|
|
.append('<div style="display: none;" class="designator">' + nodeData.designator + '</div>')
|
|
.addClass('node ' + (nodeData.className || '') + (level >= opts.depth ? ' slide-up' : ''))
|
|
.append('<div class="image">'+ (nodeData.image ? nodeData.image : '') + '</div>')
|
|
.append('<div class="title">' + nodeData[opts.nodeTitle] + '</div>')
|
|
.append('<div class="unit">' + (nodeData.isUnit === 'true' ? opts.unit_name : nodeData.eqNr) + '</div>')
|
|
.append(typeof opts.nodeContent !== 'undefined' ? '<div class="content">' + (nodeData[opts.nodeContent] || '') + '</div>' : '');
|
|
// append 4 direction arrows or expand/collapse buttons
|
|
var flags = nodeData.relationship || '';
|
|
|
|
if (opts.verticalDepth && (level + 2) > opts.verticalDepth) {
|
|
if ((level + 1) >= opts.verticalDepth && Number(flags.substr(2,1))) {
|
|
var icon = level + 1 >= opts.depth ? 'plus' : 'minus';
|
|
$nodeDiv.append('<i class="toggleBtn fa fa-' + icon + '-square"></i>');
|
|
}
|
|
} else {
|
|
if (Number(flags.substr(0,1))) {
|
|
$nodeDiv.append('<b title="Show/Hide Parent" class="edge verticalEdge topEdge fa"></b>');
|
|
}
|
|
if(Number(flags.substr(1,1))) {
|
|
$nodeDiv.append('<b title="Show/Hide Siblings" class="edge horizontalEdge rightEdge fa"></b>' +
|
|
'<b title="Show/Hide Siblings" class="edge horizontalEdge leftEdge fa"></b>');
|
|
}
|
|
if(Number(flags.substr(2,1))) {
|
|
$nodeDiv.append(' <b title="Show/Hide Children" class="edge verticalEdge bottomEdge fa"></b>')
|
|
.children('.title').prepend('<i class="fa '+ opts.parentNodeSymbol + ' symbol"></i>');
|
|
}
|
|
}
|
|
|
|
$nodeDiv.on('mouseenter mouseleave', function(event) {
|
|
var $node = $(this), flag = false;
|
|
var $topEdge = $node.children('.topEdge');
|
|
var $rightEdge = $node.children('.rightEdge');
|
|
var $bottomEdge = $node.children('.bottomEdge');
|
|
var $leftEdge = $node.children('.leftEdge');
|
|
if (event.type === 'mouseenter') {
|
|
if ($topEdge.length) {
|
|
flag = getNodeState($node, 'parent').visible;
|
|
$topEdge.toggleClass('fa-chevron-up', !flag).toggleClass('fa-chevron-down', flag);
|
|
}
|
|
if ($bottomEdge.length) {
|
|
flag = getNodeState($node, 'children').visible;
|
|
$bottomEdge.toggleClass('fa-chevron-down', !flag).toggleClass('fa-chevron-up', flag);
|
|
}
|
|
if ($leftEdge.length) {
|
|
switchHorizontalArrow($node);
|
|
}
|
|
} else {
|
|
$node.children('.edge').removeClass('fa-chevron-up fa-chevron-down fa-chevron-right fa-chevron-left');
|
|
}
|
|
});
|
|
|
|
$nodeDiv.on('dblclick', function(event)
|
|
{
|
|
window.webNotifier.startDBClick(nodeData.ddsId);
|
|
});
|
|
// define click event handler
|
|
$nodeDiv.on('click', function(event)
|
|
{
|
|
//hide menu if already shown
|
|
var menu = $(".menu");
|
|
menu.hide();
|
|
window.currentSelectedDiv=-1;
|
|
$(this).addClass('focused');
|
|
return false
|
|
});
|
|
|
|
|
|
// define click event handler
|
|
$nodeDiv.on('contextmenu', function(e)
|
|
{
|
|
if(!opts.menu)
|
|
return;
|
|
var menu = $(".menu");
|
|
menu.find(".menuDelete").text(opts.menu_delete)
|
|
menu.find(".menuEdit").text(opts.menu_edit)
|
|
|
|
|
|
// window.currentSelectedId=nodeData.ddsId;
|
|
//create new object!
|
|
var $tr = $(this)
|
|
window.currentSelectedDiv =
|
|
{
|
|
'id': nodeData.id,
|
|
'ddsId': parseInt($tr.find('.ddsid').text()),
|
|
'typeId': parseInt($tr.find('.typeid').text()),
|
|
'symbol': $tr.find('.symbol').text(),
|
|
'eqNr': parseInt($tr.find('.eqnr').text()),
|
|
'isUnit': $tr.find('.unit').text(),
|
|
'name': $tr.find('.title').text(),
|
|
'info': $tr.find('.info').text(),
|
|
'assignState': parseInt($tr.find('.assignstate').text()),
|
|
'tactIdentifier': $tr.find('.tactidentifier').text(),
|
|
'sign': $tr.find('.sign').text(),
|
|
'designator': $tr.find('.designator').text(),
|
|
'title': $tr.find('.content').text()
|
|
};
|
|
//console.error(JSON.stringify(window.currentSelectedDiv))
|
|
|
|
//hide menu if already shown
|
|
menu.hide();
|
|
|
|
//get x and y values of the click event
|
|
var pageX = e.pageX;
|
|
var pageY = e.pageY;
|
|
|
|
//position menu div near mouse cliked area
|
|
menu.css({top: pageY , left: pageX});
|
|
|
|
var mwidth = menu.width();
|
|
var mheight = menu.height();
|
|
var screenWidth = $(window).width();
|
|
var screenHeight = $(window).height();
|
|
|
|
//if window is scrolled
|
|
var scrTop = $(window).scrollTop();
|
|
|
|
//if the menu is close to right edge of the window
|
|
if(pageX+mwidth > screenWidth){
|
|
menu.css({left:pageX-mwidth});
|
|
}
|
|
|
|
//if the menu is close to bottom edge of the window
|
|
if(pageY+mheight > screenHeight+scrTop){
|
|
menu.css({top:pageY-mheight});
|
|
}
|
|
|
|
//finally show the menu
|
|
menu.show();
|
|
//return false;
|
|
});
|
|
|
|
// define click event handler for the top edge
|
|
$nodeDiv.on('click', '.topEdge', function(event) {
|
|
event.stopPropagation();
|
|
var $that = $(this);
|
|
var $node = $that.parent();
|
|
var parentState = getNodeState($node, 'parent');
|
|
if (parentState.exist) {
|
|
var $parent = $node.closest('table').closest('tr').siblings(':first').find('.node');
|
|
if ($parent.is('.slide')) { return; }
|
|
// hide the ancestor nodes and sibling nodes of the specified node
|
|
if (parentState.visible) {
|
|
hideParent($node);
|
|
$parent.one('transitionend', function() {
|
|
if (isInAction($node)) {
|
|
switchVerticalArrow($that);
|
|
switchHorizontalArrow($node);
|
|
}
|
|
});
|
|
} else { // show the ancestors and siblings
|
|
showParent($node);
|
|
}
|
|
} else {
|
|
// load the new parent node of the specified node by ajax request
|
|
var nodeId = $that.parent()[0].id;
|
|
// start up loading status
|
|
if (startLoading($that, $node, opts)) {
|
|
// load new nodes
|
|
$.ajax({ 'url': $.isFunction(opts.ajaxURL.parent) ? opts.ajaxURL.parent(nodeData) : opts.ajaxURL.parent + nodeId, 'dataType': 'json' })
|
|
.done(function(data) {
|
|
if ($node.closest('.orgchart').data('inAjax')) {
|
|
if (!$.isEmptyObject(data)) {
|
|
addParent.call($node.closest('.orgchart').parent(), $node, data, opts);
|
|
}
|
|
}
|
|
})
|
|
.fail(function() { console.log('Failed to get parent node data'); })
|
|
.always(function() { endLoading($that, $node, opts); });
|
|
}
|
|
}
|
|
});
|
|
|
|
// bind click event handler for the bottom edge
|
|
$nodeDiv.on('click', '.bottomEdge', function(event) {
|
|
event.stopPropagation();
|
|
var $that = $(this);
|
|
var $node = $that.parent();
|
|
var childrenState = getNodeState($node, 'children');
|
|
if (childrenState.exist) {
|
|
var $children = $node.closest('tr').siblings(':last');
|
|
if ($children.find('.node:visible').is('.slide')) { return; }
|
|
// hide the descendant nodes of the specified node
|
|
if (childrenState.visible) {
|
|
hideChildren($node);
|
|
} else { // show the descendants
|
|
showChildren($node);
|
|
}
|
|
} else { // load the new children nodes of the specified node by ajax request
|
|
var nodeId = $that.parent()[0].id;
|
|
if (startLoading($that, $node, opts)) {
|
|
$.ajax({ 'url': $.isFunction(opts.ajaxURL.children) ? opts.ajaxURL.children(nodeData) : opts.ajaxURL.children + nodeId, 'dataType': 'json' })
|
|
.done(function(data, textStatus, jqXHR) {
|
|
if ($node.closest('.orgchart').data('inAjax')) {
|
|
if (data.children.length) {
|
|
addChildren($node, data, $.extend({}, opts, { depth: 0 }));
|
|
}
|
|
}
|
|
})
|
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
|
console.log('Failed to get children nodes data');
|
|
})
|
|
.always(function() {
|
|
endLoading($that, $node, opts);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// event handler for toggle buttons in Hybrid(horizontal + vertical) OrgChart
|
|
$nodeDiv.on('click', '.toggleBtn', function(event) {
|
|
var $this = $(this);
|
|
var $descWrapper = $this.parent().next();
|
|
var $descendants = $descWrapper.find('.node');
|
|
var $children = $descWrapper.children().children('.node');
|
|
if ($children.is('.slide')) { return; }
|
|
$this.toggleClass('fa-plus-square fa-minus-square');
|
|
if ($descendants.eq(0).is('.slide-up')) {
|
|
$descWrapper.removeClass('hidden');
|
|
repaint($children.get(0));
|
|
$children.addClass('slide').removeClass('slide-up').eq(0).one('transitionend', function() {
|
|
$children.removeClass('slide');
|
|
});
|
|
} else {
|
|
$descendants.addClass('slide slide-up').eq(0).one('transitionend', function() {
|
|
$descendants.removeClass('slide');
|
|
// $descWrapper.addClass('hidden');
|
|
$descendants.closest('ul').addClass('hidden');
|
|
});
|
|
$descendants.find('.toggleBtn').removeClass('fa-minus-square').addClass('fa-plus-square');
|
|
}
|
|
});
|
|
|
|
// bind click event handler for the left and right edges
|
|
$nodeDiv.on('click', '.leftEdge, .rightEdge', function(event) {
|
|
event.stopPropagation();
|
|
var $that = $(this);
|
|
var $node = $that.parent();
|
|
var siblingsState = getNodeState($node, 'siblings');
|
|
if (siblingsState.exist) {
|
|
var $siblings = $node.closest('table').parent().siblings();
|
|
if ($siblings.find('.node:visible').is('.slide')) { return; }
|
|
if (opts.toggleSiblingsResp) {
|
|
var $prevSib = $node.closest('table').parent().prev();
|
|
var $nextSib = $node.closest('table').parent().next();
|
|
if ($that.is('.leftEdge')) {
|
|
if ($prevSib.is('.hidden')) {
|
|
showSiblings($node, 'left');
|
|
} else {
|
|
hideSiblings($node, 'left');
|
|
}
|
|
} else {
|
|
if ($nextSib.is('.hidden')) {
|
|
showSiblings($node, 'right');
|
|
} else {
|
|
hideSiblings($node, 'right');
|
|
}
|
|
}
|
|
} else {
|
|
if (siblingsState.visible) {
|
|
hideSiblings($node);
|
|
} else {
|
|
showSiblings($node);
|
|
}
|
|
}
|
|
} else {
|
|
// load the new sibling nodes of the specified node by ajax request
|
|
var nodeId = $that.parent()[0].id;
|
|
var url = (getNodeState($node, 'parent').exist) ?
|
|
($.isFunction(opts.ajaxURL.siblings) ? opts.ajaxURL.siblings(nodeData) : opts.ajaxURL.siblings + nodeId) :
|
|
($.isFunction(opts.ajaxURL.families) ? opts.ajaxURL.families(nodeData) : opts.ajaxURL.families + nodeId);
|
|
if (startLoading($that, $node, opts)) {
|
|
$.ajax({ 'url': url, 'dataType': 'json' })
|
|
.done(function(data, textStatus, jqXHR) {
|
|
if ($node.closest('.orgchart').data('inAjax')) {
|
|
if (data.siblings || data.children) {
|
|
addSiblings($node, data, opts);
|
|
}
|
|
}
|
|
})
|
|
.fail(function(jqXHR, textStatus, errorThrown) {
|
|
console.log('Failed to get sibling nodes data');
|
|
})
|
|
.always(function() {
|
|
endLoading($that, $node, opts);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (opts.draggable)
|
|
{
|
|
$nodeDiv.on('dragstart', function(event)
|
|
{
|
|
|
|
var origEvent = event.originalEvent;
|
|
var isFirefox = /firefox/.test(window.navigator.userAgent.toLowerCase());
|
|
if (isFirefox) {
|
|
origEvent.dataTransfer.setData('text/html', 'hack for firefox');
|
|
}
|
|
// if users enable zoom or direction options
|
|
if ($nodeDiv.closest('.orgchart').css('transform') !== 'none')
|
|
{
|
|
|
|
var ghostNode, nodeCover;
|
|
if (!document.querySelector('.ghost-node')) {
|
|
ghostNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
ghostNode.classList.add('ghost-node');
|
|
nodeCover = document.createElementNS('http://www.w3.org/2000/svg','rect');
|
|
ghostNode.appendChild(nodeCover);
|
|
$nodeDiv.closest('.orgchart').append(ghostNode);
|
|
} else {
|
|
ghostNode = $nodeDiv.closest('.orgchart').children('.ghost-node').get(0);
|
|
nodeCover = $(ghostNode).children().get(0);
|
|
}
|
|
var transValues = $nodeDiv.closest('.orgchart').css('transform').split(',');
|
|
var scale = Math.abs(window.parseFloat((opts.direction === 't2b' || opts.direction === 'b2t') ? transValues[0].slice(transValues[0].indexOf('(') + 1) : transValues[1]));
|
|
ghostNode.setAttribute('width', $nodeDiv.outerWidth(false));
|
|
ghostNode.setAttribute('height', $nodeDiv.outerHeight(false));
|
|
nodeCover.setAttribute('x',5 * scale);
|
|
nodeCover.setAttribute('y',5 * scale);
|
|
nodeCover.setAttribute('width', 120 * scale);
|
|
nodeCover.setAttribute('height', 40 * scale);
|
|
nodeCover.setAttribute('rx', 4 * scale);
|
|
nodeCover.setAttribute('ry', 4 * scale);
|
|
nodeCover.setAttribute('stroke-width', 1 * scale);
|
|
var xOffset = origEvent.offsetX * scale;
|
|
var yOffset = origEvent.offsetY * scale;
|
|
if (opts.direction === 'l2r') {
|
|
xOffset = origEvent.offsetY * scale;
|
|
yOffset = origEvent.offsetX * scale;
|
|
} else if (opts.direction === 'r2l') {
|
|
xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetY * scale;
|
|
yOffset = origEvent.offsetX * scale;
|
|
} else if (opts.direction === 'b2t') {
|
|
xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetX * scale;
|
|
yOffset = $nodeDiv.outerHeight(false) - origEvent.offsetY * scale;
|
|
}
|
|
if (isFirefox) { // hack for old version of Firefox(< 48.0)
|
|
nodeCover.setAttribute('fill', 'rgb(255, 255, 255)');
|
|
nodeCover.setAttribute('stroke', 'rgb(191, 0, 0)');
|
|
var ghostNodeWrapper = document.createElement('img');
|
|
ghostNodeWrapper.src = 'data:image/svg+xml;utf8,' + (new XMLSerializer()).serializeToString(ghostNode);
|
|
origEvent.dataTransfer.setDragImage(ghostNodeWrapper, xOffset, yOffset);
|
|
} else {
|
|
origEvent.dataTransfer.setDragImage(ghostNode, xOffset, yOffset);
|
|
}
|
|
}
|
|
|
|
var $dragged = $(this);
|
|
window.draggedId = parseInt($dragged.find('.ddsid').text())
|
|
var $dragZone = $dragged.closest('.nodes').siblings().eq(0).find('.node:first');
|
|
var $dragHier = $dragged.closest('table').find('.node');
|
|
$dragged.closest('.orgchart')
|
|
.data('dragged', $dragged)
|
|
.find('.node').each(function(index, node)
|
|
{
|
|
if ($dragHier.index(node) === -1)
|
|
{
|
|
if (opts.dropCriteria)
|
|
{
|
|
if (opts.dropCriteria($dragged, $dragZone, $(node)))
|
|
{
|
|
$(node).addClass('allowedDrop');
|
|
}else
|
|
{
|
|
$(node).removeClass('allowedDrop');
|
|
}
|
|
} else
|
|
{
|
|
$(node).addClass('allowedDrop');
|
|
}
|
|
}
|
|
});
|
|
})
|
|
.on('mouseout', function()
|
|
{
|
|
var $dropZone = $(this);
|
|
var $orgchart = $dropZone.closest('.orgchart');
|
|
$orgchart.find('.allowedDrop').removeClass('allowedDrop');
|
|
window.draggedId = -1;
|
|
|
|
})
|
|
.on('dragover', function(event)
|
|
{
|
|
event.preventDefault();
|
|
|
|
//console.error(window.draggedId)
|
|
|
|
if(window.draggedId < 0) //drag from outer space
|
|
{
|
|
//console.error("HEZ THATS WRONG!")
|
|
//if we drag from outside just check local hover:
|
|
if( $(this).find('.unit').text() === opts.unit_name )
|
|
{
|
|
$(this).addClass('allowedDrop');
|
|
}
|
|
else
|
|
{
|
|
$(this).removeClass('allowedDrop');
|
|
}
|
|
}
|
|
if (!$(this).is('.allowedDrop'))
|
|
{
|
|
event.originalEvent.dataTransfer.dropEffect = 'none';
|
|
}
|
|
|
|
})
|
|
.on('dragend', function(event)
|
|
{
|
|
window.draggedId = -1;
|
|
$(this).closest('.orgchart').find('.allowedDrop').removeClass('allowedDrop');
|
|
})
|
|
.on('dragstop', function(event)
|
|
{
|
|
window.draggedId = -1;
|
|
$(this).closest('.orgchart').find('.allowedDrop').removeClass('allowedDrop');
|
|
})
|
|
.on('drop', function(event)
|
|
{
|
|
|
|
var $dropZone = $(this);
|
|
var $orgchart = $dropZone.closest('.orgchart');
|
|
event.preventDefault();
|
|
var $transferData = event.originalEvent.dataTransfer.getData("text/plain");
|
|
$orgchart.find('.allowedDrop').removeClass('allowedDrop');
|
|
window.draggedId = -1;
|
|
if(!!$transferData)
|
|
{
|
|
var $jsonData = JSON.parse($transferData);
|
|
$jsonData.parentId = parseInt($dropZone.find('.ddsId').text());
|
|
|
|
if($jsonData.id < 0)
|
|
$jsonData.id = modId++;
|
|
console.log($dropZone.closest('tr').siblings('.nodes').find('.node:first'));
|
|
console.log(getNodeState($dropZone, 'children'));
|
|
if(getNodeState($dropZone, 'children').exist)
|
|
{
|
|
console.log("has Children ad a sibling")
|
|
$jsonData.relationship = '110'
|
|
var $arr = [$jsonData]
|
|
|
|
var myObject = {
|
|
siblings: $arr
|
|
}
|
|
addSiblings($dropZone.closest('tr').siblings('.nodes').find('.node:first'), myObject, opts);
|
|
//addChildren($dropZone, myObject, opts);
|
|
|
|
}else
|
|
{
|
|
console.log("has no Children ad a new one")
|
|
$jsonData.relationship = '100'
|
|
var $arr = [$jsonData]
|
|
|
|
var myObject = {
|
|
children: $arr
|
|
}
|
|
|
|
|
|
addChildren($dropZone, myObject, opts);
|
|
}
|
|
}else
|
|
{
|
|
var $dragged = $orgchart.data('dragged');
|
|
// firstly, deal with the hierarchy of drop zone
|
|
var $dragZone = $dragged.closest('.nodes').siblings().eq(0).children();
|
|
|
|
if (!$dropZone.closest('tr').siblings().length) { // if the drop zone is a leaf node
|
|
$dropZone.append('<i class="edge verticalEdge bottomEdge fa"></i>')
|
|
.parent().attr('colspan', 2)
|
|
.parent().after('<tr class="lines"><td colspan="2"><div class="downLine"></div></td></tr>'
|
|
+ '<tr class="lines"><td class="rightLine"> </td><td class="leftLine"> </td></tr>'
|
|
+ '<tr class="nodes"></tr>')
|
|
.siblings(':last').append($dragged.find('.horizontalEdge').remove().end().closest('table').parent());
|
|
} else {
|
|
var dropColspan = parseInt($dropZone.parent().attr('colspan')) + 2;
|
|
var horizontalEdges = '<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>';
|
|
$dropZone.closest('tr').next().addBack().children().attr('colspan', dropColspan);
|
|
if (!$dragged.find('.horizontalEdge').length) {
|
|
$dragged.append(horizontalEdges);
|
|
}
|
|
$dropZone.closest('tr').siblings().eq(1).children(':last').before('<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>')
|
|
.end().next().append($dragged.closest('table').parent());
|
|
var $dropSibs = $dragged.closest('table').parent().siblings().find('.node:first');
|
|
if ($dropSibs.length === 1) {
|
|
$dropSibs.append(horizontalEdges);
|
|
}
|
|
}
|
|
// secondly, deal with the hierarchy of dragged node
|
|
var dragColspan = parseInt($dragZone.attr('colspan'));
|
|
if (dragColspan > 2) {
|
|
$dragZone.attr('colspan', dragColspan - 2)
|
|
.parent().next().children().attr('colspan', dragColspan - 2)
|
|
.end().next().children().slice(1, 3).remove();
|
|
var $dragSibs = $dragZone.parent().siblings('.nodes').children().find('.node:first');
|
|
if ($dragSibs.length ===1) {
|
|
$dragSibs.find('.horizontalEdge').remove();
|
|
}
|
|
} else {
|
|
$dragZone.removeAttr('colspan')
|
|
.find('.bottomEdge').remove()
|
|
.end().end().siblings().remove();
|
|
}
|
|
|
|
$orgchart.triggerHandler({ 'type': 'nodedropped.orgchart', 'draggedNode': $dragged, 'dragZone': $dragZone.children(), 'dropZone': $dropZone });
|
|
}
|
|
//console.log(getHierarchy());
|
|
window.webNotifier.doMoveChanged();
|
|
|
|
});
|
|
}
|
|
// allow user to append dom modification after finishing node create of orgchart
|
|
if (opts.createNode) {
|
|
opts.createNode($nodeDiv, nodeData);
|
|
}
|
|
dtd.resolve($nodeDiv);
|
|
return dtd.promise();
|
|
}
|
|
// recursively build the tree
|
|
function buildHierarchy ($appendTo, nodeData, level, opts, callback) {
|
|
var $nodeWrapper;
|
|
// Construct the node
|
|
var $childNodes = nodeData.children;
|
|
var hasChildren = $childNodes ? $childNodes.length : false;
|
|
var isVerticalNode = (opts.verticalDepth && (level + 1) >= opts.verticalDepth) ? true : false;
|
|
if (Object.keys(nodeData).length > 1) { // if nodeData has nested structure
|
|
$nodeWrapper = isVerticalNode ? $appendTo : $('<table>');
|
|
if (!isVerticalNode) {
|
|
$appendTo.append($nodeWrapper);
|
|
}
|
|
$.when(createNode(nodeData, level, opts))
|
|
.done(function($nodeDiv) {
|
|
if (isVerticalNode) {
|
|
$nodeWrapper.append($nodeDiv);
|
|
}else {
|
|
$nodeWrapper.append($nodeDiv.wrap('<tr><td' + (hasChildren ? ' colspan="' + $childNodes.length * 2 + '"' : '') + '></td></tr>').closest('tr'));
|
|
}
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
})
|
|
.fail(function() {
|
|
console.log('Failed to creat node')
|
|
});
|
|
}
|
|
// Construct the inferior nodes and connectiong lines
|
|
if (hasChildren) {
|
|
if (Object.keys(nodeData).length === 1) { // if nodeData is just an array
|
|
$nodeWrapper = $appendTo;
|
|
}
|
|
var isHidden = (level + 1 >= opts.depth || nodeData.collapsed) ? ' hidden' : '';
|
|
var isVerticalLayer = (opts.verticalDepth && (level + 2) >= opts.verticalDepth) ? true : false;
|
|
|
|
// draw the line close to parent node
|
|
if (!isVerticalLayer) {
|
|
$nodeWrapper.append('<tr class="lines' + isHidden + '"><td colspan="' + $childNodes.length * 2 + '"><div class="downLine"></div></td></tr>');
|
|
}
|
|
// draw the lines close to children nodes
|
|
var lineLayer = '<tr class="lines' + isHidden + '"><td class="rightLine"> </td>';
|
|
for (var i=1; i<$childNodes.length; i++) {
|
|
lineLayer += '<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>';
|
|
}
|
|
lineLayer += '<td class="leftLine"> </td></tr>';
|
|
var $nodeLayer;
|
|
if (isVerticalLayer) {
|
|
$nodeLayer = $('<ul>');
|
|
if (isHidden) {
|
|
$nodeLayer.addClass(isHidden);
|
|
}
|
|
if (level + 2 === opts.verticalDepth) {
|
|
$nodeWrapper.append('<tr class="verticalNodes' + isHidden + '"><td></td></tr>')
|
|
.find('.verticalNodes').children().append($nodeLayer);
|
|
} else {
|
|
$nodeWrapper.append($nodeLayer);
|
|
}
|
|
} else {
|
|
$nodeLayer = $('<tr class="nodes' + isHidden + '">');
|
|
$nodeWrapper.append(lineLayer).append($nodeLayer);
|
|
}
|
|
// recurse through children nodes
|
|
$.each($childNodes, function() {
|
|
var $nodeCell = isVerticalLayer ? $('<li>') : $('<td colspan="2">');
|
|
$nodeLayer.append($nodeCell);
|
|
buildHierarchy($nodeCell, this, level + 1, opts, callback);
|
|
});
|
|
}
|
|
}
|
|
|
|
// build the child nodes of specific node
|
|
function buildChildNode ($appendTo, nodeData, opts, callback) {
|
|
var opts = opts || $appendTo.closest('.orgchart').data('options');
|
|
var data = nodeData.children || nodeData.siblings;
|
|
console.log("buildChild");
|
|
console.log(nodeData);
|
|
console.log(data);
|
|
|
|
var length = 1;
|
|
if(data)
|
|
length = data.length;
|
|
$appendTo.find('td:first').attr('colspan', length * 2);
|
|
buildHierarchy($appendTo, { 'children': data }, 0, opts, callback);
|
|
}
|
|
// exposed method
|
|
function addChildren($node, data, opts) {
|
|
var opts = opts || $node.closest('.orgchart').data('options');
|
|
var count = 0;
|
|
console.log("AddChildren")
|
|
buildChildNode.call($node.closest('.orgchart').parent(), $node.closest('table'), data, opts, function()
|
|
{
|
|
console.log("BuildCHildNode: " + data.children.length);
|
|
if (++count === data.children.length)
|
|
{
|
|
if (!$node.children('.bottomEdge').length) {
|
|
$node.append('<i class="edge verticalEdge bottomEdge fa"></i>');
|
|
}
|
|
if (!$node.find('.symbol').length) {
|
|
$node.children('.title').prepend('<i class="fa '+ opts.parentNodeSymbol + ' symbol"></i>');
|
|
}
|
|
console.log($node);
|
|
|
|
showChildren($node);
|
|
}
|
|
});
|
|
}
|
|
|
|
// build the parent node of specific node
|
|
function buildParentNode($currentRoot, nodeData, opts, callback) {
|
|
var that = this;
|
|
var $table = $('<table>');
|
|
nodeData.relationship = nodeData.relationship || '001';
|
|
$.when(createNode(nodeData, 0, opts || $currentRoot.closest('.orgchart').data('options')))
|
|
.done(function($nodeDiv) {
|
|
$table.append($nodeDiv.removeClass('slide-up').addClass('slide-down').wrap('<tr class="hidden"><td colspan="2"></td></tr>').closest('tr'));
|
|
$table.append('<tr class="lines hidden"><td colspan="2"><div class="downLine"></div></td></tr>');
|
|
var linesRow = '<td class="rightLine"> </td><td class="leftLine"> </td>';
|
|
$table.append('<tr class="lines hidden">' + linesRow + '</tr>');
|
|
var $oc = that.children('.orgchart');
|
|
$oc.prepend($table)
|
|
.children('table:first').append('<tr class="nodes"><td colspan="2"></td></tr>')
|
|
.children('tr:last').children().append($oc.children('table').last());
|
|
callback();
|
|
})
|
|
.fail(function() {
|
|
console.log('Failed to create parent node');
|
|
});
|
|
}
|
|
|
|
// exposed method
|
|
function addParent($currentRoot, data, opts) {
|
|
buildParentNode.call(this, $currentRoot, data, opts, function() {
|
|
if (!$currentRoot.children('.topEdge').length) {
|
|
$currentRoot.children('.title').after('<i class="edge verticalEdge topEdge fa"></i>');
|
|
}
|
|
showParent($currentRoot);
|
|
});
|
|
}
|
|
|
|
// subsequent processing of build sibling nodes
|
|
function complementLine($oneSibling, siblingCount, existingSibligCount) {
|
|
var lines = '';
|
|
for (var i = 0; i < existingSibligCount; i++) {
|
|
lines += '<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>';
|
|
}
|
|
$oneSibling.parent().prevAll('tr:gt(0)').children().attr('colspan', siblingCount * 2)
|
|
.end().next().children(':first').after(lines);
|
|
}
|
|
|
|
// build the sibling nodes of specific node
|
|
function buildSiblingNode($nodeChart, nodeData, opts, callback) {
|
|
var opts = opts || $nodeChart.closest('.orgchart').data('options');
|
|
var newSiblingCount = nodeData.siblings ? nodeData.siblings.length : nodeData.children.length;
|
|
var existingSibligCount = $nodeChart.parent().is('td') ? $nodeChart.closest('tr').children().length : 1;
|
|
var siblingCount = existingSibligCount + newSiblingCount;
|
|
var insertPostion = (siblingCount > 1) ? Math.floor(siblingCount/2 - 1) : 0;
|
|
|
|
console.log("Build Sibling note from: " );
|
|
console.log(nodeData);
|
|
console.log("to: ");
|
|
console.log($nodeChart);
|
|
console.log(newSiblingCount)
|
|
|
|
// just build the sibling nodes for the specific node
|
|
if ($nodeChart.parent().is('td')) {
|
|
var $parent = $nodeChart.closest('tr').prevAll('tr:last');
|
|
$nodeChart.closest('tr').prevAll('tr:lt(2)').remove();
|
|
var childCount = 0;
|
|
buildChildNode.call($nodeChart.closest('.orgchart').parent(),$nodeChart.parent().closest('table'), nodeData, opts, function() {
|
|
if (++childCount === newSiblingCount) {
|
|
var $siblingTds = $nodeChart.parent().closest('table').children('tr:last').children('td');
|
|
if (existingSibligCount > 1) {
|
|
complementLine($siblingTds.eq(0).before($nodeChart.closest('td').siblings().addBack().unwrap()), siblingCount, existingSibligCount);
|
|
$siblingTds.addClass('hidden').find('.node').addClass('slide-left');
|
|
} else {
|
|
complementLine($siblingTds.eq(insertPostion).after($nodeChart.closest('td').unwrap()), siblingCount, 1);
|
|
$siblingTds.not(':eq(' + insertPostion + 1 + ')').addClass('hidden')
|
|
.slice(0, insertPostion).find('.node').addClass('slide-right')
|
|
.end().end().slice(insertPostion).find('.node').addClass('slide-left');
|
|
}
|
|
callback();
|
|
}
|
|
});
|
|
} else { // build the sibling nodes and parent node for the specific ndoe
|
|
var nodeCount = 0;
|
|
buildHierarchy($nodeChart.closest('.orgchart'), nodeData, 0, opts, function() {
|
|
if (++nodeCount === siblingCount) {
|
|
complementLine($nodeChart.next().children('tr:last')
|
|
.children().eq(insertPostion).after($('<td colspan="2">')
|
|
.append($nodeChart)), siblingCount, 1);
|
|
$nodeChart.closest('tr').siblings().eq(0).addClass('hidden').find('.node').addClass('slide-down');
|
|
$nodeChart.parent().siblings().addClass('hidden')
|
|
.slice(0, insertPostion).find('.node').addClass('slide-right')
|
|
.end().end().slice(insertPostion).find('.node').addClass('slide-left');
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function addSiblings($node, data, opts) {
|
|
|
|
buildSiblingNode.call($node.closest('.orgchart').parent(), $node.closest('table'), data, opts, function()
|
|
{
|
|
$node.closest('.nodes').data('siblingsLoaded', true);
|
|
if (!$node.children('.leftEdge').length)
|
|
{
|
|
console.log("No Left Edge");
|
|
$node.children('.topEdge').after('<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>');
|
|
}
|
|
showSiblings($node);
|
|
});
|
|
}
|
|
function removeNodes($node) {
|
|
console.log("remove nodes")
|
|
var $parent = $node.closest('table').parent();
|
|
|
|
var $sibs = $parent.parent().siblings();
|
|
if ($parent.is('td')) {
|
|
|
|
if (getNodeState($node, 'siblings').exist) {
|
|
$sibs.eq(2).children('.topLine:lt(2)').remove();
|
|
$sibs.slice(0, 2).children().attr('colspan', $sibs.eq(2).children().length);
|
|
$parent.remove();
|
|
} else {
|
|
$sibs.eq(0).children().removeAttr('colspan')
|
|
.find('.bottomEdge').remove()
|
|
.end().end().siblings().remove();
|
|
}
|
|
} else {
|
|
$parent.add($parent.siblings()).remove();
|
|
}
|
|
}
|
|
|
|
function removeNode($nodeId)
|
|
{
|
|
var $chart = $(this).find('.orgchart');
|
|
console.log("Try to remove node with id: " + $nodeId);
|
|
$chart.find('tr').each(function ()
|
|
{
|
|
|
|
var node = $( this );
|
|
console.log(node.find('.modid').text());
|
|
if(parseInt(node.find('.modid').text()) !== $nodeId)
|
|
{
|
|
return true;
|
|
}
|
|
console.log("Found Note with Id: " + $nodeId);
|
|
removeNodes(node);
|
|
return false;
|
|
});
|
|
return true;
|
|
|
|
|
|
}
|
|
}));
|