orgchart 4.0.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +32 -0
- package/README.md +205 -89
- package/README.zh-cn.md +221 -68
- package/README.zh-tw.md +221 -68
- package/bower.json +6 -9
- package/demo/10000-nodes.html +139 -0
- package/demo/ajax-datasource.html +46 -45
- package/demo/bottom2top.html +12 -9
- package/demo/center-on-selected-node.html +21 -17
- package/demo/color-coded.html +12 -9
- package/demo/css/style.css +28 -0
- package/demo/custom-icons.html +12 -9
- package/demo/custom-template.html +12 -9
- package/demo/data-prop-compact.html +11 -8
- package/demo/data-prop-hybrid.html +79 -50
- package/demo/default-collapsed.html +12 -9
- package/demo/drag-drop-hybrid-chart.html +17 -14
- package/demo/drag-drop.html +21 -16
- package/demo/edit-chart.html +148 -94
- package/demo/edit-hybrid-chart.html +178 -135
- package/demo/expand-to-given-depth.html +30 -22
- package/demo/export-chart-pdf.html +12 -9
- package/demo/export-chart-with-pictures.html +19 -12
- package/demo/export-chart.html +12 -9
- package/demo/family-tree.html +100 -0
- package/demo/familytree-custom-properties.html +108 -0
- package/demo/filter-node.html +104 -57
- package/demo/get-hierarchy.html +17 -12
- package/demo/get-related-nodes.html +27 -17
- package/demo/index.html +7 -4
- package/demo/integrate-map.html +32 -27
- package/demo/left2right.html +12 -9
- package/demo/level-offset.html +16 -15
- package/demo/link-node.html +12 -9
- package/demo/local-datasource.html +11 -8
- package/demo/minimap.html +224 -0
- package/demo/nodes-of-different-widths.html +11 -8
- package/demo/ondemand-loading-data.html +88 -91
- package/demo/option-createNode.html +31 -20
- package/demo/pan-zoom.html +20 -11
- package/demo/reload-data.html +17 -14
- package/demo/report-path.html +59 -29
- package/demo/responsive-design.html +14 -11
- package/demo/right2left.html +12 -9
- package/demo/toggle-sibs-resp.html +12 -9
- package/demo/ul-datasource.html +11 -8
- package/demo/vertical-level.html +12 -9
- package/dist/css/{jquery.orgchart.css → orgchart.css} +210 -78
- package/dist/css/orgchart.min.css +1 -0
- package/dist/js/orgchart.js +4101 -0
- package/dist/js/orgchart.min.js +2 -0
- package/dist/js/orgchart.min.js.map +1 -0
- package/gulpfile.js +37 -26
- package/package.json +32 -32
- package/src/css/{jquery.orgchart.css → orgchart.css} +210 -78
- package/src/js/orgchart.js +4101 -0
- package/test/cypress/e2e/demo-interactions.cy.js +91 -0
- package/test/cypress/e2e/demo-rendering.cy.js +100 -0
- package/test/cypress/e2e/drag-drop-hybrid-chart.cy.js +22 -0
- package/test/cypress/e2e/edit-chart.cy.js +20 -0
- package/test/cypress/e2e/level-offset.cy.js +21 -0
- package/test/cypress/e2e/minimap.cy.js +66 -0
- package/test/cypress/e2e/pan-zoom.cy.js +165 -0
- package/test/cypress/e2e/report-path.cy.js +20 -0
- package/test/cypress/e2e/vertical-vevel.cy.js +5 -3
- package/test/e2e/local-datasource/test.js +1 -1
- package/test/integration/init-tests.js +168 -62
- package/test/integration/tests.js +1034 -56
- package/test/unit/lifecycle-builders-export.tests.js +1426 -0
- package/test/unit/minimap.tests.js +210 -0
- package/test/unit/tests.js +2372 -252
- package/demo/multiple-layers.html +0 -128
- package/dist/css/jquery.orgchart.min.css +0 -1
- package/dist/js/jquery.orgchart.js +0 -1686
- package/dist/js/jquery.orgchart.min.js +0 -5
- package/dist/js/jquery.orgchart.min.js.map +0 -1
- package/src/js/jquery.orgchart.js +0 -1686
|
@@ -1,1686 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* jQuery OrgChart Plugin
|
|
3
|
-
* https://github.com/dabeng/OrgChart
|
|
4
|
-
*
|
|
5
|
-
* Copyright 2016, dabeng
|
|
6
|
-
* https://github.com/dabeng
|
|
7
|
-
*
|
|
8
|
-
* Licensed under the MIT license:
|
|
9
|
-
* http://www.opensource.org/licenses/MIT
|
|
10
|
-
*/
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
(function (factory) {
|
|
14
|
-
if (typeof module === 'object' && typeof module.exports === 'object') {
|
|
15
|
-
factory(require('jquery'), window, document);
|
|
16
|
-
} else {
|
|
17
|
-
factory(jQuery, window, document);
|
|
18
|
-
}
|
|
19
|
-
}(function ($, window, document, undefined) {
|
|
20
|
-
var OrgChart = function (elem, opts) {
|
|
21
|
-
this.$chartContainer = $(elem);
|
|
22
|
-
this.opts = opts;
|
|
23
|
-
this.defaultOptions = {
|
|
24
|
-
'icons': {
|
|
25
|
-
'theme': 'oci',
|
|
26
|
-
'parentNode': 'oci-menu',
|
|
27
|
-
'expandToUp': 'oci-chevron-up',
|
|
28
|
-
'collapseToDown': 'oci-chevron-down',
|
|
29
|
-
'collapseToLeft': 'oci-chevron-left',
|
|
30
|
-
'expandToRight': 'oci-chevron-right',
|
|
31
|
-
'backToCompact': 'oci-corner-top-left',
|
|
32
|
-
'backToLoose': 'oci-corner-bottom-right',
|
|
33
|
-
'collapsed': 'oci-plus-square',
|
|
34
|
-
'expanded': 'oci-minus-square',
|
|
35
|
-
'spinner': 'oci-spinner'
|
|
36
|
-
},
|
|
37
|
-
'nodeTitle': 'name',
|
|
38
|
-
'nodeId': 'id',
|
|
39
|
-
'toggleSiblingsResp': false,
|
|
40
|
-
'visibleLevel': 999,
|
|
41
|
-
'chartClass': '',
|
|
42
|
-
'exportButton': false,
|
|
43
|
-
'exportButtonName': 'Export',
|
|
44
|
-
'exportFilename': 'OrgChart',
|
|
45
|
-
'exportFileextension': 'png',
|
|
46
|
-
'draggable': false,
|
|
47
|
-
'direction': 't2b',
|
|
48
|
-
'pan': false,
|
|
49
|
-
'zoom': false,
|
|
50
|
-
'zoominLimit': 7,
|
|
51
|
-
'zoomoutLimit': 0.5
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
//
|
|
55
|
-
OrgChart.prototype = {
|
|
56
|
-
//
|
|
57
|
-
init: function (opts) {
|
|
58
|
-
var that = this;
|
|
59
|
-
this.options = $.extend({}, this.defaultOptions, this.opts, opts);
|
|
60
|
-
// build the org-chart
|
|
61
|
-
var $chartContainer = this.$chartContainer;
|
|
62
|
-
if (this.$chart) {
|
|
63
|
-
this.$chart.remove();
|
|
64
|
-
}
|
|
65
|
-
var data = this.options.data;
|
|
66
|
-
var $chart = this.$chart = $('<div>', {
|
|
67
|
-
'data': { 'options': this.options },
|
|
68
|
-
'class': 'orgchart' + (this.options.chartClass !== '' ? ' ' + this.options.chartClass : '') + (this.options.direction !== 't2b' ? ' ' + this.options.direction : ''),
|
|
69
|
-
'click': function(event) {
|
|
70
|
-
if (!$(event.target).closest('.node').length) {
|
|
71
|
-
$chart.find('.node.focused').removeClass('focused');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
if (typeof MutationObserver !== 'undefined') {
|
|
76
|
-
this.triggerInitEvent();
|
|
77
|
-
}
|
|
78
|
-
var $root = $chart.append($('<ul class="nodes"><li class="hierarchy"></li></ul>')).find('.hierarchy');
|
|
79
|
-
|
|
80
|
-
if (data instanceof $) { // ul datasource
|
|
81
|
-
this.buildHierarchy($root, this.buildJsonDS(data.children()), 0, this.options);
|
|
82
|
-
} else { // local json datasource
|
|
83
|
-
if (data.relationship) {
|
|
84
|
-
this.buildHierarchy($root, data);
|
|
85
|
-
} else {
|
|
86
|
-
this.buildHierarchy($root, this.attachRel(data, '00'));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
$chartContainer.append($chart);
|
|
91
|
-
|
|
92
|
-
// append the export button
|
|
93
|
-
if (this.options.exportButton && !$('.oc-export-btn').length) {
|
|
94
|
-
this.attachExportButton();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (this.options.pan) {
|
|
98
|
-
this.bindPan();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (this.options.zoom) {
|
|
102
|
-
this.bindZoom();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return this;
|
|
106
|
-
},
|
|
107
|
-
handleCompactNodes: function () {
|
|
108
|
-
// caculate the compact nodes' level which is used to add different styles
|
|
109
|
-
this.$chart.find('.node.compact')
|
|
110
|
-
.each((index, node) => {
|
|
111
|
-
$(node).addClass($(node).parents('.compact').length % 2 === 0 ? 'even' : 'odd');
|
|
112
|
-
}); // the following code snippets is used to add direction arrows for the most top compact node, however the styles is not adjusted correctly
|
|
113
|
-
// .filter((index, node) => !$(node).parent().is('.compact'))
|
|
114
|
-
// .each((index, node) => {
|
|
115
|
-
// $(node).append(`<i class="edge verticalEdge topEdge ${this.options.icons.theme}"></i>`);
|
|
116
|
-
// if (this.getSiblings($(node)).length) {
|
|
117
|
-
// $(node).append(`<i class="edge horizontalEdge rightEdge ${this.options.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${this.options.icons.theme}"></i>`);
|
|
118
|
-
// }
|
|
119
|
-
// });
|
|
120
|
-
},
|
|
121
|
-
//
|
|
122
|
-
triggerInitEvent: function () {
|
|
123
|
-
var that = this;
|
|
124
|
-
var mo = new MutationObserver(function (mutations) {
|
|
125
|
-
mo.disconnect();
|
|
126
|
-
initTime:
|
|
127
|
-
for (var i = 0; i < mutations.length; i++) {
|
|
128
|
-
for (var j = 0; j < mutations[i].addedNodes.length; j++) {
|
|
129
|
-
if (mutations[i].addedNodes[j].classList.contains('orgchart')) {
|
|
130
|
-
that.handleCompactNodes();
|
|
131
|
-
if (that.options.initCompleted && typeof that.options.initCompleted === 'function') {
|
|
132
|
-
that.options.initCompleted(that.$chart);
|
|
133
|
-
}
|
|
134
|
-
var initEvent = $.Event('init.orgchart');
|
|
135
|
-
that.$chart.trigger(initEvent);
|
|
136
|
-
break initTime;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
mo.observe(this.$chartContainer[0], { childList: true });
|
|
142
|
-
},
|
|
143
|
-
triggerShowEvent: function ($target, rel) {
|
|
144
|
-
var initEvent = $.Event('show-' + rel + '.orgchart');
|
|
145
|
-
$target.trigger(initEvent);
|
|
146
|
-
},
|
|
147
|
-
triggerHideEvent: function ($target, rel) {
|
|
148
|
-
var initEvent = $.Event('hide-' + rel + '.orgchart');
|
|
149
|
-
$target.trigger(initEvent);
|
|
150
|
-
},
|
|
151
|
-
// add export button for orgchart
|
|
152
|
-
attachExportButton: function () {
|
|
153
|
-
var that = this;
|
|
154
|
-
var $exportBtn = $('<button>', {
|
|
155
|
-
'class': 'oc-export-btn',
|
|
156
|
-
'text': this.options.exportButtonName,
|
|
157
|
-
'click': function(e) {
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
that.export();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
this.$chartContainer.after($exportBtn);
|
|
163
|
-
},
|
|
164
|
-
setOptions: function (opts, val) {
|
|
165
|
-
if (typeof opts === 'string') {
|
|
166
|
-
if (opts === 'pan') {
|
|
167
|
-
if (val) {
|
|
168
|
-
this.bindPan();
|
|
169
|
-
} else {
|
|
170
|
-
this.unbindPan();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (opts === 'zoom') {
|
|
174
|
-
if (val) {
|
|
175
|
-
this.bindZoom();
|
|
176
|
-
} else {
|
|
177
|
-
this.unbindZoom();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (typeof opts === 'object') {
|
|
182
|
-
if (opts.data) {
|
|
183
|
-
this.init(opts);
|
|
184
|
-
} else {
|
|
185
|
-
if (typeof opts.pan !== 'undefined') {
|
|
186
|
-
if (opts.pan) {
|
|
187
|
-
this.bindPan();
|
|
188
|
-
} else {
|
|
189
|
-
this.unbindPan();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (typeof opts.zoom !== 'undefined') {
|
|
193
|
-
if (opts.zoom) {
|
|
194
|
-
this.bindZoom();
|
|
195
|
-
} else {
|
|
196
|
-
this.unbindZoom();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return this;
|
|
203
|
-
},
|
|
204
|
-
//
|
|
205
|
-
panStartHandler: function (e) {
|
|
206
|
-
var $chart = $(e.delegateTarget);
|
|
207
|
-
if ($(e.target).closest('.node').length || (e.touches && e.touches.length > 1)) {
|
|
208
|
-
$chart.data('panning', false);
|
|
209
|
-
return;
|
|
210
|
-
} else {
|
|
211
|
-
$chart.css('cursor', 'move').data('panning', true);
|
|
212
|
-
}
|
|
213
|
-
var lastX = 0;
|
|
214
|
-
var lastY = 0;
|
|
215
|
-
var lastTf = $chart.css('transform');
|
|
216
|
-
if (lastTf !== 'none') {
|
|
217
|
-
var temp = lastTf.split(',');
|
|
218
|
-
if (lastTf.indexOf('3d') === -1) {
|
|
219
|
-
lastX = parseInt(temp[4]);
|
|
220
|
-
lastY = parseInt(temp[5]);
|
|
221
|
-
} else {
|
|
222
|
-
lastX = parseInt(temp[12]);
|
|
223
|
-
lastY = parseInt(temp[13]);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
var startX = 0;
|
|
227
|
-
var startY = 0;
|
|
228
|
-
if (!e.targetTouches) { // pand on desktop
|
|
229
|
-
startX = e.pageX - lastX;
|
|
230
|
-
startY = e.pageY - lastY;
|
|
231
|
-
} else if (e.targetTouches.length === 1) { // pan on mobile device
|
|
232
|
-
startX = e.targetTouches[0].pageX - lastX;
|
|
233
|
-
startY = e.targetTouches[0].pageY - lastY;
|
|
234
|
-
} else if (e.targetTouches.length > 1) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
$chart.on('mousemove touchmove',function(e) {
|
|
238
|
-
if (!$chart.data('panning')) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
var newX = 0;
|
|
242
|
-
var newY = 0;
|
|
243
|
-
if (!e.targetTouches) { // pand on desktop
|
|
244
|
-
newX = e.pageX - startX;
|
|
245
|
-
newY = e.pageY - startY;
|
|
246
|
-
} else if (e.targetTouches.length === 1) { // pan on mobile device
|
|
247
|
-
newX = e.targetTouches[0].pageX - startX;
|
|
248
|
-
newY = e.targetTouches[0].pageY - startY;
|
|
249
|
-
} else if (e.targetTouches.length > 1) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
var lastTf = $chart.css('transform');
|
|
253
|
-
if (lastTf === 'none') {
|
|
254
|
-
if (lastTf.indexOf('3d') === -1) {
|
|
255
|
-
$chart.css('transform', 'matrix(1, 0, 0, 1, ' + newX + ', ' + newY + ')');
|
|
256
|
-
} else {
|
|
257
|
-
$chart.css('transform', 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + newX + ', ' + newY + ', 0, 1)');
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
var matrix = lastTf.split(',');
|
|
261
|
-
if (lastTf.indexOf('3d') === -1) {
|
|
262
|
-
matrix[4] = ' ' + newX;
|
|
263
|
-
matrix[5] = ' ' + newY + ')';
|
|
264
|
-
} else {
|
|
265
|
-
matrix[12] = ' ' + newX;
|
|
266
|
-
matrix[13] = ' ' + newY;
|
|
267
|
-
}
|
|
268
|
-
$chart.css('transform', matrix.join(','));
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
},
|
|
272
|
-
//
|
|
273
|
-
panEndHandler: function (e) {
|
|
274
|
-
if (e.data.chart.data('panning')) {
|
|
275
|
-
e.data.chart.data('panning', false).css('cursor', 'default').off('mousemove');
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
//
|
|
279
|
-
bindPan: function () {
|
|
280
|
-
this.$chartContainer.css('overflow', 'hidden');
|
|
281
|
-
this.$chart.on('mousedown touchstart', this.panStartHandler);
|
|
282
|
-
$(document).on('mouseup touchend', { 'chart': this.$chart }, this.panEndHandler);
|
|
283
|
-
},
|
|
284
|
-
//
|
|
285
|
-
unbindPan: function () {
|
|
286
|
-
this.$chartContainer.css('overflow', 'auto');
|
|
287
|
-
this.$chart.off('mousedown touchstart', this.panStartHandler);
|
|
288
|
-
$(document).off('mouseup touchend', this.panEndHandler);
|
|
289
|
-
},
|
|
290
|
-
//
|
|
291
|
-
zoomWheelHandler: function (e) {
|
|
292
|
-
var oc = e.data.oc;
|
|
293
|
-
e.preventDefault();
|
|
294
|
-
var newScale = 1 + (e.originalEvent.deltaY > 0 ? -0.2 : 0.2);
|
|
295
|
-
oc.setChartScale(oc.$chart, newScale);
|
|
296
|
-
},
|
|
297
|
-
//
|
|
298
|
-
zoomStartHandler: function (e) {
|
|
299
|
-
if(e.touches && e.touches.length === 2) {
|
|
300
|
-
var oc = e.data.oc;
|
|
301
|
-
oc.$chart.data('pinching', true);
|
|
302
|
-
var dist = oc.getPinchDist(e);
|
|
303
|
-
oc.$chart.data('pinchDistStart', dist);
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
zoomingHandler: function (e) {
|
|
307
|
-
var oc = e.data.oc;
|
|
308
|
-
if(oc.$chart.data('pinching')) {
|
|
309
|
-
var dist = oc.getPinchDist(e);
|
|
310
|
-
oc.$chart.data('pinchDistEnd', dist);
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
zoomEndHandler: function (e) {
|
|
314
|
-
var oc = e.data.oc;
|
|
315
|
-
if(oc.$chart.data('pinching')) {
|
|
316
|
-
oc.$chart.data('pinching', false);
|
|
317
|
-
var diff = oc.$chart.data('pinchDistEnd') - oc.$chart.data('pinchDistStart');
|
|
318
|
-
if (diff > 0) {
|
|
319
|
-
oc.setChartScale(oc.$chart, 1.2);
|
|
320
|
-
} else if (diff < 0) {
|
|
321
|
-
oc.setChartScale(oc.$chart, 0.8);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
//
|
|
326
|
-
bindZoom: function () {
|
|
327
|
-
this.$chartContainer.on('wheel', { 'oc': this }, this.zoomWheelHandler);
|
|
328
|
-
this.$chartContainer.on('touchstart', { 'oc': this }, this.zoomStartHandler);
|
|
329
|
-
$(document).on('touchmove', { 'oc': this }, this.zoomingHandler);
|
|
330
|
-
$(document).on('touchend', { 'oc': this }, this.zoomEndHandler);
|
|
331
|
-
},
|
|
332
|
-
unbindZoom: function () {
|
|
333
|
-
this.$chartContainer.off('wheel', this.zoomWheelHandler);
|
|
334
|
-
this.$chartContainer.off('touchstart', this.zoomStartHandler);
|
|
335
|
-
$(document).off('touchmove', this.zoomingHandler);
|
|
336
|
-
$(document).off('touchend', this.zoomEndHandler);
|
|
337
|
-
},
|
|
338
|
-
//
|
|
339
|
-
getPinchDist: function (e) {
|
|
340
|
-
return Math.sqrt((e.touches[0].clientX - e.touches[1].clientX) * (e.touches[0].clientX - e.touches[1].clientX) +
|
|
341
|
-
(e.touches[0].clientY - e.touches[1].clientY) * (e.touches[0].clientY - e.touches[1].clientY));
|
|
342
|
-
},
|
|
343
|
-
//
|
|
344
|
-
setChartScale: function ($chart, newScale) {
|
|
345
|
-
var opts = $chart.data('options');
|
|
346
|
-
var lastTf = $chart.css('transform');
|
|
347
|
-
var matrix = '';
|
|
348
|
-
var targetScale = 1;
|
|
349
|
-
if (lastTf === 'none') {
|
|
350
|
-
$chart.css('transform', 'scale(' + newScale + ',' + newScale + ')');
|
|
351
|
-
} else {
|
|
352
|
-
matrix = lastTf.split(',');
|
|
353
|
-
if (lastTf.indexOf('3d') === -1) {
|
|
354
|
-
targetScale = Math.abs(window.parseFloat(matrix[3]) * newScale);
|
|
355
|
-
if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) {
|
|
356
|
-
$chart.css('transform', lastTf + ' scale(' + newScale + ',' + newScale + ')');
|
|
357
|
-
}
|
|
358
|
-
} else {
|
|
359
|
-
targetScale = Math.abs(window.parseFloat(matrix[1]) * newScale);
|
|
360
|
-
if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) {
|
|
361
|
-
$chart.css('transform', lastTf + ' scale3d(' + newScale + ',' + newScale + ', 1)');
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
},
|
|
366
|
-
//
|
|
367
|
-
buildJsonDS: function ($li) {
|
|
368
|
-
var that = this;
|
|
369
|
-
var subObj = {
|
|
370
|
-
'name': $li.contents().eq(0).text().trim(),
|
|
371
|
-
'relationship': ($li.parent().parent().is('li') ? '1': '0') + ($li.siblings('li').length ? 1: 0) + ($li.children('ul').length ? 1 : 0)
|
|
372
|
-
};
|
|
373
|
-
$.each($li.data(), function(key, value) {
|
|
374
|
-
subObj[key] = value;
|
|
375
|
-
});
|
|
376
|
-
$li.children('ul').children().each(function() {
|
|
377
|
-
if (!subObj.children) { subObj.children = []; }
|
|
378
|
-
subObj.children.push(that.buildJsonDS($(this)));
|
|
379
|
-
});
|
|
380
|
-
return subObj;
|
|
381
|
-
},
|
|
382
|
-
// process datasource and add necessary information
|
|
383
|
-
attachRel: function (data, flags) {
|
|
384
|
-
var that = this;
|
|
385
|
-
data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0);
|
|
386
|
-
if (this.options?.compact?.constructor === Function && this.options.compact(data)) {
|
|
387
|
-
data.compact = true;
|
|
388
|
-
}
|
|
389
|
-
if (data.children) {
|
|
390
|
-
data.children.forEach(function(item) {
|
|
391
|
-
if (data.hybrid || data.vertical) { // identify all the descendant nodes except the root node of hybrid structure
|
|
392
|
-
item.vertical = true;
|
|
393
|
-
} else if (data.compact && item.children) { // identify all the compact ancestor nodes
|
|
394
|
-
item.compact = true;
|
|
395
|
-
} else if (data.compact && !item.children) { // identify all the compact descendant nodes
|
|
396
|
-
item.associatedCompact = true;
|
|
397
|
-
}
|
|
398
|
-
that.attachRel(item, '1' + (data.children.length > 1 ? 1 : 0));
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
return data;
|
|
402
|
-
},
|
|
403
|
-
//
|
|
404
|
-
loopChart: function ($chart, includeNodeData) {
|
|
405
|
-
includeNodeData = (includeNodeData !== null && includeNodeData !== undefined) ? includeNodeData : false;
|
|
406
|
-
var that = this;
|
|
407
|
-
var $node = $chart.find('.node:first');
|
|
408
|
-
var subObj = { 'id': $node[0].id };
|
|
409
|
-
if (includeNodeData) {
|
|
410
|
-
$.each($node.data('nodeData'), function (key, value) {
|
|
411
|
-
subObj[key] = value;
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
$node.siblings('.nodes').children().each(function() {
|
|
415
|
-
if (!subObj.children) { subObj.children = []; }
|
|
416
|
-
subObj.children.push(that.loopChart($(this), includeNodeData));
|
|
417
|
-
});
|
|
418
|
-
return subObj;
|
|
419
|
-
},
|
|
420
|
-
//
|
|
421
|
-
getHierarchy: function (includeNodeData) {
|
|
422
|
-
includeNodeData = (includeNodeData !== null && includeNodeData !== undefined) ? includeNodeData : false;
|
|
423
|
-
if (typeof this.$chart === 'undefined') {
|
|
424
|
-
return 'Error: orgchart does not exist'
|
|
425
|
-
} else {
|
|
426
|
-
if (!this.$chart.find('.node').length) {
|
|
427
|
-
return 'Error: nodes do not exist'
|
|
428
|
-
} else {
|
|
429
|
-
var valid = true;
|
|
430
|
-
this.$chart.find('.node').each(function () {
|
|
431
|
-
if (!this.id) {
|
|
432
|
-
valid = false;
|
|
433
|
-
return false;
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
if (!valid) {
|
|
437
|
-
return 'Error: All nodes of orghcart to be exported must have data-id attribute!';
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return this.loopChart(this.$chart, includeNodeData);
|
|
442
|
-
},
|
|
443
|
-
// detect the exist/display state of related node
|
|
444
|
-
getNodeState: function ($node, relation) {
|
|
445
|
-
var $target = {};
|
|
446
|
-
var isVerticalNode = !!$node.closest('vertical').length;
|
|
447
|
-
var relation = relation || 'self';
|
|
448
|
-
if (relation === 'parent') {
|
|
449
|
-
if (isVerticalNode) {
|
|
450
|
-
$target = $node.closest('ul').parents('ul');
|
|
451
|
-
if (!$target.length) {
|
|
452
|
-
$target = $node.closest('.nodes');
|
|
453
|
-
if (!$target.length) {
|
|
454
|
-
$target = $node.closest('.vertical').siblings(':first');
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
$target = $node.closest('.nodes').siblings('.node');
|
|
459
|
-
}
|
|
460
|
-
if ($target.length) {
|
|
461
|
-
if ($target.is('.hidden') || (!$target.is('.hidden') && $target.closest('.nodes').is('.hidden')) || (!$target.is('.hidden') && $target.closest('.vertical').is('.hidden'))) {
|
|
462
|
-
return { 'exist': true, 'visible': false };
|
|
463
|
-
}
|
|
464
|
-
return { 'exist': true, 'visible': true };
|
|
465
|
-
}
|
|
466
|
-
} else if (relation === 'children') {
|
|
467
|
-
$target = isVerticalNode ? $node.parent().children('ul') : $node.siblings('.nodes');
|
|
468
|
-
if ($target.length) {
|
|
469
|
-
if (!$target.is('.hidden')) {
|
|
470
|
-
return { 'exist': true, 'visible': true };
|
|
471
|
-
}
|
|
472
|
-
return { 'exist': true, 'visible': false };
|
|
473
|
-
}
|
|
474
|
-
} else if (relation === 'siblings') {
|
|
475
|
-
$target = isVerticalNode ? $node.closest('ul') : $node.parent().siblings();
|
|
476
|
-
if ($target.length && (!isVerticalNode || $target.children('li').length > 1)) {
|
|
477
|
-
if (!$target.is('.hidden') && !$target.parent().is('.hidden') && (!isVerticalNode || !$target.closest('.vertical').is('.hidden'))) {
|
|
478
|
-
return { 'exist': true, 'visible': true };
|
|
479
|
-
}
|
|
480
|
-
return { 'exist': true, 'visible': false };
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
$target = $node;
|
|
484
|
-
if ($target.length) {
|
|
485
|
-
if (!(($target.closest('.nodes').length && $target.closest('.nodes').is('.hidden')) ||
|
|
486
|
-
($target.closest('.hierarchy').length && $target.closest('.hierarchy').is('.hidden')) ||
|
|
487
|
-
($target.closest('.vertical').length && ($target.closest('.nodes').is('.hidden') || $target.closest('.vertical').is('.hidden')))
|
|
488
|
-
)) {
|
|
489
|
-
return { 'exist': true, 'visible': true };
|
|
490
|
-
}
|
|
491
|
-
return { 'exist': true, 'visible': false };
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return { 'exist': false, 'visible': false };
|
|
495
|
-
},
|
|
496
|
-
getParent: function ($node) {
|
|
497
|
-
return this.getRelatedNodes($node, 'parent');
|
|
498
|
-
},
|
|
499
|
-
getChildren: function ($node) {
|
|
500
|
-
return this.getRelatedNodes($node, 'children');
|
|
501
|
-
},
|
|
502
|
-
getSiblings: function ($node) {
|
|
503
|
-
return this.getRelatedNodes($node, 'siblings');
|
|
504
|
-
},
|
|
505
|
-
// find the related nodes
|
|
506
|
-
getRelatedNodes: function ($node, relation) {
|
|
507
|
-
if (!$node || !($node instanceof $) || !$node.is('.node')) {
|
|
508
|
-
return $();
|
|
509
|
-
}
|
|
510
|
-
if (relation === 'parent') {
|
|
511
|
-
return $node.closest('.nodes').siblings('.node');
|
|
512
|
-
} else if (relation === 'children') {
|
|
513
|
-
return $node.siblings('.nodes').children('.hierarchy').find('.node:first');
|
|
514
|
-
} else if (relation === 'siblings') {
|
|
515
|
-
return $node.closest('.hierarchy').siblings().find('.node:first');
|
|
516
|
-
} else {
|
|
517
|
-
return $();
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
hideParentEnd: function (event) {
|
|
521
|
-
$(event.target).removeClass('sliding');
|
|
522
|
-
event.data.parent.addClass('hidden');
|
|
523
|
-
},
|
|
524
|
-
// recursively hide the ancestor node and sibling nodes of the specified node
|
|
525
|
-
hideParent: function ($node) {
|
|
526
|
-
var $parent = $node.closest('.nodes').siblings('.node');
|
|
527
|
-
if ($parent.find('.spinner').length) {
|
|
528
|
-
$node.closest('.orgchart').data('inAjax', false);
|
|
529
|
-
}
|
|
530
|
-
// hide the sibling nodes
|
|
531
|
-
if (this.getNodeState($node, 'siblings').visible) {
|
|
532
|
-
this.hideSiblings($node);
|
|
533
|
-
}
|
|
534
|
-
// hide the lines
|
|
535
|
-
$node.parent().addClass('isAncestorsCollapsed');
|
|
536
|
-
// hide the superior nodes with transition
|
|
537
|
-
if (this.getNodeState($parent).visible) {
|
|
538
|
-
$parent.addClass('sliding slide-down').one('transitionend', { 'parent': $parent }, this.hideParentEnd);
|
|
539
|
-
}
|
|
540
|
-
// if the current node has the parent node, hide it recursively
|
|
541
|
-
if (this.getNodeState($parent, 'parent').visible) {
|
|
542
|
-
this.hideParent($parent);
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
showParentEnd: function (event) {
|
|
546
|
-
var $node = event.data.node;
|
|
547
|
-
$(event.target).removeClass('sliding');
|
|
548
|
-
if (this.isInAction($node)) {
|
|
549
|
-
this.switchVerticalArrow($node.children('.topEdge'));
|
|
550
|
-
}
|
|
551
|
-
},
|
|
552
|
-
// show the parent node of the specified node
|
|
553
|
-
showParent: function ($node) {
|
|
554
|
-
// just show only one superior level
|
|
555
|
-
var $parent = $node.closest('.nodes').siblings('.node').removeClass('hidden');
|
|
556
|
-
// just show only one line
|
|
557
|
-
$node.closest('.hierarchy').removeClass('isAncestorsCollapsed');
|
|
558
|
-
// show parent node with animation
|
|
559
|
-
this.repaint($parent[0]);
|
|
560
|
-
$parent.addClass('sliding').removeClass('slide-down').one('transitionend', { 'node': $node }, this.showParentEnd.bind(this));
|
|
561
|
-
},
|
|
562
|
-
stopAjax: function ($nodeLevel) {
|
|
563
|
-
if ($nodeLevel.find('.spinner').length) {
|
|
564
|
-
$nodeLevel.closest('.orgchart').data('inAjax', false);
|
|
565
|
-
}
|
|
566
|
-
},
|
|
567
|
-
isVisibleNode: function (index, elem) {
|
|
568
|
-
return this.getNodeState($(elem)).visible;
|
|
569
|
-
},
|
|
570
|
-
isCompactDescendant: function (index, elem) {
|
|
571
|
-
return $(elem).parent().is('.node.compact');
|
|
572
|
-
},
|
|
573
|
-
// do some necessary cleanup tasks when hide animation is finished
|
|
574
|
-
hideChildrenEnd: function (event) {
|
|
575
|
-
var $node = event.data.node;
|
|
576
|
-
event.data.animatedNodes.removeClass('sliding');
|
|
577
|
-
event.data.animatedNodes.closest('.nodes').addClass('hidden');
|
|
578
|
-
if (this.isInAction($node)) {
|
|
579
|
-
this.switchVerticalArrow($node.children('.bottomEdge'));
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
// recursively hide the descendant nodes of the specified node
|
|
583
|
-
hideChildren: function ($node) {
|
|
584
|
-
$node.closest('.hierarchy').addClass('isChildrenCollapsed');
|
|
585
|
-
var $lowerLevel = $node.siblings('.nodes');
|
|
586
|
-
this.stopAjax($lowerLevel);
|
|
587
|
-
var $animatedNodes = $lowerLevel.find('.node').filter(this.isVisibleNode.bind(this)).not(this.isCompactDescendant.bind(this));
|
|
588
|
-
var isVerticalDesc = $lowerLevel.is('.vertical');
|
|
589
|
-
if (!isVerticalDesc) {
|
|
590
|
-
$animatedNodes.closest('.hierarchy').addClass('isCollapsedDescendant');
|
|
591
|
-
}
|
|
592
|
-
if ($lowerLevel.is('.vertical') || $lowerLevel.find('.vertical').length) {
|
|
593
|
-
$animatedNodes.find(this.options.icons.expanded).removeClass(this.options.icons.expanded).addClass(this.options.icons.collapsed);
|
|
594
|
-
}
|
|
595
|
-
this.repaint($animatedNodes.get(0));
|
|
596
|
-
$animatedNodes.addClass('sliding slide-up').eq(0).one('transitionend', { 'animatedNodes': $animatedNodes, 'lowerLevel': $lowerLevel, 'node': $node }, this.hideChildrenEnd.bind(this));
|
|
597
|
-
},
|
|
598
|
-
//
|
|
599
|
-
showChildrenEnd: function (event) {
|
|
600
|
-
var $node = event.data.node;
|
|
601
|
-
event.data.animatedNodes.removeClass('sliding');
|
|
602
|
-
if (this.isInAction($node)) {
|
|
603
|
-
this.switchVerticalArrow($node.children('.bottomEdge'));
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
// show the children nodes of the specified node
|
|
607
|
-
showChildren: function ($node) {
|
|
608
|
-
var that = this;
|
|
609
|
-
$node.closest('.hierarchy').removeClass('isChildrenCollapsed');
|
|
610
|
-
var $levels = $node.siblings('.nodes');
|
|
611
|
-
var isVerticalDesc = $levels.is('.vertical');
|
|
612
|
-
var $animatedNodes = isVerticalDesc
|
|
613
|
-
? $levels.removeClass('hidden').find('.node').filter(this.isVisibleNode.bind(this))
|
|
614
|
-
: $levels.removeClass('hidden').children('.hierarchy').find('.node:first').filter(this.isVisibleNode.bind(this));
|
|
615
|
-
if (!isVerticalDesc) {
|
|
616
|
-
$animatedNodes.filter(':not(:only-child)').closest('.hierarchy').addClass('isChildrenCollapsed');
|
|
617
|
-
$animatedNodes.closest('.hierarchy').removeClass('isCollapsedDescendant');
|
|
618
|
-
}
|
|
619
|
-
// the two following statements are used to enforce browser to repaint
|
|
620
|
-
this.repaint($animatedNodes.get(0));
|
|
621
|
-
$animatedNodes.addClass('sliding').removeClass('slide-up').eq(0).one('transitionend', { 'node': $node, 'animatedNodes': $animatedNodes }, this.showChildrenEnd.bind(this));
|
|
622
|
-
},
|
|
623
|
-
//
|
|
624
|
-
hideSiblingsEnd: function (event) {
|
|
625
|
-
var that = this;
|
|
626
|
-
var $node = event.data.node;
|
|
627
|
-
var $nodeContainer = event.data.nodeContainer;
|
|
628
|
-
var direction = event.data.direction;
|
|
629
|
-
var $siblings = direction ? (direction === 'left' ? $nodeContainer.prevAll(':not(.hidden)') : $nodeContainer.nextAll(':not(.hidden)')) : $nodeContainer.siblings();
|
|
630
|
-
event.data.animatedNodes.removeClass('sliding');
|
|
631
|
-
$siblings.find('.node:gt(0)').filter(this.isVisibleNode.bind(this))
|
|
632
|
-
.removeClass('slide-left slide-right')
|
|
633
|
-
.addClass(function() {
|
|
634
|
-
if (that.options.compact) {
|
|
635
|
-
return '';
|
|
636
|
-
} else {
|
|
637
|
-
return 'slide-up';
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
$siblings.find('.nodes, .vertical').addClass('hidden')
|
|
641
|
-
.end().addClass('hidden');
|
|
642
|
-
|
|
643
|
-
if (this.isInAction($node)) {
|
|
644
|
-
this.switchHorizontalArrow($node);
|
|
645
|
-
}
|
|
646
|
-
},
|
|
647
|
-
// hide the sibling nodes of the specified node
|
|
648
|
-
hideSiblings: function ($node, direction) {
|
|
649
|
-
var that = this;
|
|
650
|
-
var $nodeContainer = $node.closest('.hierarchy').addClass('isSiblingsCollapsed');
|
|
651
|
-
if ($nodeContainer.siblings().find('.spinner').length) {
|
|
652
|
-
$node.closest('.orgchart').data('inAjax', false);
|
|
653
|
-
}
|
|
654
|
-
if (direction) {
|
|
655
|
-
if (direction === 'left') {
|
|
656
|
-
$nodeContainer.addClass('left-sibs')
|
|
657
|
-
.prevAll('.isSiblingsCollapsed').removeClass('isSiblingsCollapsed left-sibs').end()
|
|
658
|
-
.prevAll().addClass('isCollapsedSibling isChildrenCollapsed')
|
|
659
|
-
.find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-right');
|
|
660
|
-
} else {
|
|
661
|
-
$nodeContainer.addClass('right-sibs')
|
|
662
|
-
.nextAll('.isSiblingsCollapsed').removeClass('isSiblingsCollapsed right-sibs').end()
|
|
663
|
-
.nextAll().addClass('isCollapsedSibling isChildrenCollapsed')
|
|
664
|
-
.find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-left');
|
|
665
|
-
}
|
|
666
|
-
} else {
|
|
667
|
-
$nodeContainer.prevAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-right');
|
|
668
|
-
$nodeContainer.nextAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-left');
|
|
669
|
-
$nodeContainer.siblings().addClass('isCollapsedSibling isChildrenCollapsed');
|
|
670
|
-
}
|
|
671
|
-
var $animatedNodes = $nodeContainer.siblings().find('.sliding');
|
|
672
|
-
$animatedNodes.eq(0).one('transitionend', { 'node': $node, 'nodeContainer': $nodeContainer, 'direction': direction, 'animatedNodes': $animatedNodes }, this.hideSiblingsEnd.bind(this));
|
|
673
|
-
},
|
|
674
|
-
//
|
|
675
|
-
showSiblingsEnd: function (event) {
|
|
676
|
-
var $node = event.data.node;
|
|
677
|
-
event.data.visibleNodes.removeClass('sliding');
|
|
678
|
-
if (this.isInAction($node)) {
|
|
679
|
-
this.switchHorizontalArrow($node);
|
|
680
|
-
$node.children('.topEdge').removeClass(this.options.icons.expandToUp).addClass(this.options.icons.collapseToDown);
|
|
681
|
-
}
|
|
682
|
-
},
|
|
683
|
-
//
|
|
684
|
-
showRelatedParentEnd: function(event) {
|
|
685
|
-
$(event.target).removeClass('sliding');
|
|
686
|
-
},
|
|
687
|
-
// show the sibling nodes of the specified node
|
|
688
|
-
showSiblings: function ($node, direction) {
|
|
689
|
-
var that = this;
|
|
690
|
-
// firstly, show the sibling nodes
|
|
691
|
-
var $siblings = $();
|
|
692
|
-
var $nodeContainer = $node.closest('.hierarchy');
|
|
693
|
-
if (direction) {
|
|
694
|
-
if (direction === 'left') {
|
|
695
|
-
$siblings = $nodeContainer.prevAll().removeClass('hidden');
|
|
696
|
-
} else {
|
|
697
|
-
$siblings = $nodeContainer.nextAll().removeClass('hidden');
|
|
698
|
-
}
|
|
699
|
-
} else {
|
|
700
|
-
$siblings = $node.closest('.hierarchy').siblings().removeClass('hidden');
|
|
701
|
-
}
|
|
702
|
-
// secondly, show the lines
|
|
703
|
-
var $upperLevel = $node.closest('.nodes').siblings('.node');
|
|
704
|
-
if (direction) {
|
|
705
|
-
$nodeContainer.removeClass(direction + '-sibs');
|
|
706
|
-
if (!$nodeContainer.is('[class*=-sibs]')) {
|
|
707
|
-
$nodeContainer.removeClass('isSiblingsCollapsed');
|
|
708
|
-
}
|
|
709
|
-
$siblings.removeClass('isCollapsedSibling ' + direction + '-sibs');
|
|
710
|
-
} else {
|
|
711
|
-
$node.closest('.hierarchy').removeClass('isSiblingsCollapsed');
|
|
712
|
-
$siblings.removeClass('isCollapsedSibling');
|
|
713
|
-
}
|
|
714
|
-
// thirdly, show parent node if it is collapsed
|
|
715
|
-
if (!this.getNodeState($node, 'parent').visible) {
|
|
716
|
-
$node.closest('.hierarchy').removeClass('isAncestorsCollapsed');
|
|
717
|
-
$upperLevel.removeClass('hidden');
|
|
718
|
-
this.repaint($upperLevel[0]);
|
|
719
|
-
$upperLevel.addClass('sliding').removeClass('slide-down').one('transitionend', this.showRelatedParentEnd);
|
|
720
|
-
}
|
|
721
|
-
// lastly, show the sibling nodes with animation
|
|
722
|
-
var $visibleNodes = $siblings.find('.node').filter(this.isVisibleNode.bind(this));
|
|
723
|
-
this.repaint($visibleNodes.get(0));
|
|
724
|
-
$visibleNodes.addClass('sliding').removeClass('slide-left slide-right');
|
|
725
|
-
$visibleNodes.eq(0).one('transitionend', { 'node': $node, 'visibleNodes': $visibleNodes }, this.showSiblingsEnd.bind(this));
|
|
726
|
-
},
|
|
727
|
-
// start up loading status for requesting new nodes
|
|
728
|
-
startLoading: function ($edge) {
|
|
729
|
-
var $chart = this.$chart;
|
|
730
|
-
if (typeof $chart.data('inAjax') !== 'undefined' && $chart.data('inAjax') === true) {
|
|
731
|
-
return false;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
$edge.addClass('hidden');
|
|
735
|
-
$edge.parent().append(`<i class="${this.options.icons.theme} ${this.options.icons.spinner} spinner"></i>`)
|
|
736
|
-
.children().not('.spinner').css('opacity', 0.2);
|
|
737
|
-
$chart.data('inAjax', true);
|
|
738
|
-
$('.oc-export-btn').prop('disabled', true);
|
|
739
|
-
return true;
|
|
740
|
-
},
|
|
741
|
-
// terminate loading status for requesting new nodes
|
|
742
|
-
endLoading: function ($edge) {
|
|
743
|
-
var $node = $edge.parent();
|
|
744
|
-
$edge.removeClass('hidden');
|
|
745
|
-
$node.find('.spinner').remove();
|
|
746
|
-
$node.children().removeAttr('style');
|
|
747
|
-
this.$chart.data('inAjax', false);
|
|
748
|
-
$('.oc-export-btn').prop('disabled', false);
|
|
749
|
-
},
|
|
750
|
-
// whether the cursor is hovering over the node
|
|
751
|
-
isInAction: function ($node) {
|
|
752
|
-
return [
|
|
753
|
-
this.options.icons.expandToUp,
|
|
754
|
-
this.options.icons.collapseToDown,
|
|
755
|
-
this.options.icons.collapseToLeft,
|
|
756
|
-
this.options.icons.expandToRight
|
|
757
|
-
].some((icon) => $node.children('.edge').attr('class').indexOf(icon) > -1);
|
|
758
|
-
},
|
|
759
|
-
//
|
|
760
|
-
switchVerticalArrow: function ($arrow) {
|
|
761
|
-
$arrow.toggleClass(`${this.options.icons.expandToUp} ${this.options.icons.collapseToDown}`);
|
|
762
|
-
},
|
|
763
|
-
//
|
|
764
|
-
switchHorizontalArrow: function ($node) {
|
|
765
|
-
var opts = this.options;
|
|
766
|
-
if (opts.toggleSiblingsResp && (typeof opts.ajaxURL === 'undefined' || $node.closest('.nodes').data('siblingsLoaded'))) {
|
|
767
|
-
var $prevSib = $node.parent().prev();
|
|
768
|
-
if ($prevSib.length) {
|
|
769
|
-
if ($prevSib.is('.hidden')) {
|
|
770
|
-
$node.children('.leftEdge').addClass(opts.icons.collapseToLeft).removeClass(opts.icons.expandToRight);
|
|
771
|
-
} else {
|
|
772
|
-
$node.children('.leftEdge').addClass(opts.icons.expandToRight).removeClass(opts.icons.collapseToLeft);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
var $nextSib = $node.parent().next();
|
|
776
|
-
if ($nextSib.length) {
|
|
777
|
-
if ($nextSib.is('.hidden')) {
|
|
778
|
-
$node.children('.rightEdge').addClass(opts.icons.expandToRight).removeClass(opts.icons.collapseToLeft);
|
|
779
|
-
} else {
|
|
780
|
-
$node.children('.rightEdge').addClass(opts.icons.collapseToLeft).removeClass(opts.icons.expandToRight);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
} else {
|
|
784
|
-
var $sibs = $node.parent().siblings();
|
|
785
|
-
var sibsVisible = $sibs.length ? !$sibs.is('.hidden') : false;
|
|
786
|
-
$node.children('.leftEdge').toggleClass(opts.icons.expandToRight, sibsVisible).toggleClass(opts.icons.collapseToLeft, !sibsVisible);
|
|
787
|
-
$node.children('.rightEdge').toggleClass(opts.icons.collapseToLeft, sibsVisible).toggleClass(opts.icons.expandToRight, !sibsVisible);
|
|
788
|
-
}
|
|
789
|
-
},
|
|
790
|
-
//
|
|
791
|
-
repaint: function (node) {
|
|
792
|
-
if (node) {
|
|
793
|
-
node.style.offsetWidth = node.offsetWidth;
|
|
794
|
-
}
|
|
795
|
-
},
|
|
796
|
-
// determines how to show arrow buttons
|
|
797
|
-
nodeEnterLeaveHandler: function (event) {
|
|
798
|
-
var $node = $(event.delegateTarget);
|
|
799
|
-
var flag = false;
|
|
800
|
-
if ($node.closest('.nodes.vertical').length) {
|
|
801
|
-
var $toggleBtn = $node.children('.toggleBtn');
|
|
802
|
-
if (event.type === 'mouseenter') {
|
|
803
|
-
if ($node.children('.toggleBtn').length) {
|
|
804
|
-
flag = this.getNodeState($node, 'children').visible;
|
|
805
|
-
$toggleBtn.toggleClass(this.options.icons.collapsed, !flag).toggleClass(this.options.icons.expanded, flag);
|
|
806
|
-
}
|
|
807
|
-
} else {
|
|
808
|
-
$toggleBtn.removeClass(`${this.options.icons.collapsed} ${this.options.icons.expanded}`);
|
|
809
|
-
}
|
|
810
|
-
} else {
|
|
811
|
-
var $topEdge = $node.children('.topEdge');
|
|
812
|
-
var $rightEdge = $node.children('.rightEdge');
|
|
813
|
-
var $bottomEdge = $node.children('.bottomEdge');
|
|
814
|
-
var $leftEdge = $node.children('.leftEdge');
|
|
815
|
-
if (event.type === 'mouseenter') {
|
|
816
|
-
if ($topEdge.length) {
|
|
817
|
-
flag = this.getNodeState($node, 'parent').visible;
|
|
818
|
-
$topEdge.toggleClass(this.options.icons.expandToUp, !flag).toggleClass(this.options.icons.collapseToDown, flag);
|
|
819
|
-
}
|
|
820
|
-
if ($bottomEdge.length) {
|
|
821
|
-
flag = this.getNodeState($node, 'children').visible;
|
|
822
|
-
$bottomEdge.toggleClass(this.options.icons.collapseToDown, !flag).toggleClass(this.options.icons.expandToUp, flag);
|
|
823
|
-
}
|
|
824
|
-
if ($leftEdge.length) {
|
|
825
|
-
this.switchHorizontalArrow($node);
|
|
826
|
-
}
|
|
827
|
-
} else {
|
|
828
|
-
$node.children('.edge').removeClass(`${this.options.icons.expandToUp} ${this.options.icons.collapseToDown} ${this.options.icons.collapseToLeft} ${this.options.icons.expandToRight}`);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
},
|
|
832
|
-
//
|
|
833
|
-
nodeClickHandler: function (event) {
|
|
834
|
-
this.$chart.find('.focused').removeClass('focused');
|
|
835
|
-
$(event.delegateTarget).addClass('focused');
|
|
836
|
-
},
|
|
837
|
-
addAncestors: function (data, parentId) {
|
|
838
|
-
var $root = this.$chart.children('.nodes').children('.hierarchy');
|
|
839
|
-
this.buildHierarchy($root, data);
|
|
840
|
-
$root.children().slice(0, 2)
|
|
841
|
-
.wrapAll('<li class="hierarchy"></li>').parent()
|
|
842
|
-
.appendTo($('#' + parentId).siblings('.nodes'));
|
|
843
|
-
},
|
|
844
|
-
addDescendants:function (data, $parent) {
|
|
845
|
-
var that = this;
|
|
846
|
-
var $descendants = $('<ul class="nodes"></ul>');
|
|
847
|
-
$parent.after($descendants);
|
|
848
|
-
$.each(data, function (i) {
|
|
849
|
-
$descendants.append($('<li class="hierarchy"></li>'));
|
|
850
|
-
that.buildHierarchy($descendants.children().eq(i), this);
|
|
851
|
-
});
|
|
852
|
-
},
|
|
853
|
-
//
|
|
854
|
-
HideFirstParentEnd: function (event) {
|
|
855
|
-
var $topEdge = event.data.topEdge;
|
|
856
|
-
var $node = $topEdge.parent();
|
|
857
|
-
if (this.isInAction($node)) {
|
|
858
|
-
this.switchVerticalArrow($topEdge);
|
|
859
|
-
this.switchHorizontalArrow($node);
|
|
860
|
-
}
|
|
861
|
-
},
|
|
862
|
-
// actions on clinking top edge of a node
|
|
863
|
-
topEdgeClickHandler: function (event) {
|
|
864
|
-
var that = this;
|
|
865
|
-
var $topEdge = $(event.target);
|
|
866
|
-
var $node = $(event.delegateTarget);
|
|
867
|
-
var parentState = this.getNodeState($node, 'parent');
|
|
868
|
-
if (parentState.exist) {
|
|
869
|
-
var $parent = $node.closest('.nodes').siblings('.node');
|
|
870
|
-
if ($parent.is('.sliding')) { return; }
|
|
871
|
-
// hide the ancestor nodes and sibling nodes of the specified node
|
|
872
|
-
if (parentState.visible) {
|
|
873
|
-
this.hideParent($node);
|
|
874
|
-
$parent.one('transitionend', { 'topEdge': $topEdge }, this.HideFirstParentEnd.bind(this));
|
|
875
|
-
this.triggerHideEvent($node, 'parent');
|
|
876
|
-
} else { // show the ancestors and siblings
|
|
877
|
-
this.showParent($node);
|
|
878
|
-
this.triggerShowEvent($node, 'parent');
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
},
|
|
882
|
-
// actions on clinking bottom edge of a node
|
|
883
|
-
bottomEdgeClickHandler: function (event) {
|
|
884
|
-
var $bottomEdge = $(event.target);
|
|
885
|
-
var $node = $(event.delegateTarget);
|
|
886
|
-
var childrenState = this.getNodeState($node, 'children');
|
|
887
|
-
if (childrenState.exist) {
|
|
888
|
-
var $children = $node.siblings('.nodes').children().children('.node');
|
|
889
|
-
if ($children.is('.sliding')) { return; }
|
|
890
|
-
// hide the descendant nodes of the specified node
|
|
891
|
-
if (childrenState.visible) {
|
|
892
|
-
this.hideChildren($node);
|
|
893
|
-
this.triggerHideEvent($node, 'children');
|
|
894
|
-
} else { // show the descendants
|
|
895
|
-
this.showChildren($node);
|
|
896
|
-
this.triggerShowEvent($node, 'children');
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
},
|
|
900
|
-
// actions on clicking horizontal edges
|
|
901
|
-
hEdgeClickHandler: function (event) {
|
|
902
|
-
var $hEdge = $(event.target);
|
|
903
|
-
var $node = $(event.delegateTarget);
|
|
904
|
-
var opts = this.options;
|
|
905
|
-
var siblingsState = this.getNodeState($node, 'siblings');
|
|
906
|
-
if (siblingsState.exist) {
|
|
907
|
-
var $siblings = $node.closest('.hierarchy').siblings();
|
|
908
|
-
if ($siblings.find('.sliding').length) { return; }
|
|
909
|
-
if (opts.toggleSiblingsResp) {
|
|
910
|
-
var $prevSib = $node.closest('.hierarchy').prev();
|
|
911
|
-
var $nextSib = $node.closest('.hierarchy').next();
|
|
912
|
-
if ($hEdge.is('.leftEdge')) {
|
|
913
|
-
if ($prevSib.is('.hidden')) {
|
|
914
|
-
this.showSiblings($node, 'left');
|
|
915
|
-
this.triggerShowEvent($node,'siblings');
|
|
916
|
-
} else {
|
|
917
|
-
this.hideSiblings($node, 'left');
|
|
918
|
-
this.triggerHideEvent($node, 'siblings');
|
|
919
|
-
}
|
|
920
|
-
} else {
|
|
921
|
-
if ($nextSib.is('.hidden')) {
|
|
922
|
-
this.showSiblings($node, 'right');
|
|
923
|
-
this.triggerShowEvent($node,'siblings');
|
|
924
|
-
} else {
|
|
925
|
-
this.hideSiblings($node, 'right');
|
|
926
|
-
this.triggerHideEvent($node, 'siblings');
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
} else {
|
|
930
|
-
if (siblingsState.visible) {
|
|
931
|
-
this.hideSiblings($node);
|
|
932
|
-
this.triggerHideEvent($node, 'siblings');
|
|
933
|
-
} else {
|
|
934
|
-
this.showSiblings($node);
|
|
935
|
-
this.triggerShowEvent($node, 'siblings');
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
},
|
|
940
|
-
// show the compact node's children in the compact mode
|
|
941
|
-
backToCompactHandler: function (event) {
|
|
942
|
-
$(event.delegateTarget).removeClass('looseMode')
|
|
943
|
-
.find('.looseMode').removeClass('looseMode')
|
|
944
|
-
.children('.backToCompactSymbol').addClass('hidden').end()
|
|
945
|
-
.children('.backToLooseSymbol').removeClass('hidden');
|
|
946
|
-
$(event.delegateTarget).children('.backToCompactSymbol').addClass('hidden').end()
|
|
947
|
-
.children('.backToLooseSymbol').removeClass('hidden');
|
|
948
|
-
},
|
|
949
|
-
// show the compact node's children in the loose mode
|
|
950
|
-
backToLooseHandler: function (event) {
|
|
951
|
-
$(event.delegateTarget)
|
|
952
|
-
.addClass('looseMode')
|
|
953
|
-
.children('.backToLooseSymbol').addClass('hidden').end()
|
|
954
|
-
.children('.backToCompactSymbol').removeClass('hidden');
|
|
955
|
-
},
|
|
956
|
-
//
|
|
957
|
-
expandVNodesEnd: function (event) {
|
|
958
|
-
event.data.vNodes.removeClass('sliding');
|
|
959
|
-
},
|
|
960
|
-
//
|
|
961
|
-
collapseVNodesEnd: function (event) {
|
|
962
|
-
event.data.vNodes.removeClass('sliding').closest('ul').addClass('hidden');
|
|
963
|
-
},
|
|
964
|
-
// event handler for toggle buttons in Hybrid(horizontal + vertical) OrgChart
|
|
965
|
-
toggleVNodes: function (event) {
|
|
966
|
-
var $toggleBtn = $(event.target);
|
|
967
|
-
var $descWrapper = $toggleBtn.parent().next();
|
|
968
|
-
var $descendants = $descWrapper.find('.node');
|
|
969
|
-
var $children = $descWrapper.children().children('.node');
|
|
970
|
-
if ($children.is('.sliding')) { return; }
|
|
971
|
-
$toggleBtn.toggleClass(`${this.options.icons.collapsed} ${this.options.icons.expanded}`);
|
|
972
|
-
if ($descendants.eq(0).is('.slide-up')) {
|
|
973
|
-
$descWrapper.removeClass('hidden');
|
|
974
|
-
this.repaint($children.get(0));
|
|
975
|
-
$children.addClass('sliding').removeClass('slide-up').eq(0).one('transitionend', { 'vNodes': $children }, this.expandVNodesEnd);
|
|
976
|
-
} else {
|
|
977
|
-
$descendants.addClass('sliding slide-up').eq(0).one('transitionend', { 'vNodes': $descendants }, this.collapseVNodesEnd);
|
|
978
|
-
$descendants.find('.toggleBtn').removeClass(`${this.options.icons.collapsed} ${this.options.icons.expanded}`);
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
//
|
|
982
|
-
createGhostNode: function (event) {
|
|
983
|
-
var $nodeDiv = $(event.target);
|
|
984
|
-
var opts = this.options;
|
|
985
|
-
var origEvent = event.originalEvent;
|
|
986
|
-
var isFirefox = /firefox/.test(window.navigator.userAgent.toLowerCase());
|
|
987
|
-
var ghostNode, nodeCover;
|
|
988
|
-
if (!document.querySelector('.ghost-node')) {
|
|
989
|
-
ghostNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
990
|
-
if (!ghostNode.classList) return;
|
|
991
|
-
ghostNode.classList.add('ghost-node');
|
|
992
|
-
nodeCover = document.createElementNS('http://www.w3.org/2000/svg','rect');
|
|
993
|
-
ghostNode.appendChild(nodeCover);
|
|
994
|
-
$nodeDiv.closest('.orgchart').append(ghostNode);
|
|
995
|
-
} else {
|
|
996
|
-
ghostNode = $nodeDiv.closest('.orgchart').children('.ghost-node').get(0);
|
|
997
|
-
nodeCover = $(ghostNode).children().get(0);
|
|
998
|
-
}
|
|
999
|
-
var transValues = $nodeDiv.closest('.orgchart').css('transform').split(',');
|
|
1000
|
-
var isHorizontal = opts.direction === 't2b' || opts.direction === 'b2t';
|
|
1001
|
-
var scale = Math.abs(window.parseFloat(isHorizontal ? transValues[0].slice(transValues[0].indexOf('(') + 1) : transValues[1]));
|
|
1002
|
-
ghostNode.setAttribute('width', isHorizontal ? $nodeDiv.outerWidth(false) : $nodeDiv.outerHeight(false));
|
|
1003
|
-
ghostNode.setAttribute('height', isHorizontal ? $nodeDiv.outerHeight(false) : $nodeDiv.outerWidth(false));
|
|
1004
|
-
nodeCover.setAttribute('x',5 * scale);
|
|
1005
|
-
nodeCover.setAttribute('y',5 * scale);
|
|
1006
|
-
nodeCover.setAttribute('width', 120 * scale);
|
|
1007
|
-
nodeCover.setAttribute('height', 40 * scale);
|
|
1008
|
-
nodeCover.setAttribute('rx', 4 * scale);
|
|
1009
|
-
nodeCover.setAttribute('ry', 4 * scale);
|
|
1010
|
-
nodeCover.setAttribute('stroke-width', 1 * scale);
|
|
1011
|
-
var xOffset = origEvent.offsetX * scale;
|
|
1012
|
-
var yOffset = origEvent.offsetY * scale;
|
|
1013
|
-
if (opts.direction === 'l2r') {
|
|
1014
|
-
xOffset = origEvent.offsetY * scale;
|
|
1015
|
-
yOffset = origEvent.offsetX * scale;
|
|
1016
|
-
} else if (opts.direction === 'r2l') {
|
|
1017
|
-
xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetY * scale;
|
|
1018
|
-
yOffset = origEvent.offsetX * scale;
|
|
1019
|
-
} else if (opts.direction === 'b2t') {
|
|
1020
|
-
xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetX * scale;
|
|
1021
|
-
yOffset = $nodeDiv.outerHeight(false) - origEvent.offsetY * scale;
|
|
1022
|
-
}
|
|
1023
|
-
if (isFirefox) { // hack for old version of Firefox(< 48.0)
|
|
1024
|
-
nodeCover.setAttribute('fill', 'rgb(255, 255, 255)');
|
|
1025
|
-
nodeCover.setAttribute('stroke', 'rgb(191, 0, 0)');
|
|
1026
|
-
var ghostNodeWrapper = document.createElement('img');
|
|
1027
|
-
ghostNodeWrapper.src = 'data:image/svg+xml;utf8,' + (new XMLSerializer()).serializeToString(ghostNode);
|
|
1028
|
-
origEvent.dataTransfer.setDragImage(ghostNodeWrapper, xOffset, yOffset);
|
|
1029
|
-
} else {
|
|
1030
|
-
// IE/Edge do not support this, so only use it if we can
|
|
1031
|
-
if (origEvent.dataTransfer.setDragImage)
|
|
1032
|
-
origEvent.dataTransfer.setDragImage(ghostNode, xOffset, yOffset);
|
|
1033
|
-
}
|
|
1034
|
-
},
|
|
1035
|
-
// get the level amount of a hierachy
|
|
1036
|
-
getUpperLevel: function ($node) {
|
|
1037
|
-
if (!$node.is('.node')) {
|
|
1038
|
-
return 0;
|
|
1039
|
-
}
|
|
1040
|
-
return $node.parents('.hierarchy').length;
|
|
1041
|
-
},
|
|
1042
|
-
// get the level amount of a hierachy
|
|
1043
|
-
getLowerLevel: function ($node) {
|
|
1044
|
-
if (!$node.is('.node')) {
|
|
1045
|
-
return 0;
|
|
1046
|
-
}
|
|
1047
|
-
return $node.closest('.hierarchy').find('.nodes').length + 1;
|
|
1048
|
-
},
|
|
1049
|
-
// get nodes in level order traversal
|
|
1050
|
-
getLevelOrderNodes: function ($root) {
|
|
1051
|
-
if(!$root) return [];
|
|
1052
|
-
var queue = [];
|
|
1053
|
-
var output = [];
|
|
1054
|
-
queue.push($root);
|
|
1055
|
-
while(queue.length) {
|
|
1056
|
-
var row = [];
|
|
1057
|
-
for(var i = 0; i < queue.length; i++) {
|
|
1058
|
-
var cur = queue.shift();
|
|
1059
|
-
var children = this.getChildren(cur);
|
|
1060
|
-
if(children.length) {
|
|
1061
|
-
queue.push(children.toArray().flat());
|
|
1062
|
-
}
|
|
1063
|
-
row.push($(cur));
|
|
1064
|
-
}
|
|
1065
|
-
output.push(row);
|
|
1066
|
-
}
|
|
1067
|
-
return output;
|
|
1068
|
-
},
|
|
1069
|
-
//
|
|
1070
|
-
filterAllowedDropNodes: function ($dragged) {
|
|
1071
|
-
var opts = this.options;
|
|
1072
|
-
// what is being dragged? a node, or something within a node?
|
|
1073
|
-
var draggingNode = $dragged.closest('[draggable]').hasClass('node');
|
|
1074
|
-
var $dragZone = $dragged.closest('.nodes').siblings('.node'); // parent node
|
|
1075
|
-
var $dragHier = $dragged.closest('.hierarchy').find('.node'); // this node, and its children
|
|
1076
|
-
this.$chart.data('dragged', $dragged)
|
|
1077
|
-
.find('.node').each(function (index, node) {
|
|
1078
|
-
if (!draggingNode || $dragHier.index(node) === -1) {
|
|
1079
|
-
if (opts.dropCriteria) {
|
|
1080
|
-
if (opts.dropCriteria($dragged, $dragZone, $(node))) {
|
|
1081
|
-
$(node).addClass('allowedDrop');
|
|
1082
|
-
}
|
|
1083
|
-
} else {
|
|
1084
|
-
$(node).addClass('allowedDrop');
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
},
|
|
1089
|
-
//
|
|
1090
|
-
dragstartHandler: function (event) {
|
|
1091
|
-
event.originalEvent.dataTransfer.setData('text/html', 'hack for firefox');
|
|
1092
|
-
// if users enable zoom or direction options
|
|
1093
|
-
if (this.$chart.css('transform') !== 'none') {
|
|
1094
|
-
this.createGhostNode(event);
|
|
1095
|
-
}
|
|
1096
|
-
this.filterAllowedDropNodes($(event.target));
|
|
1097
|
-
},
|
|
1098
|
-
//
|
|
1099
|
-
dragoverHandler: function (event) {
|
|
1100
|
-
if (!$(event.delegateTarget).is('.allowedDrop')) {
|
|
1101
|
-
event.originalEvent.dataTransfer.dropEffect = 'none';
|
|
1102
|
-
} else {
|
|
1103
|
-
// default action for drag-and-drop of div is not to drop, so preventing default action for nodes which have allowedDrop class
|
|
1104
|
-
//to fix drag and drop on IE and Edge
|
|
1105
|
-
event.preventDefault();
|
|
1106
|
-
}
|
|
1107
|
-
},
|
|
1108
|
-
//
|
|
1109
|
-
dragendHandler: function (event) {
|
|
1110
|
-
this.$chart.find('.allowedDrop').removeClass('allowedDrop');
|
|
1111
|
-
},
|
|
1112
|
-
// when user drops the node, it will be removed from original parent node and be added to new parent node
|
|
1113
|
-
dropHandler: async function (event) {
|
|
1114
|
-
var that = this;
|
|
1115
|
-
var $dropZone = $(event.delegateTarget);
|
|
1116
|
-
var $dragged = this.$chart.data('dragged');
|
|
1117
|
-
|
|
1118
|
-
// Pass on drops which are not nodes (since they are not our doing)
|
|
1119
|
-
if (!$dragged.hasClass('node')) {
|
|
1120
|
-
this.$chart.triggerHandler({ 'type': 'otherdropped.orgchart', 'draggedItem': $dragged, 'dropZone': $dropZone });
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
if (!$dropZone.hasClass('allowedDrop')) {
|
|
1125
|
-
// We are trying to drop a node into a node which isn't allowed
|
|
1126
|
-
// IE/Edge have a habit of allowing this, so we need our own double-check
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
var $dragZone = $dragged.closest('.nodes').siblings('.node');
|
|
1131
|
-
var dropEvent = $.Event('nodedrop.orgchart');
|
|
1132
|
-
this.$chart.trigger(dropEvent, { 'draggedNode': $dragged, 'dragZone': $dragZone, 'dropZone': $dropZone });
|
|
1133
|
-
if (dropEvent.isDefaultPrevented()) {
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
// special process for hybrid chart
|
|
1137
|
-
var datasource = this.$chart.data('options').data;
|
|
1138
|
-
var digger = new JSONDigger(datasource, this.$chart.data('options').nodeId, 'children');
|
|
1139
|
-
const hybridNode = digger.findOneNode({ 'hybrid': true });
|
|
1140
|
-
if (this.$chart.data('options').verticalLevel > 1 || hybridNode) {
|
|
1141
|
-
var draggedNode = digger.findNodeById($dragged.data('nodeData').id);
|
|
1142
|
-
var copy = Object.assign({}, draggedNode);
|
|
1143
|
-
digger.removeNode(draggedNode.id);
|
|
1144
|
-
var dropNode = digger.findNodeById($dropZone.data('nodeData').id);
|
|
1145
|
-
if (dropNode.children) {
|
|
1146
|
-
dropNode.children.push(copy);
|
|
1147
|
-
} else {
|
|
1148
|
-
dropNode.children = [copy];
|
|
1149
|
-
}
|
|
1150
|
-
that.init({ 'data': datasource });
|
|
1151
|
-
} else {
|
|
1152
|
-
// The folowing code snippets are used to process horizontal chart
|
|
1153
|
-
// firstly, deal with the hierarchy of drop zone
|
|
1154
|
-
if (!$dropZone.siblings('.nodes').length) { // if the drop zone is a leaf node
|
|
1155
|
-
$dropZone.append(`<i class="edge verticalEdge bottomEdge ${this.options.icons.theme}"></i>`)
|
|
1156
|
-
.after('<ul class="nodes"></ul>')
|
|
1157
|
-
.siblings('.nodes').append($dragged.find('.horizontalEdge').remove().end().closest('.hierarchy'));
|
|
1158
|
-
if ($dropZone.children('.title').length) {
|
|
1159
|
-
$dropZone.children('.title').prepend(`<i class="${this.options.icons.theme} ${this.$chart.data('options').icons.parentNode} parentNodeSymbol"></i>`);
|
|
1160
|
-
}
|
|
1161
|
-
} else {
|
|
1162
|
-
var horizontalEdges = `<i class="edge horizontalEdge rightEdge ${this.options.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${this.options.icons.theme}"></i>`;
|
|
1163
|
-
if (!$dragged.find('.horizontalEdge').length) {
|
|
1164
|
-
$dragged.append(horizontalEdges);
|
|
1165
|
-
}
|
|
1166
|
-
$dropZone.siblings('.nodes').append($dragged.closest('.hierarchy'));
|
|
1167
|
-
var $dropSibs = $dragged.closest('.hierarchy').siblings().find('.node:first');
|
|
1168
|
-
if ($dropSibs.length === 1) {
|
|
1169
|
-
$dropSibs.append(horizontalEdges);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
// secondly, deal with the hierarchy of dragged node
|
|
1173
|
-
if ($dragZone.siblings('.nodes').children('.hierarchy').length === 1) { // if there is only one sibling node left
|
|
1174
|
-
$dragZone.siblings('.nodes').children('.hierarchy').find('.node:first')
|
|
1175
|
-
.find('.horizontalEdge').remove();
|
|
1176
|
-
} else if ($dragZone.siblings('.nodes').children('.hierarchy').length === 0) {
|
|
1177
|
-
$dragZone.find('.bottomEdge, .parentNodeSymbol').remove()
|
|
1178
|
-
.end().siblings('.nodes').remove();
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
},
|
|
1182
|
-
//
|
|
1183
|
-
touchstartHandler: function (event) {
|
|
1184
|
-
if (this.touchHandled)
|
|
1185
|
-
return;
|
|
1186
|
-
|
|
1187
|
-
if (event.touches && event.touches.length > 1)
|
|
1188
|
-
return;
|
|
1189
|
-
|
|
1190
|
-
this.touchHandled = true;
|
|
1191
|
-
this.touchMoved = false; // this is so we can work out later if this was a 'press' or a 'drag' touch
|
|
1192
|
-
event.preventDefault();
|
|
1193
|
-
},
|
|
1194
|
-
//
|
|
1195
|
-
touchmoveHandler: function (event) {
|
|
1196
|
-
if (!this.touchHandled)
|
|
1197
|
-
return;
|
|
1198
|
-
|
|
1199
|
-
if (event.touches && event.touches.length > 1)
|
|
1200
|
-
return;
|
|
1201
|
-
|
|
1202
|
-
event.preventDefault();
|
|
1203
|
-
|
|
1204
|
-
if (!this.touchMoved) {
|
|
1205
|
-
// we do not bother with createGhostNode (dragstart does) since the touch event does not have a dataTransfer property
|
|
1206
|
-
this.filterAllowedDropNodes($(event.currentTarget)); // will also set 'this.$chart.data('dragged')' for us
|
|
1207
|
-
// create an image which can be used to illustrate the drag (our own createGhostNode)
|
|
1208
|
-
this.touchDragImage = this.createDragImage(event, this.$chart.data('dragged')[0]);
|
|
1209
|
-
}
|
|
1210
|
-
this.touchMoved = true;
|
|
1211
|
-
|
|
1212
|
-
// move our dragimage so it follows our finger
|
|
1213
|
-
this.moveDragImage(event, this.touchDragImage);
|
|
1214
|
-
|
|
1215
|
-
var $touching = $(document.elementFromPoint(event.touches[0].clientX, event.touches[0].clientY));
|
|
1216
|
-
var $touchingNodes = $touching.closest('div.node');
|
|
1217
|
-
if ($touchingNodes.length > 0) {
|
|
1218
|
-
var touchingNodeElement = $touchingNodes[0];
|
|
1219
|
-
if ($touchingNodes.is('.allowedDrop')) {
|
|
1220
|
-
this.touchTargetNode = touchingNodeElement;
|
|
1221
|
-
}
|
|
1222
|
-
else {
|
|
1223
|
-
this.touchTargetNode = null;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
else {
|
|
1227
|
-
this.touchTargetNode = null;
|
|
1228
|
-
}
|
|
1229
|
-
},
|
|
1230
|
-
//
|
|
1231
|
-
touchendHandler: function (event) {
|
|
1232
|
-
if (!this.touchHandled) {
|
|
1233
|
-
return;
|
|
1234
|
-
}
|
|
1235
|
-
this.destroyDragImage();
|
|
1236
|
-
if (this.touchMoved) {
|
|
1237
|
-
// we've had movement, so this was a 'drag' touch
|
|
1238
|
-
if (this.touchTargetNode) {
|
|
1239
|
-
var fakeEventForDropHandler = { delegateTarget: this.touchTargetNode };
|
|
1240
|
-
this.dropHandler(fakeEventForDropHandler);
|
|
1241
|
-
this.touchTargetNode = null;
|
|
1242
|
-
}
|
|
1243
|
-
this.dragendHandler(event);
|
|
1244
|
-
}
|
|
1245
|
-
else {
|
|
1246
|
-
// we did not move, so this was a 'press' touch (fake a click)
|
|
1247
|
-
var firstTouch = event.changedTouches[0];
|
|
1248
|
-
var fakeMouseClickEvent = document.createEvent('MouseEvents');
|
|
1249
|
-
fakeMouseClickEvent.initMouseEvent('click', true, true, window, 1, firstTouch.screenX, firstTouch.screenY, firstTouch.clientX, firstTouch.clientY, event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, 0, null);
|
|
1250
|
-
event.target.dispatchEvent(fakeMouseClickEvent);
|
|
1251
|
-
}
|
|
1252
|
-
this.touchHandled = false;
|
|
1253
|
-
},
|
|
1254
|
-
//
|
|
1255
|
-
createDragImage: function (event, source) {
|
|
1256
|
-
var dragImage = source.cloneNode(true);
|
|
1257
|
-
this.copyStyle(source, dragImage);
|
|
1258
|
-
dragImage.style.top = dragImage.style.left = '-9999px';
|
|
1259
|
-
var sourceRectangle = source.getBoundingClientRect();
|
|
1260
|
-
var sourcePoint = this.getTouchPoint(event);
|
|
1261
|
-
this.touchDragImageOffset = { x: sourcePoint.x - sourceRectangle.left, y: sourcePoint.y - sourceRectangle.top };
|
|
1262
|
-
dragImage.style.opacity = '0.5';
|
|
1263
|
-
document.body.appendChild(dragImage);
|
|
1264
|
-
return dragImage;
|
|
1265
|
-
},
|
|
1266
|
-
//
|
|
1267
|
-
destroyDragImage: function () {
|
|
1268
|
-
if (this.touchDragImage && this.touchDragImage.parentElement)
|
|
1269
|
-
this.touchDragImage.parentElement.removeChild(this.touchDragImage);
|
|
1270
|
-
this.touchDragImageOffset = null;
|
|
1271
|
-
this.touchDragImage = null;
|
|
1272
|
-
},
|
|
1273
|
-
//
|
|
1274
|
-
copyStyle: function (src, dst) {
|
|
1275
|
-
// remove potentially troublesome attributes
|
|
1276
|
-
var badAttributes = ['id', 'class', 'style', 'draggable'];
|
|
1277
|
-
badAttributes.forEach(function (att) {
|
|
1278
|
-
dst.removeAttribute(att);
|
|
1279
|
-
});
|
|
1280
|
-
// copy canvas content
|
|
1281
|
-
if (src instanceof HTMLCanvasElement) {
|
|
1282
|
-
var cSrc = src, cDst = dst;
|
|
1283
|
-
cDst.width = cSrc.width;
|
|
1284
|
-
cDst.height = cSrc.height;
|
|
1285
|
-
cDst.getContext('2d').drawImage(cSrc, 0, 0);
|
|
1286
|
-
}
|
|
1287
|
-
// copy style (without transitions)
|
|
1288
|
-
var cs = getComputedStyle(src);
|
|
1289
|
-
for (var i = 0; i < cs.length; i++) {
|
|
1290
|
-
var key = cs[i];
|
|
1291
|
-
if (key.indexOf('transition') < 0) {
|
|
1292
|
-
dst.style[key] = cs[key];
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
dst.style.pointerEvents = 'none';
|
|
1296
|
-
// and repeat for all children
|
|
1297
|
-
for (var i = 0; i < src.children.length; i++) {
|
|
1298
|
-
this.copyStyle(src.children[i], dst.children[i]);
|
|
1299
|
-
}
|
|
1300
|
-
},
|
|
1301
|
-
//
|
|
1302
|
-
getTouchPoint: function (event) {
|
|
1303
|
-
if (event && event.touches) {
|
|
1304
|
-
event = event.touches[0];
|
|
1305
|
-
}
|
|
1306
|
-
return {
|
|
1307
|
-
x: event.clientX,
|
|
1308
|
-
y: event.clientY
|
|
1309
|
-
};
|
|
1310
|
-
},
|
|
1311
|
-
//
|
|
1312
|
-
moveDragImage: function (event, image) {
|
|
1313
|
-
if (!event || !image)
|
|
1314
|
-
return;
|
|
1315
|
-
var orgChartMaster = this;
|
|
1316
|
-
requestAnimationFrame(function () {
|
|
1317
|
-
var pt = orgChartMaster.getTouchPoint(event);
|
|
1318
|
-
var s = image.style;
|
|
1319
|
-
s.position = 'absolute';
|
|
1320
|
-
s.pointerEvents = 'none';
|
|
1321
|
-
s.zIndex = '999999';
|
|
1322
|
-
if (orgChartMaster.touchDragImageOffset) {
|
|
1323
|
-
s.left = Math.round(pt.x - orgChartMaster.touchDragImageOffset.x) + 'px';
|
|
1324
|
-
s.top = Math.round(pt.y - orgChartMaster.touchDragImageOffset.y) + 'px';
|
|
1325
|
-
}
|
|
1326
|
-
});
|
|
1327
|
-
},
|
|
1328
|
-
//
|
|
1329
|
-
bindDragDrop: function ($node) {
|
|
1330
|
-
$node.on('dragstart', this.dragstartHandler.bind(this))
|
|
1331
|
-
.on('dragover', this.dragoverHandler.bind(this))
|
|
1332
|
-
.on('dragend', this.dragendHandler.bind(this))
|
|
1333
|
-
.on('drop', this.dropHandler.bind(this))
|
|
1334
|
-
.on('touchstart', this.touchstartHandler.bind(this))
|
|
1335
|
-
.on('touchmove', this.touchmoveHandler.bind(this))
|
|
1336
|
-
.on('touchend', this.touchendHandler.bind(this));
|
|
1337
|
-
},
|
|
1338
|
-
// create node
|
|
1339
|
-
createNode: function (data) {
|
|
1340
|
-
var that = this;
|
|
1341
|
-
var opts = this.options;
|
|
1342
|
-
var level = data.level;
|
|
1343
|
-
if (data.children && data[opts.nodeId]) {
|
|
1344
|
-
$.each(data.children, function (index, child) {
|
|
1345
|
-
child.parentId = data[opts.nodeId]
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
|
-
// construct the content of node
|
|
1349
|
-
var $nodeDiv = $('<div' + (opts.draggable ? ' draggable="true"' : '') + (data[opts.nodeId] ? ' id="' + data[opts.nodeId] + '"' : '') + (data.parentId ? ' data-parent="' + data.parentId + '"' : '') + '>')
|
|
1350
|
-
.addClass('node ' + (data.className || '') + (level > opts.visibleLevel ? ' slide-up' : ''));
|
|
1351
|
-
if (opts.nodeTemplate) {
|
|
1352
|
-
$nodeDiv.append(opts.nodeTemplate(data));
|
|
1353
|
-
} else {
|
|
1354
|
-
$nodeDiv.append('<div class="title">' + data[opts.nodeTitle] + '</div>')
|
|
1355
|
-
.append(typeof opts.nodeContent !== 'undefined' ? '<div class="content">' + (data[opts.nodeContent] || '') + '</div>' : '');
|
|
1356
|
-
}
|
|
1357
|
-
//
|
|
1358
|
-
var nodeData = $.extend({}, data);
|
|
1359
|
-
delete nodeData.children;
|
|
1360
|
-
$nodeDiv.data('nodeData', nodeData);
|
|
1361
|
-
// append 4 direction arrows or expand/collapse buttons or reset buttons
|
|
1362
|
-
var flags = data.relationship || '';
|
|
1363
|
-
if ((opts.verticalLevel && level >= opts.verticalLevel) || data.vertical) {
|
|
1364
|
-
if (Number(flags.substr(2,1))) {
|
|
1365
|
-
$nodeDiv.append(`<i class="toggleBtn ${opts.icons.theme}"></i>`)
|
|
1366
|
-
.children('.title').prepend(`<i class="${opts.icons.theme} ${opts.icons.parentNode} parentNodeSymbol"></i>`);
|
|
1367
|
-
}
|
|
1368
|
-
} else if (data.hybrid) {
|
|
1369
|
-
if (Number(flags.substr(2,1))) {
|
|
1370
|
-
$nodeDiv.append(`<i class="edge verticalEdge bottomEdge ${opts.icons.theme}"></i>`)
|
|
1371
|
-
.children('.title').prepend(`<i class="${opts.icons.theme} ${opts.icons.parentNode} parentNodeSymbol"></i>`);
|
|
1372
|
-
}
|
|
1373
|
-
} else if (data.compact) {
|
|
1374
|
-
$nodeDiv.css('grid-template-columns', `repeat(${Math.floor(Math.sqrt(data.children.length + 1))}, auto)`);
|
|
1375
|
-
if (Number(flags.substr(2,1))) {
|
|
1376
|
-
$nodeDiv.append(`
|
|
1377
|
-
<i class="${opts.icons.theme} ${opts.icons.backToCompact} backToCompactSymbol hidden"></i>
|
|
1378
|
-
<i class="${opts.icons.theme} ${opts.icons.backToLoose} backToLooseSymbol"></i>
|
|
1379
|
-
`)
|
|
1380
|
-
.children('.title').prepend(`<i class="${opts.icons.theme} ${opts.icons.parentNode} parentNodeSymbol"></i>`);
|
|
1381
|
-
}
|
|
1382
|
-
} else if (data.associatedCompact) {
|
|
1383
|
-
|
|
1384
|
-
} else {
|
|
1385
|
-
if (Number(flags.substr(0,1))) {
|
|
1386
|
-
$nodeDiv.append(`<i class="edge verticalEdge topEdge ${opts.icons.theme}"></i>`);
|
|
1387
|
-
}
|
|
1388
|
-
if(Number(flags.substr(1,1))) {
|
|
1389
|
-
$nodeDiv.append(`<i class="edge horizontalEdge rightEdge ${opts.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${opts.icons.theme}"></i>`);
|
|
1390
|
-
}
|
|
1391
|
-
if(Number(flags.substr(2,1))) {
|
|
1392
|
-
$nodeDiv.append(`<i class="edge verticalEdge bottomEdge ${opts.icons.theme}"></i>`)
|
|
1393
|
-
.children('.title').prepend(`<i class="${opts.icons.theme} ${opts.icons.parentNode} parentNodeSymbol"></i>`);
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
$nodeDiv.on('mouseenter mouseleave', this.nodeEnterLeaveHandler.bind(this));
|
|
1398
|
-
$nodeDiv.on('click', this.nodeClickHandler.bind(this));
|
|
1399
|
-
$nodeDiv.on('click', '.topEdge', this.topEdgeClickHandler.bind(this));
|
|
1400
|
-
$nodeDiv.on('click', '.bottomEdge', this.bottomEdgeClickHandler.bind(this));
|
|
1401
|
-
$nodeDiv.on('click', '.leftEdge, .rightEdge', this.hEdgeClickHandler.bind(this));
|
|
1402
|
-
$nodeDiv.on('click', '.toggleBtn', this.toggleVNodes.bind(this));
|
|
1403
|
-
$nodeDiv.on('click', '> .backToCompactSymbol',this.backToCompactHandler.bind(this));
|
|
1404
|
-
$nodeDiv.on('click', '> .backToLooseSymbol',this.backToLooseHandler.bind(this));
|
|
1405
|
-
|
|
1406
|
-
if (opts.draggable) {
|
|
1407
|
-
this.bindDragDrop($nodeDiv);
|
|
1408
|
-
this.touchHandled = false;
|
|
1409
|
-
this.touchMoved = false;
|
|
1410
|
-
this.touchTargetNode = null;
|
|
1411
|
-
}
|
|
1412
|
-
// allow user to append dom modification after finishing node create of orgchart
|
|
1413
|
-
if (opts.createNode) {
|
|
1414
|
-
opts.createNode($nodeDiv, data);
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
return $nodeDiv;
|
|
1418
|
-
},
|
|
1419
|
-
// recursively build the tree
|
|
1420
|
-
buildHierarchy: function ($appendTo, data) {
|
|
1421
|
-
var that = this;
|
|
1422
|
-
var opts = this.options;
|
|
1423
|
-
var level = 0;
|
|
1424
|
-
var $nodeDiv;
|
|
1425
|
-
if (data.level) {
|
|
1426
|
-
level = data.level;
|
|
1427
|
-
} else {
|
|
1428
|
-
level = data.level = $appendTo.parentsUntil('.orgchart', '.nodes').length;
|
|
1429
|
-
}
|
|
1430
|
-
// Construct the node
|
|
1431
|
-
if (Object.keys(data).length > 2) {
|
|
1432
|
-
$nodeDiv = this.createNode(data);
|
|
1433
|
-
$appendTo.append($nodeDiv);
|
|
1434
|
-
}
|
|
1435
|
-
// Construct the "inferior nodes"
|
|
1436
|
-
if (data.children && data.children.length) {
|
|
1437
|
-
var isHidden = level + 1 > opts.visibleLevel || (data.collapsed !== undefined && data.collapsed);
|
|
1438
|
-
var $nodesLayer;
|
|
1439
|
-
if ((opts.verticalLevel && (level + 1) >= opts.verticalLevel) || data.hybrid) {
|
|
1440
|
-
$nodesLayer = $('<ul class="nodes">');
|
|
1441
|
-
if (isHidden && (opts.verticalLevel && (level + 1) >= opts.verticalLevel)) {
|
|
1442
|
-
$nodesLayer.addClass('hidden');
|
|
1443
|
-
}
|
|
1444
|
-
if (((opts.verticalLevel && level + 1 === opts.verticalLevel) || data.hybrid)
|
|
1445
|
-
&& !$appendTo.closest('.vertical').length) {
|
|
1446
|
-
$appendTo.append($nodesLayer.addClass('vertical'));
|
|
1447
|
-
} else {
|
|
1448
|
-
$appendTo.append($nodesLayer);
|
|
1449
|
-
}
|
|
1450
|
-
} else if (data.compact) {
|
|
1451
|
-
$nodeDiv.addClass('compact');
|
|
1452
|
-
} else {
|
|
1453
|
-
$nodesLayer = $('<ul class="nodes' + (isHidden ? ' hidden' : '') + '">');
|
|
1454
|
-
if (Object.keys(data).length === 2) {
|
|
1455
|
-
$appendTo.append($nodesLayer);
|
|
1456
|
-
} else {
|
|
1457
|
-
if (isHidden) {
|
|
1458
|
-
$appendTo.addClass('isChildrenCollapsed');
|
|
1459
|
-
}
|
|
1460
|
-
$appendTo.append($nodesLayer);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
// recurse through children nodes
|
|
1464
|
-
$.each(data.children, function () {
|
|
1465
|
-
this.level = level + 1;
|
|
1466
|
-
if (data.compact) {
|
|
1467
|
-
that.buildHierarchy($nodeDiv, this);
|
|
1468
|
-
} else {
|
|
1469
|
-
var $nodeCell = $('<li class="hierarchy">');
|
|
1470
|
-
$nodesLayer.append($nodeCell);
|
|
1471
|
-
that.buildHierarchy($nodeCell, this);
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
},
|
|
1476
|
-
// build the child nodes of specific node
|
|
1477
|
-
buildChildNode: function ($appendTo, data) {
|
|
1478
|
-
this.buildHierarchy($appendTo, { 'children': data });
|
|
1479
|
-
},
|
|
1480
|
-
// exposed method
|
|
1481
|
-
addChildren: function ($node, data) {
|
|
1482
|
-
this.buildChildNode($node.closest('.hierarchy'), data);
|
|
1483
|
-
if (!$node.find('.parentNodeSymbol').length) {
|
|
1484
|
-
$node.children('.title').prepend(`<i class="${this.options.icons.theme} ${this.options.icons.parentNode} parentNodeSymbol"></i>`);
|
|
1485
|
-
}
|
|
1486
|
-
if ($node.closest('.nodes.vertical').length) {
|
|
1487
|
-
if (!$node.children('.toggleBtn').length) {
|
|
1488
|
-
$node.append(`<i class="toggleBtn ${this.options.icons.theme}"></i>`);
|
|
1489
|
-
}
|
|
1490
|
-
} else {
|
|
1491
|
-
if (!$node.children('.bottomEdge').length) {
|
|
1492
|
-
$node.append(`<i class="edge verticalEdge bottomEdge ${this.options.icons.theme}"></i>`);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
if (this.isInAction($node)) {
|
|
1496
|
-
this.switchVerticalArrow($node.children('.bottomEdge'));
|
|
1497
|
-
}
|
|
1498
|
-
},
|
|
1499
|
-
// build the parent node of specific node
|
|
1500
|
-
buildParentNode: function ($currentRoot, data) {
|
|
1501
|
-
data.relationship = data.relationship || '001';
|
|
1502
|
-
var $newRootWrapper = $('<ul class="nodes"><li class="hierarchy"></li></ul>')
|
|
1503
|
-
.find('.hierarchy').append(this.createNode(data)).end();
|
|
1504
|
-
this.$chart.prepend($newRootWrapper)
|
|
1505
|
-
.find('.hierarchy:first').append($currentRoot.closest('ul').addClass('nodes'));
|
|
1506
|
-
},
|
|
1507
|
-
// exposed method
|
|
1508
|
-
addParent: function ($currentRoot, data) {
|
|
1509
|
-
this.buildParentNode($currentRoot, data);
|
|
1510
|
-
if (!$currentRoot.children('.topEdge').length) {
|
|
1511
|
-
$currentRoot.children('.title').after(`<i class="edge verticalEdge topEdge ${this.options.icons.theme}"></i>`);
|
|
1512
|
-
}
|
|
1513
|
-
if (this.isInAction($currentRoot)) {
|
|
1514
|
-
this.switchVerticalArrow($currentRoot.children('.topEdge'));
|
|
1515
|
-
}
|
|
1516
|
-
},
|
|
1517
|
-
// build the sibling nodes of specific node
|
|
1518
|
-
buildSiblingNode: function ($nodeChart, data) {
|
|
1519
|
-
var newSiblingCount = $.isArray(data) ? data.length : data.children.length;
|
|
1520
|
-
var existingSibligCount = $nodeChart.parent().is('.nodes') ? $nodeChart.siblings().length + 1 : 1;
|
|
1521
|
-
var siblingCount = existingSibligCount + newSiblingCount;
|
|
1522
|
-
var insertPostion = (siblingCount > 1) ? Math.floor(siblingCount/2 - 1) : 0;
|
|
1523
|
-
// just build the sibling nodes for the specific node
|
|
1524
|
-
if ($nodeChart.closest('.nodes').parent().is('.hierarchy')) {
|
|
1525
|
-
this.buildChildNode($nodeChart.parent().closest('.hierarchy'), data);
|
|
1526
|
-
var $siblings = $nodeChart.parent().closest('.hierarchy').children('.nodes:last').children('.hierarchy');
|
|
1527
|
-
if (existingSibligCount > 1) {
|
|
1528
|
-
$siblings.eq(0).before($nodeChart.siblings().addBack().unwrap());
|
|
1529
|
-
} else {
|
|
1530
|
-
$siblings.eq(insertPostion).after($nodeChart.unwrap());
|
|
1531
|
-
}
|
|
1532
|
-
} else { // build the sibling nodes and parent node for the specific ndoe
|
|
1533
|
-
this.buildHierarchy($nodeChart.parent().prepend($('<li class="hierarchy">')).children('.hierarchy:first'), data);
|
|
1534
|
-
$nodeChart.prevAll('.hierarchy').children('.nodes').children().eq(insertPostion).after($nodeChart);
|
|
1535
|
-
}
|
|
1536
|
-
},
|
|
1537
|
-
//
|
|
1538
|
-
addSiblings: function ($node, data) {
|
|
1539
|
-
this.buildSiblingNode($node.closest('.hierarchy'), data);
|
|
1540
|
-
$node.closest('.nodes').data('siblingsLoaded', true);
|
|
1541
|
-
if (!$node.children('.leftEdge').length) {
|
|
1542
|
-
$node.children('.topEdge').after(`<i class="edge horizontalEdge rightEdge ${this.options.icons.theme}"></i><i class="edge horizontalEdge leftEdge ${this.options.icons.theme}"></i>`);
|
|
1543
|
-
}
|
|
1544
|
-
if (this.isInAction($node)) {
|
|
1545
|
-
this.switchHorizontalArrow($node);
|
|
1546
|
-
$node.children('.topEdge').removeClass(this.options.icons.expandToUp).addClass(this.options.icons.collapseToDown);
|
|
1547
|
-
}
|
|
1548
|
-
},
|
|
1549
|
-
// remove node and its descendent nodes
|
|
1550
|
-
removeNodes: function ($node) {
|
|
1551
|
-
var $wrapper = $node.closest('.hierarchy').parent();
|
|
1552
|
-
if ($wrapper.parent().is('.hierarchy')) {
|
|
1553
|
-
if (this.getNodeState($node, 'siblings').exist) {
|
|
1554
|
-
$node.closest('.hierarchy').remove();
|
|
1555
|
-
if ($wrapper.children().length === 1) {
|
|
1556
|
-
$wrapper.find('.node:first .horizontalEdge').remove();
|
|
1557
|
-
}
|
|
1558
|
-
} else {
|
|
1559
|
-
$wrapper.siblings('.node').find('.bottomEdge').remove()
|
|
1560
|
-
.end().end().remove();
|
|
1561
|
-
}
|
|
1562
|
-
} else { // if $node is root node
|
|
1563
|
-
$wrapper.closest('.orgchart').remove();
|
|
1564
|
-
}
|
|
1565
|
-
},
|
|
1566
|
-
//
|
|
1567
|
-
hideDropZones: function () {
|
|
1568
|
-
// Remove all the 'this is a drop zone' indicators
|
|
1569
|
-
var orgChartObj = this;
|
|
1570
|
-
orgChartObj.$chart.find('.allowedDrop')
|
|
1571
|
-
.removeClass('allowedDrop');
|
|
1572
|
-
},
|
|
1573
|
-
//
|
|
1574
|
-
showDropZones: function (dragged) {
|
|
1575
|
-
// Highlight all the 'drop zones', and set dragged, so that the drop/enter can work out what happens later
|
|
1576
|
-
// TODO: This assumes all nodes are droppable: it doesn't run the custom isDroppable function - it should!
|
|
1577
|
-
var orgChartObj = this;
|
|
1578
|
-
orgChartObj.$chart.find('.node')
|
|
1579
|
-
.each(function (index, node) {
|
|
1580
|
-
$(node).addClass('allowedDrop');
|
|
1581
|
-
});
|
|
1582
|
-
orgChartObj.$chart.data('dragged', $(dragged));
|
|
1583
|
-
},
|
|
1584
|
-
//
|
|
1585
|
-
processExternalDrop: function (dropZone, dragged) {
|
|
1586
|
-
// Allow an external drop event to be handled by one of our nodes
|
|
1587
|
-
if (dragged) {
|
|
1588
|
-
this.$chart.data('dragged', $(dragged));
|
|
1589
|
-
}
|
|
1590
|
-
var droppedOnNode = dropZone.closest('.node');
|
|
1591
|
-
// would like to just call 'dropZoneHandler', but I can't reach it from here
|
|
1592
|
-
// instead raise a drop event on the node element
|
|
1593
|
-
droppedOnNode.triggerHandler({ 'type': 'drop' });
|
|
1594
|
-
},
|
|
1595
|
-
//
|
|
1596
|
-
exportPDF: function(canvas, exportFilename){
|
|
1597
|
-
var doc = {};
|
|
1598
|
-
var docWidth = Math.floor(canvas.width);
|
|
1599
|
-
var docHeight = Math.floor(canvas.height);
|
|
1600
|
-
if (!window.jsPDF) {
|
|
1601
|
-
window.jsPDF = window.jspdf.jsPDF;
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
if (docWidth > docHeight) {
|
|
1605
|
-
doc = new jsPDF({
|
|
1606
|
-
orientation: 'landscape',
|
|
1607
|
-
unit: 'px',
|
|
1608
|
-
format: [docWidth, docHeight]
|
|
1609
|
-
});
|
|
1610
|
-
} else {
|
|
1611
|
-
doc = new jsPDF({
|
|
1612
|
-
orientation: 'portrait',
|
|
1613
|
-
unit: 'px',
|
|
1614
|
-
format: [docHeight, docWidth]
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
doc.addImage(canvas.toDataURL(), 'png', 0, 0);
|
|
1618
|
-
doc.save(exportFilename + '.pdf');
|
|
1619
|
-
},
|
|
1620
|
-
//
|
|
1621
|
-
exportPNG: function(canvas, exportFilename){
|
|
1622
|
-
var that = this;
|
|
1623
|
-
var isWebkit = 'WebkitAppearance' in document.documentElement.style;
|
|
1624
|
-
var isFf = !!window.sidebar;
|
|
1625
|
-
var isEdge = navigator.appName === 'Microsoft Internet Explorer' || (navigator.appName === "Netscape" && navigator.appVersion.indexOf('Edge') > -1);
|
|
1626
|
-
var $chartContainer = this.$chartContainer;
|
|
1627
|
-
|
|
1628
|
-
if ((!isWebkit && !isFf) || isEdge) {
|
|
1629
|
-
window.navigator.msSaveBlob(canvas.msToBlob(), exportFilename + '.png');
|
|
1630
|
-
} else {
|
|
1631
|
-
var selector = '.download-btn' + (that.options.chartClass !== '' ? '.' + that.options.chartClass : '');
|
|
1632
|
-
|
|
1633
|
-
if (!$chartContainer.find(selector).length) {
|
|
1634
|
-
$chartContainer.append('<a class="download-btn' + (that.options.chartClass !== '' ? ' ' + that.options.chartClass : '') + '"'
|
|
1635
|
-
+ ' download="' + exportFilename + '.png"></a>');
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
$chartContainer.find(selector).attr('href', canvas.toDataURL())[0].click();
|
|
1639
|
-
}
|
|
1640
|
-
},
|
|
1641
|
-
//
|
|
1642
|
-
export: function (exportFilename, exportFileextension) {
|
|
1643
|
-
var that = this;
|
|
1644
|
-
exportFilename = (typeof exportFilename !== 'undefined') ? exportFilename : this.options.exportFilename;
|
|
1645
|
-
exportFileextension = (typeof exportFileextension !== 'undefined') ? exportFileextension : this.options.exportFileextension;
|
|
1646
|
-
if ($(this).children('.spinner').length) {
|
|
1647
|
-
return false;
|
|
1648
|
-
}
|
|
1649
|
-
var $chartContainer = this.$chartContainer;
|
|
1650
|
-
var $mask = $chartContainer.find('.mask');
|
|
1651
|
-
if (!$mask.length) {
|
|
1652
|
-
$chartContainer.append(`<div class="mask"><i class="${this.options.icons.theme} ${this.options.icons.spinner} spinner"></i></div>`);
|
|
1653
|
-
} else {
|
|
1654
|
-
$mask.removeClass('hidden');
|
|
1655
|
-
}
|
|
1656
|
-
var sourceChart = $chartContainer.addClass('canvasContainer').find('.orgchart:not(".hidden")').get(0);
|
|
1657
|
-
var flag = that.options.direction === 'l2r' || that.options.direction === 'r2l';
|
|
1658
|
-
html2canvas(sourceChart, {
|
|
1659
|
-
'width': flag ? sourceChart.clientHeight : sourceChart.clientWidth,
|
|
1660
|
-
'height': flag ? sourceChart.clientWidth : sourceChart.clientHeight,
|
|
1661
|
-
'onclone': function (cloneDoc) {
|
|
1662
|
-
$(cloneDoc).find('.canvasContainer').css('overflow', 'visible')
|
|
1663
|
-
.find('.orgchart:not(".hidden"):first').css('transform', '');
|
|
1664
|
-
}
|
|
1665
|
-
})
|
|
1666
|
-
.then(function (canvas) {
|
|
1667
|
-
$chartContainer.find('.mask').addClass('hidden');
|
|
1668
|
-
|
|
1669
|
-
if (exportFileextension.toLowerCase() === 'pdf') {
|
|
1670
|
-
that.exportPDF(canvas, exportFilename);
|
|
1671
|
-
} else {
|
|
1672
|
-
that.exportPNG(canvas, exportFilename);
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
$chartContainer.removeClass('canvasContainer');
|
|
1676
|
-
}, function () {
|
|
1677
|
-
$chartContainer.removeClass('canvasContainer');
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
|
|
1682
|
-
$.fn.orgchart = function (opts) {
|
|
1683
|
-
return new OrgChart(this, opts).init();
|
|
1684
|
-
};
|
|
1685
|
-
|
|
1686
|
-
}));
|